Skip to content
Merged
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,15 @@ public async Task MTP_WhenTelemetryDisabled_DoesNotSendMSTestEvent(string tfm)
[DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))]
public async Task VSTest_RunTests_Succeeds(string tfm)
{
// Pre-build the VSTest project for this TFM exactly once, then run `dotnet test`
// with `--no-build --no-restore` so the build/restore targets that write to
// `bin/<tfm>/TelemetryVSTestProject.runtimeconfig.json` do not run per-test.
// This removes the GenerateRuntimeConfigurationFiles race that was still flaking
// intermittently even with [DoNotParallelize] on the class.
await AssetFixture.EnsureVSTestProjectBuiltAsync(tfm, TestContext.CancellationToken);

DotnetMuxerResult testResult = await DotnetCli.RunAsync(
$"test -c Release {AssetFixture.VSTestProjectPath} --framework {tfm}",
$"test -c Release {AssetFixture.VSTestProjectPath} --framework {tfm} --no-build --no-restore",
workingDirectory: AssetFixture.VSTestProjectPath,
failIfReturnValueIsNotZero: false,
cancellationToken: TestContext.CancellationToken);
Expand All @@ -147,8 +154,10 @@ public async Task VSTest_RunTests_Succeeds(string tfm)
[DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))]
public async Task VSTest_DiscoverTests_Succeeds(string tfm)
{
await AssetFixture.EnsureVSTestProjectBuiltAsync(tfm, TestContext.CancellationToken);

DotnetMuxerResult testResult = await DotnetCli.RunAsync(
$"test -c Release {AssetFixture.VSTestProjectPath} --framework {tfm} --list-tests",
$"test -c Release {AssetFixture.VSTestProjectPath} --framework {tfm} --list-tests --no-build --no-restore",
workingDirectory: AssetFixture.VSTestProjectPath,
failIfReturnValueIsNotZero: false,
cancellationToken: TestContext.CancellationToken);
Expand Down Expand Up @@ -192,6 +201,9 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase()
{
private const string AssetId = nameof(TelemetryTests);

private readonly SemaphoreSlim _vstestBuildLock = new(1, 1);
private readonly HashSet<string> _builtVSTestFrameworks = [with(StringComparer.OrdinalIgnoreCase)];
Comment thread
Evangelink marked this conversation as resolved.

public string MTPProjectPath => GetAssetPath(AssetId);

public string VSTestProjectPath => Path.Combine(GetAssetPath(AssetId), "vstest");
Expand All @@ -203,6 +215,43 @@ public override (string ID, string Name, string Code) GetAssetsToGenerate()
.PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)
.PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion));

// Pre-builds the VSTest project for the given TFM exactly once per TFM. Callers can
// then invoke `dotnet test ... --no-build --no-restore` to avoid having every test
// method redo the build, which is what produces the
// `GenerateRuntimeConfigurationFiles` file-in-use races on shared build outputs.
public async Task EnsureVSTestProjectBuiltAsync(string targetFramework, CancellationToken cancellationToken)
{
await _vstestBuildLock.WaitAsync(cancellationToken);
try
{
if (_builtVSTestFrameworks.Contains(targetFramework))
{
return;
}

await DotnetCli.RunAsync(
$"build -c Release {VSTestProjectPath} --framework {targetFramework}",
workingDirectory: VSTestProjectPath,
cancellationToken: cancellationToken);

_builtVSTestFrameworks.Add(targetFramework);
}
finally
{
_vstestBuildLock.Release();
}
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_vstestBuildLock.Dispose();
}

base.Dispose(disposing);
}

private const string SourceCode = """
#file TelemetryMTPProject.csproj
<Project Sdk="Microsoft.NET.Sdk">
Expand Down
Loading