Working on that PR reminded me how complex our build and test run code is.
Sampling of issues:
YAML
, Powershell
, Bash
, Batch\Cmd.EXE
, python
, MSBUILD
, C#
YAML
-> dotnet MSBuild helixpublishwitharcade.proj
run-test.sh
run-test.py
dotnet MSBuild runtest.proj
, <test>.XUnitWrapper.dll
bash
<test.sh>
corerun <assembly>
Target
specific disabling of tests by scenario at run timeCI
moving parts for efficiency and reliabilitydotnet test
for running testsruntime
runtime
Currently test metadata is stored in csproj
/ilproj
files.
Refine metadata tags for ability to disable running tests globally and per scenario
Condition
true
- all targetsarm
, arm64
, x64
...Windows
, Unix
, ...;
-> or with clear precedence semantics-
-> and with grammatical connection 32bit
, 64bit
crossgen2
, varargs
, COM
longrunning
test runner assembly
belowCreate an <test>.xunit.cs
for each test (with MSBuild during build-test.*
)
xUnit.net
ClrTestCaseAttribute
```C#
// Sample Attri
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)]
public class ClrTestCaseAttribute : Attribute
{
public string? Test {get; set;}
public string? TargetUnsupported {get; set;}
public string? GCStressIncompatible {get; set;}
public string? HeapVerifyIncompatible {get; set;}
public string? JitOptimizationSensitive {get; set;}
public string? UnloadabilityIncompatible {get; set;}
}
```C#
// Sample MSBuild generate C# xUnit test case initial minimal approach
public partial class ClrTests
{
[ClrTestCase(Test:"Interop.COM.ComWrappers.GlobalInstance.GlobalInstanceMarshallingTests",
TargetUnsupported:"Unix")]
static void Test_GlobalInstanceMarshallingTests_hash12345678()
{
Run("Interop.COM.ComWrappers.GlobalInstance.GlobalInstanceMarshallingTests");
}
}
+ This approach makes it more difficult to fully support `xUnit.net` features.
- The simple consumer would simply treat the test cases as `[Theory]` data. But wouldn't enable filtering on test features (see below)
- A full featured approach would require implementing `ITestDiscovery` and `ITraitDiscovery`
```C#
// Simple assembly ClrTestCaseAttribute consumer
public partial class ClrTests
{
[Theory]
[MemberData(nameof(TestCases))]
public void RunTestCase(ClrTestCaseAttribute testCase)
{
// TBD
}
public static IEnumerable<ClrTestCaseAttribute> TestCases => GetTestCases();
private static IEnumerable<ClrTestCaseAttribute> GetTestCases()
{
foreach (Attribute a in Attribute.GetCustomAttributes(typeof(ClrTests).Assembly, typeof(ClrTestCaseAttribute)))
{
yield return (ClrTestCaseAttribute) a;
}
}
}
Includes <test_artifacts\**\*.xunit.cs
described above
Will run as dotnet test ClrTests.dll
Also will run as dotnet ClrTests.dll <arguments>
Supports running tests with or without bash/batch scripts
bash
/batch
scripts using MSBuild, use ClrTests.dll insteadSaves compressing and decompressing 10K*2 highly similar scripts
If we are worried about bit rot, we could test the scripts periodically. Weekly might be sufficient.
dev/infrastructure
branchI would like feedback around the proposal.
/cc @janvorli @jkotas @echesakovMSFT @dotnet/runtime-infrastructure @vitek-karas @AaronRobinsonMSFT
@sdmaclea Thanks for starting this issue. I think there are a lot of good ideas above, but I don't know if they could be used for some areas.
1) Would all tests be run in the same test process? This isn't possible for many interop scenarios because we pollute the environment which means previous tests could have poisoned the runtime and future tests would fail. The gist here is we would need to run each interop test in a separate process. The JIT probably has similar test constraints - @dotnet/jit-contrib.
1) What does the inner loop look like? Right now, I can build the runtime and libraries once and then build and run a single test multiple times without issue. It is also very easy to debug by launching that single test under a debugger or an EXE project in Visual Studio. I can debug as native/mixed/managed and know exactly where to set a break point without having to provide a long list of arguments to the runner to satisfy hidden attributes checks - see below about XUnit.
1) How are environment variables that control the runtime employed? The COMPlus_GCStress
and COMPlus_JITStress
features are incredible expensive. How do other variables respond to this workflow.
1) I dislike XUnit personally. I think it is overly complex and slow for what is needed in most scenario based tests like what we have in CoreCLR. It works great for the libraries unit testing style but making it work nicely with CoreCLR tests seems difficult - especially debugging the native side of the runtime. It is already partially included in CoreCLR, but my inner dev loop doesn't have any XUnit parts and and I would prefer that. Also, rumor has it we are looking into using dotnet test
for CoreCLR would that affect this proposal?
For each test populate a ClrTestCaseAttribute
It may be nice to use the same traits used by libraries where possible. For example:
[ActiveIssue("https://github.com/dotnet/runtime/issues/26798")]
[PlatformSpecific(TestPlatforms.Windows)]
The simple consumer would simply treat the test cases as [Theory] data
I would expect each test to be one [Fact]
method. What's the reason for it to be theory data?
Would all tests be run in the same test process?
It would be nice to plan for having an option to run tests that can run in a single process (majority of tests) in a single process.
Would all tests be run in the same test process?
It would be nice to plan for having an option to run tests that can run in a single process (majority of tests) in a single process.
I agree where possible. This does make debugging the test more complicated and that is something we should consider. I don't think we pay enough attention to the inner dev loop. I would like to see a scenario where we can run everything in a single process in the CI, but locally I should be to able to run a single test outside of VS without a sequence of command line arguments that is longer than my arm.
/cc @dotnet/jit-contrib
Would all tests be run in the same test process? This isn't possible for many interop scenarios because we pollute the environment which means previous tests could have poisoned the runtime and future tests would fail. The gist here is we would need to run each interop test in a separate process. The JIT probably has similar test constraints - @dotnet/jit-contrib.
To my knowledge this proposal still maintains each test running in its own process. @sdmaclea can confirm or not.
It would be nice to plan for having an option to run tests that can run in a single process (majority of tests) in a single process.
I would suggest this as future work, it is costly as almost all tests inherently assume somehow that they are running as a single application.
Also, rumor has it we are looking into using dotnet test for CoreCLR would that affect this proposal?
This proposal would most likely make transitioning to dotnet test easier.
Running tests in single process would be difficult for the cases when we run with GC stress on. That would mean we would have to run the xunit with GC stress enabled too.
As for inner dev loop, I find the cmd / sh scripts and one process per test ideal. When debugging runtime issues, it is much easier to debug when xunit doesn't get into my way. Xunit runs so much code before getting to the test itself. And it adds a lot of stuff to managed heap / stack so finding stuff there gets complicated.
It is also important to keep in mind that when debugging an issue in coreclr tests, in majority of the cases we are debugging issues in the native runtime, so WinDbg is the only reasonable tool on Windows. So I am not quite convinced that enabling running those tests in VS (which I guess is the main motivation for the dotnet test) has much of a value.
Running tests in single process would be difficult for the cases when we run with GC stress on. That would mean we would have to run the xunit with GC stress enabled too.
Not necessarily. We can use https://github.com/dotnet/runtime/tree/master/src/libraries/Common/tests/StaticTestGenerator or something alike. (FWIW, .NET Native used scheme like this for codegen and low-level runtime tests.)
XUnit is really two parts:
Would all tests be run in the same test process?
It would be nice to plan for having an option to run tests that can run in a single process
I would suggest this as future work,
I hadn't considered running the tests in the same process.
I am happy to keep it as a background future process thought
It may be nice to use the same traits used by libraries where possible
I'll have to look more carefully at those and see if they fully meets our needs. IT is likely we will need a superset.
Since I was thinking the MSBuild would construct the attributes, I was trying to keep my life simple.
But perhaps the simple thing would be to write the csproj XML like
<CLRTestAttributes><![CDATA[
[ActiveIssue("https://github.com/dotnet/runtime/issues/26798")]
[PlatformSpecific(TestPlatforms.Windows)]
]]><CLRTestAttributes/>
And get the best of both worlds
What's the reason for it to be theory data?
I wanted a simple list of tests with the attributes for each test.
[Assembly:Attribute()] with reflection was a cute way to do it.
I am thinking I need a tool which is somewhat the opposite of StaticTestGenerator
. Extract the test metadata from the .*proj
files directly or indirectly.
json
snippets.I'd like to add that, since we are now drawing on the same set of test for multiple runtimes, we should preserve the ability to disable tests based on RuntimeFlavor.
One of the other questions is whether to
issues.targets
*.csproj
files The mixture makes adding new Targets
and RuntimePlatfoms
easier, but it comes with added complexity cost.
Most helpful comment
Not necessarily. We can use https://github.com/dotnet/runtime/tree/master/src/libraries/Common/tests/StaticTestGenerator or something alike. (FWIW, .NET Native used scheme like this for codegen and low-level runtime tests.)
XUnit is really two parts: