Skip to content

Commit

Permalink
(chocolateyGH-121) Remove unchanged files on uninstall
Browse files Browse the repository at this point in the history
When the files that were installed during install are unchanged, they
should be removed from the package directory. Any files that have
changed should stick around.
  • Loading branch information
ferventcoder committed May 12, 2015
1 parent 8b22eeb commit 2fdf3bc
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/chocolatey.tests/chocolatey.tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
<Compile Include="infrastructure.app\configuration\ConfigurationOptionsSpec.cs" />
<Compile Include="infrastructure.app\services\AutomaticUninstallerServiceSpecs.cs" />
<Compile Include="infrastructure.app\services\FilesServiceSpecs.cs" />
<Compile Include="infrastructure.app\services\NugetServiceSpecs.cs" />
<Compile Include="infrastructure\commands\ExternalCommandArgsBuilderSpecs.cs" />
<Compile Include="infrastructure\commandline\InteractivePromptSpecs.cs" />
<Compile Include="infrastructure\commands\CommandExecutorSpecs.cs" />
Expand Down
166 changes: 166 additions & 0 deletions src/chocolatey.tests/infrastructure.app/services/NugetServiceSpecs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright © 2011 - Present RealDimensions Software, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace chocolatey.tests.infrastructure.app.services
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Moq;
using NuGet;
using chocolatey.infrastructure.app.domain;
using chocolatey.infrastructure.app.services;
using IFileSystem = chocolatey.infrastructure.filesystem.IFileSystem;

public class NugetServiceSpecs
{
public abstract class NugetServiceSpecsBase : TinySpec
{
protected NugetService service;
protected Mock<IChocolateyPackageInformationService> packageInfoService = new Mock<IChocolateyPackageInformationService>();
protected Mock<IFileSystem> fileSystem = new Mock<IFileSystem>();
protected Mock<ILogger> nugetLogger = new Mock<ILogger>();
protected Mock<IFilesService> filesService = new Mock<IFilesService>();
protected Mock<IPackage> package = new Mock<IPackage>();

public override void Context()
{
fileSystem.ResetCalls();
nugetLogger.ResetCalls();
packageInfoService.ResetCalls();
filesService.ResetCalls();
package.ResetCalls();

service = new NugetService(fileSystem.Object, nugetLogger.Object, packageInfoService.Object, filesService.Object);
}
}

public class when_NugetService_removes_installation_files_on_uninstall : NugetServiceSpecsBase
{
private Action because;
private ChocolateyPackageInformation packageInfo;
private const string filePath = "c:\\tests";
private IList<PackageFile> packageFiles;

public override void Context()
{
base.Context();
package.Setup(x => x.Id).Returns("bob");
packageInfo = new ChocolateyPackageInformation(package.Object);
packageInfo.FilesSnapshot = new PackageFiles();
packageFiles = new List<PackageFile>();
fileSystem.Setup(x => x.directory_exists(It.IsAny<string>())).Returns(true);

}

public override void Because()
{
because = () => service.remove_installation_files(package.Object, packageInfo);
}

[Fact]
public void should_do_nothing_if_the_directory_no_longer_exists()
{
Context();
fileSystem.ResetCalls();
fileSystem.Setup(x => x.directory_exists(It.IsAny<string>())).Returns(false);

var packageFile = new PackageFile { Path = filePath, Checksum = "1234" };
packageFiles.Add(packageFile);

packageInfo.FilesSnapshot.Files = packageFiles.ToList();

var fileSystemFiles = new List<string>() { filePath };

fileSystem.Setup(x => x.get_files(It.IsAny<string>(), It.IsAny<string>(), SearchOption.AllDirectories)).Returns(fileSystemFiles);
filesService.Setup(x => x.get_package_file(It.IsAny<string>())).Returns(packageFile);

because();

filesService.Verify(x => x.get_package_file(It.IsAny<string>()), Times.Never);
fileSystem.Verify(x => x.delete_file(filePath),Times.Never);
}

[Fact]
public void should_remove_an_unchanged_file()
{
Context();

var packageFile = new PackageFile { Path = filePath, Checksum = "1234" };
packageFiles.Add(packageFile);

packageInfo.FilesSnapshot.Files = packageFiles.ToList();

var fileSystemFiles = new List<string>() { filePath };


fileSystem.Setup(x => x.get_files(It.IsAny<string>(), It.IsAny<string>(), SearchOption.AllDirectories)).Returns(fileSystemFiles);

filesService.Setup(x => x.get_package_file(It.IsAny<string>())).Returns(packageFile);

because();

fileSystem.Verify(x => x.delete_file(filePath));
}

[Fact]
public void should_not_delete_a_changed_file()
{
Context();

var packageFile = new PackageFile { Path = filePath, Checksum = "1234" };
var packageFileWithUpdatedChecksum = new PackageFile { Path = filePath, Checksum = "4321" };
packageFiles.Add(packageFile);

packageInfo.FilesSnapshot.Files = packageFiles.ToList();

var fileSystemFiles = new List<string>() { filePath };


fileSystem.Setup(x => x.get_files(It.IsAny<string>(), It.IsAny<string>(), SearchOption.AllDirectories)).Returns(fileSystemFiles);

filesService.Setup(x => x.get_package_file(It.IsAny<string>())).Returns(packageFileWithUpdatedChecksum);

because();

fileSystem.Verify(x => x.delete_file(filePath),Times.Never);
}

[Fact]
public void should_not_delete_an_unfound_file()
{
Context();

var packageFile = new PackageFile { Path = filePath, Checksum = "1234" };
var packageFileNotInOriginal = new PackageFile { Path ="c:\\files", Checksum = "4321" };
packageFiles.Add(packageFile);

packageInfo.FilesSnapshot.Files = packageFiles.ToList();

var fileSystemFiles = new List<string>() { filePath };


fileSystem.Setup(x => x.get_files(It.IsAny<string>(), It.IsAny<string>(), SearchOption.AllDirectories)).Returns(fileSystemFiles);

filesService.Setup(x => x.get_package_file(It.IsAny<string>())).Returns(packageFileNotInOriginal);

because();

fileSystem.Verify(x => x.delete_file(filePath),Times.Never);
}
}
}
}
12 changes: 9 additions & 3 deletions src/chocolatey/infrastructure.app/services/FilesService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,18 @@ public PackageFiles capture_package_files(PackageResult packageResult, Chocolate
var files = _fileSystem.get_files(installDirectory, pattern: "*.*", option: SearchOption.AllDirectories);
foreach (string file in files.or_empty_list_if_null())
{
var hash = _hashProvider.hash_file(file);
this.Log().Debug(() => " Found '{0}'{1} with checksum '{2}'".format_with(file, Environment.NewLine, hash));
packageFiles.Files.Add(new PackageFile { Path = file, Checksum = hash});
packageFiles.Files.Add(get_package_file(file));
}

return packageFiles;
}

public PackageFile get_package_file(string file)
{
var hash = _hashProvider.hash_file(file);
this.Log().Debug(() => " Found '{0}'{1} with checksum '{2}'".format_with(file, Environment.NewLine, hash));

return new PackageFile { Path = file, Checksum = hash };
}
}
}
7 changes: 7 additions & 0 deletions src/chocolatey/infrastructure.app/services/IFilesService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,12 @@ public interface IFilesService
/// <param name="config">The configuration.</param>
/// <returns>PackageFiles with entries based on the install location of the package.</returns>
PackageFiles capture_package_files(PackageResult packageResult, ChocolateyConfiguration config);

/// <summary>
/// Gets a PackageFile from the filepath
/// </summary>
/// <param name="file">The file.</param>
/// <returns>PackageFile object</returns>
PackageFile get_package_file(string file);
}
}
24 changes: 23 additions & 1 deletion src/chocolatey/infrastructure.app/services/NugetService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class NugetService : INugetService
private readonly IFileSystem _fileSystem;
private readonly ILogger _nugetLogger;
private readonly IChocolateyPackageInformationService _packageInfoService;
private readonly IFilesService _filesService;
private readonly Lazy<IDateTime> datetime_initializer = new Lazy<IDateTime>(() => new DateTime());

private IDateTime DateTime
Expand All @@ -55,11 +56,13 @@ private IDateTime DateTime
/// <param name="fileSystem">The file system.</param>
/// <param name="nugetLogger">The nuget logger</param>
/// <param name="packageInfoService">Package information service</param>
public NugetService(IFileSystem fileSystem, ILogger nugetLogger, IChocolateyPackageInformationService packageInfoService)
/// <param name="filesService">The files service</param>
public NugetService(IFileSystem fileSystem, ILogger nugetLogger, IChocolateyPackageInformationService packageInfoService, IFilesService filesService)
{
_fileSystem = fileSystem;
_nugetLogger = nugetLogger;
_packageInfoService = packageInfoService;
_filesService = filesService;
}

public void list_noop(ChocolateyConfiguration config)
Expand Down Expand Up @@ -861,6 +864,7 @@ public ConcurrentDictionary<string, PackageResult> uninstall_run(ChocolateyConfi
backup_existing_version(config, packageVersion);
packageManager.UninstallPackage(packageVersion, forceRemove: config.Force, removeDependencies: config.ForceDependencies);
ensure_nupkg_is_removed(packageVersion, pkgInfo);
remove_installation_files(packageVersion, pkgInfo);
}
}
catch (Exception ex)
Expand Down Expand Up @@ -900,6 +904,24 @@ private void ensure_nupkg_is_removed(IPackage removedPackage, ChocolateyPackageI
_fileSystem.delete_file(nupkg);
}

public void remove_installation_files(IPackage removedPackage, ChocolateyPackageInformation pkgInfo)
{
var isSideBySide = pkgInfo != null && pkgInfo.IsSideBySide;
var installDir = _fileSystem.combine_paths(ApplicationParameters.PackagesLocation, "{0}{1}".format_with(removedPackage.Id, isSideBySide ? "." + removedPackage.Version.to_string() : string.Empty));

if (_fileSystem.directory_exists(installDir) && pkgInfo != null && pkgInfo.FilesSnapshot != null)
{
foreach (var file in _fileSystem.get_files(installDir, "*.*", SearchOption.AllDirectories).or_empty_list_if_null())
{
var fileSnapshot = pkgInfo.FilesSnapshot.Files.FirstOrDefault(f => f.Path.is_equal_to(file));
if (fileSnapshot != null && fileSnapshot.Checksum == _filesService.get_package_file(file).Checksum)
{
_fileSystem.delete_file(file);
}
}
}
}

private void set_package_names_if_all_is_specified(ChocolateyConfiguration config, Action customAction)
{
if (config.PackageNames.is_equal_to("all"))
Expand Down

0 comments on commit 2fdf3bc

Please sign in to comment.