Files
velopack/test/UpdateManagerTests.cs
2020-10-13 18:15:30 +02:00

391 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32;
using Squirrel;
using Squirrel.Tests.TestHelpers;
using Xunit;
using System.Net;
using NuGet;
namespace Squirrel.Tests
{
public class UpdateManagerTests
{
public class CreateUninstallerRegKeyTests
{
[Fact]
public async Task CallingMethodTwiceShouldUpdateInstaller()
{
string remotePkgPath;
string path;
using (Utility.WithTempDirectory(out path)) {
using (Utility.WithTempDirectory(out remotePkgPath))
using (var mgr = new UpdateManager(remotePkgPath, "theApp", path)) {
IntegrationTestHelper.CreateFakeInstalledApp("1.0.0.1", remotePkgPath);
await mgr.FullInstall();
}
using (var mgr = new UpdateManager("http://lol", "theApp", path)) {
await mgr.CreateUninstallerRegistryEntry();
var regKey = await mgr.CreateUninstallerRegistryEntry();
Assert.False(String.IsNullOrWhiteSpace((string)regKey.GetValue("DisplayName")));
mgr.RemoveUninstallerRegistryEntry();
}
// NB: Squirrel-Aware first-run might still be running, slow
// our roll before blowing away the temp path
Thread.Sleep(1000);
}
var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall");
using (key) {
Assert.False(key.GetSubKeyNames().Contains("theApp"));
}
}
}
public class UpdateLocalReleasesTests
{
[Fact]
public async Task UpdateLocalReleasesSmokeTest()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir)) {
var appDir = Path.Combine(tempDir, "theApp");
var packageDir = Directory.CreateDirectory(Path.Combine(appDir, "packages"));
new[] {
"Squirrel.Core.1.0.0.0-full.nupkg",
"Squirrel.Core.1.1.0.0-delta.nupkg",
"Squirrel.Core.1.1.0.0-full.nupkg",
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x)));
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
await fixture.updateLocalReleasesFile();
var releasePath = Path.Combine(packageDir.FullName, "RELEASES");
File.Exists(releasePath).ShouldBeTrue();
var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8));
entries.Count().ShouldEqual(3);
}
}
[Fact]
public async Task InitialInstallSmokeTest()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir)) {
var remotePackageDir = Directory.CreateDirectory(Path.Combine(tempDir, "remotePackages"));
var localAppDir = Path.Combine(tempDir, "theApp");
new[] {
"Squirrel.Core.1.0.0.0-full.nupkg",
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(remotePackageDir.FullName, x)));
using (var fixture = new UpdateManager(remotePackageDir.FullName, "theApp", tempDir)) {
await fixture.FullInstall();
}
var releasePath = Path.Combine(localAppDir, "packages", "RELEASES");
File.Exists(releasePath).ShouldBeTrue();
var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8));
entries.Count().ShouldEqual(1);
new[] {
"ReactiveUI.dll",
"NSync.Core.dll",
}.ForEach(x => File.Exists(Path.Combine(localAppDir, "app-1.0.0.0", x)).ShouldBeTrue());
}
}
[Fact(Skip = "This test is currently failing in CI")]
public async Task SpecialCharactersInitialInstallTest()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir))
{
var remotePackageDir = Directory.CreateDirectory(Path.Combine(tempDir, "remotePackages"));
var localAppDir = Path.Combine(tempDir, "theApp");
new[] {
"SpecialCharacters-0.1.0-full.nupkg",
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(remotePackageDir.FullName, x)));
using (var fixture = new UpdateManager(remotePackageDir.FullName, "theApp", tempDir))
{
await fixture.FullInstall();
}
var releasePath = Path.Combine(localAppDir, "packages", "RELEASES");
File.Exists(releasePath).ShouldBeTrue();
var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8));
entries.Count().ShouldEqual(1);
new[] {
"file space name.txt"
}.ForEach(x => File.Exists(Path.Combine(localAppDir, "app-0.1.0", x)).ShouldBeTrue());
}
}
[Fact]
public async Task WhenBothFilesAreInSyncNoUpdatesAreApplied()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir))
{
var appDir = Path.Combine(tempDir, "theApp");
var localPackages = Path.Combine(appDir, "packages");
var remotePackages = Path.Combine(tempDir, "releases");
Directory.CreateDirectory(localPackages);
Directory.CreateDirectory(remotePackages);
new[] {
"Squirrel.Core.1.0.0.0-full.nupkg",
"Squirrel.Core.1.1.0.0-delta.nupkg",
"Squirrel.Core.1.1.0.0-full.nupkg",
}.ForEach(x => {
var path = IntegrationTestHelper.GetPath("fixtures", x);
File.Copy(path, Path.Combine(localPackages, x));
File.Copy(path, Path.Combine(remotePackages, x));
});
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
// sync both release files
await fixture.updateLocalReleasesFile();
ReleaseEntry.BuildReleasesFile(remotePackages);
// check for an update
UpdateInfo updateInfo;
using (var mgr = new UpdateManager(remotePackages, "theApp", tempDir, new FakeUrlDownloader())) {
updateInfo = await mgr.CheckForUpdate();
}
Assert.NotNull(updateInfo);
Assert.Empty(updateInfo.ReleasesToApply);
}
}
[Fact]
public async Task WhenRemoteReleasesDoNotHaveDeltasNoUpdatesAreApplied()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir))
{
var appDir = Path.Combine(tempDir, "theApp");
var localPackages = Path.Combine(appDir, "packages");
var remotePackages = Path.Combine(tempDir, "releases");
Directory.CreateDirectory(localPackages);
Directory.CreateDirectory(remotePackages);
new[] {
"Squirrel.Core.1.0.0.0-full.nupkg",
"Squirrel.Core.1.1.0.0-delta.nupkg",
"Squirrel.Core.1.1.0.0-full.nupkg",
}.ForEach(x => {
var path = IntegrationTestHelper.GetPath("fixtures", x);
File.Copy(path, Path.Combine(localPackages, x));
});
new[] {
"Squirrel.Core.1.0.0.0-full.nupkg",
"Squirrel.Core.1.1.0.0-full.nupkg",
}.ForEach(x => {
var path = IntegrationTestHelper.GetPath("fixtures", x);
File.Copy(path, Path.Combine(remotePackages, x));
});
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
// sync both release files
await fixture.updateLocalReleasesFile();
ReleaseEntry.BuildReleasesFile(remotePackages);
UpdateInfo updateInfo;
using (var mgr = new UpdateManager(remotePackages, "theApp", tempDir, new FakeUrlDownloader())) {
updateInfo = await mgr.CheckForUpdate();
}
Assert.NotNull(updateInfo);
Assert.Empty(updateInfo.ReleasesToApply);
}
}
[Fact]
public async Task WhenTwoRemoteUpdatesAreAvailableChoosesDeltaVersion()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir))
{
var appDir = Path.Combine(tempDir, "theApp");
var localPackages = Path.Combine(appDir, "packages");
var remotePackages = Path.Combine(tempDir, "releases");
Directory.CreateDirectory(localPackages);
Directory.CreateDirectory(remotePackages);
new[] { "Squirrel.Core.1.0.0.0-full.nupkg", }.ForEach(x => {
var path = IntegrationTestHelper.GetPath("fixtures", x);
File.Copy(path, Path.Combine(localPackages, x));
});
new[] {
"Squirrel.Core.1.0.0.0-full.nupkg",
"Squirrel.Core.1.1.0.0-delta.nupkg",
"Squirrel.Core.1.1.0.0-full.nupkg",
}.ForEach(x => {
var path = IntegrationTestHelper.GetPath("fixtures", x);
File.Copy(path, Path.Combine(remotePackages, x));
});
var fixture = new UpdateManager.ApplyReleasesImpl(appDir);
// sync both release files
await fixture.updateLocalReleasesFile();
ReleaseEntry.BuildReleasesFile(remotePackages);
using (var mgr = new UpdateManager(remotePackages, "theApp", tempDir, new FakeUrlDownloader())) {
UpdateInfo updateInfo;
updateInfo = await mgr.CheckForUpdate();
Assert.True(updateInfo.ReleasesToApply.First().IsDelta);
updateInfo = await mgr.CheckForUpdate(ignoreDeltaUpdates: true);
Assert.False(updateInfo.ReleasesToApply.First().IsDelta);
}
}
}
[Fact]
public async Task WhenFolderDoesNotExistThrowHelpfulError()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir)) {
var directory = Path.Combine(tempDir, "missing-folder");
var fixture = new UpdateManager(directory, "MyAppName");
using (fixture) {
await Assert.ThrowsAsync<Exception>(() => fixture.CheckForUpdate());
}
}
}
[Fact]
public async Task WhenReleasesFileDoesntExistThrowACustomError()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir)) {
var fixture = new UpdateManager(tempDir, "MyAppName");
using (fixture) {
await Assert.ThrowsAsync<Exception>(() => fixture.CheckForUpdate());
}
}
}
[Fact]
public async Task WhenReleasesFileIsBlankThrowAnException()
{
string tempDir;
using (Utility.WithTempDirectory(out tempDir)) {
var fixture = new UpdateManager(tempDir, "MyAppName");
File.WriteAllText(Path.Combine(tempDir, "RELEASES"), "");
using (fixture) {
await Assert.ThrowsAsync(typeof(Exception), () => fixture.CheckForUpdate());
}
}
}
[Fact]
public async Task WhenUrlResultsInWebExceptionWeShouldThrow()
{
// This should result in a WebException (which gets caught) unless you can actually access http://lol
using (var fixture = new UpdateManager("http://lol", "theApp")) {
await Assert.ThrowsAsync(typeof(WebException), () => fixture.CheckForUpdate());
}
}
[Theory]
[InlineData("C:\\Foo\\Bar\\Test.exe", default(string))]
[InlineData("%LocalAppData%\\theApp\\app-1.0.0.1\\Test.exe", "1.0.0.1")]
[InlineData("%LocalAppData%\\aDifferentApp\\app-1.0.0.1\\Test.exe", default(string))]
public void CurrentlyInstalledVersionTests(string input, string expectedVersion)
{
input = Environment.ExpandEnvironmentVariables(input);
var expected = expectedVersion != null ? new SemanticVersion(expectedVersion) : default(SemanticVersion);
using (var fixture = new UpdateManager("http://lol", "theApp")) {
Assert.Equal(expected, fixture.CurrentlyInstalledVersion(input));
}
}
[Theory]
[InlineData(0, 0, 25, 0)]
[InlineData(12, 0, 25, 3)]
[InlineData(55, 0, 25, 13)]
[InlineData(100, 0, 25, 25)]
[InlineData(0, 25, 50, 25)]
[InlineData(12, 25, 50, 28)]
[InlineData(55, 25, 50, 38)]
[InlineData(100, 25, 50, 50)]
public void CalculatesPercentageCorrectly(int percentageOfCurrentStep, int stepStartPercentage, int stepEndPercentage, int expectedPercentage)
{
var percentage = UpdateManager.CalculateProgress(percentageOfCurrentStep, stepStartPercentage, stepEndPercentage);
Assert.Equal(expectedPercentage, percentage);
}
[Fact]
public void CalculatesPercentageCorrectlyForUpdateExe()
{
// Note: this mimicks the update.exe progress reporting of multiple steps
var progress = new List<int>();
// 3 % (3 stages), check for updates
foreach (var step in new [] { 0, 33, 66, 100 })
{
progress.Add(UpdateManager.CalculateProgress(step, 0, 3));
Assert.InRange(progress.Last(), 0, 3);
}
Assert.Equal(3, progress.Last());
// 3 - 30 %, download releases
for (var step = 0; step <= 100; step++)
{
progress.Add(UpdateManager.CalculateProgress(step, 3, 30));
Assert.InRange(progress.Last(), 3, 30);
}
Assert.Equal(30, progress.Last());
// 30 - 100 %, apply releases
for (var step = 0; step <= 100; step++)
{
progress.Add(UpdateManager.CalculateProgress(step, 30, 100));
Assert.InRange(progress.Last(), 30, 100);
}
Assert.Equal(100, progress.Last());
}
}
}
}