using DodoSSH.Client.Session; namespace DodoSSH.Client.App.Tests; /// /// A release channel that answers whatever the test tells it to. /// /// /// /// records the call instead of making it, and that is the whole reason /// IUpdateChannel exists as an interface. The real one replaces the running process and never /// returns, so a test could not observe it at all — and the single most important thing to be able to /// assert about this feature is a negative: that a downloaded update is never applied unless /// somebody pressed the button. A counter is what makes that assertion writable. /// /// /// exists for the same reason in the other direction: without a way to stop a /// download halfway, the Downloading state would be a state no test could ever catch the view model in. /// /// internal sealed class FakeUpdateChannel : IUpdateChannel { /// What the next check finds. Null means this build is current. internal AvailableUpdate? Available { get; set; } /// When set, checking throws it. internal Exception? CheckFailure { get; set; } /// When set, downloading throws it. internal Exception? DownloadFailure { get; set; } /// When set, a download waits on it before completing. internal TaskCompletionSource? HoldDownload { get; set; } /// The percentages a download reports on its way through. internal IReadOnlyList ProgressSteps { get; set; } = [25, 50, 100]; internal int Checks { get; private set; } internal int Downloads { get; private set; } internal int Restarts { get; private set; } internal AvailableUpdate? RestartedWith { get; private set; } /// public bool IsSupported { get; set; } = true; /// public string CurrentVersion { get; set; } = "1.0.0"; /// public Task CheckAsync(CancellationToken cancellationToken) { Checks++; return CheckFailure is { } failure ? Task.FromException(failure) : Task.FromResult(Available); } /// public async Task DownloadAsync( AvailableUpdate update, IProgress progress, CancellationToken cancellationToken) { Downloads++; if (DownloadFailure is { } failure) { throw failure; } if (HoldDownload is { } gate) { await gate.Task.WaitAsync(cancellationToken).ConfigureAwait(false); } foreach (var percent in ProgressSteps) { cancellationToken.ThrowIfCancellationRequested(); progress.Report(percent); } } /// public void ApplyAndRestart(AvailableUpdate update) { Restarts++; RestartedWith = update; } }