diff --git a/src/Cli/dotnet/CliStrings.resx b/src/Cli/dotnet/CliStrings.resx index 58f8ae903efe..e3a268cf073f 100644 --- a/src/Cli/dotnet/CliStrings.resx +++ b/src/Cli/dotnet/CliStrings.resx @@ -416,6 +416,12 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + Tool '{0}' (version '{1}') is already installed. diff --git a/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs b/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs index d17874fdb2c8..8253562c236d 100644 --- a/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs +++ b/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs @@ -97,7 +97,7 @@ public ToolPackageInstance(PackageId id, ResolvedPackageVersion = Version; } - var toolConfiguration = DeserializeToolConfiguration(library, packageDirectory, _fileSystem); + var toolConfiguration = DeserializeToolConfiguration(library, packageDirectory, ResolvedPackageId, _fileSystem); Warnings = toolConfiguration.Warnings; var installPath = new VersionFolderPathResolver(PackageDirectory.Value).GetInstallPath(ResolvedPackageId.ToString(), ResolvedPackageVersion); @@ -165,17 +165,58 @@ public static ToolConfiguration GetToolConfiguration(PackageId id, { var lockFile = new LockFileFormat().Read(assetsJsonParentDirectory.WithFile(AssetsFileName).Value); var lockFileTargetLibrary = FindLibraryInLockFile(lockFile); - return DeserializeToolConfiguration(lockFileTargetLibrary, packageDirectory, fileSystem); + return DeserializeToolConfiguration(lockFileTargetLibrary, packageDirectory, id, fileSystem); } - private static ToolConfiguration DeserializeToolConfiguration(LockFileTargetLibrary library, DirectoryPath packageDirectory, IFileSystem fileSystem) + private static ToolConfiguration DeserializeToolConfiguration(LockFileTargetLibrary library, DirectoryPath packageDirectory, PackageId packageId, IFileSystem fileSystem) { try { var dotnetToolSettings = FindItemInTargetLibrary(library, ToolSettingsFileName); if (dotnetToolSettings == null) { + // Check if this is because of framework incompatibility + // Load available frameworks from the package to provide better error messages + var installPath = new VersionFolderPathResolver(packageDirectory.Value).GetInstallPath(library.Name, library.Version); + var toolsPackagePath = Path.Combine(installPath, "tools"); + + if (fileSystem.Directory.Exists(toolsPackagePath)) + { + var availableFrameworks = fileSystem.Directory.EnumerateDirectories(toolsPackagePath) + .Select(path => NuGetFramework.ParseFolder(Path.GetFileName(path))) + .Where(f => f.Framework == FrameworkConstants.FrameworkIdentifiers.NetCoreApp) + .ToList(); + + if (availableFrameworks.Count > 0) + { + var currentFramework = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, new Version(Environment.Version.Major, Environment.Version.Minor)); + + // Find the minimum framework version required by the tool + var minRequiredFramework = availableFrameworks.MinBy(f => f.Version); + + // If all available frameworks require a higher version than current runtime + if (minRequiredFramework != null && minRequiredFramework.Version > currentFramework.Version) + { + var requiredVersionString = $".NET {minRequiredFramework.Version.Major}.{minRequiredFramework.Version.Minor}"; + var currentVersionString = $".NET {currentFramework.Version.Major}.{currentFramework.Version.Minor}"; + + var errorMessage = string.Format( + CliStrings.ToolRequiresHigherDotNetVersion, + packageId, + requiredVersionString, + currentVersionString); + + var suggestion = string.Format( + CliStrings.ToolRequiresHigherDotNetVersionSuggestion, + minRequiredFramework.Version.Major, + currentFramework.Version.Major); + + throw new GracefulException($"{errorMessage} {suggestion}", isUserError: false); + } + } + } + throw new ToolConfigurationException( CliStrings.MissingToolSettingsFile); } diff --git a/src/Cli/dotnet/xlf/CliStrings.cs.xlf b/src/Cli/dotnet/xlf/CliStrings.cs.xlf index 65d8392b66f2..341d7f33bafe 100644 --- a/src/Cli/dotnet/xlf/CliStrings.cs.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.cs.xlf @@ -1084,6 +1084,16 @@ Výchozí hodnota je false. Pokud však cílíte na .NET 7 nebo nižší a je za Balíček {0} není nástroj .NET. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. Příkaz {0} obsahuje minimálně jeden neplatný znak: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.de.xlf b/src/Cli/dotnet/xlf/CliStrings.de.xlf index d79a4f07b422..8d371aa78f27 100644 --- a/src/Cli/dotnet/xlf/CliStrings.de.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.de.xlf @@ -1083,6 +1083,16 @@ Der Standardwert lautet FALSE. Wenn sie jedoch auf .NET 7 oder niedriger abziele Paket {0} ist kein .NET-Tool. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. Der Befehl "{0}" enthält mindestens eines der folgenden ungültigen Zeichen: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.es.xlf b/src/Cli/dotnet/xlf/CliStrings.es.xlf index c72c86d6fddb..16f12512b3b0 100644 --- a/src/Cli/dotnet/xlf/CliStrings.es.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.es.xlf @@ -1083,6 +1083,16 @@ El valor predeterminado es "false." Sin embargo, cuando el destino es .NET 7 o i El paquete {0} no es una herramienta de .NET. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. El comando "{0}" contiene uno o varios de los siguientes caracteres no válidos: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.fr.xlf b/src/Cli/dotnet/xlf/CliStrings.fr.xlf index ffce7f406825..659a0a26e85f 100644 --- a/src/Cli/dotnet/xlf/CliStrings.fr.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.fr.xlf @@ -1084,6 +1084,16 @@ La valeur par défaut est « false ». Toutefois, lorsque vous ciblez .NET 7 o Le package {0} n’est pas un outil .NET. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. La commande '{0}' contient un ou plusieurs caractères non valides suivants : {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.it.xlf b/src/Cli/dotnet/xlf/CliStrings.it.xlf index f4bb209f3056..593bd9f66e44 100644 --- a/src/Cli/dotnet/xlf/CliStrings.it.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.it.xlf @@ -1083,6 +1083,16 @@ Il valore predefinito è 'false'. Tuttavia, quando la destinazione è .NET 7 o u Il pacchetto {0} non è uno strumento .NET. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. Il comando '{0}' contiene uno o più dei caratteri seguenti non validi: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.ja.xlf b/src/Cli/dotnet/xlf/CliStrings.ja.xlf index 025a2a04c987..16a80141b682 100644 --- a/src/Cli/dotnet/xlf/CliStrings.ja.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.ja.xlf @@ -1083,6 +1083,16 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is パッケージ {0} は .NET ツールではありません。 + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. コマンド '{0}' には次の無効な文字が 1 つまたは複数含まれています: {1}。 diff --git a/src/Cli/dotnet/xlf/CliStrings.ko.xlf b/src/Cli/dotnet/xlf/CliStrings.ko.xlf index 0718276866cf..f50fae32b387 100644 --- a/src/Cli/dotnet/xlf/CliStrings.ko.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.ko.xlf @@ -1083,6 +1083,16 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is {0} 패키지는 .NET 도구가 아닙니다. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. '{0}' 명령에 다음과 같은 잘못된 문자가 하나 이상 포함되어 있습니다. {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.pl.xlf b/src/Cli/dotnet/xlf/CliStrings.pl.xlf index 49e51a8dab95..c4b5de3606ed 100644 --- a/src/Cli/dotnet/xlf/CliStrings.pl.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.pl.xlf @@ -1083,6 +1083,16 @@ Wartość domyślna to „false”. Jednak w przypadku określania wartości doc Pakiet {0} nie jest narzędziem platformy .NET. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. Polecenie „{0}” zawiera co najmniej jeden nieprawidłowy znak: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf b/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf index 18d6646955a9..af15ad2fdbe1 100644 --- a/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf @@ -1084,6 +1084,16 @@ O padrão é 'false.' No entanto, ao direcionar para .NET 7 ou inferior, o padr O pacote {0} não é uma ferramenta .NET. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. O comando '{0}' contém um ou mais dos seguintes caracteres inválidos: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.ru.xlf b/src/Cli/dotnet/xlf/CliStrings.ru.xlf index 341f9d41bc55..cc3b304b9081 100644 --- a/src/Cli/dotnet/xlf/CliStrings.ru.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.ru.xlf @@ -1084,6 +1084,16 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is Пакет {0} не является средством .NET. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. Команда "{0}" содержит следующие недопустимы символы: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.tr.xlf b/src/Cli/dotnet/xlf/CliStrings.tr.xlf index 26da8f61300c..bdb920e0a076 100644 --- a/src/Cli/dotnet/xlf/CliStrings.tr.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.tr.xlf @@ -1083,6 +1083,16 @@ Varsayılan değer 'false.' Ancak çalışma zamanı tanımlayıcısı belirtild {0} paketi bir .NET aracı değil. + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. '{0}' komutu şu geçersiz karakterlerden birini veya daha fazlasını içeriyor: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf b/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf index 380a74f4c834..eea160a146c8 100644 --- a/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf @@ -1084,6 +1084,16 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is 包 {0} 不是 .NET 工具。 + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. 命令“{0}”包含一个或多个以下无效字符: {1}。 diff --git a/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf b/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf index c7a96f081838..d97b3f1aa637 100644 --- a/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf @@ -1083,6 +1083,16 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is 套件 {0} 不是 .NET 工具。 + + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + Tool '{0}' requires a higher version of .NET than is currently installed. The tool targets {1}, but the current runtime is {2}. + + + + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + To use this tool, upgrade to .NET {0} or later, or use a version of the tool that is compatible with .NET {1}. + + Command '{0}' contains one or more of the following invalid characters: {1}. 命令 '{0}' 包含下列一或多個無效的字元: {1}。 diff --git a/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs b/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs index 3eae1ab6133e..8746779afa6f 100644 --- a/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs +++ b/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs @@ -500,6 +500,117 @@ static XElement GetToolSettingsFile(string packagePath) } + [Fact] + public void InstallToolWithHigherFrameworkAsGlobalToolShowsAppropriateError() + { + var toolPackagesPath = CreateNet99ToolPackage(); + var testDirectory = _testAssetsManager.CreateTestDirectory(); + var homeFolder = Path.Combine(testDirectory.Path, "home"); + + var result = new DotnetToolCommand(Log, "install", "-g", "Net99Tool", "--add-source", toolPackagesPath) + .WithEnvironmentVariables(homeFolder) + .WithWorkingDirectory(testDirectory.Path) + .Execute(); + + result.Should().Fail() + .And.HaveStdErrContaining("requires a higher version of .NET") + .And.HaveStdErrContaining(".NET 99"); + } + + [Fact] + public void InstallToolWithHigherFrameworkAsLocalToolShowsAppropriateError() + { + var toolPackagesPath = CreateNet99ToolPackage(); + var testDirectory = _testAssetsManager.CreateTestDirectory(); + var homeFolder = Path.Combine(testDirectory.Path, "home"); + + new DotnetCommand(Log, "new", "tool-manifest") + .WithEnvironmentVariables(homeFolder) + .WithWorkingDirectory(testDirectory.Path) + .Execute() + .Should().Pass(); + + var result = new DotnetToolCommand(Log, "install", "Net99Tool", "--add-source", toolPackagesPath) + .WithEnvironmentVariables(homeFolder) + .WithWorkingDirectory(testDirectory.Path) + .Execute(); + + result.Should().Fail() + .And.HaveStdErrContaining("requires a higher version of .NET") + .And.HaveStdErrContaining(".NET 99"); + } + + [Fact] + public void RunToolWithHigherFrameworkUsingDnxShowsAppropriateError() + { + var toolPackagesPath = CreateNet99ToolPackage(); + var testDirectory = _testAssetsManager.CreateTestDirectory(); + var homeFolder = Path.Combine(testDirectory.Path, "home"); + + var result = new DotnetToolCommand(Log, "exec", "Net99Tool", "--yes", "--source", toolPackagesPath) + .WithEnvironmentVariables(homeFolder) + .WithWorkingDirectory(testDirectory.Path) + .Execute(); + + result.Should().Fail() + .And.HaveStdErrContaining("requires a higher version of .NET") + .And.HaveStdErrContaining(".NET 99"); + } + + /// + /// Creates a tool package that targets net99.0 to simulate a tool requiring a higher .NET version + /// + private string CreateNet99ToolPackage() + { + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: "net99tool"); + var projectDirectory = Path.Combine(testDirectory.Path, "toolproject"); + Directory.CreateDirectory(projectDirectory); + + // Create the directory structure for the tool files + var toolsDir = Path.Combine(projectDirectory, "tools", "net99.0", "any"); + Directory.CreateDirectory(toolsDir); + + // Create DotnetToolSettings.xml + File.WriteAllText(Path.Combine(toolsDir, "DotnetToolSettings.xml"), @" + + + + +"); + + // Create a minimal DLL file + File.WriteAllText(Path.Combine(toolsDir, "Net99Tool.dll"), ""); + + // Create a .nuspec file + var nuspecPath = Path.Combine(projectDirectory, "Net99Tool.nuspec"); + File.WriteAllText(nuspecPath, @" + + + Net99Tool + 1.0.0 + Test + Test tool targeting net99.0 + + + + + + + +"); + + // Use NuGet pack to create the package + var packageOutputPath = Path.Combine(testDirectory.Path, "packages"); + Directory.CreateDirectory(packageOutputPath); + + new DotnetCommand(Log, "pack", nuspecPath, "-o", packageOutputPath) + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + return packageOutputPath; + } + /// /// Opens the nupkg and verifies that it does not contain a dependency on the given dll. /// @@ -516,9 +627,11 @@ static class EndToEndToolTestExtensions { public static TestCommand WithEnvironmentVariables(this TestCommand command, string homeFolder) { + var nugetPackagesFolder = Path.Combine(homeFolder, ".nuget", "packages"); return command.WithEnvironmentVariable("DOTNET_CLI_HOME", homeFolder) .WithEnvironmentVariable("DOTNET_NOLOGO", "1") - .WithEnvironmentVariable("DOTNET_ADD_GLOBAL_TOOLS_TO_PATH", "0"); + .WithEnvironmentVariable("DOTNET_ADD_GLOBAL_TOOLS_TO_PATH", "0") + .WithEnvironmentVariable("NUGET_PACKAGES", nugetPackagesFolder); } } } diff --git a/test/Microsoft.DotNet.PackageInstall.Tests/ToolPackageDownloaderTests.cs b/test/Microsoft.DotNet.PackageInstall.Tests/ToolPackageDownloaderTests.cs index 32f6f731397a..b5ef60470c47 100644 --- a/test/Microsoft.DotNet.PackageInstall.Tests/ToolPackageDownloaderTests.cs +++ b/test/Microsoft.DotNet.PackageInstall.Tests/ToolPackageDownloaderTests.cs @@ -965,5 +965,68 @@ public ToolPackageDownloaderTests(ITestOutputHelper log, TestToolBuilder toolBui { ToolBuilder = toolBuilder; } + + [Fact] + public void GivenAToolWithHigherFrameworkItShowsAppropriateErrorMessage() + { + // Create a mock tool package with net99.0 framework to simulate a tool requiring a higher .NET version + var testDir = _testAssetsManager.CreateTestDirectory(); + var fileSystem = new FileSystemWrapper(); + var packageId = new PackageId("test.tool.higher.framework"); + var packageVersion = new NuGetVersion("1.0.0"); + var packageRoot = new DirectoryPath(testDir.Path).WithSubDirectories(".store", packageId.ToString(), packageVersion.ToNormalizedString()); + + // Create the package directory structure with net99.0 framework + var toolsPath = Path.Combine(packageRoot.Value, "tools", "net99.0", "any"); + fileSystem.Directory.CreateDirectory(toolsPath); + + // Create DotnetToolSettings.xml + var settingsContent = @" + + + + +"; + fileSystem.File.WriteAllText(Path.Combine(toolsPath, "DotnetToolSettings.xml"), settingsContent); + + // Create a dummy assembly file + fileSystem.File.WriteAllText(Path.Combine(toolsPath, "test.dll"), "dummy"); + + // Create an empty asset file (simulating NuGet restore with no compatible frameworks) + var assetFilePath = Path.Combine(packageRoot.Value, "project.assets.json"); + var currentFramework = $"net{Environment.Version.Major}.{Environment.Version.Minor}"; + var assetFileContents = $$""" + { + "version": 3, + "targets": { + "{{currentFramework}}/{{RuntimeInformation.RuntimeIdentifier}}": { + "{{packageId}}/{{packageVersion}}": { + "type": "package", + "tools": { + } + } + } + }, + "libraries": {}, + "projectFileDependencyGroups": {} + } + """; + fileSystem.File.WriteAllText(assetFilePath, assetFileContents); + + // Try to create a ToolPackageInstance, which should throw an informative error + Action action = () => + { + _ = new ToolPackageInstance( + packageId, + packageVersion, + new DirectoryPath(testDir.Path).WithSubDirectories(".store"), + packageRoot, + fileSystem); + }; + + action.Should().Throw() + .WithMessage("*requires a higher version of .NET*") + .WithMessage("*.NET 99*"); + } } }