diff --git a/src/NuGet.Core/NuGet.Protocol/GlobalSuppressions.cs b/src/NuGet.Core/NuGet.Protocol/GlobalSuppressions.cs index fbb29505816..c8e0b1cab94 100644 --- a/src/NuGet.Core/NuGet.Protocol/GlobalSuppressions.cs +++ b/src/NuGet.Core/NuGet.Protocol/GlobalSuppressions.cs @@ -133,7 +133,6 @@ [assembly: SuppressMessage("Build", "CA1031:Modify 'FireBeforeClose' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "", Scope = "member", Target = "~M:NuGet.Protocol.Plugins.Plugin.FireBeforeClose")] [assembly: SuppressMessage("Build", "CA1031:Modify 'FireClosed' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "", Scope = "member", Target = "~M:NuGet.Protocol.Plugins.Plugin.FireClosed")] [assembly: SuppressMessage("Build", "CA2000:Call System.IDisposable.Dispose on object created by 'new MonitorNuGetProcessExitRequestHandler(plugin)' before all references to it are out of scope.", Justification = "", Scope = "member", Target = "~M:NuGet.Protocol.Plugins.PluginFactory.CreateFromCurrentProcessAsync(NuGet.Protocol.Plugins.IRequestHandlers,NuGet.Protocol.Plugins.ConnectionOptions,System.Threading.CancellationToken)~System.Threading.Tasks.Task{NuGet.Protocol.Plugins.IPlugin}")] -[assembly: SuppressMessage("Build", "CA2000:Use recommended dispose pattern to ensure that object created by 'new PluginProcess(startInfo)' is disposed on all paths. If possible, wrap the creation within a 'using' statement or a 'using' declaration. Otherwise, use a try-finally pattern, with a dedicated local variable declared before the try region and an unconditional Dispose invocation on non-null value in the 'finally' region, say 'x?.Dispose()'. If the object is explicitly disposed within the try region or the dispose ownership is transfered to another object or method, assign 'null' to the local variable just after such an operation to prevent double dispose in 'finally'.", Justification = "The responsibility to dispose the object is transferred to another object or wrapper that's created in the method and returned to the caller", Scope = "member", Target = "~M:NuGet.Protocol.Plugins.PluginFactory.CreatePluginAsync(System.String,System.Collections.Generic.IEnumerable{System.String},NuGet.Protocol.Plugins.IRequestHandlers,NuGet.Protocol.Plugins.ConnectionOptions,System.Threading.CancellationToken)~System.Threading.Tasks.Task{NuGet.Protocol.Plugins.IPlugin}")] [assembly: SuppressMessage("Build", "CA1031:Modify 'SendCloseRequest' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "", Scope = "member", Target = "~M:NuGet.Protocol.Plugins.PluginFactory.SendCloseRequest(NuGet.Protocol.Plugins.IPlugin)")] [assembly: SuppressMessage("Build", "CA1822:Member GetPluginOperationClaimsAsync does not access instance data and can be marked as static (Shared in VisualBasic)", Justification = "", Scope = "member", Target = "~M:NuGet.Protocol.Plugins.PluginManager.GetPluginOperationClaimsAsync(NuGet.Protocol.Plugins.IPlugin,System.String,Newtonsoft.Json.Linq.JObject,System.Threading.CancellationToken)~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyList{NuGet.Protocol.Plugins.OperationClaim}}")] [assembly: SuppressMessage("Build", "CA1031:Modify 'TryCreatePluginAsync' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "", Scope = "member", Target = "~M:NuGet.Protocol.Plugins.PluginManager.TryCreatePluginAsync(NuGet.Protocol.Plugins.PluginDiscoveryResult,NuGet.Protocol.Plugins.OperationClaim,NuGet.Protocol.Plugins.PluginManager.PluginRequestKey,System.String,Newtonsoft.Json.Linq.JObject,System.Threading.CancellationToken)~System.Threading.Tasks.Task{System.Tuple{System.Boolean,NuGet.Protocol.Plugins.PluginCreationResult}}")] diff --git a/src/NuGet.Core/NuGet.Protocol/Plugins/IPluginFactory.cs b/src/NuGet.Core/NuGet.Protocol/Plugins/IPluginFactory.cs index ae187ee4f9f..1c5b0f3c697 100644 --- a/src/NuGet.Core/NuGet.Protocol/Plugins/IPluginFactory.cs +++ b/src/NuGet.Core/NuGet.Protocol/Plugins/IPluginFactory.cs @@ -11,12 +11,12 @@ namespace NuGet.Protocol.Plugins /// /// A plugin factory. /// - public interface IPluginFactory : IDisposable + internal interface IPluginFactory : IDisposable { /// /// Asynchronously gets an existing plugin instance or creates a new instance and connects to it. /// - /// The file path of the plugin. + /// A plugin file. /// Command-line arguments to be supplied to the plugin. /// Request handlers. /// Connection options. @@ -24,7 +24,7 @@ public interface IPluginFactory : IDisposable /// A task that represents the asynchronous operation. /// The task result () returns a /// instance. - /// Thrown if + /// Thrown if /// is either or empty. /// Thrown if /// is . @@ -37,7 +37,7 @@ public interface IPluginFactory : IDisposable /// Thrown if this object is disposed. /// This is intended to be called by NuGet client tools. Task GetOrCreateAsync( - string filePath, + PluginFile pluginFile, IEnumerable arguments, IRequestHandlers requestHandlers, ConnectionOptions options, diff --git a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginDiscoverer.cs b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginDiscoverer.cs index d342827a370..00c5ab489c2 100644 --- a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginDiscoverer.cs +++ b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginDiscoverer.cs @@ -23,6 +23,17 @@ public sealed class PluginDiscoverer : IPluginDiscoverer private IEnumerable _results; private readonly SemaphoreSlim _semaphore; private readonly IEnvironmentVariableReader _environmentVariableReader; + private static bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } public PluginDiscoverer() : this(EnvironmentVariableWrapper.Instance) @@ -168,7 +179,7 @@ private static List GetPluginFiles(IEnumerable filePaths, Ca { return PluginFileState.InvalidFilePath; } - })); + }), requiresDotnetHost: !IsDesktop); files.Add(pluginFile); } @@ -197,7 +208,7 @@ internal List GetPluginsInNuGetPluginPaths() // A DotNet tool plugin if (IsValidPluginFile(fileInfo)) { - PluginFile pluginFile = new PluginFile(fileInfo.FullName, new Lazy(() => PluginFileState.Valid), isDotnetToolsPlugin: true); + PluginFile pluginFile = new PluginFile(fileInfo.FullName, new Lazy(() => PluginFileState.Valid), requiresDotnetHost: false); pluginFiles.Add(pluginFile); } } @@ -205,7 +216,7 @@ internal List GetPluginsInNuGetPluginPaths() { // A non DotNet tool plugin file var state = new Lazy(() => PluginFileState.Valid); - pluginFiles.Add(new PluginFile(fileInfo.FullName, state)); + pluginFiles.Add(new PluginFile(fileInfo.FullName, state, requiresDotnetHost: !IsDesktop)); } } else if (Directory.Exists(path)) @@ -215,7 +226,7 @@ internal List GetPluginsInNuGetPluginPaths() } else { - pluginFiles.Add(new PluginFile(path, new Lazy(() => PluginFileState.InvalidFilePath))); + pluginFiles.Add(new PluginFile(path, new Lazy(() => PluginFileState.InvalidFilePath), requiresDotnetHost: !IsDesktop)); } } @@ -240,7 +251,7 @@ internal List GetPluginsInPath() } else { - pluginFiles.Add(new PluginFile(path, new Lazy(() => PluginFileState.InvalidFilePath))); + pluginFiles.Add(new PluginFile(path, new Lazy(() => PluginFileState.InvalidFilePath), requiresDotnetHost: false)); } } @@ -260,7 +271,7 @@ private static List GetNetToolsPluginsInDirectory(string directoryPa { if (IsValidPluginFile(file)) { - PluginFile pluginFile = new PluginFile(file.FullName, new Lazy(() => PluginFileState.Valid), isDotnetToolsPlugin: true); + PluginFile pluginFile = new PluginFile(file.FullName, new Lazy(() => PluginFileState.Valid), requiresDotnetHost: false); pluginFiles.Add(pluginFile); } } diff --git a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFactory.cs b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFactory.cs index 2b2e6b44a01..4034c79086c 100644 --- a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFactory.cs +++ b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFactory.cs @@ -17,13 +17,15 @@ namespace NuGet.Protocol.Plugins /// /// A plugin factory. /// - public sealed class PluginFactory : IPluginFactory + public class PluginFactory : IPluginFactory { private bool _isDisposed; private readonly IPluginLogger _logger; private readonly TimeSpan _pluginIdleTimeout; private readonly ConcurrentDictionary>> _plugins; + internal PluginFactory() { } + /// /// Instantiates a new class. /// @@ -48,7 +50,7 @@ public PluginFactory(TimeSpan pluginIdleTimeout) /// /// Disposes of this instance. /// - public void Dispose() + public virtual void Dispose() { if (_isDisposed) { @@ -77,7 +79,7 @@ public void Dispose() /// /// Asynchronously gets an existing plugin instance or creates a new instance and connects to it. /// - /// The file path of the plugin. + /// A plugin file. /// Command-line arguments to be supplied to the plugin. /// Request handlers. /// Connection options. @@ -85,7 +87,7 @@ public void Dispose() /// A task that represents the asynchronous operation. /// The task result () returns a /// instance. - /// Thrown if + /// Thrown if /// is either or empty. /// Thrown if /// is . @@ -99,8 +101,8 @@ public void Dispose() /// Thrown if a plugin protocol error occurs. /// Thrown for a plugin failure during creation. /// This is intended to be called by NuGet client tools. - public async Task GetOrCreateAsync( - string filePath, + public virtual async Task GetOrCreateAsync( + PluginFile pluginFile, IEnumerable arguments, IRequestHandlers requestHandlers, ConnectionOptions options, @@ -111,9 +113,9 @@ public async Task GetOrCreateAsync( throw new ObjectDisposedException(nameof(PluginFactory)); } - if (string.IsNullOrEmpty(filePath)) + if (string.IsNullOrEmpty(pluginFile.Path)) { - throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(filePath)); + throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(pluginFile.Path)); } if (arguments == null) @@ -134,9 +136,9 @@ public async Task GetOrCreateAsync( sessionCancellationToken.ThrowIfCancellationRequested(); var lazyTask = _plugins.GetOrAdd( - filePath, + pluginFile.Path, (path) => new Lazy>( - () => CreatePluginAsync(filePath, arguments, requestHandlers, options, sessionCancellationToken))); + () => CreatePluginAsync(pluginFile, arguments, requestHandlers, options, sessionCancellationToken))); await lazyTask.Value; @@ -145,40 +147,48 @@ public async Task GetOrCreateAsync( } private async Task CreatePluginAsync( - string filePath, + PluginFile pluginFile, IEnumerable arguments, IRequestHandlers requestHandlers, ConnectionOptions options, CancellationToken sessionCancellationToken) { var args = string.Join(" ", arguments); -#if IS_DESKTOP - var startInfo = new ProcessStartInfo(filePath) + + ProcessStartInfo startInfo; + + if (pluginFile.RequiresDotnetHost) { - Arguments = args, - UseShellExecute = false, - RedirectStandardError = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), - WindowStyle = ProcessWindowStyle.Hidden, - }; -#else - var startInfo = new ProcessStartInfo + startInfo = new ProcessStartInfo + { + FileName = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? + (NuGet.Common.RuntimeEnvironmentHelper.IsWindows ? + "dotnet.exe" : + "dotnet"), + Arguments = $"\"{pluginFile.Path}\" " + args, + UseShellExecute = false, + RedirectStandardError = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + WindowStyle = ProcessWindowStyle.Hidden, + }; + } + else { - FileName = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? - (NuGet.Common.RuntimeEnvironmentHelper.IsWindows ? - "dotnet.exe" : - "dotnet"), - Arguments = $"\"{filePath}\" " + args, - UseShellExecute = false, - RedirectStandardError = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), - WindowStyle = ProcessWindowStyle.Hidden, - }; -#endif + // Execute file directly. + startInfo = new ProcessStartInfo(pluginFile.Path) + { + Arguments = args, + UseShellExecute = false, + RedirectStandardError = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + WindowStyle = ProcessWindowStyle.Hidden, + }; + } + var pluginProcess = new PluginProcess(startInfo); string pluginId = Plugin.CreateNewId(); @@ -219,7 +229,7 @@ private async Task CreatePluginAsync( connection = new Connection(messageDispatcher, sender, receiver, options, _logger); var plugin = new Plugin( - filePath, + pluginFile.Path, connection, pluginProcess, isOwnProcess: false, diff --git a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFile.cs b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFile.cs index 96c24f2c86f..0d4f799d0e0 100644 --- a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFile.cs +++ b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginFile.cs @@ -21,33 +21,24 @@ public sealed class PluginFile public Lazy State { get; } /// - /// Is the plugin file, a dotnet tools plugin file? + /// Indicates if the plugin file requires a dotnet host. /// - internal bool IsDotnetToolsPlugin { get; } + internal bool RequiresDotnetHost { get; } /// /// Instantiates a new class. /// /// The plugin's file path. /// A lazy that evaluates the plugin file state. - /// Is the plugin file, a dotnet tools plugin file? - internal PluginFile(string filePath, Lazy state, bool isDotnetToolsPlugin) : this(filePath, state) - { - IsDotnetToolsPlugin = isDotnetToolsPlugin; - } - - /// - /// Instantiates a new class. - /// - /// The plugin's file path. - /// A lazy that evaluates the plugin file state. - public PluginFile(string filePath, Lazy state) + /// Indicates if the plugin file requires a dotnet host. + public PluginFile(string filePath, Lazy state, bool requiresDotnetHost) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(filePath)); } + RequiresDotnetHost = requiresDotnetHost; Path = filePath; State = state; } diff --git a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginManager.cs b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginManager.cs index 6ff4407f862..11acfc56c71 100644 --- a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginManager.cs +++ b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginManager.cs @@ -55,7 +55,7 @@ private PluginManager() public PluginManager( IEnvironmentVariableReader reader, Lazy pluginDiscoverer, - Func pluginFactoryCreator, + Func pluginFactoryCreator, Lazy pluginsCacheDirectoryPath) { Initialize( @@ -212,12 +212,14 @@ private async Task> TryCreatePluginAsync( { if (result.PluginFile.State.Value == PluginFileState.Valid) { - var plugin = await _pluginFactory.GetOrCreateAsync( - result.PluginFile.Path, - PluginConstants.PluginArguments, - new RequestHandlers(), - _connectionOptions, - cancellationToken); + IPlugin plugin; + + plugin = await _pluginFactory.GetOrCreateAsync( + pluginFile: result.PluginFile, + arguments: PluginConstants.PluginArguments, + requestHandlers: new RequestHandlers(), + options: _connectionOptions, + sessionCancellationToken: cancellationToken); var utilities = await PerformOneTimePluginInitializationAsync(plugin, cancellationToken); diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Shipped.txt index def020375d4..d17e6c22f10 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Shipped.txt @@ -1015,8 +1015,6 @@ NuGet.Protocol.Plugins.IPlugin.Closed -> System.EventHandler ~NuGet.Protocol.Plugins.IPlugin.Name.get -> string NuGet.Protocol.Plugins.IPluginDiscoverer ~NuGet.Protocol.Plugins.IPluginDiscoverer.DiscoverAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -NuGet.Protocol.Plugins.IPluginFactory -~NuGet.Protocol.Plugins.IPluginFactory.GetOrCreateAsync(string filePath, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task NuGet.Protocol.Plugins.IPluginManager ~NuGet.Protocol.Plugins.IPluginManager.CreatePluginsAsync(NuGet.Protocol.Core.Types.SourceRepository source, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> ~NuGet.Protocol.Plugins.IPluginManager.FindAvailablePluginsAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> @@ -1211,12 +1209,9 @@ NuGet.Protocol.Plugins.PluginException ~NuGet.Protocol.Plugins.PluginException.PluginException(string message) -> void ~NuGet.Protocol.Plugins.PluginException.PluginException(string message, System.Exception innerException) -> void NuGet.Protocol.Plugins.PluginFactory -NuGet.Protocol.Plugins.PluginFactory.Dispose() -> void -~NuGet.Protocol.Plugins.PluginFactory.GetOrCreateAsync(string filePath, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task NuGet.Protocol.Plugins.PluginFactory.PluginFactory(System.TimeSpan pluginIdleTimeout) -> void NuGet.Protocol.Plugins.PluginFile ~NuGet.Protocol.Plugins.PluginFile.Path.get -> string -~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy state) -> void ~NuGet.Protocol.Plugins.PluginFile.State.get -> System.Lazy NuGet.Protocol.Plugins.PluginFileState NuGet.Protocol.Plugins.PluginFileState.InvalidEmbeddedSignature = 3 -> NuGet.Protocol.Plugins.PluginFileState @@ -1228,7 +1223,6 @@ NuGet.Protocol.Plugins.PluginManager NuGet.Protocol.Plugins.PluginManager.Dispose() -> void ~NuGet.Protocol.Plugins.PluginManager.EnvironmentVariableReader.get -> NuGet.Common.IEnvironmentVariableReader ~NuGet.Protocol.Plugins.PluginManager.FindAvailablePluginsAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -~NuGet.Protocol.Plugins.PluginManager.PluginManager(NuGet.Common.IEnvironmentVariableReader reader, System.Lazy pluginDiscoverer, System.Func pluginFactoryCreator, System.Lazy pluginsCacheDirectoryPath) -> void ~NuGet.Protocol.Plugins.PluginManager.TryGetSourceAgnosticPluginAsync(NuGet.Protocol.Plugins.PluginDiscoveryResult pluginDiscoveryResult, NuGet.Protocol.Plugins.OperationClaim requestedOperationClaim, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> NuGet.Protocol.Plugins.PluginMulticlientUtilities ~NuGet.Protocol.Plugins.PluginMulticlientUtilities.DoOncePerPluginLifetimeAsync(string key, System.Func taskFunc, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt index 7e98009cd58..deb1138c126 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -1,2 +1,6 @@ #nullable enable NuGet.Protocol.Plugins.PluginDiscoverer.PluginDiscoverer() -> void +virtual NuGet.Protocol.Plugins.PluginFactory.Dispose() -> void +~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy state, bool requiresDotnetHost) -> void +~NuGet.Protocol.Plugins.PluginManager.PluginManager(NuGet.Common.IEnvironmentVariableReader reader, System.Lazy pluginDiscoverer, System.Func pluginFactoryCreator, System.Lazy pluginsCacheDirectoryPath) -> void +~virtual NuGet.Protocol.Plugins.PluginFactory.GetOrCreateAsync(NuGet.Protocol.Plugins.PluginFile pluginFile, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Shipped.txt index 86a9ec539e4..07c3e1d7c43 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -1015,8 +1015,6 @@ NuGet.Protocol.Plugins.IPlugin.Closed -> System.EventHandler ~NuGet.Protocol.Plugins.IPlugin.Name.get -> string NuGet.Protocol.Plugins.IPluginDiscoverer ~NuGet.Protocol.Plugins.IPluginDiscoverer.DiscoverAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -NuGet.Protocol.Plugins.IPluginFactory -~NuGet.Protocol.Plugins.IPluginFactory.GetOrCreateAsync(string filePath, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task NuGet.Protocol.Plugins.IPluginManager ~NuGet.Protocol.Plugins.IPluginManager.CreatePluginsAsync(NuGet.Protocol.Core.Types.SourceRepository source, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> ~NuGet.Protocol.Plugins.IPluginManager.FindAvailablePluginsAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> @@ -1211,12 +1209,9 @@ NuGet.Protocol.Plugins.PluginException ~NuGet.Protocol.Plugins.PluginException.PluginException(string message) -> void ~NuGet.Protocol.Plugins.PluginException.PluginException(string message, System.Exception innerException) -> void NuGet.Protocol.Plugins.PluginFactory -NuGet.Protocol.Plugins.PluginFactory.Dispose() -> void -~NuGet.Protocol.Plugins.PluginFactory.GetOrCreateAsync(string filePath, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task NuGet.Protocol.Plugins.PluginFactory.PluginFactory(System.TimeSpan pluginIdleTimeout) -> void NuGet.Protocol.Plugins.PluginFile ~NuGet.Protocol.Plugins.PluginFile.Path.get -> string -~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy state) -> void ~NuGet.Protocol.Plugins.PluginFile.State.get -> System.Lazy NuGet.Protocol.Plugins.PluginFileState NuGet.Protocol.Plugins.PluginFileState.InvalidEmbeddedSignature = 3 -> NuGet.Protocol.Plugins.PluginFileState @@ -1228,7 +1223,6 @@ NuGet.Protocol.Plugins.PluginManager NuGet.Protocol.Plugins.PluginManager.Dispose() -> void ~NuGet.Protocol.Plugins.PluginManager.EnvironmentVariableReader.get -> NuGet.Common.IEnvironmentVariableReader ~NuGet.Protocol.Plugins.PluginManager.FindAvailablePluginsAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -~NuGet.Protocol.Plugins.PluginManager.PluginManager(NuGet.Common.IEnvironmentVariableReader reader, System.Lazy pluginDiscoverer, System.Func pluginFactoryCreator, System.Lazy pluginsCacheDirectoryPath) -> void ~NuGet.Protocol.Plugins.PluginManager.TryGetSourceAgnosticPluginAsync(NuGet.Protocol.Plugins.PluginDiscoveryResult pluginDiscoveryResult, NuGet.Protocol.Plugins.OperationClaim requestedOperationClaim, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> NuGet.Protocol.Plugins.PluginMulticlientUtilities ~NuGet.Protocol.Plugins.PluginMulticlientUtilities.DoOncePerPluginLifetimeAsync(string key, System.Func taskFunc, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 7e98009cd58..deb1138c126 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -1,2 +1,6 @@ #nullable enable NuGet.Protocol.Plugins.PluginDiscoverer.PluginDiscoverer() -> void +virtual NuGet.Protocol.Plugins.PluginFactory.Dispose() -> void +~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy state, bool requiresDotnetHost) -> void +~NuGet.Protocol.Plugins.PluginManager.PluginManager(NuGet.Common.IEnvironmentVariableReader reader, System.Lazy pluginDiscoverer, System.Func pluginFactoryCreator, System.Lazy pluginsCacheDirectoryPath) -> void +~virtual NuGet.Protocol.Plugins.PluginFactory.GetOrCreateAsync(NuGet.Protocol.Plugins.PluginFile pluginFile, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt index 86a9ec539e4..07c3e1d7c43 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -1015,8 +1015,6 @@ NuGet.Protocol.Plugins.IPlugin.Closed -> System.EventHandler ~NuGet.Protocol.Plugins.IPlugin.Name.get -> string NuGet.Protocol.Plugins.IPluginDiscoverer ~NuGet.Protocol.Plugins.IPluginDiscoverer.DiscoverAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -NuGet.Protocol.Plugins.IPluginFactory -~NuGet.Protocol.Plugins.IPluginFactory.GetOrCreateAsync(string filePath, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task NuGet.Protocol.Plugins.IPluginManager ~NuGet.Protocol.Plugins.IPluginManager.CreatePluginsAsync(NuGet.Protocol.Core.Types.SourceRepository source, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> ~NuGet.Protocol.Plugins.IPluginManager.FindAvailablePluginsAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> @@ -1211,12 +1209,9 @@ NuGet.Protocol.Plugins.PluginException ~NuGet.Protocol.Plugins.PluginException.PluginException(string message) -> void ~NuGet.Protocol.Plugins.PluginException.PluginException(string message, System.Exception innerException) -> void NuGet.Protocol.Plugins.PluginFactory -NuGet.Protocol.Plugins.PluginFactory.Dispose() -> void -~NuGet.Protocol.Plugins.PluginFactory.GetOrCreateAsync(string filePath, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task NuGet.Protocol.Plugins.PluginFactory.PluginFactory(System.TimeSpan pluginIdleTimeout) -> void NuGet.Protocol.Plugins.PluginFile ~NuGet.Protocol.Plugins.PluginFile.Path.get -> string -~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy state) -> void ~NuGet.Protocol.Plugins.PluginFile.State.get -> System.Lazy NuGet.Protocol.Plugins.PluginFileState NuGet.Protocol.Plugins.PluginFileState.InvalidEmbeddedSignature = 3 -> NuGet.Protocol.Plugins.PluginFileState @@ -1228,7 +1223,6 @@ NuGet.Protocol.Plugins.PluginManager NuGet.Protocol.Plugins.PluginManager.Dispose() -> void ~NuGet.Protocol.Plugins.PluginManager.EnvironmentVariableReader.get -> NuGet.Common.IEnvironmentVariableReader ~NuGet.Protocol.Plugins.PluginManager.FindAvailablePluginsAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> -~NuGet.Protocol.Plugins.PluginManager.PluginManager(NuGet.Common.IEnvironmentVariableReader reader, System.Lazy pluginDiscoverer, System.Func pluginFactoryCreator, System.Lazy pluginsCacheDirectoryPath) -> void ~NuGet.Protocol.Plugins.PluginManager.TryGetSourceAgnosticPluginAsync(NuGet.Protocol.Plugins.PluginDiscoveryResult pluginDiscoveryResult, NuGet.Protocol.Plugins.OperationClaim requestedOperationClaim, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task> NuGet.Protocol.Plugins.PluginMulticlientUtilities ~NuGet.Protocol.Plugins.PluginMulticlientUtilities.DoOncePerPluginLifetimeAsync(string key, System.Func taskFunc, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index 7e98009cd58..deb1138c126 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,2 +1,6 @@ #nullable enable NuGet.Protocol.Plugins.PluginDiscoverer.PluginDiscoverer() -> void +virtual NuGet.Protocol.Plugins.PluginFactory.Dispose() -> void +~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy state, bool requiresDotnetHost) -> void +~NuGet.Protocol.Plugins.PluginManager.PluginManager(NuGet.Common.IEnvironmentVariableReader reader, System.Lazy pluginDiscoverer, System.Func pluginFactoryCreator, System.Lazy pluginsCacheDirectoryPath) -> void +~virtual NuGet.Protocol.Plugins.PluginFactory.GetOrCreateAsync(NuGet.Protocol.Plugins.PluginFile pluginFile, System.Collections.Generic.IEnumerable arguments, NuGet.Protocol.Plugins.IRequestHandlers requestHandlers, NuGet.Protocol.Plugins.ConnectionOptions options, System.Threading.CancellationToken sessionCancellationToken) -> System.Threading.Tasks.Task diff --git a/test/NuGet.Core.FuncTests/NuGet.Protocol.FuncTest/PluginTests.cs b/test/NuGet.Core.FuncTests/NuGet.Protocol.FuncTest/PluginTests.cs index 157f638d25e..4fc22ae9e0d 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Protocol.FuncTest/PluginTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Protocol.FuncTest/PluginTests.cs @@ -26,6 +26,18 @@ namespace NuGet.Protocol.FuncTest { public class PluginTests { + public static bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } + private static readonly FileInfo PluginFile; private static readonly ushort PortNumber = 11000; private static readonly IEnumerable PluginArguments = PluginConstants.PluginArguments @@ -60,7 +72,7 @@ public async Task GetOrCreateAsync_WithUnhandledExceptionInPlugin_Throws() using (var pluginFactory = new PluginFactory(PluginConstants.IdleTimeout)) { var exception = await Assert.ThrowsAsync(() => pluginFactory.GetOrCreateAsync( - PluginFile.FullName, + new PluginFile(filePath: PluginFile.FullName, state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), PluginConstants.PluginArguments.Concat(new[] { "-ThrowException Unhandled" }), new RequestHandlers(), ConnectionOptions.CreateDefault(), @@ -81,7 +93,7 @@ public async Task GetOrCreateAsync_WithHandledExceptionAndExitInPlugin_Throws() using (var pluginFactory = new PluginFactory(PluginConstants.IdleTimeout)) { var exception = await Assert.ThrowsAsync(() => pluginFactory.GetOrCreateAsync( - PluginFile.FullName, + new PluginFile(filePath: PluginFile.FullName, state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), PluginConstants.PluginArguments.Concat(new[] { "-ThrowException Handled" }), new RequestHandlers(), ConnectionOptions.CreateDefault(), @@ -102,7 +114,7 @@ public async Task GetOrCreateAsync_WhenPluginFreezes_Throws() using (var pluginFactory = new PluginFactory(PluginConstants.IdleTimeout)) { var exception = await Assert.ThrowsAsync(() => pluginFactory.GetOrCreateAsync( - PluginFile.FullName, + new PluginFile(filePath: PluginFile.FullName, state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), PluginConstants.PluginArguments.Concat(new[] { "-Freeze" }), new RequestHandlers(), ConnectionOptions.CreateDefault(), @@ -123,7 +135,7 @@ public async Task GetOrCreateAsync_WhenPluginCausesProtocolException_Throws() using (var pluginFactory = new PluginFactory(PluginConstants.IdleTimeout)) { var exception = await Assert.ThrowsAsync(() => pluginFactory.GetOrCreateAsync( - PluginFile.FullName, + new PluginFile(filePath: PluginFile.FullName, state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), PluginConstants.PluginArguments.Concat(new[] { "-CauseProtocolException" }), new RequestHandlers(), ConnectionOptions.CreateDefault(), @@ -318,7 +330,7 @@ internal static async Task CreateAsync() var pluginFactory = new PluginFactory(PluginConstants.IdleTimeout); var options = ConnectionOptions.CreateDefault(); var plugin = await pluginFactory.GetOrCreateAsync( - PluginFile.FullName, + new PluginFile(filePath: PluginFile.FullName, state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), PluginArguments, new RequestHandlers(), options, diff --git a/test/NuGet.Core.Tests/NuGet.Credentials.Test/PluginManagerMock.cs b/test/NuGet.Core.Tests/NuGet.Credentials.Test/PluginManagerMock.cs index 7273edc056e..ce13791271e 100644 --- a/test/NuGet.Core.Tests/NuGet.Credentials.Test/PluginManagerMock.cs +++ b/test/NuGet.Core.Tests/NuGet.Credentials.Test/PluginManagerMock.cs @@ -66,9 +66,21 @@ internal TestExpectation( internal sealed class PluginManagerMock : IDisposable { + public static bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } + private readonly Mock _connection; private readonly TestExpectation _expectations; - private readonly Mock _factory; + private readonly Mock _factory; private readonly Mock _plugin; private readonly Mock _pluginDiscoverer; private readonly Mock _reader; @@ -99,7 +111,7 @@ internal PluginManagerMock( _plugin = new Mock(MockBehavior.Strict); EnsurePluginSetupCalls(); - _factory = new Mock(MockBehavior.Strict); + _factory = new Mock(MockBehavior.Strict); EnsureFactorySetupCalls(pluginFilePath); // Setup connection @@ -238,7 +250,7 @@ private void EnsureDiscovererIsCalled(string pluginFilePath, PluginFileState plu _pluginDiscoverer.Setup(x => x.DiscoverAsync(It.IsAny())) .ReturnsAsync(new[] { - new PluginDiscoveryResult(new PluginFile(pluginFilePath, new Lazy(() => pluginFileState))) + new PluginDiscoveryResult(new PluginFile(pluginFilePath, new Lazy(() => pluginFileState), requiresDotnetHost: !IsDesktop)) }); } @@ -281,7 +293,7 @@ private void EnsureFactorySetupCalls(string pluginFilePath) { _factory.Setup(x => x.Dispose()); _factory.Setup(x => x.GetOrCreateAsync( - It.Is(p => p == pluginFilePath), + It.Is(p => p.Path == pluginFilePath), It.IsNotNull>(), It.IsNotNull(), It.IsNotNull(), diff --git a/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderBuilderTests.cs b/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderBuilderTests.cs index 9acf4a02f70..49d868ff7cd 100644 --- a/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderBuilderTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderBuilderTests.cs @@ -17,6 +17,17 @@ namespace NuGet.Credentials.Test { public class SecurePluginCredentialProviderBuilderTests : IDisposable { + public static bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } private readonly TestDirectory _testDirectory; public SecurePluginCredentialProviderBuilderTests() @@ -156,7 +167,7 @@ internal PluginManagerBuilderMock(List> pl PluginManager = new PluginManager( reader.Object, new Lazy(() => pluginDiscoverer.Object), - (TimeSpan idleTimeout) => Mock.Of(), + (TimeSpan idleTimeout) => Mock.Of(), new Lazy(() => _testDirectory.Path)); } @@ -170,7 +181,7 @@ private static IEnumerable GetPluginDiscoveryResults(List var results = new List(); foreach (var plugin in plugins) { - var file = new PluginFile(plugin.Key, new Lazy(() => plugin.Value)); + var file = new PluginFile(plugin.Key, new Lazy(() => plugin.Value), requiresDotnetHost: !IsDesktop); results.Add(new PluginDiscoveryResult(file)); } @@ -183,7 +194,7 @@ private PluginManager CreateDefaultPluginManager() return new PluginManager( Mock.Of(), new Lazy(), - (TimeSpan idleTimeout) => Mock.Of(), + (TimeSpan idleTimeout) => Mock.Of(), new Lazy(() => _testDirectory.Path)); } } diff --git a/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderTests.cs b/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderTests.cs index bbf4d232df5..d17d61c3c81 100644 --- a/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Credentials.Test/SecurePluginCredentialProviderTests.cs @@ -17,6 +17,18 @@ namespace NuGet.Credentials.Test { public sealed class SecurePluginCredentialProviderTests : IDisposable { + public bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } + private static readonly Uri _uri = new Uri("https://unit.test"); private const string _username = "username"; private const string _password = "password"; @@ -94,7 +106,7 @@ public async Task GetAsync_WithValidArguments_ReturnsValidCredentials() pluginFileState: PluginFileState.Valid, expectations: expectation)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; @@ -129,7 +141,7 @@ public async Task GetAsync_WhenCalledMultipleTimes_DoesNotCreateMultipleInstance pluginFileState: PluginFileState.Valid, expectations: expectation)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; @@ -166,7 +178,7 @@ public async Task GetAsync_WhenPluginClaimsMultipleOperations_ReturnsValidCreden pluginFileState: PluginFileState.Valid, expectations: expectation)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; @@ -206,7 +218,7 @@ public async Task GetAsync_WhenProxyIsUsed_SetsProxyCredentials() pluginFileState: PluginFileState.Valid, expectations: expectation)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); var proxy = new System.Net.WebProxy() { @@ -243,7 +255,7 @@ public async Task GetAsync_WhenCalledMultipleTimes_CachesCapabilities() pluginFileState: PluginFileState.Valid, expectations: expectation)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; @@ -273,7 +285,7 @@ public async Task GetAsync_WhenCalledMultipleTimes_CachesCapabilities() pluginFileState: PluginFileState.Valid, expectations: expectations2)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; @@ -311,7 +323,7 @@ public async Task GetAsync_SendsCorrectCanShowDialogValue() pluginFileState: PluginFileState.Valid, expectations: expectation)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog, logger: NullLogger.Instance); IWebProxy proxy = null; @@ -344,7 +356,7 @@ public async Task GetAsync_WhenPluginManagerReturnsException_ExceptionIsPropagat It.IsAny())) .ReturnsAsync(result); - var pluginDiscoveryResult = new PluginDiscoveryResult(new PluginFile("c", new Lazy(() => PluginFileState.Valid))); + var pluginDiscoveryResult = new PluginDiscoveryResult(new PluginFile("c", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var logger = new Mock(MockBehavior.Strict); logger.Setup(x => x.LogError(It.Is(data => data == expectedMessage))); @@ -379,7 +391,7 @@ public async Task GetAsync_WhenCredentialPluginIsUnableToAcquireCredentials_Retu pluginFileState: PluginFileState.Valid, expectations: expectation)) { - var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid))); + var discoveryResult = new PluginDiscoveryResult(new PluginFile("a", new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); var provider = new SecurePluginCredentialProvider(test.PluginManager, discoveryResult, canShowDialog: true, logger: NullLogger.Instance); IWebProxy proxy = null; @@ -397,7 +409,7 @@ public async Task GetAsync_WhenCredentialPluginIsUnableToAcquireCredentials_Retu private PluginDiscoveryResult CreatePluginDiscoveryResult(PluginFileState pluginState = PluginFileState.Valid) { - return new PluginDiscoveryResult(new PluginFile(Path.Combine(_testDirectory.Path, "plugin.exe"), new Lazy(() => pluginState))); + return new PluginDiscoveryResult(new PluginFile(Path.Combine(_testDirectory.Path, "plugin.exe"), new Lazy(() => pluginState), requiresDotnetHost: !IsDesktop)); } private PluginManager CreateDefaultPluginManager() @@ -405,7 +417,7 @@ private PluginManager CreateDefaultPluginManager() return new PluginManager( Mock.Of(), new Lazy(), - (TimeSpan idleTimeout) => Mock.Of(), + (TimeSpan idleTimeout) => Mock.Of(), new Lazy(() => _testDirectory.Path)); } diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscovererTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscovererTests.cs index f27749db149..317dc08d39c 100644 --- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscovererTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscovererTests.cs @@ -436,7 +436,7 @@ public void GetPluginsInNuGetPluginPaths_WithNuGetPluginPathsSet_ReturnsPluginsI // Assert Assert.Single(plugins); Assert.Equal(pluginInNuGetPluginPathDirectoryFilePath, plugins[0].Path); - Assert.True(plugins[0].IsDotnetToolsPlugin); + Assert.False(plugins[0].RequiresDotnetHost); } [PlatformFact(Platform.Windows)] @@ -480,7 +480,7 @@ public void GetPluginsInPATH_WithPATHSet_ReturnsPlugin() // Assert Assert.Single(plugins); Assert.Equal(pluginInPathDirectoryFilePath, plugins[0].Path); - Assert.True(plugins[0].IsDotnetToolsPlugin); + Assert.False(plugins[0].RequiresDotnetHost); } [PlatformFact(Platform.Windows)] @@ -503,7 +503,7 @@ public void GetPluginsInNuGetPluginPaths_NuGetPluginPathsPointsToAFile_TreatsAsP // Assert Assert.Single(plugins); Assert.Equal(pluginFilePath, plugins[0].Path); - Assert.True(plugins[0].IsDotnetToolsPlugin); + Assert.False(plugins[0].RequiresDotnetHost); } [PlatformFact(Platform.Windows)] @@ -525,7 +525,6 @@ public void GetPluginsInNuGetPluginPaths_NuGetPluginPathsPointsToAFileThatDoesNo // Assert Assert.Single(plugins); - Assert.False(plugins[0].IsDotnetToolsPlugin); } [PlatformFact(Platform.Windows)] @@ -549,7 +548,7 @@ public void GetPluginsInPATH_PATHPointsToADirectory_ContainsValidPluginFiles() // Assert Assert.Single(plugins); Assert.Equal(validPluginFile, plugins[0].Path); - Assert.True(plugins[0].IsDotnetToolsPlugin); + Assert.False(plugins[0].RequiresDotnetHost); } [PlatformFact(Platform.Windows)] diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscoveryResultTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscoveryResultTests.cs index 17493c4913b..2c5b57521e6 100644 --- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscoveryResultTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginDiscoveryResultTests.cs @@ -8,6 +8,18 @@ namespace NuGet.Protocol.Plugins.Tests { public class PluginDiscoveryResultTests { + public bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } + [Fact] public void Constructor_ThrowsForNullPluginFile() { @@ -20,7 +32,7 @@ public void Constructor_ThrowsForNullPluginFile() [Fact] public void Constructor_InitializesProperties() { - var pluginFile = new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.InvalidEmbeddedSignature)); + var pluginFile = new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.InvalidEmbeddedSignature), requiresDotnetHost: !IsDesktop); var result = new PluginDiscoveryResult(pluginFile); diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFactoryTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFactoryTests.cs index 4b560bf1acb..bffb021f6f6 100644 --- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFactoryTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFactoryTests.cs @@ -2,14 +2,28 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IO; using System.Threading; using System.Threading.Tasks; +using NuGet.Test.Utility; using Xunit; namespace NuGet.Protocol.Plugins.Tests { public class PluginFactoryTests { + public bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } + [Fact] public void Constructor_ThrowsForTimeSpanBelowMinimum() { @@ -46,7 +60,7 @@ public async Task GetOrCreateAsync_ThrowsForNullOrEmptyFilePath(string filePath) var exception = await Assert.ThrowsAsync( () => factory.GetOrCreateAsync( - filePath, + new PluginFile(filePath: filePath, state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), PluginConstants.PluginArguments, new RequestHandlers(), ConnectionOptions.CreateDefault(), @@ -62,7 +76,7 @@ public async Task GetOrCreateAsync_ThrowsForNullArguments() var exception = await Assert.ThrowsAsync( () => factory.GetOrCreateAsync( - filePath: "a", + new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), arguments: null, requestHandlers: new RequestHandlers(), options: ConnectionOptions.CreateDefault(), @@ -78,7 +92,7 @@ public async Task GetOrCreateAsync_ThrowsForNullRequestHandlers() var exception = await Assert.ThrowsAsync( () => factory.GetOrCreateAsync( - filePath: "a", + new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), arguments: PluginConstants.PluginArguments, requestHandlers: null, options: ConnectionOptions.CreateDefault(), @@ -94,7 +108,7 @@ public async Task GetOrCreateAsync_ThrowsForNullConnectionOptions() var exception = await Assert.ThrowsAsync( () => factory.GetOrCreateAsync( - filePath: "a", + new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), arguments: PluginConstants.PluginArguments, requestHandlers: new RequestHandlers(), options: null, @@ -110,7 +124,7 @@ public async Task GetOrCreateAsync_ThrowsIfCancelled() await Assert.ThrowsAsync( () => factory.GetOrCreateAsync( - filePath: "a", + new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), arguments: PluginConstants.PluginArguments, requestHandlers: new RequestHandlers(), options: ConnectionOptions.CreateDefault(), @@ -126,7 +140,7 @@ public async Task GetOrCreateAsync_ThrowsIfDisposed() var exception = await Assert.ThrowsAsync( () => factory.GetOrCreateAsync( - filePath: "a", + new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop), arguments: PluginConstants.PluginArguments, requestHandlers: new RequestHandlers(), options: ConnectionOptions.CreateDefault(), @@ -135,6 +149,34 @@ public async Task GetOrCreateAsync_ThrowsIfDisposed() Assert.Equal(nameof(PluginFactory), exception.ObjectName); } + [PlatformFact(Platform.Windows)] + public async Task GetOrCreateNetPluginAsync_UsingBatchFile_CreatesPluginAndExecutes() + { + using TestDirectory testDirectory = TestDirectory.Create(); + string pluginPath = Path.Combine(testDirectory.Path, "nuget-plugin-batFile.bat"); + string outputPath = Path.Combine(testDirectory.Path, "plugin-output.txt"); + + string batFileContent = $@" + @echo off + echo File executed > ""{outputPath}"" + "; + + File.WriteAllText(pluginPath, batFileContent); + + var args = PluginConstants.PluginArguments; + var reqHandler = new RequestHandlers(); + var options = ConnectionOptions.CreateDefault(); + + var pluginFactory = new PluginFactory(Timeout.InfiniteTimeSpan); + + // Act + var plugin = await Assert.ThrowsAnyAsync(() => pluginFactory.GetOrCreateAsync(new PluginFile(filePath: pluginPath, state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: false), args, reqHandler, options, CancellationToken.None)); + + // Assert + string outputContent = File.ReadAllText(outputPath); + Assert.Contains("File executed", outputContent); + } + [Fact] public async Task CreateFromCurrentProcessAsync_ThrowsForNullRequestHandlers() { diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFileTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFileTests.cs index 660f690216f..923c44842ea 100644 --- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFileTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginFileTests.cs @@ -8,12 +8,24 @@ namespace NuGet.Protocol.Plugins.Tests { public class PluginFileTests { + public bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } + [Theory] [InlineData(null)] [InlineData("")] public void Constructor_ThrowsForNullOrEmptyFilePath(string filePath) { - var exception = Assert.Throws(() => new PluginFile(filePath, state: new Lazy(() => PluginFileState.NotFound))); + var exception = Assert.Throws(() => new PluginFile(filePath, state: new Lazy(() => PluginFileState.NotFound), requiresDotnetHost: !IsDesktop)); Assert.Equal("filePath", exception.ParamName); } @@ -21,7 +33,7 @@ public void Constructor_ThrowsForNullOrEmptyFilePath(string filePath) [Fact] public void Constructor_InitializesProperties() { - var pluginFile = new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.Valid)); + var pluginFile = new PluginFile(filePath: "a", state: new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop); Assert.Equal("a", pluginFile.Path); Assert.Equal(PluginFileState.Valid, pluginFile.State.Value); diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginManagerTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginManagerTests.cs index 07e965955e3..230227c289f 100644 --- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginManagerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginManagerTests.cs @@ -17,6 +17,17 @@ namespace NuGet.Protocol.Plugins.Tests public class PluginManagerTests { private const string PluginFilePath = "a"; + public bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } [Fact] public async Task TryGetSourceAgnosticPluginAsync_WhenExceptionIsThrownDuringPluginCreation_PropagatesException() @@ -24,11 +35,11 @@ public async Task TryGetSourceAgnosticPluginAsync_WhenExceptionIsThrownDuringPlu const string message = "b"; var reader = Mock.Of(); - var pluginFactory = new Mock(MockBehavior.Strict); + var pluginFactory = new Mock(MockBehavior.Strict); var exception = new Exception(message); pluginFactory.Setup(x => x.GetOrCreateAsync( - It.Is(filePath => string.Equals(filePath, PluginFilePath, StringComparison.Ordinal)), + It.Is(pluginFile => string.Equals(pluginFile.Path, PluginFilePath, StringComparison.Ordinal)), It.Is>(arguments => arguments != null && arguments.Any()), It.IsNotNull(), It.IsNotNull(), @@ -46,7 +57,7 @@ public async Task TryGetSourceAgnosticPluginAsync_WhenExceptionIsThrownDuringPlu var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, - new Lazy(() => PluginFileState.Valid))); + new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); Tuple result = await pluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, @@ -77,7 +88,7 @@ public async Task TryGetSourceAgnosticPluginAsync_WhenSuccessfullyCreated_Operat var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, - new Lazy(() => PluginFileState.Valid))); + new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); Tuple result = await test.PluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, @@ -115,7 +126,7 @@ public async Task TryGetSourceAgnosticPluginAsync_WhenCacheFileIndicatesIndicate var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, - new Lazy(() => PluginFileState.Valid))); + new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); Tuple result = await test.PluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, @@ -164,7 +175,7 @@ public async Task PluginManager_CreatePlugin_PrefersFrameworkSpecificEnvironment var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, - new Lazy(() => PluginFileState.Valid))); + new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); Tuple result = await test.PluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, @@ -214,7 +225,7 @@ public async Task PluginManager_CreatePlugin_EmptyFrameworkSpecificEnvironmentVa var discoveryResult = new PluginDiscoveryResult( new PluginFile( PluginFilePath, - new Lazy(() => PluginFileState.Valid))); + new Lazy(() => PluginFileState.Valid), requiresDotnetHost: !IsDesktop)); Tuple result = await test.PluginManager.TryGetSourceAgnosticPluginAsync( discoveryResult, @@ -236,8 +247,19 @@ public async Task PluginManager_CreatePlugin_EmptyFrameworkSpecificEnvironmentVa private sealed class PluginManagerTest : IDisposable { + public bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } private readonly Mock _connection; - private readonly Mock _factory; + private readonly Mock _factory; private readonly Mock _plugin; private readonly Mock _pluginDiscoverer; private readonly Mock _reader; @@ -289,7 +311,7 @@ internal PluginManagerTest( _pluginDiscoverer.Setup(x => x.DiscoverAsync(It.IsAny())) .ReturnsAsync(new[] { - new PluginDiscoveryResult(new PluginFile(pluginFilePath, new Lazy(() => pluginFileState))) + new PluginDiscoveryResult(new PluginFile(pluginFilePath, new Lazy(() => pluginFileState), requiresDotnetHost : ! IsDesktop)) }); _connection = new Mock(MockBehavior.Strict); @@ -327,11 +349,11 @@ internal PluginManagerTest( _plugin.SetupGet(x => x.Id) .Returns("id"); - _factory = new Mock(MockBehavior.Strict); + _factory = new Mock(MockBehavior.Strict); _factory.Setup(x => x.Dispose()); _factory.Setup(x => x.GetOrCreateAsync( - It.Is(p => p == pluginFilePath), + It.Is(p => p.Path == pluginFilePath), It.IsNotNull>(), It.IsNotNull(), It.IsNotNull(), diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginResourceProviderTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginResourceProviderTests.cs index 787f6b6db73..d4661dc82f4 100644 --- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginResourceProviderTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/PluginResourceProviderTests.cs @@ -226,6 +226,17 @@ private static SourceRepository CreateSourceRepository( private sealed class PluginResourceProviderNegativeTest : IDisposable { + public static bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } private readonly Mock _pluginDiscoverer; private readonly PluginManager _pluginManager; private readonly Mock _environmentVariableReader; @@ -273,7 +284,7 @@ internal PluginResourceProviderNegativeTest(string serviceIndexJson, string sour _pluginManager = new PluginManager( _environmentVariableReader.Object, new Lazy(() => _pluginDiscoverer.Object), - (TimeSpan idleTimeout) => Mock.Of(), + (TimeSpan idleTimeout) => Mock.Of(), new Lazy(() => _testDirectory.Path)); Provider = new PluginResourceProvider(_pluginManager); } @@ -299,7 +310,7 @@ private static IEnumerable GetPluginDiscoveryResults(stri foreach (var path in pluginPaths.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { var state = path == "a" ? PluginFileState.Valid : PluginFileState.InvalidEmbeddedSignature; - var file = new PluginFile(path, new Lazy(() => state)); + var file = new PluginFile(path, new Lazy(() => state), requiresDotnetHost: !IsDesktop); results.Add(new PluginDiscoveryResult(file)); } @@ -309,9 +320,20 @@ private static IEnumerable GetPluginDiscoveryResults(stri private sealed class PluginResourceProviderPositiveTest : IDisposable { + public static bool IsDesktop + { + get + { +#if IS_DESKTOP + return true; +#else + return false; +#endif + } + } private readonly Mock _connection; private readonly IEnumerable _expectations; - private readonly Mock _factory; + private readonly Mock _factory; private readonly Mock _plugin; private readonly Mock _pluginDiscoverer; private readonly Mock _reader; @@ -355,7 +377,7 @@ internal PluginResourceProviderPositiveTest( _pluginDiscoverer.Setup(x => x.DiscoverAsync(It.IsAny())) .ReturnsAsync(new[] { - new PluginDiscoveryResult(new PluginFile(pluginFilePath, new Lazy(() => pluginFileState))) + new PluginDiscoveryResult(new PluginFile(pluginFilePath, new Lazy(() => pluginFileState), requiresDotnetHost : ! IsDesktop)) }); _connection = new Mock(MockBehavior.Strict); @@ -407,11 +429,11 @@ internal PluginResourceProviderPositiveTest( _plugin.SetupGet(x => x.Id) .Returns("id"); - _factory = new Mock(MockBehavior.Strict); + _factory = new Mock(MockBehavior.Strict); _factory.Setup(x => x.Dispose()); _factory.Setup(x => x.GetOrCreateAsync( - It.Is(p => p == pluginFilePath), + It.Is(p => p.Path == pluginFilePath), It.IsNotNull>(), It.IsNotNull(), It.IsNotNull(),