File: ManifestTests.cs
Web Access
Project: ..\..\..\test\Microsoft.NET.Sdk.WorkloadManifestReader.Tests\Microsoft.NET.Sdk.WorkloadManifestReader.Tests.csproj (Microsoft.NET.Sdk.WorkloadManifestReader.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using Microsoft.NET.Sdk.WorkloadManifestReader;
 
namespace ManifestReaderTests
{
    public class ManifestTests : SdkTest
    {
        private const string fakeRootPath = "fakeRootPath";
        private readonly string ManifestPath;
        private readonly string SampleProjectPath;
 
        public ManifestTests(ITestOutputHelper log) : base(log)
        {
            SampleProjectPath = _testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest");
            ManifestPath = GetSampleManifestPath("Sample.json");
        }
 
        string GetSampleManifestPath(string name) => Path.Combine(SampleProjectPath, name);
 
        [Fact]
        public void ItCanDeserialize()
        {
            using (FileStream fsSource = new(ManifestPath, FileMode.Open, FileAccess.Read))
            {
                var result = WorkloadManifestReader.ReadWorkloadManifest("Sample", fsSource, ManifestPath);
                result.Version.Should().Be("5.0.0-preview1");
                var xamAndroidId = new WorkloadPackId("Xamarin.Android.Sdk");
 
                result.Packs[xamAndroidId].Id.Should().Be(xamAndroidId);
                result.Packs[xamAndroidId].IsAlias.Should().Be(false);
                result.Packs[xamAndroidId].Kind.Should().Be(WorkloadPackKind.Sdk);
                result.Packs[xamAndroidId].Version.Should().Be("8.4.7");
            }
        }
 
        [Fact]
        public void AliasedPackPath()
        {
            var manifestProvider = new FakeManifestProvider(ManifestPath);
            var resolver = WorkloadResolver.CreateForTests(manifestProvider, fakeRootPath);
 
            resolver.ReplaceFilesystemChecksForTest(_ => true, _ => true);
 
            var buildToolsPack = resolver.GetInstalledWorkloadPacksOfKind(WorkloadPackKind.Sdk).FirstOrDefault(pack => pack.Id == "Xamarin.Android.BuildTools");
 
            buildToolsPack.Should().NotBeNull();
            buildToolsPack!.Id.ToString().Should().Be("Xamarin.Android.BuildTools");
            buildToolsPack.Version.Should().Be("8.4.7");
            buildToolsPack.Path.Should().Be(Path.Combine(fakeRootPath, "packs", "Xamarin.Android.BuildTools.Win64Host", "8.4.7"));
        }
 
        [Fact]
        public void UnresolvedAliasedPackPath()
        {
            var manifestProvider = new FakeManifestProvider(ManifestPath);
            var resolver = WorkloadResolver.CreateForTests(manifestProvider, fakeRootPath, currentRuntimeIdentifiers: new[] { "fake-platform" });
 
            resolver.ReplaceFilesystemChecksForTest(_ => true, _ => true);
 
            var buildToolsPack = resolver.GetInstalledWorkloadPacksOfKind(WorkloadPackKind.Sdk).FirstOrDefault(pack => pack.Id == "Xamarin.Android.BuildTools");
 
            buildToolsPack.Should().BeNull();
        }
 
        [Fact]
        public void GivenMultiplePackRoots_ItUsesTheFirstInstallableIfThePackDoesntExist()
        {
            TestMultiplePackRoots(false, false);
        }
 
        [Fact]
        public void GivenMultiplePackRoots_ItUsesTheFirstOneIfBothExist()
        {
            TestMultiplePackRoots(true, true);
        }
 
        [Fact]
        public void GivenMultiplePackRoots_ItUsesTheFirstOneIfOnlyItExists()
        {
            TestMultiplePackRoots(false, true);
        }
 
        [Fact]
        public void GivenMultiplePackRoots_ItUsesTheSecondOneIfOnlyItExists()
        {
            TestMultiplePackRoots(true, false);
        }
 
        void TestMultiplePackRoots(bool defaultExists, bool additionalExists)
        {
            var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: defaultExists.ToString() + "_" + additionalExists.ToString()).Path;
            var dotnetRoot = Path.Combine(testDirectory, "dotnet");
            Directory.CreateDirectory(dotnetRoot);
            var additionalRoot = Path.Combine(testDirectory, "additionalPackRoot");
            Directory.CreateDirectory(additionalRoot);
 
            var defaultPackPath = Path.Combine(dotnetRoot, "packs", "Xamarin.Android.Sdk", "8.4.7");
            var additionalPackPath = Path.Combine(additionalRoot, "packs", "Xamarin.Android.Sdk", "8.4.7");
 
            if (defaultExists)
            {
                Directory.CreateDirectory(defaultPackPath);
            }
            if (additionalExists)
            {
                Directory.CreateDirectory(additionalPackPath);
            }
 
            var manifestProvider = new FakeManifestProvider(ManifestPath);
            var resolver = WorkloadResolver.CreateForTests(manifestProvider, [ new(additionalRoot, false), new(dotnetRoot, true), new("other", true) ]);
 
            var pack = resolver.TryGetPackInfo(new WorkloadPackId("Xamarin.Android.Sdk"));
            pack.Should().NotBeNull();
 
            string expectedPath = additionalExists ? additionalPackPath : defaultPackPath;
 
            pack!.Path.Should().Be(expectedPath);
        }
 
        [Fact]
        public void GivenNonExistentPackRoot_ItIgnoresIt()
        {
            var testDirectory = _testAssetsManager.CreateTestDirectory().Path;
            var dotnetRoot = Path.Combine(testDirectory, "dotnet");
            Directory.CreateDirectory(dotnetRoot);
            var additionalRoot = Path.Combine(testDirectory, "additionalPackRoot");
 
            var defaultPackPath = Path.Combine(dotnetRoot, "packs", "Xamarin.Android.Sdk", "8.4.7");
            Directory.CreateDirectory(defaultPackPath);
 
            var manifestProvider = new FakeManifestProvider(ManifestPath);
            var resolver = WorkloadResolver.CreateForTests(manifestProvider, [ new(additionalRoot, false), new(dotnetRoot, true) ]);
 
            var pack = resolver.TryGetPackInfo(new WorkloadPackId("Xamarin.Android.Sdk"));
            pack.Should().NotBeNull();
 
            pack!.Path.Should().Be(defaultPackPath);
        }
 
        [Fact]
        public void ItChecksDependencies()
        {
            static string MakeManifest(string version, params (string id, string version)[] dependsOn)
            {
                var sb = new StringBuilder();
                sb.AppendLine("{");
                sb.AppendFormat("  \"version\": \"{0}\"", version);
                sb.AppendLine(dependsOn.Length > 0 ? "," : "");
                if (dependsOn.Length > 0)
                {
                    sb.AppendLine("  \"depends-on\": {");
                    for (int i = 0; i < dependsOn.Length; i++)
                    {
                        var dep = dependsOn[i];
                        sb.AppendFormat("    \"{0}\": \"{1}\"", dep.id, dep.version);
                        sb.AppendLine(i < dependsOn.Length - 1 ? "," : "");
                    }
                    sb.AppendLine("  }");
                }
                sb.AppendLine("}");
                return sb.ToString();
            }
 
            var goodManifestProvider = new InMemoryFakeManifestProvider
            {
                {  "AAA", MakeManifest("20.0.0", ("BBB", "5.0.0"), ("CCC", "63.0.0"), ("DDD", "25.0.0")) },
                {  "BBB", MakeManifest("8.0.0", ("DDD", "22.0.0")) },
                {  "CCC", MakeManifest("63.0.0") },
                {  "DDD", MakeManifest("25.0.0") },
            };
 
            WorkloadResolver.CreateForTests(goodManifestProvider, fakeRootPath);
 
            var missingManifestProvider = new InMemoryFakeManifestProvider
            {
                {  "AAA", MakeManifest("20.0.0", ("BBB", "5.0.0"), ("CCC", "63.0.0"), ("DDD", "25.0.0")) }
            };
 
            var missingManifestResolver = WorkloadResolver.CreateForTests(missingManifestProvider, fakeRootPath);
 
            var missingManifestEx = Assert.Throws<WorkloadManifestCompositionException>(() => missingManifestResolver.GetAvailableWorkloads().ToList());
            Assert.StartsWith("Did not find workload manifest dependency 'BBB' required by manifest 'AAA'", missingManifestEx.Message);
 
            var inconsistentManifestProvider = new InMemoryFakeManifestProvider
            {
                {  "AAA", MakeManifest("20.0.0", ("BBB", "5.0.0"), ("CCC", "63.0.0"), ("DDD", "25.0.0")) },
                {  "BBB", MakeManifest("8.0.0", ("DDD", "39.0.0")) },
                {  "CCC", MakeManifest("63.0.0") },
                {  "DDD", MakeManifest("30.0.0") },
            };
 
            var inconsistentManifestResolver = WorkloadResolver.CreateForTests(inconsistentManifestProvider, fakeRootPath);
            var inconsistentManifestEx = Assert.Throws<WorkloadManifestCompositionException>(() => inconsistentManifestResolver.GetAvailableWorkloads().ToList());
            Assert.StartsWith("Workload manifest dependency 'DDD' version '30.0.0' is lower than version '39.0.0' required by manifest 'BBB'", inconsistentManifestEx.Message);
        }
 
        [Fact]
        public void WillNotLoadManifestWithNullAlias()
        {
            var manifestPath = GetSampleManifestPath("NullAliasError.json");
            using FileStream fsSource = new(manifestPath, FileMode.Open, FileAccess.Read);
 
            var ex = Assert.Throws<WorkloadManifestFormatException>(() => WorkloadManifestReader.ReadWorkloadManifest("NullAliasError", fsSource, manifestPath));
            Assert.Contains("Expected string value at offset", ex.Message);
        }
 
        [Fact]
        public void ItCanFindLocalizationCatalog()
        {
            string expected = MakePathNative("manifests/My.Manifest/localize/WorkloadManifest.pt-BR.json");
 
            string? locPath = WorkloadManifestReader.GetLocalizationCatalogFilePath(
                    "manifests/My.Manifest/WorkloadManifest.json",
                    CultureInfo.GetCultureInfo("pt-BR"),
                    s => true
                );
 
            Assert.Equal(expected, locPath);
        }
 
        [Fact]
        public void ItCanFindParentCultureLocalizationCatalog()
        {
            string expected = MakePathNative("manifests/My.Manifest/localize/WorkloadManifest.pt.json");
 
            string? locPath = WorkloadManifestReader.GetLocalizationCatalogFilePath(
                    "manifests/My.Manifest/WorkloadManifest.json",
                    CultureInfo.GetCultureInfo("pt-BR"),
                    s => s == expected
                );
 
            Assert.Equal(expected, locPath);
        }
 
        static string MakePathNative(string path) => path.Replace('/', Path.DirectorySeparatorChar);
 
        [Fact]
        public void ItCanLocalizeDescriptions()
        {
            var manifest = GetSampleManifestPath("Sample.json");
            var locCatalog = GetSampleManifestPath("Sample.loc.json");
 
            var provider = new FakeManifestProvider((manifest, locCatalog));
 
            var resolver = WorkloadResolver.CreateForTests(provider, fakeRootPath);
 
            var workloads = resolver.GetAvailableWorkloads().ToList();
 
            var xamAndroid = workloads.FirstOrDefault(w => w.Id == "xamarin-android");
            Assert.Equal("Localized description for xamarin-android", xamAndroid?.Description);
 
            var xamAndroidBuild = workloads.FirstOrDefault(w => w.Id == "xamarin-android-build");
            Assert.Equal("Localized description for xamarin-android-build", xamAndroidBuild?.Description);
        }
    }
}