public InstallDotNetToolTests()
        {
            _fileSystemMock = new Mock <IFileSystem>();
            _commandMock    = new Mock <ICommand>();

            bool succeeded = false;

            _helpersMock = new Mock <IHelpers>();
            _helpersMock
            .Setup(x => x.DirectoryMutexExec(It.IsAny <Func <bool> >(), It.IsAny <string>()))
            .Callback <Func <bool>, string>((function, path) => {
                succeeded = function();
            })
            .Returns(() => succeeded);

            _commandFactoryMock = new Mock <ICommandFactory>();
            _commandFactoryMock
            .Setup(x => x.Create(
                       It.Is <string>(s => s == _dotnetPath),
                       It.Is <IEnumerable <string> >(args => args.All(y => _expectedArgs.Contains(y)))))
            .Returns(_commandMock.Object)
            .Verifiable();

            _task = new InstallDotNetTool()
            {
                DestinationPath = InstallPath,
                Name            = ToolName,
                Version         = ToolVersion,
                BuildEngine     = new MockBuildEngine(),
            };
        }
        public void AreDependenciesRegistered()
        {
            var task = new InstallDotNetTool();

            var collection = new ServiceCollection();

            task.ConfigureServices(collection);
            var provider = collection.BuildServiceProvider();

            DependencyInjectionValidation.IsDependencyResolutionCoherent(
                s =>
            {
                task.ConfigureServices(s);
            },
                out string message,
                additionalSingletonTypes: task.GetExecuteParameterTypes()
                )
            .Should()
            .BeTrue(message);
        }
        public async Task InstallsInParallelWithRealMutex()
        {
            // Setup
            var fileSystemMock1 = new Mock <IFileSystem>();

            fileSystemMock1
            .Setup(x => x.DirectoryExists(It.IsAny <string>()))
            .Returns(false);

            var fileSystemMock2 = new Mock <IFileSystem>();

            fileSystemMock2
            .Setup(x => x.DirectoryExists(It.IsAny <string>()))
            .Returns(true);

            var helpers = new Helpers();
            var hangingCommandCalled = new TaskCompletionSource <bool>();
            var dotnetToolInstalled  = new TaskCompletionSource <bool>();

            var hangingCommand = new Mock <ICommand>();

            hangingCommand
            .Setup(x => x.Execute())
            .Callback(() =>
            {
                hangingCommandCalled.SetResult(true);
                dotnetToolInstalled.Task.GetAwaiter().GetResult();
            })
            .Returns(new CommandResult(new ProcessStartInfo(), 0, "Tool installed", null));

            var hangingCommandFactoryMock = new Mock <ICommandFactory>();

            hangingCommandFactoryMock
            .Setup(x => x.Create(
                       It.Is <string>(s => s == _dotnetPath),
                       It.Is <IEnumerable <string> >(args => args.All(y => _expectedArgs.Contains(y)))))
            .Returns(hangingCommand.Object)
            .Verifiable();

            var collection1 = new ServiceCollection();

            collection1.AddSingleton(hangingCommandFactoryMock.Object);
            collection1.AddSingleton(fileSystemMock1.Object);
            collection1.AddTransient <IHelpers, Helpers>();

            var collection2 = new ServiceCollection();

            collection2.AddSingleton(_commandFactoryMock.Object);
            collection2.AddSingleton(fileSystemMock2.Object);
            collection2.AddTransient <IHelpers, Helpers>();

            var task1 = new InstallDotNetTool()
            {
                DestinationPath = InstallPath,
                Name            = ToolName,
                Version         = ToolVersion,
                BuildEngine     = new MockBuildEngine(),
            };

            var task2 = new InstallDotNetTool()
            {
                DestinationPath = InstallPath,
                Name            = ToolName,
                Version         = ToolVersion,
                BuildEngine     = new MockBuildEngine(),
            };

            // Act
            using var provider1 = collection1.BuildServiceProvider();
            using var provider2 = collection2.BuildServiceProvider();

            var installationTask = Task.Run(() => task1.InvokeExecute(provider1).Should().BeTrue());

            // Let's wait for the first `dotnet tool install` to be called (it will stay spinning)
            await hangingCommandCalled.Task;

            var skipTask = Task.Run(() => task2.InvokeExecute(provider2).Should().BeTrue());

            // The first command must have been executed, let's verify the parameters
            hangingCommandFactoryMock
            .Verify(
                x => x.Create(
                    It.Is <string>(dotnet => dotnet == _dotnetPath),
                    It.Is <IEnumerable <string> >(args => args.Count() == _expectedArgs.Count() && args.All(y => _expectedArgs.Contains(y)))),
                Times.Once);

            // The other command is waiting on the Mutex now
            skipTask.IsCompleted.Should().BeFalse();

            _commandFactoryMock
            .Verify(
                x => x.Create(
                    It.Is <string>(dotnet => dotnet == _dotnetPath),
                    It.Is <IEnumerable <string> >(args => args.Count() == _expectedArgs.Count() && args.All(y => _expectedArgs.Contains(y)))),
                Times.Never);

            // We now let `dotnet tool install` run to completion
            dotnetToolInstalled.SetResult(true);

            // Let's give the installation task time to evaluate the command result
            // Let's give the skip task get its own Mutex and verify existence of the installation
            await Task.WhenAll(installationTask, skipTask);

            // Verify
            _commandFactoryMock
            .Verify(
                x => x.Create(
                    It.Is <string>(dotnet => dotnet == _dotnetPath),
                    It.Is <IEnumerable <string> >(args => args.Count() == _expectedArgs.Count() && args.All(y => _expectedArgs.Contains(y)))),
                Times.Never);

            task1.ToolPath.Should().Be(task2.ToolPath);
            task1.ToolPath.Should().Be(s_installedPath);
        }