From fb8c377a0d1e5b1c6cdbb42b04abd179329f5b49 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Tue, 29 Aug 2017 11:06:55 -0500 Subject: [PATCH] (GH-1133) Autouninstaller - Use Uninstall Args / Override If a user provides uninstall arguments and optionally to override the original arguments, then those should be used with AutoUninstaller as well. Previously, auto uninstaller would calculate its own and ignore any input from the user. With this change, it will use those arguments that are provided. --- .../AutomaticUninstallerServiceSpecs.cs | 108 +++++++++++++++++- .../services/AutomaticUninstallerService.cs | 34 +++++- 2 files changed, 139 insertions(+), 3 deletions(-) diff --git a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs index 5706edd806..b30e6baa6a 100644 --- a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs @@ -64,6 +64,7 @@ public override void Context() service.WaitForCleanup = false; config.Features.AutoUninstaller = true; config.PromptForConfirmation = false; + config.PackageNames = "regular"; package.Setup(p => p.Id).Returns("regular"); package.Setup(p => p.Version).Returns(new SemanticVersion("1.2.0")); packageResult = new PackageResult(package.Object, "c:\\packages\\thispackage"); @@ -471,7 +472,7 @@ public void should_call_command_executor() Times.Once); } } - + public class when_uninstall_string_is_split_by_quotes : AutomaticUninstallerServiceSpecsBase { private readonly string uninstallStringWithQuoteSeparation = @"""C:\Program Files (x86)\WinDirStat\Uninstall.exe"" ""WinDir Stat"""; @@ -612,6 +613,111 @@ public void should_not_call_command_executor() } } + public class when_AutomaticUninstallerService_is_passed_uninstall_arguments_from_command_line : AutomaticUninstallerServiceSpecsBase + { + IInstaller _installerType = new InnoSetupInstaller(); + + public override void Context() + { + base.Context(); + registryKeys.Clear(); + registryKeys.Add( + new RegistryApplicationKey + { + DisplayName = expectedDisplayName, + InstallLocation = @"C:\Program Files (x86)\WinDirStat", + UninstallString = originalUninstallString, + HasQuietUninstall = false, + KeyPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\WinDirStat", + InstallerType = _installerType.InstallerType, + }); + packageInformation.RegistrySnapshot = new Registry("123", registryKeys); + + config.InstallArguments = "/bob /nope"; + } + + public override void Because() + { + service.run(packageResult, config); + } + + [Fact] + public void should_call_get_package_information() + { + packageInfoService.Verify(s => s.get_package_information(It.IsAny()), Times.Once); + } + + [Fact] + public void should_call_command_executor_appending_passed_arguments() + { + var uninstallArgs = _installerType.build_uninstall_command_arguments().trim_safe(); + + uninstallArgs += " {0}".format_with(config.InstallArguments); + + commandExecutor.Verify( + c => + c.execute( + expectedUninstallString, + uninstallArgs, + It.IsAny(), + It.IsAny>(), + It.IsAny>(), + It.IsAny()), + Times.Once); + } + } + + public class when_AutomaticUninstallerService_is_passed_overriding_uninstall_arguments_from_command_line : AutomaticUninstallerServiceSpecsBase + { + IInstaller _installerType = new InnoSetupInstaller(); + + public override void Context() + { + base.Context(); + registryKeys.Clear(); + registryKeys.Add( + new RegistryApplicationKey + { + DisplayName = expectedDisplayName, + InstallLocation = @"C:\Program Files (x86)\WinDirStat", + UninstallString = originalUninstallString, + HasQuietUninstall = false, + KeyPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\WinDirStat", + InstallerType = _installerType.InstallerType, + }); + packageInformation.RegistrySnapshot = new Registry("123", registryKeys); + + config.InstallArguments = "/bob /nope"; + config.OverrideArguments = true; + } + + public override void Because() + { + service.run(packageResult, config); + } + + [Fact] + public void should_call_get_package_information() + { + packageInfoService.Verify(s => s.get_package_information(It.IsAny()), Times.Once); + } + + [Fact] + public void should_call_command_executor_with_only_passed_arguments() + { + commandExecutor.Verify( + c => + c.execute( + expectedUninstallString, + config.InstallArguments, + It.IsAny(), + It.IsAny>(), + It.IsAny>(), + It.IsAny()), + Times.Once); + } + } + public class when_AutomaticUninstallerService_defines_uninstall_switches : AutomaticUninstallerServiceSpecsBase { private Action because; diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index a749186a6e..5bf0d2be41 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -30,6 +30,7 @@ namespace chocolatey.infrastructure.app.services using infrastructure.commands; using logging; using results; + using utility; public class AutomaticUninstallerService : IAutomaticUninstallerService { @@ -97,16 +98,30 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) this.Log().Debug(" Sleeping for {0} seconds to allow Windows to finish cleaning up.".format_with(SLEEP_TIME)); Thread.Sleep((int)TimeSpan.FromSeconds(SLEEP_TIME).TotalMilliseconds); } - + foreach (var key in registryKeys.or_empty_list_if_null()) { - var packageCacheLocation = _fileSystem.combine_paths(_fileSystem.get_full_path(config.CacheLocation), pkgInfo.Package.Id, pkgInfo.Package.Version.to_string()); + var packageCacheLocation = _fileSystem.combine_paths(_fileSystem.get_full_path(config.CacheLocation), package.Id, package.Version.to_string()); remove(key, config, packageResult, packageCacheLocation); } } public void remove(RegistryApplicationKey key, ChocolateyConfiguration config, PackageResult packageResult, string packageCacheLocation) { + var userProvidedUninstallArguments = string.Empty; + var userOverrideUninstallArguments = false; + var package = packageResult.Package; + if (package != null) + { + if (!PackageUtility.package_is_a_dependency(config, package.Id) || config.ApplyInstallArgumentsToDependencies) + { + userProvidedUninstallArguments = config.InstallArguments; + userOverrideUninstallArguments = config.OverrideArguments; + + if (!string.IsNullOrWhiteSpace(userProvidedUninstallArguments)) this.Log().Debug(ChocolateyLoggers.Verbose, " Using user passed {2}uninstaller args for {0}:'{1}'".format_with(package.Id, userProvidedUninstallArguments.escape_curly_braces(), userOverrideUninstallArguments ? "overriding " : string.Empty)); + } + } + //todo: if there is a local package, look to use it in the future if (string.IsNullOrWhiteSpace(key.UninstallString)) { @@ -145,6 +160,7 @@ public void remove(RegistryApplicationKey key, ChocolateyConfiguration config, P } } var uninstallArgs = key.UninstallString.to_string().Replace(uninstallExe.to_string(), string.Empty).trim_safe(); + uninstallExe = uninstallExe.remove_surrounding_quotes(); this.Log().Debug(() => " Uninstaller path is '{0}'".format_with(uninstallExe)); @@ -175,6 +191,20 @@ public void remove(RegistryApplicationKey key, ChocolateyConfiguration config, P uninstallArgs += " " + installer.build_uninstall_command_arguments(); } + if (!string.IsNullOrWhiteSpace(userProvidedUninstallArguments)) + { + if (userOverrideUninstallArguments) + { + this.Log().Debug(() => " Replacing original uninstall arguments of '{0}' with '{1}'".format_with(uninstallArgs.escape_curly_braces(),userProvidedUninstallArguments.escape_curly_braces())); + uninstallArgs = userProvidedUninstallArguments; + } + else + { + this.Log().Debug(() => " Appending original uninstall arguments with '{0}'".format_with(userProvidedUninstallArguments.escape_curly_braces())); + uninstallArgs += " " + userProvidedUninstallArguments; + } + } + this.Log().Debug(() => " Setting up uninstall logging directory at {0}".format_with(packageCacheLocation.escape_curly_braces())); _fileSystem.create_directory_if_not_exists(_fileSystem.get_directory_name(packageCacheLocation)); uninstallArgs = uninstallArgs.Replace(InstallTokens.PACKAGE_LOCATION, packageCacheLocation);