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

[Continuation] - Enable the fundamentals behind libraries tests compiled via Crossgen2 #80946

Merged
merged 7 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 52 additions & 9 deletions docs/workflow/testing/libraries/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,88 @@
These example commands automate the test run and all pre-requisite build steps in a single command from a clean enlistment.

- Run all tests - Builds clr in release, libs+tests in debug:

```
build.cmd/sh -subset clr+libs+libs.tests -test -rc Release
```

- Run all tests - Builds Mono in release, libs+tests in debug:

```
build.cmd/sh -subset mono+libs+libs.tests -test -rc Release
```

- Run all tests - Build Mono and libs for x86 architecture in debug (choosing debug for runtime will run very slowly):

```
build.cmd/sh -subset mono+libs+libs.tests -test -arch x86
```

## Partial Build and Test Runs

Doing full build and test runs takes a long time and is very inefficient if you need to iterate on a change.
For greater control and efficiency individual parts of the build + testing workflow can be run in isolation.
See the [Building instructions](../../building/libraries/README.md) for more info on build options.
Doing full build and test runs takes a long time and is very inefficient if you need to iterate on a change. For greater control and efficiency individual parts of the build + testing workflow can be run in isolation. See the [Building instructions](../../building/libraries/README.md) for more info on build options.

### Test Run Pre-requisites
Before any tests can run we need a complete build to run them on. This requires building (1) a runtime, and
(2) all the libraries. Examples:

Before any tests can run we need a complete build to run them on. This requires building (1) a runtime, and (2) all the libraries. Examples:

- Build release clr + debug libraries

```
build.cmd/sh -subset clr+libs -rc Release
```

- Build release mono + debug libraries

```
build.cmd/sh -subset mono+libs -rc Release
```

Building the `libs` subset or any of individual library projects automatically copies product binaries into the testhost folder
in the bin directory. This is where the tests will load the binaries from during the run. However System.Private.CorLib is an
exception - the build does not automatically copy it to the testhost folder. If you [rebuild System.Private.CoreLib](https://github.com/dotnet/runtime/blob/main/docs/workflow/building/libraries/README.md#iterating-on-systemprivatecorelib-changes) you must also build the `libs.pretest` subset to ensure S.P.C is copied before running tests.
Building the `libs` subset or any of individual library projects automatically copies product binaries into the testhost folder in the bin directory. This is where the tests will load the binaries from during the run. However System.Private.CorLib is an exception - the build does not automatically copy it to the testhost folder. If you [rebuild System.Private.CoreLib](https://github.com/dotnet/runtime/blob/main/docs/workflow/building/libraries/README.md#iterating-on-systemprivatecorelib-changes) you must also build the `libs.pretest` subset to ensure S.P.C is copied before running tests.
Copy link
Member

Choose a reason for hiding this comment

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

Please excuse me if I'm mixing things up as it's been a long time since we last talked about. Why do we mention the testhost folder or the libs.pretest subset? I think that we discussed that we want to exercise Crossgen2 testing in self-contained deployment only in the dev innerloop.

Copy link
Member Author

Choose a reason for hiding this comment

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

I regret to say I don't know the answer. This change was just reformatting what was already written there to match the format of most of the repo's Markdown files. @trylek do you have context for Viktor's question?

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 there are indeed two facets to this discussion - (a) using Crossgen2-compiled System.Private.CoreLib in library testing and (b) compiling the framework and / or the library tests themselves using Crossgen2. For (a), AFAIK that's always been the case, to my knowledge no testing involves the IL version of SPC. For (b), that's what Ivan is now creating foundations for unless I'm mistaken as at least weekly library lab testing involving Crossgen2 would be very beneficial for improving our test coverage; @davidwrighton also believes Crossgen2 library testing could be used for better regression monitoring of some of our features like the cross-module inlining. Having said that, I'm not sufficiently familiar with library testing to know off the top of my head whether the comment under discussion is actually accurate or not.

Copy link
Member

Choose a reason for hiding this comment

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

Right. I think we all agree on that we want to have crossgen2 coverage for our libraries testing. What I commented on is that I don't think we want to do framework dependent crossgen2 testing but self-contained deployment testing. We use self-contained deployment for AOT testing, linker testing, single file testing... cc @jkotas for opinions.

For (a), AFAIK that's always been the case, to my knowledge no testing involves the IL version of SPC.

We do use the IL version of SPC for Debug builds or when performing code coverage measurements:

<SwapNativeForIL Condition="'$(SwapNativeForIL)' == '' and ('$(Configuration)' == 'Debug' or '$(Coverage)' == 'true') and '$(RuntimeFlavor)' != 'Mono'">true</SwapNativeForIL>

Copy link
Member

Choose a reason for hiding this comment

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

Thanks Viktor for clarifying the usage of IL SPC. For the rest of your comment, so am I right to understand your point is about "Native-AOT compiled self-contained Crossgen2 app" vs. "raw framework-dependent Crossgen2 app produced by CoreCLR build"? I certainly agree with that, we should do our best to test the actual customer-facing product, not some intermediate artifact of our build system (not mentioning the fact that in the past it also identified bugs in NativeAOT). I apologize, I didn't get that from your original PR comment at the top of this thread, in fact it seems you're saying the opposite in

we want to exercise Crossgen2 testing in self-contained deployment only in the dev innerloop

I apologize if I misread or misunderstood one of your responses, in such case please have patience with me being slow on the uptake and clarify.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think we want to do framework dependent crossgen2 testing but self-contained deployment testing.

crossgening the whole framework takes a while. Would self-contained deployment mean that the whole framework needs to be crossgened for each libraries test? It does not look very efficient.

Copy link
Member

Choose a reason for hiding this comment

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

In CoreCLR, crossgenning the framework takes something like 2~3 minutes. It should certainly be done only once, not in Helix for each test separately, that would be hugely wasteful.

Copy link
Member

@ViktorHofer ViktorHofer Jan 24, 2023

Choose a reason for hiding this comment

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

crossgening the whole framework takes a while. Would self-contained deployment mean that the whole framework needs to be crossgened for each libraries test? It does not look very efficient.

No. It would mean that we could crossgen the runtime pack that we use for self-contained deployment once, instead of the testhost folder that we use for framework-dependent deployment. It boils down to which app-mode we want to add crossgen validation to. FDD, SCD or both?


### Running tests for all libraries

- Build and run all tests in release configuration.

```
build.cmd/sh -subset libs.tests -test -c Release
```

- Build the tests without running them

```
build.cmd/sh -subset libs.tests
```

- Run the tests without building them

```
build.cmd/sh -subset libs.tests -test -testnobuild
```

- The following example shows how to pass extra msbuild properties to ignore tests ignored in CI.

```
build.cmd/sh -subset libs.tests -test /p:WithoutCategories=IgnoreForCI
```

### Running tests for a single library

The easiest (and recommended) way to build and run the tests for a specific library, is to invoke the `Test` target on that library:

```cmd
cd src\libraries\System.Collections.Immutable\tests
dotnet build /t:Test
```

It is possible to pass parameters to the underlying xunit runner via the `XUnitOptions` parameter, e.g.:

```cmd
dotnet build /t:Test /p:XUnitOptions="-class Test.ClassUnderTests"
```

Which is very useful when you want to run tests as `x86` on a `x64` machine:

```cmd
dotnet build /t:Test /p:TargetArchitecture=x86
```
Expand All @@ -88,17 +96,52 @@ There may be multiple projects in some directories so you may need to specify th
### Running a single test on the command line

To quickly run or debug a single test from the command line, set the XunitMethodName property, e.g.:

```cmd
dotnet build /t:Test /p:XunitMethodName={FullyQualifiedNamespace}.{ClassName}.{MethodName}
```

### Running outer loop tests

To run all tests, including "outer loop" tests (which are typically slower and in some test suites less reliable, but which are more comprehensive):

```cmd
dotnet build /t:Test /p:Outerloop=true
```

### Running tests on a different target framework

Each test project can potentially have multiple target frameworks. There are some tests that might be OS-specific, or might be testing an API that is available only on some target frameworks, so the `TargetFrameworks` property specifies the valid target frameworks.
Each test project can potentially have multiple target frameworks. There are some tests that might be OS-specific, or might be testing an API that is available only on some target frameworks, so the `TargetFrameworks` property specifies the valid target frameworks.

### Running tests in custom compilation modes

There are several custom compilation modes for tests. These are enabled by setting a switch during the configuration. These switches are described in the following table:

| Mode | Description | Prerequisite Subsets |
| -------------- | --------------------------------------------------------- | -------------------- |
| TestSingleFile | Test using the single file compilation mode | libs+clr |
| TestNativeAot | Test by compiling using NativeAOT | libs+clr.aot |
| TestReadyToRun | Test compilation of the tests/libraries into R2R binaries | libs+clr |

To run a test in a specific mode, simply build the tests after building the prerequisite subsets, and specify the test mode in the command-line. For example, to use the _TestReadyToRun_ mode in Release configuration:

```bash
dotnet build -c Release -t:Test -p:TestReadyToRun=true
```

<!-- NOTE: It might be worth it to explain what each of these flags actually does. -->
It is important to highlight that these tests do not use the standard XUnit test runner. Instead, they run with the [SingleFileTestRunner](/src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs). The set of available commands is listed here:

- `-xml`
- `-notrait`
- `-class`
- `-class-`
- `-noclass`
- `-method`
- `-method-`
- `-nomethod`
- `-namespace`
- `-namespace-`
- `-nonamespace`
- `-parallel`

1 change: 1 addition & 0 deletions eng/testing/tests.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<VSTestNoLogo>true</VSTestNoLogo>
<ILLinkDescriptorsPath>$(MSBuildThisFileDirectory)ILLinkDescriptors\</ILLinkDescriptorsPath>
<TestSingleFile Condition="'$(TestNativeAot)' == 'true'">true</TestSingleFile>
<TestSingleFile Condition="'$(TestReadyToRun)' == 'true'">true</TestSingleFile>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetsMobile)' == 'true'">
Expand Down
44 changes: 22 additions & 22 deletions eng/testing/tests.singlefile.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>

<DefineConstants>$(DefineConstants);SINGLE_FILE_TEST_RUNNER</DefineConstants>

<BundleDir>$([MSBuild]::NormalizeDirectory('$(OutDir)', 'publish'))</BundleDir>
<RunScriptOutputPath>$([MSBuild]::NormalizePath('$(BundleDir)', '$(RunScriptOutputName)'))</RunScriptOutputPath>
<RuntimeIdentifier>$(OutputRid)</RuntimeIdentifier>
Expand All @@ -18,9 +16,16 @@
<SelfContained>true</SelfContained>
</PropertyGroup>

<PropertyGroup Condition="'$(TestReadyToRun)' == 'true'">
<PublishReadyToRun>true</PublishReadyToRun>
<PublishSingleFile>false</PublishSingleFile>
</PropertyGroup>

<PropertyGroup Condition="'$(TestNativeAot)' == 'true'">
<IlcToolsPath>$(CoreCLRILCompilerDir)</IlcToolsPath>
<IlcToolsPath Condition="'$(TargetArchitecture)' != '$(BuildArchitecture)'">$(CoreCLRCrossILCompilerDir)</IlcToolsPath>
<CppCompilerAndLinker Condition="'$(TargetArchitecture)' != '$(BuildArchitecture)' and '$(HostOS)' == 'linux' and '$(RuntimeIdentifier)' == 'linux-musl-arm64'">clang-15</CppCompilerAndLinker>
Copy link
Member

Choose a reason for hiding this comment

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

This looks to be undoing #77304. Why?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's odd, those changes shouldn't be modified here. Will restore them in the next iteration.

<CppCompilerAndLinker Condition="'$(TargetArchitecture)' != '$(BuildArchitecture)' and '$(HostOS)' == 'linux' and '$(RuntimeIdentifier)' != 'linux-musl-arm64'">clang-9</CppCompilerAndLinker>
<SysRoot Condition="'$(TargetArchitecture)' != '$(BuildArchitecture)' and '$(HostOS)' != 'windows'">$(ROOTFS_DIR)</SysRoot>
<IlcBuildTasksPath>$(CoreCLRILCompilerDir)netstandard/ILCompiler.Build.Tasks.dll</IlcBuildTasksPath>
<IlcSdkPath>$(CoreCLRAotSdkDir)</IlcSdkPath>
Expand All @@ -34,6 +39,10 @@
<SelfContained>true</SelfContained>
</PropertyGroup>

<PropertyGroup Condition="">
<DefineConstants>$(DefineConstants);SINGLE_FILE_TEST_RUN</DefineConstants>
ivdiazsa marked this conversation as resolved.
Show resolved Hide resolved
</PropertyGroup>

<Import Project="$(CoreCLRBuildIntegrationDir)Microsoft.DotNet.ILCompiler.SingleEntry.targets" Condition="'$(TestNativeAot)' == 'true'" />

<ItemGroup Condition="'$(TestNativeAot)' == 'true'">
Expand Down Expand Up @@ -82,26 +91,17 @@
</ItemGroup>
</Target>

<!--
Copy link
Member

Choose a reason for hiding this comment

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

This is still deleting the comment. Could you make is so that this is not even part of the diff?

Use init-compiler.sh to locate the compiler toolchain which was resolved for rest of the build.
This target is essentially an override hook which is called before `SetupOSSpecificProps` in
`Microsoft.NETCore.Native.Unix.targets`. Note that the input is `CppCompilerAndLinker`
and the output is `CppLinker`, because from `SetupOSSpecificProps` onwards, we only use `CppLinker`
(when alternative compiler, i.e. gcc, is not selected)
-->
<Target Name="LocateNativeCompiler"
Copy link
Member

Choose a reason for hiding this comment

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

Also undoing #77304? Why?

Copy link
Member Author

Choose a reason for hiding this comment

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

See my comment above.

Condition="'$(TestNativeAot)' == 'true' and '$(HostOS)' != 'windows'"
BeforeTargets="SetupOSSpecificProps">
<PropertyGroup>
<CppCompilerAndLinker Condition="'$(CppCompilerAndLinker)' == ''">clang</CppCompilerAndLinker>
</PropertyGroup>

<Exec Command="sh -c 'build_arch=&quot;$(TargetArchitecture)&quot; compiler=&quot;$(CppCompilerAndLinker)&quot; . &quot;$(RepositoryEngineeringDir)/common/native/init-compiler.sh&quot; &amp;&amp; echo $CC' 2>/dev/null"
EchoOff="true"
ConsoleToMsBuild="true"
StandardOutputImportance="Low">
<Output TaskParameter="ConsoleOutput" PropertyName="CppLinker" />
</Exec>
<Target Name="__ReplaceCrossgen2ExecutableWithFreshlyBuiltOne"
BeforeTargets="_PrepareForReadyToRunCompilation">
<PropertyGroup>
<Crossgen2ArtifactPath>$([MSBuild]::NormalizeDirectory('$(CoreCLRArtifactsPath)', 'crossgen2'))crossgen2$(ExeSuffix)</Crossgen2ArtifactPath>
Copy link
Member

Choose a reason for hiding this comment

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

This should use $(CoreCLRCrossgen2Dir) instead of recomputing it.

</PropertyGroup>

<ItemGroup>
<Crossgen2CurrentTool Include="@(Crossgen2Tool->'$(Crossgen2ArtifactPath)')" />
<Crossgen2Tool Remove="@(Crossgen2Tool)" />
<Crossgen2Tool Include="@(Crossgen2CurrentTool)" />
</ItemGroup>
</Target>

<Target Name="PublishTestAsSingleFile"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public static int Main(string[] args)
var asm = typeof(SingleFileTestRunner).Assembly;
Console.WriteLine("Running assembly:" + asm.FullName);

// The current RemoteExecutor implementation is not compatible with the SingleFileTestRunner.
Environment.SetEnvironmentVariable("DOTNET_REMOTEEXECUTOR_SUPPORTED", "0");
Comment on lines +30 to +31
Copy link
Member

Choose a reason for hiding this comment

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

The reason why the existing users of the SingleFileTestRunner.cs didn't need this line is: dotnet/arcade#6553

It is now redundant. Could you submit a removal of that in Arcade?

Copy link
Member Author

Choose a reason for hiding this comment

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

By removal do you mean undoing dotnet/arcade#6553's changes, and then removing this line?

Copy link
Member

Choose a reason for hiding this comment

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

The arcade change covers the existing use cases of the single file runner - the single file deployments (where assembly.location is null/empty, like the arcade PR checks).

With this PR, we'll be using the single file runner for non-single file scenarios as well. The line you're adding will disable remote executor for any consumer - single file or not.

Therefore, I think we won't need the arcade part anymore because remote executor will be disabled by this.

Copy link
Member Author

@ivdiazsa ivdiazsa Jan 24, 2023

Choose a reason for hiding this comment

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

Got it. Then, let me see if I understood correctly. The arcade change and this line help with the same issue for this PR's purposes, so they are redundant in this case. Removing the arcade change while keeping this line will remove that redundancy without breaking other scenarios. Is this the appropriate course of action?

Copy link
Member

Choose a reason for hiding this comment

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

Yep. Your change is making the arcade part redundant.


var diagnosticSink = new ConsoleDiagnosticMessageSink();
var testsFinished = new TaskCompletionSource();
var testSink = new TestMessageSink();
Expand Down Expand Up @@ -68,16 +71,77 @@ public static int Main(string[] args)
{
if (args[i].Equals("-notrait", StringComparison.OrdinalIgnoreCase))
{
var traitKeyValue=args[i + 1].Split("=", StringSplitOptions.TrimEntries);
var traitKeyValue = args[i + 1].Split("=", StringSplitOptions.TrimEntries);

if (!noTraits.TryGetValue(traitKeyValue[0], out List<string> values))
{
noTraits.Add(traitKeyValue[0], values = new List<string>());
}

values.Add(traitKeyValue[1]);
i++;
}

if (args[i].Equals("-xml", StringComparison.OrdinalIgnoreCase))
{
xmlResultFileName=args[i + 1].Trim();
xmlResultFileName = args[i + 1].Trim();
i++;
}

if (args[i].Equals("-class", StringComparison.OrdinalIgnoreCase))
{
filters.IncludedClasses.Add(args[i + 1].Trim());
i++;
}

if (args[i].Equals("-noclass", StringComparison.OrdinalIgnoreCase) ||
args[i].Equals("-class-", StringComparison.OrdinalIgnoreCase))
{
filters.ExcludedClasses.Add(args[i + 1].Trim());
i++;
}

if (args[i].Equals("-method", StringComparison.OrdinalIgnoreCase))
{
filters.IncludedMethods.Add(args[i + 1].Trim());
i++;
}

if (args[i].Equals("-nomethod", StringComparison.OrdinalIgnoreCase) ||
args[i].Equals("-method-", StringComparison.OrdinalIgnoreCase))
{
filters.ExcludedMethods.Add(args[i + 1].Trim());
i++;
}

if (args[i].Equals("-namespace", StringComparison.OrdinalIgnoreCase))
{
filters.IncludedNamespaces.Add(args[i + 1].Trim());
i++;
}

if (args[i].Equals("-nonamespace", StringComparison.OrdinalIgnoreCase) ||
args[i].Equals("-namespace-", StringComparison.OrdinalIgnoreCase))
{
filters.ExcludedNamespaces.Add(args[i + 1].Trim());
i++;
}

if (args[i].Equals("-parallel", StringComparison.OrdinalIgnoreCase))
{
string parallelismArg = args[i + 1].Trim().ToLower();
var (parallelizeAssemblies, parallelizeTestCollections) = parallelismArg switch
{
"all" => (true, true),
"assemblies" => (true, false),
"collections" => (false, true),
"none" => (false, false),
_ => throw new ArgumentException($"Unknown parallelism option '{parallelismArg}'.")
};

assemblyConfig.ParallelizeAssembly = parallelizeAssemblies;
assemblyConfig.ParallelizeTestCollections = parallelizeTestCollections;
i++;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,8 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ProjectReference>
<ProjectReference Include="..\System.Diagnostics.FileVersionInfo.TestAssembly\System.Diagnostics.FileVersionInfo.TestAssembly.csproj" Condition="'$(TargetOS)' == 'browser'" />
<!-- R2R testing does not tolerate the combination of a regular project reference and a content reference. -->
<!-- This is actually a bug in the SDK and ought to be fixed there. -->
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a link to the tracking SDK bug?

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe we don't have one. Will have to file it.

Copy link
Member

Choose a reason for hiding this comment

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

Did you file the bug? If so could you add a link here same as we have on line 46 (for a similar issue)?

<PublishReadyToRunExclude Include="System.Diagnostics.FileVersionInfo.TestAssembly.dll" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,8 @@
<OutputItemType>Content</OutputItemType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ProjectReference>
<!-- R2R testing does not tolerate the combination of a regular project reference and a content reference. -->
<!-- This is actually a bug in the SDK and ought to be fixed there. -->
<PublishReadyToRunExclude Include="LongPath.dll" />
</ItemGroup>
</Project>