Skip to content
Open
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
140 changes: 109 additions & 31 deletions test/Microsoft.TestPlatform.Acceptance.IntegrationTests/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Xml.Linq;

using Microsoft.TestPlatform.TestUtilities;
Expand All @@ -29,10 +31,46 @@ public class Build : IntegrationTestBase
[AssemblyInitialize]
public static void AssemblyInitialize(TestContext _)
{
var sw = Stopwatch.StartNew();
SetDotnetEnvironment();
BuildTestAssetsAndUnzipPackages();
BuildTestAssetsCompatibility();
Debug.WriteLine($"Setting dotnet environment took: {sw.ElapsedMilliseconds} ms");
sw.Restart();

var nugetCache = Path.GetFullPath(Path.Combine(Root, ".packages"));
var packagesAreNew = UnzipExecutablePackages();
if (packagesAreNew)
{
CleanNugetCacheAndProjects(nugetCache);
}
Debug.WriteLine($"Building test assets and unzipping packages took: {sw.ElapsedMilliseconds} ms");
sw.Restart();
BuildTestAssets(nugetCache);
BuildTestAssetsCompatibility(nugetCache);
Debug.WriteLine($"Building test assets compatibility matrix took: {sw.ElapsedMilliseconds} ms");
sw.Restart();
CopyAndPatchDotnet();
Debug.WriteLine($"Copying and patching dotnet took: {sw.ElapsedMilliseconds} ms");
}

private static void BuildTestAssets(string nugetCache)
{
var testAssets = Path.GetFullPath(Path.Combine(Root, "test", "TestAssets", "TestAssets.sln"));
var nugetFeeds = GetNugetSourceParameters(Root);

var netTestSdkVersion = IntegrationTestEnvironment.LatestLocallyBuiltNugetVersion;

ExecuteApplication2(Dotnet, $"""restore --packages {nugetCache} {nugetFeeds} --source "{IntegrationTestEnvironment.LocalPackageSource}" -p:PackageVersion={netTestSdkVersion} "{testAssets}" """);
ExecuteApplication2(Dotnet, $"""build "{testAssets}" --configuration {IntegrationTestEnvironment.BuildConfiguration} --no-restore""");

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Build special project written in IL.
// This project is used on Windows only Tests. On non-Windows the build fails with: "IlasmToolPath must be set in order to build ilproj's outside of Windows.".
var cilProject = Path.Combine(Root, "test", "TestAssets", "CILProject", "CILProject.proj");
var binPath = Path.Combine(Root, "artifacts", "bin", "TestAssets", "CILProject", IntegrationTestEnvironment.BuildConfiguration, "net462");
ExecuteApplication2(Dotnet, $"""restore --packages {nugetCache} {nugetFeeds} --source "{IntegrationTestEnvironment.LocalPackageSource}" "{cilProject}" """);
ExecuteApplication2(Dotnet, $"""build "{cilProject}" --configuration {IntegrationTestEnvironment.BuildConfiguration} --no-restore --output {binPath}""");
}
}

private static void SetDotnetEnvironment()
Expand All @@ -50,13 +88,22 @@ private static void CopyAndPatchDotnet()
{
var patchedDotnetDir = Path.GetFullPath(Path.Combine(Root, "artifacts", "tmp", ".dotnet"));

// Copy dotnet.
DirectoryUtils.CopyDirectory(new DirectoryInfo(DotnetDir), new DirectoryInfo(patchedDotnetDir));
var dotnetExe = OSUtils.IsWindows ? "dotnet.exe" : "dotnet";
var originalDotnetExePath = Path.Combine(DotnetDir, dotnetExe);
var patchedDotnetExePath = Path.Combine(patchedDotnetDir, dotnetExe);

// Copy target file and build task dll into it.
var netTestSdkVersion = IntegrationTestEnvironment.LatestLocallyBuiltNugetVersion;
var packageName = $"Microsoft.TestPlatform.Build.{netTestSdkVersion}.nupkg";
var packagePath = Path.GetFullPath(Path.Combine(IntegrationTestEnvironment.PublishDirectory, packageName));
// It is not necessary to copy whole dotnet folder before each test run
// we just need to make sure the build files are updated automatically,
// so dotnet test tests reflect what is in our local build targets.
bool skipCopy = File.Exists(originalDotnetExePath)
&& File.Exists(patchedDotnetExePath)
&& File.GetLastWriteTime(originalDotnetExePath) == File.GetLastWriteTime(patchedDotnetExePath);

if (!skipCopy)
{
// Copy .dotnet
DirectoryUtils.CopyDirectory(new DirectoryInfo(DotnetDir), new DirectoryInfo(patchedDotnetDir));
}

// e.g. artifacts\tmp\.dotnet\sdk\
var sdkDirectory = Path.Combine(patchedDotnetDir, "sdk");
Expand All @@ -72,14 +119,20 @@ private static void CopyAndPatchDotnet()
}

var dotnetSdkDirectory = dotnetSdkDirectories.Single();

// Copy target file and build task dll into it.
// This updates the definition for running dotnet test from what we have built locally.
var netTestSdkVersion = IntegrationTestEnvironment.LatestLocallyBuiltNugetVersion;
var packageName = $"Microsoft.TestPlatform.Build.{netTestSdkVersion}.nupkg";
var packagePath = Path.GetFullPath(Path.Combine(IntegrationTestEnvironment.PublishDirectory, packageName));

DirectoryUtils.CopyDirectory(Path.Combine(packagePath, "lib", "netstandard2.0"), dotnetSdkDirectory);
DirectoryUtils.CopyDirectory(Path.Combine(packagePath, "runtimes", "any", "native"), dotnetSdkDirectory);
}

private static void BuildTestAssetsCompatibility()
private static void BuildTestAssetsCompatibility(string nugetCache)
{
var testAssetsDir = Path.GetFullPath(Path.Combine(Root, "test", "TestAssets"));
var nugetCache = Path.GetFullPath(Path.Combine(Root, ".packages"));

var generated = Path.GetFullPath(Path.Combine(Root, "artifacts", "tmp", "GeneratedTestAssets"));
var generatedSln = Path.Combine(generated, "CompatibilityTestAssets.slnx");
Expand Down Expand Up @@ -169,7 +222,7 @@ private static void BuildTestAssetsCompatibility()
var rebuild = true;
if (cacheIdText == currentCacheId)
{
// Cache is up-to-date, just rebuilding solution.
// Project cache is up-to-date, just rebuilding solution.
ExecuteApplication2(Dotnet, $"""restore --packages {nugetCache} {nugetFeeds} --source "{IntegrationTestEnvironment.LocalPackageSource}" "{generatedSln}" """);
ExecuteApplication2(Dotnet, $"build {generatedSln} --no-restore --configuration {IntegrationTestEnvironment.BuildConfiguration} -v:minimal");
rebuild = false;
Expand Down Expand Up @@ -333,46 +386,71 @@ protected static void ExecuteApplication2(string path, string? args,
}
}

private static void BuildTestAssetsAndUnzipPackages()
private static bool UnzipExecutablePackages()
{
var testAssets = Path.GetFullPath(Path.Combine(Root, "test", "TestAssets", "TestAssets.sln"));

var nugetCache = Path.GetFullPath(Path.Combine(Root, ".packages"));
var nugetFeeds = GetNugetSourceParameters(Root);
var netTestSdkVersion = IntegrationTestEnvironment.LatestLocallyBuiltNugetVersion;

ExecuteApplication2(Dotnet, $"""restore --packages {nugetCache} {nugetFeeds} --source "{IntegrationTestEnvironment.LocalPackageSource}" -p:PackageVersion={netTestSdkVersion} "{testAssets}" """);
ExecuteApplication2(Dotnet, $"""build "{testAssets}" --configuration {IntegrationTestEnvironment.BuildConfiguration} --no-restore""");

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Build special project written in IL.
// This project is used on Windows only Tests. On non-Windows the build fails with: "IlasmToolPath must be set in order to build ilproj's outside of Windows.".
var cilProject = Path.Combine(Root, "test", "TestAssets", "CILProject", "CILProject.proj");
var binPath = Path.Combine(Root, "artifacts", "bin", "TestAssets", "CILProject", IntegrationTestEnvironment.BuildConfiguration, "net462");
ExecuteApplication2(Dotnet, $"""restore --packages {nugetCache} {nugetFeeds} --source "{IntegrationTestEnvironment.LocalPackageSource}" "{cilProject}" """);
ExecuteApplication2(Dotnet, $"""build "{cilProject}" --configuration {IntegrationTestEnvironment.BuildConfiguration} --no-restore --output {binPath}""");
}

// Extract locally built packages that have our tools (like vstest.console.exe) into tmp directory,
// so we can use them to run tests.
var packagesToExtract = new[]
{
{
$"Microsoft.TestPlatform.{netTestSdkVersion}.nupkg",
$"Microsoft.TestPlatform.CLI.{netTestSdkVersion}.nupkg",
$"Microsoft.TestPlatform.Build.{netTestSdkVersion}.nupkg",
$"Microsoft.CodeCoverage.{netTestSdkVersion}.nupkg",
$"Microsoft.TestPlatform.Portable.{netTestSdkVersion}.nupkg",
};

var packagesAreNew = false;
foreach (var packageName in packagesToExtract)
{
var packagePath = Path.Combine(IntegrationTestEnvironment.LocalPackageSource, packageName);
var unzipPath = Path.Combine(IntegrationTestEnvironment.PublishDirectory, packageName);

var cacheMarkerPath = Path.Combine(unzipPath, packageName + ".cache");
if (File.Exists(cacheMarkerPath))
{
if (File.ReadAllText(cacheMarkerPath) == File.GetLastWriteTimeUtc(packagePath).ToString(CultureInfo.InvariantCulture))
{
// Already extracted and using the latest built packages.
continue;
}
}

// I any package is new we will clean the package cache before restore and build.
packagesAreNew |= true;

if (Directory.Exists(unzipPath))
{
Directory.Delete(unzipPath, recursive: true);
}

ZipFile.ExtractToDirectory(packagePath, unzipPath);
File.WriteAllText(cacheMarkerPath, File.GetLastWriteTimeUtc(packagePath).ToString(CultureInfo.InvariantCulture));
}

return packagesAreNew;
}

private static void CleanNugetCacheAndProjects(string nugetCache)
{
// dotnet clean needs the packages in place, but here we don't yet know what projects we will build
// luckily they are all built into artifacts/bin/TestAssets and artifacts/obj/TestAssets so we just need to delete
// the obj to force re-build in the next steps.

var objPath = Path.Combine(Root, "artifacts", "obj", "TestAssets");
Directory.Delete(objPath, recursive: true);

// Then clean all -dev and -ci packages from the cache to force updating from local source.
foreach (var packageDir in Directory.GetDirectories(nugetCache))
{
foreach (var versionDir in Directory.GetDirectories(packageDir))
{
if (versionDir.EndsWith("-dev") || versionDir.EndsWith("-ci"))
{
Directory.Delete(versionDir, recursive: true);
}
}
}
}
}
Loading