123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- using System.Reflection;
- using System.Runtime.CompilerServices;
- using System.Text.Json;
- using Microsoft.AspNetCore.StaticWebAssets.Tasks;
- namespace Microsoft.NET.Sdk.Razor.Tests
- {
- [Trait("AspNetCore", "BaselineTest")]
- public class AspNetSdkBaselineTest : AspNetSdkTest
- {
- private static readonly JsonSerializerOptions BaselineSerializationOptions = new() { WriteIndented = true };
- private readonly StaticWebAssetsBaselineComparer _comparer;
- private readonly StaticWebAssetsBaselineFactory _baselineFactory;
- private string _baselinesFolder;
- #if GENERATE_SWA_BASELINES
- public static bool GenerateBaselines = true;
- #else
- public static bool GenerateBaselines = bool.TryParse(Environment.GetEnvironmentVariable("ASPNETCORE_TEST_BASELINES"), out var result) && result;
- #endif
- private bool _generateBaselines = GenerateBaselines;
- public AspNetSdkBaselineTest(ITestOutputHelper log) : base(log)
- {
- TestAssembly = Assembly.GetCallingAssembly();
- var testAssemblyMetadata = TestAssembly.GetCustomAttributes<AssemblyMetadataAttribute>();
- RuntimeVersion = testAssemblyMetadata.SingleOrDefault(a => a.Key == "NetCoreAppRuntimePackageVersion").Value;
- DefaultPackageVersion = testAssemblyMetadata.SingleOrDefault(a => a.Key == "DefaultTestBaselinePackageVersion").Value;
- _comparer = CreateBaselineComparer();
- _baselineFactory = CreateBaselineFactory();
- }
- public AspNetSdkBaselineTest(ITestOutputHelper log, bool generateBaselines) : this(log)
- {
- _generateBaselines = generateBaselines;
- _comparer = CreateBaselineComparer();
- }
- public TestAsset ProjectDirectory { get; set; }
- public string RuntimeVersion { get; set; }
- public string DefaultPackageVersion { get; set; }
- public string BaselinesFolder =>
- _baselinesFolder ??= ComputeBaselineFolder();
- protected Assembly TestAssembly { get; }
- protected virtual StaticWebAssetsBaselineComparer CreateBaselineComparer() => StaticWebAssetsBaselineComparer.Instance;
- private StaticWebAssetsBaselineFactory CreateBaselineFactory() => StaticWebAssetsBaselineFactory.Instance;
- protected virtual string ComputeBaselineFolder() =>
- Path.Combine(TestContext.GetRepoRoot() ?? AppContext.BaseDirectory, "test", "Microsoft.NET.Sdk.Razor.Tests", "StaticWebAssetsBaselines");
- protected virtual string EmbeddedResourcePrefix => string.Join('.', "Microsoft.NET.Sdk.Razor.Tests", "StaticWebAssetsBaselines");
- public StaticWebAssetsManifest LoadBuildManifest(string suffix = "", [CallerMemberName] string name = "")
- {
- if (_generateBaselines)
- {
- return default;
- }
- else
- {
- using var stream = GetManifestEmbeddedResource(suffix, name, "Build");
- var manifest = StaticWebAssetsManifest.FromStream(stream);
- return manifest;
- }
- }
- public StaticWebAssetsManifest LoadPublishManifest(string suffix = "", [CallerMemberName] string name = "")
- {
- if (_generateBaselines)
- {
- return default;
- }
- else
- {
- using var stream = GetManifestEmbeddedResource(suffix, name, "Publish");
- var manifest = StaticWebAssetsManifest.FromStream(stream);
- return manifest;
- }
- }
- protected void AssertBuildAssets(
- StaticWebAssetsManifest manifest,
- string outputFolder,
- string intermediateOutputPath,
- string suffix = "",
- [CallerMemberName] string name = "")
- {
- var fileEnumerationOptions = new EnumerationOptions { RecurseSubdirectories = true };
- var wwwRootFolder = Path.Combine(outputFolder, "wwwroot");
- var wwwRootFiles = Directory.Exists(wwwRootFolder) ?
- Directory.GetFiles(wwwRootFolder, "*", fileEnumerationOptions) :
- [];
- var computedFiles = manifest.Assets
- .Where(a => a.SourceType is StaticWebAsset.SourceTypes.Computed &&
- a.AssetKind is not StaticWebAsset.AssetKinds.Publish);
- // We keep track of assets that need to be copied to the output folder.
- // In addition to that, we copy assets that are defined somewhere different
- // from their content root folder when the content root does not match the output folder.
- // We do this to allow copying things like Publish assets to temporary locations during the
- // build process if they are later on going to be transformed.
- var copyToOutputDirectoryAssets = manifest.Assets.Where(a => a.ShouldCopyToOutputDirectory()).ToArray();
- var temporaryAsssets = manifest.Assets
- .Where(a =>
- !a.HasContentRoot(Path.Combine(outputFolder, "wwwroot")) &&
- File.Exists(a.Identity) &&
- !File.Exists(Path.Combine(a.ContentRoot, a.RelativePath)) &&
- a.AssetTraitName != "Content-Encoding").ToArray();
- var copyToOutputDirectoryFiles = copyToOutputDirectoryAssets
- .Select(a => Path.GetFullPath(Path.Combine(outputFolder, "wwwroot", a.RelativePath)))
- .Concat(temporaryAsssets
- .Select(a => Path.GetFullPath(Path.Combine(a.ContentRoot, a.RelativePath))))
- .ToArray();
- var existingFiles = _baselineFactory.TemplatizeExpectedFiles(
- wwwRootFiles
- .Concat(computedFiles.Select(a => a.Identity))
- .Concat(copyToOutputDirectoryFiles)
- .Distinct()
- .OrderBy(f => f, StringComparer.Ordinal)
- .ToArray(),
- TestContext.Current.NuGetCachePath,
- ProjectDirectory.TestRoot,
- intermediateOutputPath,
- outputFolder).ToArray();
- if (!_generateBaselines)
- {
- var expected = LoadExpectedFilesBaseline(manifest.ManifestType, suffix, name)
- .OrderBy(f => f, StringComparer.Ordinal);
- AssertFilesCore(existingFiles, expected);
- }
- else
- {
- File.WriteAllText(
- GetExpectedFilesPath(suffix, name, manifest.ManifestType),
- JsonSerializer.Serialize(existingFiles, BaselineSerializationOptions));
- }
- }
- private void AssertFilesCore(IEnumerable<string> existingFiles, IEnumerable<string> expected)
- {
- var existingSet = new HashSet<string>(existingFiles);
- var expectedSet = new HashSet<string>(expected);
- var different = new HashSet<string>(existingFiles);
- different.SymmetricExceptWith(expectedSet);
- var messages = new List<string>();
- if (existingSet.Count < expectedSet.Count)
- {
- messages.Add("The build produced less files than expected.");
- }
- else if (expectedSet.Count < existingSet.Count)
- {
- messages.Add("The build produced more files than expected.");
- }
- else if (different.Count > 0)
- {
- messages.Add("The build produced different files than expected.");
- }
- ComputeDifferences(expectedSet, different, messages);
- string.Join(Environment.NewLine, messages).Should().BeEmpty();
- static void ComputeDifferences(HashSet<string> existingSet, HashSet<string> different, List<string> messages)
- {
- foreach (var file in different)
- {
- if (existingSet.Contains(file))
- {
- messages.Add($"The file '{file}' is not in the baseline.");
- }
- else
- {
- messages.Add($"The file '{file}' is missing from the build.");
- }
- }
- }
- }
- protected void AssertPublishAssets(
- StaticWebAssetsManifest manifest,
- string publishFolder,
- string intermediateOutputPath,
- string suffix = "",
- [CallerMemberName] string name = "")
- {
- var fileEnumerationOptions = new EnumerationOptions { RecurseSubdirectories = true };
- string wwwRootFolder = Path.Combine(publishFolder, "wwwroot");
- var wwwRootFiles = Directory.Exists(wwwRootFolder) ?
- Directory.GetFiles(wwwRootFolder, "*", fileEnumerationOptions)
- .Select(f => _baselineFactory.TemplatizeFilePath(f, null, null, intermediateOutputPath, publishFolder, null)) :
- [];
- // Computed publish assets must exist on disk (we do this check to quickly identify when something is not being
- // generated vs when its being copied to the wrong place)
- var computedFiles = manifest.Assets
- .Where(a => a.SourceType is StaticWebAsset.SourceTypes.Computed &&
- a.AssetKind is not StaticWebAsset.AssetKinds.Build);
- // For assets that are copied to the publish folder, the path is always based on
- // the wwwroot folder, the relative path and the base path for project or package
- // assets.
- var copyToPublishDirectoryFiles = manifest.Assets
- .Where(a => !string.Equals(a.SourceId, manifest.Source, StringComparison.Ordinal) ||
- !string.Equals(a.AssetMode, StaticWebAsset.AssetModes.Reference))
- .Select(a => Path.Combine(wwwRootFolder, a.ComputeTargetPath("", Path.DirectorySeparatorChar)));
- var existingFiles = _baselineFactory.TemplatizeExpectedFiles(
- [.. wwwRootFiles
- .Concat(computedFiles.Select(a => a.Identity))
- .Concat(copyToPublishDirectoryFiles)
- .Distinct()
- .OrderBy(f => f, StringComparer.Ordinal)],
- TestContext.Current.NuGetCachePath,
- ProjectDirectory.TestRoot,
- intermediateOutputPath,
- publishFolder);
- if (!_generateBaselines)
- {
- var expected = LoadExpectedFilesBaseline(manifest.ManifestType, suffix, name);
- existingFiles.Should().BeEquivalentTo(expected);
- }
- else
- {
- File.WriteAllText(
- GetExpectedFilesPath(suffix, name, manifest.ManifestType),
- JsonSerializer.Serialize(existingFiles, BaselineSerializationOptions));
- }
- }
- public string[] LoadExpectedFilesBaseline(
- string type,
- string suffix,
- string name)
- {
- if (!_generateBaselines)
- {
- using var filesBaselineStream = GetExpectedFilesEmbeddedResource(suffix, name, type);
- return JsonSerializer.Deserialize<string[]>(filesBaselineStream);
- }
- else
- {
- return [];
- }
- }
- internal void AssertManifest(
- StaticWebAssetsManifest actual,
- StaticWebAssetsManifest expected,
- string suffix = "",
- string runtimeIdentifier = null,
- [CallerMemberName] string name = "")
- {
- if (!_generateBaselines)
- {
- // We are going to compare the generated manifest with the current manifest.
- // For that, we "templatize" the current manifest to avoid issues with hashes, versions, etc.
- _baselineFactory.ToTemplate(
- actual,
- ProjectDirectory.Path,
- TestContext.Current.NuGetCachePath,
- runtimeIdentifier);
- _comparer.AssertManifest(expected, actual);
- }
- else
- {
- var template = Templatize(actual, ProjectDirectory.Path, TestContext.Current.NuGetCachePath, runtimeIdentifier);
- if (!Directory.Exists(Path.Combine(BaselinesFolder)))
- {
- Directory.CreateDirectory(Path.Combine(BaselinesFolder));
- }
- File.WriteAllText(GetManifestPath(suffix, name, actual.ManifestType), template);
- }
- }
- private string GetManifestPath(string suffix, string name, string manifestType)
- => Path.Combine(BaselinesFolder, $"{name}{(!string.IsNullOrEmpty(suffix) ? $"_{suffix}" : "")}.{manifestType}.staticwebassets.json");
- private Stream GetManifestEmbeddedResource(string suffix, string name, string manifestType)
- => TestAssembly.GetManifestResourceStream(string.Join('.', EmbeddedResourcePrefix, $"{name}{(!string.IsNullOrEmpty(suffix) ? $"_{suffix}" : "")}.{manifestType}.staticwebassets.json"));
- private string GetExpectedFilesPath(string suffix, string name, string manifestType)
- => Path.Combine(BaselinesFolder, $"{name}{(!string.IsNullOrEmpty(suffix) ? $"_{suffix}" : "")}.{manifestType}.files.json");
- private Stream GetExpectedFilesEmbeddedResource(string suffix, string name, string manifestType)
- => TestAssembly.GetManifestResourceStream(string.Join('.', EmbeddedResourcePrefix, $"{name}{(!string.IsNullOrEmpty(suffix) ? $"_{suffix}" : "")}.{manifestType}.files.json"));
- private string Templatize(StaticWebAssetsManifest manifest, string projectRoot, string restorePath, string runtimeIdentifier)
- {
- _baselineFactory.ToTemplate(manifest, projectRoot, restorePath, runtimeIdentifier);
- return JsonSerializer.Serialize(manifest, BaselineSerializationOptions);
- }
- }
- }
|