예제 #1
0
        public void NoLocalDirectoryMeansWeStartFromScratch()
        {
            string localPackagesDir  = Path.Combine(".", "theApp", "packages");
            string localReleasesFile = Path.Combine(localPackagesDir, "RELEASES");

            var fileInfo = new Mock <FileInfoBase>();

            fileInfo.Setup(x => x.Exists).Returns(false);

            var dirInfo = new Mock <DirectoryInfoBase>();

            dirInfo.Setup(x => x.Exists).Returns(false);

            var fs = new Mock <IFileSystemFactory>();

            fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object);
            fs.Setup(x => x.CreateDirectoryRecursive(It.IsAny <string>())).Verifiable();
            fs.Setup(x => x.GetDirectoryInfo(localPackagesDir)).Returns(dirInfo.Object);

            var urlDownloader = new Mock <IUrlDownloader>();
            var dlPath        = IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOne");

            urlDownloader.Setup(x => x.DownloadUrl(It.IsAny <string>()))
            .Returns(Observable.Return(File.ReadAllText(dlPath, Encoding.UTF8)));

            var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, ".", fs.Object, urlDownloader.Object);

            using (fixture.AcquireUpdateLock()) {
                fixture.CheckForUpdate().First();
            }

            fs.Verify(x => x.CreateDirectoryRecursive(localPackagesDir), Times.Once());
        }
예제 #2
0
        public void NewReleasesShouldBeDetected()
        {
            string localReleasesFile = Path.Combine(".", "theApp", "packages", "RELEASES");

            var fileInfo = new Mock <FileInfoBase>();

            fileInfo.Setup(x => x.OpenRead())
            .Returns(File.OpenRead(IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOh")));

            var fs = new Mock <IFileSystemFactory>();

            fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object);

            var urlDownloader = new Mock <IUrlDownloader>();
            var dlPath        = IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOne");

            urlDownloader.Setup(x => x.DownloadUrl(It.IsAny <string>()))
            .Returns(Observable.Return(File.ReadAllText(dlPath, Encoding.UTF8)));

            var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, ".", fs.Object, urlDownloader.Object);
            var result  = default(UpdateInfo);

            using (fixture.AcquireUpdateLock()) {
                result = fixture.CheckForUpdate().First();
            }

            Assert.NotNull(result);
            Assert.Equal(1, result.ReleasesToApply.Single().Version.Major);
            Assert.Equal(1, result.ReleasesToApply.Single().Version.Minor);
        }
예제 #3
0
            public void UpdateLocalReleasesSmokeTest()
            {
                string tempDir;

                using (Utility.WithTempDirectory(out tempDir)) {
                    var packageDir = Directory.CreateDirectory(Path.Combine(tempDir, "theApp", "packages"));

                    new[] {
                        "Shimmer.Core.1.0.0.0-full.nupkg",
                        "Shimmer.Core.1.1.0.0-delta.nupkg",
                        "Shimmer.Core.1.1.0.0-full.nupkg",
                    }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x)));

                    var urlDownloader = new Mock <IUrlDownloader>();
                    var fixture       = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, null, urlDownloader.Object);

                    using (fixture.AcquireUpdateLock()) {
                        fixture.UpdateLocalReleasesFile().Last();
                    }

                    var releasePath = Path.Combine(packageDir.FullName, "RELEASES");
                    File.Exists(releasePath).ShouldBeTrue();

                    var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8));
                    entries.Count().ShouldEqual(3);
                }
            }
예제 #4
0
        public void ExecutablesPinnedToTaskbarShouldPointToNewVersion()
        {
            string tempDir;

            using (Utility.WithTempDirectory(out tempDir)) {
                Directory.CreateDirectory(Path.Combine(tempDir, "theApp"));
                Directory.CreateDirectory(Path.Combine(tempDir, "theApp", "packages"));

                new[] {
                    "SampleUpdatingApp.1.0.0.0.nupkg",
                    "SampleUpdatingApp.1.1.0.0.nupkg",
                }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x)));

                var fixture = new UpdateManager("http://lol", "theApp", FrameworkVersion.Net40, tempDir, null, new FakeUrlDownloader());

                var baseEntry       = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "SampleUpdatingApp.1.0.0.0.nupkg"));
                var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "SampleUpdatingApp.1.1.0.0.nupkg"));

                var updateInfo = UpdateInfo.Create(null, new[] { baseEntry }, "dontcare", FrameworkVersion.Net40);
                using (fixture.AcquireUpdateLock()) {
                    fixture.ApplyReleases(updateInfo).ToList().First();
                }

                var oldExecutable = Path.Combine(tempDir, "theApp", "app-1.0.0.0", "SampleUpdatingApp.exe");
                File.Exists(oldExecutable).ShouldBeTrue();
                TaskbarHelper.PinToTaskbar(oldExecutable);

                updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, "dontcare", FrameworkVersion.Net40);
                using (fixture.AcquireUpdateLock()) {
                    fixture.ApplyReleases(updateInfo).ToList().First();
                }

                var newExecutable = Path.Combine(tempDir, "theApp", "app-1.1.0.0", "SampleUpdatingApp.exe");
                File.Exists(newExecutable).ShouldBeTrue();
                TaskbarHelper.IsPinnedToTaskbar(newExecutable).ShouldBeTrue();

                Utility.Retry(() => TaskbarHelper.UnpinFromTaskbar(newExecutable));
            }
        }
예제 #5
0
        public void DownloadReleasesFromHttpServerIntegrationTest()
        {
            string tempDir = null;

            var updateDir = new DirectoryInfo(IntegrationTestHelper.GetPath("..", "SampleUpdatingApp", "SampleReleasesFolder"));

            IDisposable disp;

            try {
                var httpServer = new StaticHttpServer(30405, updateDir.FullName);
                disp = httpServer.Start();
            }
            catch (HttpListenerException) {
                Assert.False(true, @"Windows sucks, go run 'netsh http add urlacl url=http://+:30405/ user=MYMACHINE\MyUser");
                return;
            }

            var entriesToDownload = updateDir.GetFiles("*.nupkg")
                                    .Select(x => ReleaseEntry.GenerateFromFile(x.FullName))
                                    .ToArray();

            entriesToDownload.Count().ShouldBeGreaterThan(0);

            using (disp)
                using (Utility.WithTempDirectory(out tempDir)) {
                    // NB: This is normally done by CheckForUpdates, but since
                    // we're skipping that in the test we have to do it ourselves
                    (new DirectoryInfo(Path.Combine(tempDir, "SampleUpdatingApp", "packages"))).CreateRecursive();

                    var fixture = new UpdateManager("http://localhost:30405", "SampleUpdatingApp", FrameworkVersion.Net40, tempDir);
                    using (fixture.AcquireUpdateLock()) {
                        var progress = fixture.DownloadReleases(entriesToDownload).ToList().First();
                        this.Log().Info("Progress: [{0}]", String.Join(",", progress));
                        progress.Buffer(2, 1).All(x => x.Count != 2 || x[1] > x[0]).ShouldBeTrue();
                        progress.Last().ShouldEqual(100);
                    }

                    entriesToDownload.ForEach(x => {
                        this.Log().Info("Looking for {0}", x.Filename);
                        var actualFile = Path.Combine(tempDir, "SampleUpdatingApp", "packages", x.Filename);
                        File.Exists(actualFile).ShouldBeTrue();

                        var actualEntry = ReleaseEntry.GenerateFromFile(actualFile);
                        actualEntry.SHA1.ShouldEqual(x.SHA1);
                        actualEntry.Version.ShouldEqual(x.Version);
                    });
                }
        }
예제 #6
0
        public void ApplyReleasesWithDeltaReleases()
        {
            string tempDir;

            using (Utility.WithTempDirectory(out tempDir)) {
                Directory.CreateDirectory(Path.Combine(tempDir, "theApp", "packages"));

                new[] {
                    "Shimmer.Core.1.0.0.0-full.nupkg",
                    "Shimmer.Core.1.1.0.0-delta.nupkg",
                    "Shimmer.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("http://lol", "theApp", FrameworkVersion.Net40, tempDir, null, new FakeUrlDownloader());

                var baseEntry       = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Shimmer.Core.1.0.0.0-full.nupkg"));
                var deltaEntry      = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Shimmer.Core.1.1.0.0-delta.nupkg"));
                var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Shimmer.Core.1.1.0.0-full.nupkg"));

                var updateInfo = UpdateInfo.Create(baseEntry, new[] { deltaEntry, latestFullEntry }, "dontcare", FrameworkVersion.Net40);
                updateInfo.ReleasesToApply.Contains(deltaEntry).ShouldBeTrue();

                using (fixture.AcquireUpdateLock()) {
                    var progress = fixture.ApplyReleases(updateInfo).ToList().First();
                    this.Log().Info("Progress: [{0}]", String.Join(",", progress));
                    progress.Buffer(2, 1).All(x => x.Count != 2 || x[1] > x[0]).ShouldBeTrue();
                    progress.Last().ShouldEqual(100);
                }

                var filesToFind = new[] {
                    new { Name = "NLog.dll", Version = new Version("2.0.0.0") },
                    new { Name = "NSync.Core.dll", Version = new Version("1.1.0.0") },
                    new { Name = "Ionic.Zip.dll", Version = new Version("1.9.1.8") },
                };

                filesToFind.ForEach(x => {
                    var path = Path.Combine(tempDir, "theApp", "app-1.1.0.0", x.Name);
                    this.Log().Info("Looking for {0}", path);
                    File.Exists(path).ShouldBeTrue();

                    var vi      = FileVersionInfo.GetVersionInfo(path);
                    var verInfo = new Version(vi.FileVersion ?? "1.0.0.0");
                    x.Version.ShouldEqual(verInfo);
                });
            }
        }
예제 #7
0
        public void DownloadReleasesFromFileDirectoryIntegrationTest()
        {
            string tempDir = null;

            var updateDir = new DirectoryInfo(IntegrationTestHelper.GetPath("..", "SampleUpdatingApp", "SampleReleasesFolder"));

            var entriesToDownload = updateDir.GetFiles("*.nupkg")
                                    .Select(x => ReleaseEntry.GenerateFromFile(x.FullName))
                                    .ToArray();

            entriesToDownload.Count().ShouldBeGreaterThan(0);

            using (Utility.WithTempDirectory(out tempDir)) {
                // NB: This is normally done by CheckForUpdates, but since
                // we're skipping that in the test we have to do it ourselves
                (new DirectoryInfo(Path.Combine(tempDir, "SampleUpdatingApp", "packages"))).CreateRecursive();

                var fixture = new UpdateManager(updateDir.FullName, "SampleUpdatingApp", FrameworkVersion.Net40, tempDir);
                using (fixture.AcquireUpdateLock()) {
                    var progress = fixture.DownloadReleases(entriesToDownload).ToList().First();
                    this.Log().Info("Progress: [{0}]", String.Join(",", progress));
                    progress.Buffer(2, 1).All(x => x.Count != 2 || x[1] > x[0]).ShouldBeTrue();
                    progress.Last().ShouldEqual(100);
                }

                entriesToDownload.ForEach(x => {
                    this.Log().Info("Looking for {0}", x.Filename);
                    var actualFile = Path.Combine(tempDir, "SampleUpdatingApp", "packages", x.Filename);
                    File.Exists(actualFile).ShouldBeTrue();

                    var actualEntry = ReleaseEntry.GenerateFromFile(actualFile);
                    actualEntry.SHA1.ShouldEqual(x.SHA1);
                    actualEntry.Version.ShouldEqual(x.Version);
                });
            }
        }
예제 #8
0
        public WixUiBootstrapper(IWiXEvents wixEvents, TinyIoCContainer testKernel = null, IRoutingState router = null)
        {
            Kernel = testKernel ?? createDefaultKernel();

            Kernel.Register <IWixUiBootstrapper>(this);
            Kernel.Register <IScreen>(this);
            Kernel.Register(wixEvents);

            Router    = router ?? new RoutingState();
            WiXEvents = wixEvents;

            _BundledRelease = new Lazy <ReleaseEntry>(readBundledReleasesFile);

            registerExtensionDlls(Kernel);

            RxApp.ConfigureServiceLocator(
                (type, contract) => Kernel.Resolve(type, contract),
                (type, contract) => Kernel.ResolveAll(type),
                (c, t, s) => Kernel.Register(t, c, s));

            UserError.RegisterHandler(ex => {
                if (wixEvents.Command.Display != Display.Full)
                {
                    this.Log().Error(ex.ErrorMessage);
                    wixEvents.ShouldQuit();
                }

                var errorVm   = RxApp.GetService <IErrorViewModel>();
                errorVm.Error = ex;
                Router.Navigate.Execute(errorVm);

                return(Observable.Return(RecoveryOptionResult.CancelOperation));
            });

            var bundledPackageMetadata = openBundledPackage();

            wixEvents.DetectPackageCompleteObs.Subscribe(eventArgs => {
                var error = convertHResultToError(eventArgs.Status);
                if (error != null)
                {
                    UserError.Throw(error);
                    return;
                }

                // TODO: If the app is already installed, run it and bail

                if (wixEvents.Command.Action == LaunchAction.Uninstall)
                {
                    var updateManager = new UpdateManager("http://lol", BundledRelease.PackageName, FrameworkVersion.Net40);

                    var updateLock = updateManager.AcquireUpdateLock();
                    updateManager.FullUninstall()
                    .ObserveOn(RxApp.DeferredScheduler)
                    .Log(this, "Full uninstall")
                    .Finally(updateLock.Dispose)
                    .Subscribe(
                        _ => wixEvents.Engine.Plan(LaunchAction.Uninstall),
                        ex => UserError.Throw("Failed to uninstall application", ex),
                        () => wixEvents.ShouldQuit());

                    return;
                }

                if (wixEvents.Command.Action == LaunchAction.Install)
                {
                    if (wixEvents.Command.Display != Display.Full)
                    {
                        wixEvents.Engine.Plan(LaunchAction.Install);
                        return;
                    }

                    var welcomeVm             = RxApp.GetService <IWelcomeViewModel>();
                    welcomeVm.PackageMetadata = bundledPackageMetadata;
                    welcomeVm.ShouldProceed.Subscribe(_ => wixEvents.Engine.Plan(LaunchAction.Install));

                    Router.Navigate.Execute(welcomeVm);
                }
            });

            wixEvents.PlanCompleteObs.Subscribe(eventArgs => {
                var error = convertHResultToError(eventArgs.Status);
                if (error != null)
                {
                    UserError.Throw(error);
                    return;
                }

                if (wixEvents.Command.Action != LaunchAction.Install)
                {
                    wixEvents.Engine.Apply(wixEvents.MainWindowHwnd);
                    return;
                }

                // NB: Create a dummy subject to receive progress if we're in silent mode
                IObserver <int> progress = new Subject <int>();
                if (wixEvents.Command.Display == Display.Full)
                {
                    var installingVm             = RxApp.GetService <IInstallingViewModel>();
                    progress                     = installingVm.ProgressValue;
                    installingVm.PackageMetadata = bundledPackageMetadata;
                    Router.Navigate.Execute(installingVm);
                }

                // NB: This bit of code is a bit clever. The binaries that WiX
                // has installed *itself* meets the qualifications for being a
                // Shimmer update directory (a RELEASES file and the corresponding
                // NuGet packages).
                //
                // So, in order to reuse some code and not write the same things
                // twice we're going to "Eigenupdate" from our own directory;
                // UpdateManager will operate in bootstrap mode and create a
                // local directory for us.
                //
                // Then, we create a *new* UpdateManager whose target is the normal
                // update URL - we can then apply delta updates against the bundled
                // NuGet package to get up to vCurrent. The reason we go through
                // this rigamarole is so that developers don't have to rebuild the
                // installer as often (never, technically).

                var fxVersion    = determineFxVersionFromPackage(bundledPackageMetadata);
                var eigenUpdater = new UpdateManager(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), BundledRelease.PackageName, fxVersion);

                var eigenLock = eigenUpdater.AcquireUpdateLock();

                var eigenUpdateProgress = eigenUpdater.CheckForUpdate()
                                          .SelectMany(x => eigenUpdater.DownloadReleases(x.ReleasesToApply))
                                          .Finally(eigenLock.Dispose)
                                          .Select(x => x / 2)
                                          .Multicast(new Subject <int>());

                eigenUpdateProgress.Subscribe(progress);
                eigenUpdateProgress.Connect();

                var realUpdateProgress = eigenUpdateProgress.TakeLast(1)
                                         .SelectMany(_ => {
                    var realUpdateManager = new UpdateManager(bundledPackageMetadata.ProjectUrl.ToString(), BundledRelease.PackageName, fxVersion);

                    return(realUpdateManager.CheckForUpdate()
                           .SelectMany(updateInfo => {
                        return Observable.Concat(
                            realUpdateManager.DownloadReleases(updateInfo.ReleasesToApply).Select(x => x * 0.75),
                            realUpdateManager.ApplyReleases(updateInfo).Select(x => x * 0.25 + 75))
                        .Select(x => (int)x);
                    }).LoggedCatch(this, Observable.Return(100), "Failed to update to latest remote version"));
                })
                                         .Select(x => x / 2 + 50)
                                         .Multicast(new Subject <int>());

                realUpdateProgress.Subscribe(progress);
                realUpdateProgress.Connect();

                realUpdateProgress
                .TakeLast(1)
                .ObserveOn(RxApp.DeferredScheduler)
                .Subscribe(
                    _ => wixEvents.Engine.Apply(wixEvents.MainWindowHwnd),
                    ex => UserError.Throw("Failed to install application", ex));
            });

            wixEvents.ApplyCompleteObs.Subscribe(eventArgs => {
                var error = convertHResultToError(eventArgs.Status);
                if (error != null)
                {
                    UserError.Throw(error);
                    return;
                }

                if (wixEvents.Command.Display != Display.Full)
                {
                    wixEvents.ShouldQuit();
                }

                // TODO: Figure out what the "main app" is and run it
            });

            wixEvents.ErrorObs.Subscribe(eventArgs => UserError.Throw("An installation error has occurred: " + eventArgs.ErrorMessage));
        }