From 3aa140a0befbe6f05e90bfcc98ed3beda67ea65a Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Tue, 6 Jun 2017 15:17:16 -0500 Subject: [PATCH] (GH-1327) AutoUninstaller - split multiple file paths Say the uninstall string was specified like this: `C:\Programs\WinDirStat\Uninstall.exe D:\Programs\WinDirStat` Running uninstall will produce the following message from automatic uninstaller: ~~~sh 2017-06-06 13:42:13,463 5244 [INFO ] - Running auto uninstaller... 2017-06-06 13:42:13,463 5244 [DEBUG] - Sleeping for 2 seconds to allow Windows to finish cleaning up. 2017-06-06 13:42:15,479 5244 [DEBUG] - Preparing uninstall key 'C:\Programs\WinDirStat\Uninstall.exe D:\Programs\WinDirStat' for 'WinDirStat' 2017-06-06 13:42:15,479 5244 [DEBUG] - Uninstaller path is 'C:\Programs\WinDirStat\Uninstall.exe D:\Programs\WinDirStat' 2017-06-06 13:42:15,495 5244 [INFO ] - Skipping auto uninstaller - The uninstaller file no longer exists. "C:\Programs\WinDirStat\Uninstall.exe D:\Programs\WinDirStat" ~~~ Split the paths when multiple `:` are detected. --- .../AutomaticUninstallerServiceSpecs.cs | 51 +++++++++++++++++++ .../services/AutomaticUninstallerService.cs | 18 ++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs index f021c6d521..e7d41321eb 100644 --- a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs @@ -424,6 +424,7 @@ public override void Context() registryKeys.Add( new RegistryApplicationKey { + DisplayName = expectedDisplayName, InstallLocation = @"C:\Program Files (x86)\WinDirStat", UninstallString = uninstallStringWithQuoteSeparation, HasQuietUninstall = true, @@ -458,6 +459,56 @@ public void should_call_command_executor() It.IsAny()), Times.Once); } + } + + public class when_uninstall_string_has_multiple_file_paths : AutomaticUninstallerServiceSpecsBase + { + private readonly string uninstallStringPointingToPath = @"C:\Programs\WinDirStat\Uninstall.exe D:\Programs\WinDirStat"; + protected readonly string expectedUninstallStringMultiplePaths = @"C:\Programs\WinDirStat\Uninstall.exe"; + + public override void Context() + { + base.Context(); + registryKeys.Clear(); + registryKeys.Add( + new RegistryApplicationKey + { + DisplayName = expectedDisplayName, + InstallLocation = @"C:\Program Files (x86)\WinDirStat", + UninstallString = uninstallStringPointingToPath, + HasQuietUninstall = true, + KeyPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\WinDirStat", + InstallerType = installerType.InstallerType, + }); + packageInformation.RegistrySnapshot = new Registry("123", registryKeys); + fileSystem.Setup(x => x.file_exists(expectedUninstallStringMultiplePaths)).Returns(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() + { + commandExecutor.Verify( + c => + c.execute( + expectedUninstallStringMultiplePaths, + @"D:\Programs\WinDirStat".trim_safe(), + It.IsAny(), + It.IsAny>(), + It.IsAny>(), + It.IsAny()), + Times.Once); + } } public class when_AutomaticUninstallerService_cannot_determine_silent_install_arguments : AutomaticUninstallerServiceSpecsBase diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index b0485ac69f..2d76f6bec2 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -19,6 +19,7 @@ namespace chocolatey.infrastructure.app.services using System; using System.Collections.Generic; using System.Linq; + using System.Text.RegularExpressions; using System.Threading; using commandline; using configuration; @@ -104,12 +105,25 @@ public void remove(RegistryApplicationKey key, ChocolateyConfiguration config, P // split on " /" and " -" for quite a bit more accuracy IList uninstallArgsSplit = key.UninstallString.to_string().Split(new[] { " /", " -" }, StringSplitOptions.RemoveEmptyEntries).ToList(); - var uninstallExe = uninstallArgsSplit.DefaultIfEmpty(string.Empty).FirstOrDefault(); + var uninstallExe = uninstallArgsSplit.DefaultIfEmpty(string.Empty).FirstOrDefault().trim_safe(); if (uninstallExe.Count(u => u == '"') > 2) { uninstallExe = uninstallExe.Split(new []{" \""}, StringSplitOptions.RemoveEmptyEntries).First(); } - var uninstallArgs = key.UninstallString.to_string().Replace(uninstallExe.to_string(), string.Empty); + + if (uninstallExe.Count(u => u == ':') > 1) + { + try + { + var firstMatch = Regex.Match(uninstallExe, @"\s+\w\:",RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + uninstallExe = uninstallExe.Substring(0, firstMatch.Index); + } + catch (Exception ex) + { + this.Log().Debug("Error splitting the uninstall string:{0} {1}".format_with(Environment.NewLine,ex.to_string())); + } + } + 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));