Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to VSTest in CI #39923

Closed

Conversation

ViktorHofer
Copy link
Member

@ViktorHofer ViktorHofer commented Jul 25, 2020

Fixes #37953
Fixes #35752
Unblocks #960

Also moving code that isn't specific to xunit out of the xunit.props and xunit.targets files. I considered grouping not just by the test framework (xunit, nunit, mstest, etc.) but also by the runner (vstest vs custom). I didn't do that as I hope that we will be able to use VSTest for mobile targets at some point as well.

Additionally I'm enabling test hang dump support.

eng/testing/.runsettings Show resolved Hide resolved
eng/testing/mobile/outerBuild.targets Outdated Show resolved Hide resolved
eng/testing/RunnerTemplate.sh Show resolved Hide resolved
@ghost
Copy link

ghost commented Jul 25, 2020

Tagging subscribers to this area: @safern, @ViktorHofer
See info in area-owners.md if you want to be subscribed.

@danmoseley
Copy link
Member

Are any markdown edits necessary? eg in https://github.com/dotnet/runtime/blob/master/docs/workflow/testing/libraries/** ? I thnk use of xunit there is just for the library.

@danmoseley
Copy link
Member

Will this also fix #1664?

@ViktorHofer
Copy link
Member Author

Yes this will fix the issue linked above but only for libraries. CoreClr is an entirely different beast and can't speak to that.

Yeah, I'll update the markdown files but need to figure the failures out first.

@ViktorHofer ViktorHofer requested a review from a team July 26, 2020 17:44
@ViktorHofer ViktorHofer removed request for a team and marek-safar July 27, 2020 11:39
@ViktorHofer
Copy link
Member Author

ViktorHofer commented Jul 27, 2020

I believe failures in the net48 leg are due to the testhost being compiled against an older .NETFramework configuration and compat switches not being respected.

The .NETCoreApp failures looks like a missing TraceEvent subscription.

@safern do you have some time to help me with this? I believe we need to invest some time to triage the issues and speak with the VSTest team (cc @nohwnd) to see what we can do about the failures.

@@ -13,7 +13,7 @@

function BuildAndTestBinary
Copy link
Member

Choose a reason for hiding this comment

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

Who uses this script?

Copy link
Member Author

Choose a reason for hiding this comment

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

no idea. I don't think anyone. Maybe we should delete it...

Copy link
Member

Choose a reason for hiding this comment

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

Seems it was used to help stablize networking tests: dotnet/corefx#7937
@dotnet/ncl does your team use this script?

Copy link
Member Author

Choose a reason for hiding this comment

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

ping @dotnet/ncl

Copy link
Member Author

Choose a reason for hiding this comment

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

@ManickaP @scalablecory is the above handle not maintained by your team? Can you answer above's question about who uses this script?

Copy link
Member

Choose a reason for hiding this comment

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

Personally, I don't think so, it seems to have been used manually and maybe by some old pipeline.
@wfurt have you ever used this?

@antonfirsov
Copy link
Member

antonfirsov commented Jul 30, 2020

Might be an amateur question, but will it be possible to debug library tests with VSCode after this change?
Currently I'm using the following launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch-Debug",
            "type": "coreclr",
            "request": "launch",
            "program": "[....]/runtime/artifacts/bin/testhost/net5.0-OSX-Debug-x64/dotnet",
            "args": [
                "exec",
                "xunit.console.dll", 
                "System.Net.Sockets.Tests.dll",
                "-class",
                "System.Net.Sockets.Tests._Hello"
            ],
            "cwd": "[....]/runtime/artifacts/bin/System.Net.Sockets.Tests/net5.0-Unix-Debug",
            "console": "internalConsole",
            "stopAtEntry": false
        }
}

Couldn't figure out how to make it work with dotnet test.

@safern
Copy link
Member

safern commented Jul 30, 2020

I believe with dotnet test you need to set an environment variable for the dotnet test host to wait for you to attach. It would print the PID in the console.

VSTEST_HOST_DEBUG=1

However you can also use the VSCode test extension to debug a specific test or collection of tests.

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
Copy link
Member

Choose a reason for hiding this comment

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

It is confusing to have wasm under mobile. wasm != mobile. I would call the directory custom or something like that.

Copy link
Member

Choose a reason for hiding this comment

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

Or lightweight

Copy link
Member Author

@ViktorHofer ViktorHofer Oct 14, 2020

Choose a reason for hiding this comment

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

I wondered about that as well and @akoeplinger told me that wasm is declared as mobile:

<TargetsMobile Condition="'$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'Android' or '$(TargetOS)' == 'tvOS' or '$(TargetOS)' == 'Browser'">true</TargetsMobile>

Copy link
Member

Choose a reason for hiding this comment

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

What is the definition of "mobile" in plain English that we are using in this context?

I see TargetsMobile used for multiple different meanings that happen to work today, but that are not robust against changes in the platforms matrix. Unless there is non-ambiguous definition of what this means, we should not be promoting use of this name throughout the tree.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can open an issue for this and bring that discussion there? Changing this on the current PR would make the PR diff way bigger than it is as we already use the TargetsMobile property in MSBuild across multiple places in the repo.

Copy link
Member

@akoeplinger akoeplinger Oct 14, 2020

Choose a reason for hiding this comment

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

The usage of the term "mobile" traces back a few years to mono/mono where we essentially had "desktop" and "mobile" for turning on/off certain runtime features and when wasm was brought up it was closer to the existing "mobile" config :)

In dotnet/runtime we essentially mean all the targets that are not using the traditional desktop dotnet hosting and instead embed the (mono) runtime in a special way. I agree it's not totally clear though, happy for suggestions.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can open an issue for this and bring that discussion there?

Fine with me. This PR is renaming a number of files to mobile. Should that rename be undone until we agree on the right naming? (Undoing the rename should make the PR smaller.)

@safern
Copy link
Member

safern commented Oct 14, 2020

@ViktorHofer what are we going to do regarding: #39923 (comment)

I just grepped locally and we have over 1200 usages of Console.WriteLine in our tests.. some to help diagnose failures on CI, some to test System.Console, etc... Should we add a note in the docs? I know people sometimes use this to print stuff when diagnosing tests.

@akoeplinger
Copy link
Member

Should we add a note in the docs? I know people sometimes use this to print stuff when diagnosing tests.

Yeah this is going to be confusing. In addition to docs we should also print a message about the option to enable console output in RunTests.sh/cmd to raise visibility.

@am11
Copy link
Member

am11 commented Oct 14, 2020

Just curious, I know that opposite is true; xunit.console.exe does not print Console.Write* to stdout by default unless we setup an overloaded ctor which accepts ITestOutputHelper like: public Mytest(output) => Console.SetOut(new Converter(output));. In dotnet/runtime repo we are using ITestOutputHelper in 200+ test classes. However, with dotnet test, my understanding is that it just works out of the box? Is there a special circumstance when Console.WriteLine with xunit and dotnet-test is not showing the output? With new test project created by dotnet new xunit and running with default verbosity (just dotnet test), following test outputs foo:

using System;
using Xunit;

public class UnitTest1
{
    [Fact]
    public void Test1()
    {
        Console.WriteLine("foo");
        Assert.True(true); // foo is also printed if it comes after the passing assert.
    }
}

@danmoseley
Copy link
Member

I have much the same perspective as @stephentoub. It seems like xunit poorly satisfies either need - the dev productivity and the platform bringup. As @stephentoub points out, it has fat dependencies. We can do better.

@jkotas I have not had the experience of bringing up a new platform (and would be interested to learn at some point about how it is typically done). Would an approach like StaticTestGenerator (that can run, say., 90%+ of the tests in a rather inflexible way, but with minimal run-time dependencies) be an improvement over xunit for you for that purpose?

Even if so, I think we want to continue to reduce dependencies of vstest. We do not want to find ourselves in a position where we have got a bug in the Http stack but we cannot run the tests to help root cause it. StaticTestGenerator would certainly not be acceptable for everyday dev work, even temporarily.

If we can make the super-minimal solution good enough (and protected in CI), and trim vstest some more, perhaps we would then collectively feel confident enough to delete the xunit.console paths.

BTW, speaking generally, this vstest initiative has been in progress for a long time -- it is core to our test diagnostics effort @ViktorHofer and myself have been running. I simply did not have sufficient imagination to realize that the runtime devs would have an interest in this or I would have brought more folks in. In future, I will be sure to consider that aspect.

@geoffkizer
Copy link
Contributor

StaticTestGenerator would certainly not be acceptable for everyday dev work, even temporarily.

Why not? I kind of like the StaticTestGenerator approach.

@jkotas
Copy link
Member

jkotas commented Oct 14, 2020

StaticTestGenerator would certainly not be acceptable for everyday dev work, even temporarily.

FWIW, we used closed source equivalent of the static test generator for everyday during .NET Native development. It worked quite nicely.

@danmoseley
Copy link
Member

StaticTestGenerator would certainly not be acceptable for everyday dev work, even temporarily.

Why not? I kind of like the StaticTestGenerator approach.

Hmm, fair to challenge this. If the cost of doing the generation step is amortized, and we make it easy to run on a stable runtime, and it is not too slow (includes parallelism?) -- and it doesn't skip too many tests (because they are too difficult to support?) -- and you can work without Test Explorer (?) -- perhaps it could be. I like simple things as they are easy to reason about. I have enjoyed using simple hackable runners in the past. I am happy to commit to working towards making it that easy, if we follow this plan. That reduces the vstest risk somewhat.

@jkotas
Copy link
Member

jkotas commented Oct 14, 2020

Would an approach like StaticTestGenerator (that can run, say., 90%+ of the tests in a rather inflexible way, but with minimal run-time dependencies) be an improvement over xunit for you for that purpose?

I do not see where the inflexibility comes in. I think it is not that hard to push the static runner to 100%, by either restricting what you can do in theories or by extending how the generator works.

Yes, I think it would be an improvement. I would enable us to work towards using the same system to run majority of the core runtime tests too (src/tests).

@danmoseley
Copy link
Member

@ericstj do you have any thoughts to add? Does this two-pronged two-phase approach seem reasonable?

@danmoseley
Copy link
Member

One could imagine writing it as a source-generator that ran during the unit test build to automatically generate runners for optional use. But I assume (I really do not know) that it would be a bunch of work to get a good VS experience -- it would not be a good use of time when we can use our own product for this that another team maintains for us.

@jkotas
Copy link
Member

jkotas commented Oct 14, 2020

and you can work without Test Explorer (?)

I think it is perfectly fine to have vstest and test explorer with all its bells and whistles as an option for people that it works well for.

@ericstj
Copy link
Member

ericstj commented Oct 15, 2020

@ericstj do you have any thoughts to add? Does this two-pronged two-phase approach seem reasonable?

Yes, this seems reasonable and I support it.

I think using VSTest is a marked improvement over the unsupported xunit.console and a step in the right direction. I'm also supportive of investing in the lighter-weight runner so long as we can make sure we don't give up features we gain by using VSTest, nor take on too much engineering burden ourselves for a non-shipping component. What does the VSTest team think about a source-generated approach? I'd be curious to understand if we could incubate something that could make its way back into the product. I don't see those concerns as blocking this PR, but in support of a "two-pronged" approach as you mention.

@jkotas
Copy link
Member

jkotas commented Oct 23, 2020

The heavy weight runner makes the tests run about 3x longer to run for me. Some data (Windows x64, build -c Release -rc checked):

Current master:

D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release>timer "D:\runtime\artifacts\bin\testhost\net6.0-Windows_NT-Release-x64\dotnet.exe" exec --runtimeconfig System.Threading.ThreadPool.Tests.runtimeconfig.json --depsfile System.Threading.ThreadPool.Tests.deps.json xunit.console.dll System.Threading.ThreadPool.Tests.dll -xml testResults.xml -nologo -notrait category=OuterLoop -notrait category=failing
D:\runtime\artifacts\bin\testhost\net6.0-Windows_NT-Release-x64\dotnet.exe exec --runtimeconfig System.Threading.ThreadPool.Tests.runtimeconfig.json --depsfile System.Threading.ThreadPool.Tests.deps.json xunit.console.dll System.Threading.ThreadPool.Tests.dll -xml testResults.xml -nologo -notrait category=OuterLoop -notrait category=failing
  Discovering: System.Threading.ThreadPool.Tests (method display = ClassAndMethod, method display options = None)
  Discovered:  System.Threading.ThreadPool.Tests (found 40 test cases)
  Starting:    System.Threading.ThreadPool.Tests (parallel test collections = on, max threads = 32)
  Finished:    System.Threading.ThreadPool.Tests
=== TEST EXECUTION SUMMARY ===
   System.Threading.ThreadPool.Tests  Total: 62, Errors: 0, Failed: 0, Skipped: 0, Time: 8.823s
Results of execution:

    Exit code 0
    Time of execution 15.703

This change:

D:\runtime\src\libraries\System.Threading.ThreadPool\tests>timer dotnet test -c Release
dotnet test -c Release
  Determining projects to restore...
  All projects are up-to-date for restore.
  System.Security.Principal.Windows -> D:\runtime\artifacts\bin\System.Security.Principal.Windows\ref\netstandard2.0-Release\System.Security.Principal.Windows.dll
...
  System.Threading.ThreadPool.Tests -> D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release\System.Threading.ThreadPool.Tests.dll
Test run for D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release\System.Threading.ThreadPool.Tests.dll (.NETCoreApp,Version=v6.0)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.
Results File: D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release\TestResults\jkotas_JKOTAS9_2020-10-23_08_31_21.trx
Html test results file : D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release\TestResults\TestResult_jkotas_JKOTAS9_20201023_083152.html

Passed!  - Failed:     0, Passed:    41, Skipped:     0, Total:    41, Duration: 31 s - System.Threading.ThreadPool.Tests.dll (net6.0)
Results of execution:

    Exit code 0
    Time of execution 62.734

I am not sure what's the right way to run just the tests without the msbuild overhead with this change. There are no easy to follow steps printed like with the current runner. It is safe to say that it takes several times longer.

EDIT: The data above are not accurate. I have run different version of the tests. The question still remains.

I am hearing that we are hitting the capacity limits and being asked to reduce how much capacity we need. Do we understand the impact of the heavy weight runner on the capacity required by the test runs?

@ericstj
Copy link
Member

ericstj commented Oct 23, 2020

@jkotas, here's what worked for me: dotnet test --no-build

I found this by running dotnet test -?

Old runner:

C:\src\dotnet\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Debug>\src\dotnet\runtime\artifacts\bin\testhost\net6.0-Windows_NT-Debug-x64\dotnet.exe exec --runtimeconfig System.Threading.ThreadPool.Tests.runtimeconfig.json --depsfile System.Threading.ThreadPool.Tests.deps.json xunit.console.dll System.Threading.ThreadPool.Tests.dll -xml testResults.xml -nologo -notrait category=OuterLoop -notrait category=failing
  Discovering: System.Threading.ThreadPool.Tests (method display = ClassAndMethod, method display options = None)
  Discovered:  System.Threading.ThreadPool.Tests (found 19 test cases)
  Starting:    System.Threading.ThreadPool.Tests (parallel test collections = on, max threads = 8)
  Finished:    System.Threading.ThreadPool.Tests
=== TEST EXECUTION SUMMARY ===
   System.Threading.ThreadPool.Tests  Total: 41, Errors: 0, Failed: 0, Skipped: 0, Time: 30.449s

New runner

C:\src\dotnet\runtime\src\libraries\System.Threading.ThreadPool\tests>dotnet test --no-build
Test run for C:\src\dotnet\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Debug\System.Threading.ThreadPool.Tests.dll (.NETCoreApp,Version=v6.0)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.
Results File: C:\src\dotnet\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Debug\TestResults\erics_ERICSTJ-SURFACE_2020-10-23_10_26_58.trx
Html test results file : C:\src\dotnet\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Debug\TestResults\TestResult_erics_ERICSTJ-SURFACE_20201023_102728.html

Passed!  - Failed:     0, Passed:    41, Skipped:     0, Total:    41, Duration: 30 s - System.Threading.ThreadPool.Tests.dll (net6.0)

Looks to me like it's about the same amount of time.

@ViktorHofer
Copy link
Member Author

What Eric said.

dotnet test --no-build invoked on a project allows to run tests without rebuilding the project.
Similar, dotnet test invoked on test assemblies invokes the tests.

@jkotas
Copy link
Member

jkotas commented Oct 23, 2020

The time reported by the test runner is net time to run the tests. It does not include the test runner overhead. We need to be looking at the wall-clock time of the whole invocation. I see the wall-clock time with the heavy weight runner is substantially more with non-Release runtime and/or libraries.

Thanks for the --no-build switch. Here is what I am seeing on current master (Windows x64, build -c Release -rc checked):

D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release>timer  "D:\runtime\artifacts\bin\testhost\net6.0-Windows_NT-Release-x64\dotnet.exe" exec --runtimeconfig System.Threading.ThreadPool.Tests.runtimeconfig.json --depsfile System.Threading.ThreadPool.Tests.deps.json xunit.console.dll System.Threading.ThreadPool.Tests.dll -xml testResults.xml -nologo -notrait category=OuterLoop -notrait category=failing
D:\runtime\artifacts\bin\testhost\net6.0-Windows_NT-Release-x64\dotnet.exe exec --runtimeconfig System.Threading.ThreadPool.Tests.runtimeconfig.json --depsfile System.Threading.ThreadPool.Tests.deps.json xunit.console.dll System.Threading.ThreadPool.Tests.dll -xml testResults.xml -nologo -notrait category=OuterLoop -notrait category=failing
  Discovering: System.Threading.ThreadPool.Tests (method display = ClassAndMethod, method display options = None)
  Discovered:  System.Threading.ThreadPool.Tests (found 40 test cases)
  Starting:    System.Threading.ThreadPool.Tests (parallel test collections = on, max threads = 32)
  Finished:    System.Threading.ThreadPool.Tests
=== TEST EXECUTION SUMMARY ===
   System.Threading.ThreadPool.Tests  Total: 62, Errors: 0, Failed: 0, Skipped: 0, Time: 8.673s
Results of execution:

    Exit code 0
    Time of execution 15.625
D:\runtime\src\libraries\System.Threading.ThreadPool\tests>timer dotnet  test --no-build System.Threading.ThreadPool.Tests.csproj -c Release
dotnet test --no-build System.Threading.ThreadPool.Tests.csproj -c Release
Test run for D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release\System.Threading.ThreadPool.Tests.dll (.NETCoreApp,Version=v6.0)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Data collector 'Blame' message: All tests finished running, Sequence file will not be generated.
Results File: D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release\TestResults\jkotas_JKOTAS9_2020-10-23_11_43_45.trx
Html test results file : D:\runtime\artifacts\bin\System.Threading.ThreadPool.Tests\net6.0-Release\TestResults\TestResult_jkotas_JKOTAS9_20201023_114353.html

Passed!  - Failed:     0, Passed:    62, Skipped:     0, Total:    62, Duration: 9 s - System.Threading.ThreadPool.Tests.dll (net6.0)
Results of execution:

    Exit code 0
    Time of execution 29.320

timer is a simple utility that measures wall-clock execution time.

The net time to run the test is same (9s) in both cases. It makes sense since the exact same tests run in both cases. However, the total time is a lot more for the heavy weight runner (16s vs. 29s).

Can we estimate what is the total machine time for a typical dotnet/runtime PR test mix today and how much is it going to increase with this change?

@stephentoub
Copy link
Member

@ViktorHofer, we had problems previously with dotnet test where it was doing pre-enumeration of tests and that was contributing significantly to the overhead of running tests (e.g. a suite that should have take 3 seconds instead taking 15 seconds). We fixed that with #35837, but is that fix still applying with all of your changes?

@ViktorHofer
Copy link
Member Author

but is that fix still applying with all of your changes?

Yes, that fix is still in branch, pre-enumerations aren't happening.

The numbers above don't match as the the first one invokes xunit.console directly where-as the second invocation goes through a csproj which then invokes the VSTest target which then invokes the runner.

I agree that VSTest could be faster during start-up (and unrelated, it logs too much) and would encourage someone to open an issue in https://github.com/microsoft/vstest, because talking about this will benefit not just us but our customers and their CI pipelines as well.

Can we estimate what is the total machine time for a typical dotnet/runtime PR test mix today and how much is it going to increase with this change?

If you are talking about invoking all test assemblies then the numbers should be more close together as we invoke test assemblies in parallel which means the start-up cost should be less visible. If we would want to optimize for that we could even make this much faster than with xunit.console as VSTest supports invoking multiple test assemblies together.

@jkotas
Copy link
Member

jkotas commented Oct 23, 2020

If you are talking about invoking all test assemblies then the numbers should be more close together as we invoke test assemblies in parallel which means the start-up cost should be less visible

This machine capacity still needs to come from somewhere. My question is: How many extra machines we are going to need to provision for Helix, to process the same amount of test jobs we are processing today?

@ViktorHofer
Copy link
Member Author

My question is: How many extra machines we are going to need to provision for Helix, to process the same amount of test jobs we are processing today?

I assumed you were talking about local invocation of test assemblies. On Helix we invoke the test assembly directly (without msbuild in play), hence the cost is much lower than your numbers above.

@jkotas
Copy link
Member

jkotas commented Oct 23, 2020

On Helix we invoke the test assembly directly (without msbuild in play), hence the cost is much lower than your numbers above.

I disagree. The msbuild overhead is about 1 second in my number above by watching the screen scroll. If you can tell me the command line to invoke the test assembly directly just like we do it in Helix, I will be happy to run it to get the exact number.

@danmoseley
Copy link
Member

Closing this since work is not active here. We will definitely do this but we only want active PR's to be open.

@danmoseley danmoseley closed this Feb 4, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Mar 6, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet