diff --git a/src/Assets/TestProjects/SampleManifest/Sample2.json b/src/Assets/TestProjects/SampleManifest/Sample2.json index ee8cc0b7a84f..c0d8578f2146 100644 --- a/src/Assets/TestProjects/SampleManifest/Sample2.json +++ b/src/Assets/TestProjects/SampleManifest/Sample2.json @@ -1,5 +1,5 @@ { - "version": "5.0.0-preview1", + "version": "1.0.0", "workloads": { "xamarin-android": { "description": "Create, build and run Android apps", @@ -58,7 +58,7 @@ "version": "8.4.0" }, "Xamarin.Android.Runtime": { - "kind": "framework", + "kind": "library", "version": "8.4.7.4" }, "Xamarin.Android.BuildTools": { diff --git a/src/Assets/TestProjects/SampleManifest/Sample2_v2.json b/src/Assets/TestProjects/SampleManifest/Sample2_v2.json new file mode 100644 index 000000000000..5ea2c44d96bf --- /dev/null +++ b/src/Assets/TestProjects/SampleManifest/Sample2_v2.json @@ -0,0 +1,82 @@ +{ + "version": "2.0.0", + "workloads": { + "xamarin-android": { + "description": "Create, build and run Android apps", + "kind": "dev", + "packs": [ + "Xamarin.Android.Templates" + ], + "extends": [ + "xamarin-android-build" + ] + }, + "xamarin-android-build": { + "description": "Build and run Android apps", + "packs": [ + "Xamarin.Android.Sdk", + "Xamarin.Android.Framework", + "Xamarin.Android.Runtime" + ] + }, + "android-sdk-workload":{ + "description": "Test workload", + "packs": [ + "Xamarin.Android.Sdk" + ] + }, + "android-templates-workload":{ + "description": "Test workload", + "packs": [ + "Xamarin.Android.Templates" + ] + }, + "android-buildtools-workload":{ + "description": "Test workload", + "packs": [ + "Xamarin.Android.BuildTools" + ] + }, + "xamarin-empty-mock": { + "description": "Empty mock workload for testing", + "kind": "dev", + "packs": [], + "extends": [] + } + }, + "packs": { + "Xamarin.Android.Sdk": { + "kind": "sdk", + "version": "8.4.7" + }, + "Xamarin.Android.Templates": { + "kind": "template", + "version": "1.1.0" + }, + "Xamarin.Android.Framework": { + "kind": "framework", + "version": "8.5.0" + }, + "Xamarin.Android.Runtime": { + "kind": "library", + "version": "8.5.0.1" + }, + "Xamarin.Android.BuildTools": { + "version": "8.5.0", + "kind": "sdk", + "alias-to": { + "osx": "Xamarin.Android.BuildTools.MacHost", + "win": "Xamarin.Android.BuildTools.WinHost", + "linux": "Xamarin.Android.BuildTools.LinuxHost" + } + }, + "Test.Pack.A": { + "version": "1.0.0", + "kind": "sdk" + }, + "Test.Pack.B": { + "version": "2.0.0", + "kind": "framework" + } + } +} diff --git a/src/Assets/TestProjects/SampleManifest/Sample2_v3.json b/src/Assets/TestProjects/SampleManifest/Sample2_v3.json new file mode 100644 index 000000000000..b12995ea4c8c --- /dev/null +++ b/src/Assets/TestProjects/SampleManifest/Sample2_v3.json @@ -0,0 +1,82 @@ +{ + "version": "3.0.0", + "workloads": { + "xamarin-android": { + "description": "Create, build and run Android apps", + "kind": "dev", + "packs": [ + "Xamarin.Android.Templates" + ], + "extends": [ + "xamarin-android-build" + ] + }, + "xamarin-android-build": { + "description": "Build and run Android apps", + "packs": [ + "Xamarin.Android.Sdk", + "Xamarin.Android.Framework", + "Xamarin.Android.Runtime" + ] + }, + "android-sdk-workload":{ + "description": "Test workload", + "packs": [ + "Xamarin.Android.Sdk" + ] + }, + "android-templates-workload":{ + "description": "Test workload", + "packs": [ + "Xamarin.Android.Templates" + ] + }, + "android-buildtools-workload":{ + "description": "Test workload", + "packs": [ + "Xamarin.Android.BuildTools" + ] + }, + "xamarin-empty-mock": { + "description": "Empty mock workload for testing", + "kind": "dev", + "packs": [], + "extends": [] + } + }, + "packs": { + "Xamarin.Android.Sdk": { + "kind": "sdk", + "version": "8.4.7" + }, + "Xamarin.Android.Templates": { + "kind": "template", + "version": "1.2.0" + }, + "Xamarin.Android.Framework": { + "kind": "framework", + "version": "8.6.0" + }, + "Xamarin.Android.Runtime": { + "kind": "library", + "version": "8.6.0.0" + }, + "Xamarin.Android.BuildTools": { + "version": "8.6.0", + "kind": "sdk", + "alias-to": { + "osx": "Xamarin.Android.BuildTools.MacHost", + "win": "Xamarin.Android.BuildTools.WinHost", + "linux": "Xamarin.Android.BuildTools.LinuxHost" + } + }, + "Test.Pack.A": { + "version": "1.0.0", + "kind": "sdk" + }, + "Test.Pack.B": { + "version": "2.0.0", + "kind": "framework" + } + } +} diff --git a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs index b6bd4bda6343..b9a921981fcc 100644 --- a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs +++ b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs @@ -1233,7 +1233,7 @@ private void CreateMockWorkloadSet(string manifestRoot, string featureBand, stri private string CreateMockInstallState(string featureBand, string installStateContents) { - var installStateFolder = Path.Combine(_fakeDotnetRootDirectory!, "metadata", "workloads", "8.0.200", "InstallState"); + var installStateFolder = Path.Combine(_fakeDotnetRootDirectory!, "metadata", "workloads", featureBand, "InstallState"); Directory.CreateDirectory(installStateFolder); string installStatePath = Path.Combine(installStateFolder, "default.json"); diff --git a/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs b/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs index 64820fc7cc8e..3a98644e69ee 100644 --- a/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs +++ b/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs @@ -220,101 +220,6 @@ public void GivenManagedInstallItCanRollBackInstallFailures() Directory.Exists(Path.Combine(dotnetRoot, "packs", packId, packVersion)).Should().BeFalse(); } - [Fact] - public void GivenManagedInstallItCanGarbageCollect() - { - var (dotnetRoot, installer, _, getResolver) = GetTestInstaller(); - var packs = new PackInfo[] - { - CreatePackInfo("Xamarin.Android.Sdk", "8.4.7", WorkloadPackKind.Library, Path.Combine(dotnetRoot, "library-packs", "Xamarin.Android.Sdk.8.4.7.nupkg"), "Xamarin.Android.Sdk"), - CreatePackInfo("Xamarin.Android.Framework", "8.4.0", WorkloadPackKind.Framework, Path.Combine(dotnetRoot, "packs", "Xamarin.Android.Framework", "8.4.0"), "Xamarin.Android.Framework") - }; - var sdkVersions = new WorkloadId[] { new WorkloadId("6.0.100"), new WorkloadId("6.0.300") }; - - // Write fake packs - var installedPacksPath = Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1"); - foreach (var sdkVersion in sdkVersions) - { - foreach (var pack in packs) - { - var packRecordPath = Path.Combine(installedPacksPath, pack.Id, pack.Version, sdkVersion); - Directory.CreateDirectory(Path.GetDirectoryName(packRecordPath)); - var packRecordContents = JsonSerializer.Serialize(pack); - File.WriteAllText(packRecordPath, packRecordContents); - if (pack.Kind == WorkloadPackKind.Library) - { - Directory.CreateDirectory(Path.GetDirectoryName(pack.Path)); - using var _ = File.Create(pack.Path); - } - else - { - Directory.CreateDirectory(pack.Path); - } - } - } - // Write fake install record for 6.0.300 - var workloadsRecordPath = Path.Combine(dotnetRoot, "metadata", "workloads", sdkVersions[1], "InstalledWorkloads"); - Directory.CreateDirectory(workloadsRecordPath); - File.Create(Path.Combine(workloadsRecordPath, "xamarin-empty-mock")); - - installer.GarbageCollect(getResolver); - - Directory.EnumerateFileSystemEntries(installedPacksPath) - .Should() - .BeEmpty(); - foreach (var pack in packs) - { - if (pack.Kind == WorkloadPackKind.Library) - { - File.Exists(pack.Path).Should().BeFalse(); - } - else - { - Directory.Exists(pack.Path) - .Should() - .BeFalse(); - } - } - } - - [Fact] - public void GivenManagedInstallItCanGarbageCollectPacksMissingFromManifest() - { - var (dotnetRoot, installer, _, getResolver) = GetTestInstaller(); - // Define packs that don't show up in the manifest - var packs = new PackInfo[] - { - CreatePackInfo("Xamarin.Android.Sdk.fake", "8.4.7", WorkloadPackKind.Framework, Path.Combine(dotnetRoot, "packs", "Xamarin.Android.Sdk.fake", "8.4.7"), "Xamarin.Android.Sdk.fake"), - CreatePackInfo("Xamarin.Android.Framework.mock", "8.4", WorkloadPackKind.Framework, Path.Combine(dotnetRoot, "packs", "Xamarin.Android.Framework.mock", "8.4"), "Xamarin.Android.Framework.mock") - }; - var sdkVersions = new WorkloadId[] { new WorkloadId("6.0.100"), new WorkloadId("6.0.300") }; - - // Write fake packs - var installedPacksPath = Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1"); - foreach (var sdkVersion in sdkVersions) - { - foreach (var pack in packs) - { - var packRecordPath = Path.Combine(installedPacksPath, pack.Id, pack.Version, sdkVersion); - Directory.CreateDirectory(Path.GetDirectoryName(packRecordPath)); - File.WriteAllText(packRecordPath, JsonSerializer.Serialize(pack)); - Directory.CreateDirectory(pack.Path); - } - } - - installer.GarbageCollect(getResolver); - - Directory.EnumerateFileSystemEntries(installedPacksPath) - .Should() - .BeEmpty(); - foreach (var pack in packs) - { - Directory.Exists(pack.Path) - .Should() - .BeFalse(); - } - } - [Fact] public void GivenManagedInstallItDoesNotRemovePacksWithInstallRecords() { @@ -484,11 +389,11 @@ public void GivenManagedInstallItCanErrorsWhenMissingOfflineCache() var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot); var sdkFeatureBand = new SdkFeatureBand("6.0.300"); - IWorkloadResolver GetResolver(string sdkVersion) + IWorkloadResolver GetResolver(string workloadSetVersion) { - if (sdkVersion != null && !sdkFeatureBand.Equals(new SdkFeatureBand(sdkVersion))) + if (workloadSetVersion != null && !sdkFeatureBand.Equals(new SdkFeatureBand(workloadSetVersion))) { - throw new NotSupportedException("Mock doesn't support creating resolver for different feature bands: " + sdkVersion); + throw new NotSupportedException("Mock doesn't support creating resolver for different feature bands: " + workloadSetVersion); } return workloadResolver; } diff --git a/src/Tests/dotnet-workload-install.Tests/WorkloadGarbageCollectionTests.cs b/src/Tests/dotnet-workload-install.Tests/WorkloadGarbageCollectionTests.cs new file mode 100644 index 000000000000..7946e7e3ce5a --- /dev/null +++ b/src/Tests/dotnet-workload-install.Tests/WorkloadGarbageCollectionTests.cs @@ -0,0 +1,419 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using ManifestReaderTests; +using Microsoft.DotNet.Cli.NuGetPackageDownloader; +using Microsoft.DotNet.ToolPackage; +using Microsoft.DotNet.Workloads.Workload.Install; +using Microsoft.NET.Sdk.WorkloadManifestReader; +using NuGet.Versioning; +using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadResolver; +using Microsoft.Extensions.EnvironmentAbstractions; +using System.Text.Json; +using Microsoft.TemplateEngine.Edge.Constraints; + +namespace Microsoft.DotNet.Cli.Workload.Install.Tests +{ + public class WorkloadGarbageCollectionTests : SdkTest + { + private readonly BufferedReporter _reporter; + private string _testDirectory; + private string _dotnetRoot; + + public WorkloadGarbageCollectionTests(ITestOutputHelper log) : base(log) + { + _reporter = new BufferedReporter(); + } + + [Fact] + public void GivenManagedInstallItCanGarbageCollect() + { + CreateMockManifest("testmanifest", "1.0.0", "6.0.100", sourceManifestName: @"Sample2.json"); + CreateMockManifest("testmanifest", "2.0.0", "6.0.300", sourceManifestName: @"Sample2_v2.json"); + + var (installer, getResolver) = GetTestInstaller(); + var packsToKeep = new PackInfo[] + { + CreatePackInfo("Xamarin.Android.Sdk", "8.4.7", WorkloadPackKind.Sdk), + CreatePackInfo("Xamarin.Android.Framework", "8.5.0", WorkloadPackKind.Framework), + CreatePackInfo("Xamarin.Android.Runtime", "8.5.0.1", WorkloadPackKind.Library) + }; + + var packsToCollect = new PackInfo[] + { + CreatePackInfo("Xamarin.Android.Framework", "8.4.0", WorkloadPackKind.Framework), + CreatePackInfo("Xamarin.Android.Runtime", "8.4.7.4", WorkloadPackKind.Library) + }; + var sdkVersions = new [] { "6.0.100", "6.0.300" }; + + // Write packs + foreach (var sdkVersion in sdkVersions) + { + foreach (var pack in packsToKeep.Concat(packsToCollect)) + { + CreateInstalledPack(pack, sdkVersion); + } + } + + // Write workload install record for 6.0.300 + var workloadsRecordPath = Path.Combine(_dotnetRoot, "metadata", "workloads", sdkVersions[1], "InstalledWorkloads"); + Directory.CreateDirectory(workloadsRecordPath); + File.Create(Path.Combine(workloadsRecordPath, "xamarin-android-build")); + + installer.GarbageCollect(getResolver); + + foreach (var pack in packsToCollect) + { + PackShouldExist(pack, false); + PackRecord(pack, "6.0.100").Should().NotExist(); + PackRecord(pack, "6.0.300").Should().NotExist(); + } + foreach (var pack in packsToKeep) + { + PackShouldExist(pack, true); + PackRecord(pack, "6.0.100").Should().NotExist(); + PackRecord(pack, "6.0.300").Should().Exist(); + } + } + + [Fact] + public void GivenManagedInstallItCanGarbageCollectPacksMissingFromManifest() + { + CreateMockManifest("testmanifest", "1.0.0"); + var (installer, getResolver) = GetTestInstaller(); + // Define packs that don't show up in the manifest + var packs = new PackInfo[] + { + CreatePackInfo("Xamarin.Android.Sdk.fake", "8.4.7", WorkloadPackKind.Framework), + CreatePackInfo("Xamarin.Android.Framework.mock", "8.4", WorkloadPackKind.Framework) + }; + var sdkVersions = new WorkloadId[] { new WorkloadId("6.0.100"), new WorkloadId("6.0.300") }; + + // Write fake packs + var installedPacksPath = Path.Combine(_dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1"); + foreach (var sdkVersion in sdkVersions) + { + foreach (var pack in packs) + { + CreateInstalledPack(pack, sdkVersion); + } + } + + installer.GarbageCollect(getResolver); + + Directory.EnumerateFileSystemEntries(installedPacksPath) + .Should() + .BeEmpty(); + foreach (var pack in packs) + { + PackShouldExist(pack, false); + } + } + + [Fact] + public void GarbageCollectManifests() + { + // ARRANGE + // Create different versions of a manifest, one with a previous feature band + CreateMockManifest("testmanifest", "1.0.0", "6.0.100", sourceManifestName: @"Sample2.json"); + CreateMockManifest("testmanifest", "2.0.0", "6.0.300", sourceManifestName: @"Sample2_v2.json"); + CreateMockManifest("testmanifest", "3.0.0", "6.0.300", sourceManifestName: @"Sample2_v3.json"); + + // Create manifest installation records (all for "current" version of SDK: 6.0.300) + CreateManifestRecord("testmanifest", "1.0.0", "6.0.100", "6.0.300"); + CreateManifestRecord("testmanifest", "2.0.0", "6.0.300", "6.0.300"); + CreateManifestRecord("testmanifest", "3.0.0", "6.0.300", "6.0.300"); + + var (installer, getResolver) = GetTestInstaller("6.0.300"); + + // Write workload install record for xamarin-android-build workload for 6.0.300 + var workloadsRecordPath = Path.Combine(_dotnetRoot, "metadata", "workloads", "6.0.300", "InstalledWorkloads"); + Directory.CreateDirectory(workloadsRecordPath); + File.Create(Path.Combine(workloadsRecordPath, "xamarin-android-build")); + + // These packs are referenced by xamarin-android-build from the 3.0 manifest, which is the latest one and therefore the one that will be kept + var packsToKeep = new PackInfo[] + { + CreatePackInfo("Xamarin.Android.Sdk", "8.4.7", WorkloadPackKind.Sdk), + CreatePackInfo("Xamarin.Android.Framework", "8.6.0", WorkloadPackKind.Framework), + CreatePackInfo("Xamarin.Android.Runtime", "8.6.0.0", WorkloadPackKind.Library) + }; + + // These packs are referenced by earlier versions of the manifest, and should be garbage collected + var packsToCollect = new PackInfo[] + { + CreatePackInfo("Xamarin.Android.Framework", "8.4.0", WorkloadPackKind.Framework), + CreatePackInfo("Xamarin.Android.Runtime", "8.4.7.4", WorkloadPackKind.Library) + }; + + // Create packs and installation records + foreach (var pack in packsToKeep.Concat(packsToCollect)) + { + CreateInstalledPack(pack, "6.0.300"); + } + + // ACT: garbage collect + installer.GarbageCollect(getResolver); + + // ASSERT + + // Only the latest manifest version and its installation record should be kept + ManifestRecord("testmanifest", "1.0.0", "6.0.100", "6.0.300").Should().NotExist(); + ManifestRecord("testmanifest", "2.0.0", "6.0.300", "6.0.300").Should().NotExist(); + ManifestRecord("testmanifest", "3.0.0", "6.0.300", "6.0.300").Should().Exist(); + + new FileInfo(Path.Combine(_dotnetRoot, "sdk-manifests", "6.0.100", "testmanifest", "1.0.0", "WorkloadManifest.json")).Should().NotExist(); + new FileInfo(Path.Combine(_dotnetRoot, "sdk-manifests", "6.0.300", "testmanifest", "2.0.0", "WorkloadManifest.json")).Should().NotExist(); + new FileInfo(Path.Combine(_dotnetRoot, "sdk-manifests", "6.0.300", "testmanifest", "3.0.0", "WorkloadManifest.json")).Should().Exist(); + + // Packs which should be collected should be removed, as well as their installation records + foreach (var pack in packsToCollect) + { + PackShouldExist(pack, false); + PackRecord(pack, "6.0.300").Should().NotExist(); + } + // Packs which should be kept should still exist, as well as their installation records + foreach (var pack in packsToKeep) + { + PackShouldExist(pack, true); + PackRecord(pack, "6.0.300").Should().Exist(); + } + } + + [Fact] + public void GarbageCollectManifestsWithInstallState() + { + // ARRANGE + // Create different versions of a manifest, one with a previous feature band + CreateMockManifest("testmanifest", "1.0.0", "6.0.100", sourceManifestName: @"Sample2.json"); + CreateMockManifest("testmanifest", "2.0.0", "6.0.300", sourceManifestName: @"Sample2_v2.json"); + CreateMockManifest("testmanifest", "3.0.0", "6.0.300", sourceManifestName: @"Sample2_v3.json"); + + // Create manifest installation records (all for "current" version of SDK: 6.0.300) + CreateManifestRecord("testmanifest", "1.0.0", "6.0.100", "6.0.300"); + CreateManifestRecord("testmanifest", "2.0.0", "6.0.300", "6.0.300"); + CreateManifestRecord("testmanifest", "3.0.0", "6.0.300", "6.0.300"); + + // Create install state pinning the 2.0.0 version of the manifest + CreateInstallState("6.0.300", + """ + { + "manifests": { + "testmanifest": "2.0.0/6.0.300", + } + } + """); + + var (installer, getResolver) = GetTestInstaller("6.0.300"); + + // Write workload install record for xamarin-android-build workload for 6.0.300 + var workloadsRecordPath = Path.Combine(_dotnetRoot, "metadata", "workloads", "6.0.300", "InstalledWorkloads"); + Directory.CreateDirectory(workloadsRecordPath); + File.Create(Path.Combine(workloadsRecordPath, "xamarin-android-build")); + + // These packs are referenced by xamarin-android-build from the 2.0 manifest, which is the one that should be kept due to the install state + var packsToKeep = new PackInfo[] + { + CreatePackInfo("Xamarin.Android.Sdk", "8.4.7", WorkloadPackKind.Sdk), + CreatePackInfo("Xamarin.Android.Framework", "8.5.0", WorkloadPackKind.Framework), + CreatePackInfo("Xamarin.Android.Runtime", "8.5.0.1", WorkloadPackKind.Library) + }; + + // These packs are referenced by version 3.0 of the manifest, and should be garbage collected + var packsToCollect = new PackInfo[] + { + CreatePackInfo("Xamarin.Android.Framework", "8.6.0", WorkloadPackKind.Framework), + CreatePackInfo("Xamarin.Android.Runtime", "8.6.0.0", WorkloadPackKind.Library) + }; + + // Create packs and installation records + foreach (var pack in packsToKeep.Concat(packsToCollect)) + { + CreateInstalledPack(pack, "6.0.300"); + } + + // ACT: garbage collect + installer.GarbageCollect(getResolver); + + // ASSERT + + // Only the pinned manifest version (2.0.0) and its installation record should be kept + ManifestRecord("testmanifest", "1.0.0", "6.0.100", "6.0.300").Should().NotExist(); + ManifestRecord("testmanifest", "2.0.0", "6.0.300", "6.0.300").Should().Exist(); + ManifestRecord("testmanifest", "3.0.0", "6.0.300", "6.0.300").Should().NotExist(); + + new FileInfo(Path.Combine(_dotnetRoot, "sdk-manifests", "6.0.100", "testmanifest", "1.0.0", "WorkloadManifest.json")).Should().NotExist(); + new FileInfo(Path.Combine(_dotnetRoot, "sdk-manifests", "6.0.300", "testmanifest", "2.0.0", "WorkloadManifest.json")).Should().Exist(); + new FileInfo(Path.Combine(_dotnetRoot, "sdk-manifests", "6.0.300", "testmanifest", "3.0.0", "WorkloadManifest.json")).Should().NotExist(); + + // Packs which should be collected should be removed, as well as their installation records + foreach (var pack in packsToCollect) + { + PackShouldExist(pack, false); + PackRecord(pack, "6.0.300").Should().NotExist(); + } + // Packs which should be kept should still exist, as well as their installation records + foreach (var pack in packsToKeep) + { + PackShouldExist(pack, true); + PackRecord(pack, "6.0.300").Should().Exist(); + } + + } + + // TODO: Additional scenarios to add tests for once workload sets are added: + // Garbage collect workload sets + // Garbage collect with install state with workload set + // Don't garbage collect baseline workload set + + void PackShouldExist(PackInfo pack, bool shouldExist) + { + if (pack.Kind == WorkloadPackKind.Library) + { + if (shouldExist) + { + new FileInfo(pack.Path).Should().Exist(); + } + else + { + new FileInfo(pack.Path).Should().NotExist(); + } + } + else + { + if (shouldExist) + { + new DirectoryInfo(pack.Path).Should().Exist(); + } + else + { + new DirectoryInfo(pack.Path).Should().NotExist(); + } + } + } + + PackInfo CreatePackInfo(string id, string version, WorkloadPackKind kind, string resolvedPackageId = null) + { + if (resolvedPackageId == null) + { + resolvedPackageId = id; + } + + string path; + if (kind == WorkloadPackKind.Library) + { + path = Path.Combine(_dotnetRoot, "library-packs", $"{resolvedPackageId}.{version}.nupkg"); + } + else + { + path = Path.Combine(_dotnetRoot, "packs", id, version); + } + + return new PackInfo(new WorkloadPackId(id), version, kind, path, resolvedPackageId); + } + + FileInfo PackRecord(PackInfo pack, string sdkFeatureBand) + { + var installedPacksPath = Path.Combine(_dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1"); + var packRecordPath = Path.Combine(installedPacksPath, pack.Id, pack.Version, sdkFeatureBand); + return new FileInfo(packRecordPath); + } + + + private void CreateInstalledPack(PackInfo pack, string sdkFeatureBand) + { + // Write pack installation record + var packRecordPath = PackRecord(pack, sdkFeatureBand); + Directory.CreateDirectory(Path.GetDirectoryName(packRecordPath.FullName)); + var packRecordContents = JsonSerializer.Serialize(pack); + File.WriteAllText(packRecordPath.FullName, packRecordContents); + + // Create fake pack install + if (pack.Kind == WorkloadPackKind.Library) + { + Directory.CreateDirectory(Path.GetDirectoryName(pack.Path)); + using var _ = File.Create(pack.Path); + } + else + { + Directory.CreateDirectory(pack.Path); + } + } + + private void CreateInstallState(string featureBand, string installStateContents) + { + var installStateFolder = Path.Combine(_dotnetRoot!, "metadata", "workloads", featureBand, "InstallState"); + Directory.CreateDirectory(installStateFolder); + + string installStatePath = Path.Combine(installStateFolder, "default.json"); + + File.WriteAllText(installStatePath, installStateContents); + } + + private void CreateDotnetRoot([CallerMemberName]string testName = "", string identifier = "") + { + if (_dotnetRoot == null) + { + _testDirectory = _testAssetsManager.CreateTestDirectory(testName, identifier: identifier).Path; + _dotnetRoot = Path.Combine(_testDirectory, "dotnet"); + } + } + + private void CreateMockManifest(string manifestId, string manifestVersion, string featureBand = "6.0.300", bool useVersionFolder = true, + string sourceManifestName = "Sample2.json", [CallerMemberName] string testName = "", string identifier = "") + { + CreateDotnetRoot(testName, identifier); + + var manifestDirectory = Path.Combine(_dotnetRoot, "sdk-manifests", featureBand, manifestId); + if (useVersionFolder) + { + manifestDirectory = Path.Combine(manifestDirectory, manifestVersion); + } + + Directory.CreateDirectory(manifestDirectory); + + string manifestSourcePath = Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest"), sourceManifestName); + + File.Copy(manifestSourcePath, Path.Combine(manifestDirectory, "WorkloadManifest.json")); + } + + private FileInfo ManifestRecord(string manifestId, string manifestVersion, string manifestFeatureBand, string referencingFeatureBand) + { + return new FileInfo(Path.Combine(_dotnetRoot, "metadata", "workloads", "InstalledManifests", "v1", manifestId.ToLowerInvariant(), manifestVersion, manifestFeatureBand, referencingFeatureBand)); + } + + private void CreateManifestRecord(string manifestId, string manifestVersion, string manifestFeatureBand, string referencingFeatureBand) + { + var path = ManifestRecord(manifestId, manifestVersion, manifestFeatureBand, referencingFeatureBand); + path.Directory.Create(); + using var _ = path.Create(); + } + + private (FileBasedInstaller, Func) GetTestInstaller(string sdkVersion = "6.0.300", [CallerMemberName] string testName = "", string identifier = "") + { + CreateDotnetRoot(testName, identifier); + var sdkFeatureBand = new SdkFeatureBand(sdkVersion); + + INuGetPackageDownloader nugetInstaller = new MockNuGetPackageDownloader(_dotnetRoot, manifestDownload: true); + + var manifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetRoot, sdkVersion, userProfileDir: null, globalJsonPath: null); + + var workloadResolver = WorkloadResolver.CreateForTests(manifestProvider, _dotnetRoot); + + + IWorkloadResolver GetResolver(string workloadSetVersion) + { + if (workloadSetVersion != null && !sdkFeatureBand.Equals(new SdkFeatureBand(workloadSetVersion))) + { + throw new NotSupportedException("Mock doesn't support creating resolver for different feature bands: " + workloadSetVersion); + } + return workloadResolver; + } + + var installer = new FileBasedInstaller(_reporter, sdkFeatureBand, workloadResolver, userProfileDir: _testDirectory, nugetInstaller, _dotnetRoot, packageSourceLocation: null); + + return (installer, GetResolver); + } + } +}