Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions source/CreativeCoders.MacOS.HomeBrew/SynchronousProgress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using CreativeCoders.Core;

namespace CreativeCoders.MacOS.HomeBrew;

/// <summary>
/// An <see cref="IProgress{T}"/> implementation that invokes the callback synchronously on the
/// calling thread. Unlike <see cref="Progress{T}"/>, which posts to the captured
/// <see cref="SynchronizationContext"/> (or the <see cref="ThreadPool"/> when none exists),
/// this class guarantees that the handler runs inline before <see cref="Report"/> returns.
/// </summary>
public sealed class SynchronousProgress<T>(Action<T> handler) : IProgress<T>
{
private readonly Action<T> _handler = Ensure.NotNull(handler);

/// <inheritdoc />
public void Report(T value) => _handler(value);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using CreativeCoders.Cli.Core;
using CreativeCoders.Core;
using CreativeCoders.MacOS.HomeBrew;
using CreativeCoders.MacOS.HomeBrew.Import;
using CreativeCoders.SysConsole.Core;
using JetBrains.Annotations;
using Spectre.Console;

Expand Down Expand Up @@ -29,7 +29,7 @@ public async Task<CommandResult> ExecuteAsync(BrewImportOptions options)
_ansiConsole.MarkupLine($"Importing Homebrew software from '{options.InputPath}'");
_ansiConsole.WriteLine();

var progress = new Progress<BrewImportProgress>(OnProgress);
var progress = new SynchronousProgress<BrewImportProgress>(OnProgress);

try
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.Json;
using AwesomeAssertions;
using CreativeCoders.MacOS.HomeBrew;
using CreativeCoders.MacOS.HomeBrew.Import;
using CreativeCoders.MacOS.HomeBrew.Models.Export;
using FakeItEasy;
Expand Down Expand Up @@ -130,14 +131,11 @@ public async Task ImportAsync_ReportsProgressForEachStep()
Casks = [new BrewExportCaskModel { Token = "firefox" }]
};
var reports = new List<BrewImportProgress>();
var progress = new Progress<BrewImportProgress>(reports.Add);
var progress = new SynchronousProgress<BrewImportProgress>(reports.Add);

// Act
await sut.ImportAsync(exportModel, progress);

// Allow the Progress<T> SynchronizationContext to flush callbacks
await Task.Delay(50);

// Assert
reports.Should().Contain(r => r.Step == BrewImportStep.Tap
&& r.State == BrewImportStepState.Starting && r.Target == "some/tap");
Expand All @@ -162,13 +160,12 @@ public async Task ImportAsync_WhenStepFails_ReportsFailedProgressWithError()
Formulae = [new BrewExportFormulaModel { Name = "bad" }]
};
var reports = new List<BrewImportProgress>();
var progress = new Progress<BrewImportProgress>(reports.Add);
var progress = new SynchronousProgress<BrewImportProgress>(reports.Add);

// Act
var act = () => sut.ImportAsync(exportModel, progress);

await act.Should().ThrowAsync<BrewImportFailedException>();
await Task.Delay(50);

// Assert
reports.Should().Contain(r => r.State == BrewImportStepState.Failed
Expand Down
Loading