这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@KevinRansom
Copy link
Contributor

First attempt at rationalizing F# and C# interactive test cases.

Simplify and refactor: CSharpKernelTests/FSharpKernelTests/CSharpKernelTestBase/FSharpKernelTestBase/KernelTestBase into

LanguageKernelTests/LanguageKernelTestBase

Here is an example test case with C# and F# source variants:
https://github.com/dotnet/try/compare/master...KevinRansom:kr?expand=1#diff-d8de318123580f112042b15d5a087f4dR55

I really wish this was in F# the amount of ceremony in C# is unbelievable
In fact I may port this whole thing to F# just to show you.

        [Theory]
        [InlineData(Language.FSharp)]
        [InlineData(Language.CSharp)]
        public async Task it_remembers_state_between_submissions(Language language)
        {
            string[] source = null;
            switch (language)
            {
                case Language.FSharp:
                    string[] fsLines =
                    {
                        "let add x y = x + y",
                        "add 2 3"
                    };
                    source = fsLines;
                    break;
                case Language.CSharp:
                    string[] csLines =
                    {
                        "int Multiply(int x, int y) { return x + y; };",
                        "Multiply(2, 3);"
                    };
                    source = csLines;
                    break;
            }

            var kernel = CreateKernel(language);

            await SubmitLines(kernel, source);

            KernelEvents.ValuesOnly()
                .OfType<ReturnValueProduced>()
                .Last()
                .Value
                .Should()
                .Be(5);
        }

}

public async Task SubmitLines(KernelBase kernel, string[] lines)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is a bit misleading since multi-line submissions are very common.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like string[] codeSubmissions or codeFragments?

Copy link
Contributor

@jonsequitur jonsequitur Sep 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think submissions is fine.

@jonsequitur
Copy link
Contributor

Generally, agreed, though a switch expression would make this example a little more concise:

        [Theory]
        [InlineData(Language.FSharp)]
        [InlineData(Language.CSharp)]
        public async Task it_remembers_state_between_submissions(Language language)
        {
            var source = language switch
            {
                Language.FSharp => new[]
                {
                    "let add x y = x + y",
                    "add 2 3"
                },
                Language.CSharp => new[]
                {
                    "int Multiply(int x, int y) { return x + y; };",
                    "Multiply(2, 3);"
                }
            };

            var kernel = CreateKernel(language);

            await SubmitLines(kernel, source);

            KernelEvents.ValuesOnly()
                        .OfType<ReturnValueProduced>()
                        .Last()
                        .Value
                        .Should()
                        .Be(5);
        }

@jonsequitur
Copy link
Contributor

It would be helpful for reviewers to spot possible errors if you do the renames in a prior commit to the actual changes.

return kernel;
}

protected KernelBase CreateKernel()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd feel better if this function was removed. Tests that call this are really C#-specific tests so I'd like them to explicitly call CreateKernel(CSharp) or group them in a new class with IKernel CreateKernel() => base.CreateKernel(CSharp); just so it's explicit..

return CreateKernel(Language.CSharp);
}

public async Task SubmitLines(KernelBase kernel, string[] lines)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params string[] lines might be easier to consume, but ultimately I don't care too much.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It propose using submission or even array of string array

@brettfo
Copy link
Member

brettfo commented Sep 26, 2019

General request: To make the test cases easier to parse, could the switch statements/expressions be replaced with this?

// I'm not married to the name of this function, but I think the use of `T` will make it easy to in the future return more than just strings.
public T GetElementForLanguage<T>(Language language, params (Language lang, T value)[] values)
{
    // The use of `Single()` ensures that the test will fail if a value was not given for a specific language.
    return values.Single(val => val.lang == language).value;
}

That way the call sites will be a little more concise:

var code = GetElementForLanguage(language, (Language.CSharp, "using System;"),
                                           (Language.FSharp, "open System"));

@KevinRansom
Copy link
Contributor Author

Generally, agreed, though a switch expression would make this example a little more concise:

@jonsequitur, I tried it, unfortunately an array initializer is not an expression that can be returned from a switch expression.

@KevinRansom
Copy link
Contributor Author

Generally, agreed, though a switch expression would make this example a little more concise:

@jonsequitur, I tried it, unfortunately an array initializer is not an expression that can be returned from a switch expression.

Oh the trick is to use new[]. Nice.

@KevinRansom
Copy link
Contributor Author

I have added 4 different ways of doing a test based on the feedback, and other ideas. let me know what you like best.

[Theory]
[InlineData(Language.FSharp)]
[InlineData(Language.CSharp)]
public async Task it_remembers_state_between_submissions_option3(Language language)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to cast my vote for this style of test case because:

  1. The code to select different values is short and easy to read. I find the switch expression difficult because it's so verbose and requires a default case that must explicitly throw, while the helper method here handles that for you.
  2. Placing code in [InlineData] works for compile-time constants, but won't be usable for expressions. We don't have a test that does this currently, but I can easily see a test that needs to call GetElementForLanguage() where the item returned is a Type or Exception or something that can't be placed in an attribute.
  3. Finally, I like that the code variants are declared exactly where and when they're used instead of at the top of the function where I have to trace a variable through.

string[] source = null;
switch (language)
{
case Language.FSharp:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be easier to provide a list of submissions instead of assuming one stein one submission.

return CreateKernel(Language.CSharp);
}

public async Task SubmitLines(KernelBase kernel, string[] lines)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It propose using submission or even array of string array

}

public async Task SubmitLines(KernelBase kernel, string[] lines)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like string[] codeSubmissions or codeFragments?

@KevinRansom
Copy link
Contributor Author

@colombod,

SubmitSource is an overloaded method, so you can pass in a line or an array of lines. If we determine that we need an array of arrays then we can always add that overload when the need presents itself.

Kevin


KernelEvents
.ValuesOnly()
.Single(e => e is IncompleteCodeSubmissionReceived);
Copy link
Contributor

@jonsequitur jonsequitur Oct 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.Should().ContainSingle()

var source = language switch
{
Language.FSharp => "Console.WriteLine(\"Hello\")",
Language.CSharp => "Console.WriteLine(\"Hello\")"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case the test will pass for the wrong reason because this is not a complete C# statement unless you add a semicolon to the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonsequitur
Console.WriteLine("Hello, World") is a complete C# expression, if it was incomplete this would fail:

            KernelEvents
                .Should()
                .Contain(e => e.Value is CompleteCodeSubmissionReceived);

This is the notebook with a C# kernel note no ';'
image

json
"},
Language.CSharp => new[] {
$"#r @\"{dllPath}\"",
Copy link
Contributor

@jonsequitur jonsequitur Oct 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$"#r @"{dllPath}"" [](start = 0, length = 20)

I believe this works if you remove the @. The issue is not the quoted path but rather than the command line parser expects this to be a response file. That can be disabled in KernelBase.GetDirectiveParser:

= new CommandLineBuilder(root)
                                   .ParseResponseFileAs(ResponseFileHandling.Disabled)
                                   .UseMiddleware(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh … nice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants