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
48 changes: 21 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Progress
[![.NET](https://github.com/gcastellov/progress/actions/workflows/dotnet.yml/badge.svg)](https://github.com/gcastellov/progress/actions/workflows/dotnet.yml)

Report progression with ease by using multilple components such as Bars, Spinners, Pulse and more
This library helps you to spin up reporters for giving an overview of a workload's progression. These reporters can either be for console apps that output the information or background jobs hosted by APIs handling the progress via hooks.

![Progress](img/logo512x512.png)

## Report progress for console apps
Report progression with ease by using multilple components such as Bars, Spinners, Pulse and more.

```csharp
using var reporter = new ReporterBuilder()
using var reporter = new ConsoleReporterBuilder()
.UsingReportingFrequency(TimeSpan.FromMilliseconds(50))
.UsingComponentDescriptor(BarDescriptor.Default)
.Build(Worker.AllItems);
Expand All @@ -23,6 +25,8 @@ await worker.DoMywork();
```

### Define the component to show progress
Components are subjected to the ConsoleReporter.

```csharp
var descriptor = new BarDescriptor()
.UsingProgressSymbol('#')
Expand All @@ -37,7 +41,7 @@ var descriptor = BarDescriptor.Default;

### Define the reporter to use the component
```csharp
var reporter = new ReporterBuilder()
var reporter = new ConsoleReporterBuilder()
.DisplayingStartingTime()
.DisplayingElapsedTime()
.DisplayingTimeOfArrival()
Expand All @@ -51,31 +55,21 @@ var reporter = new ReporterBuilder()
.Build(Worker.AllItems);
```

### Start the reporter
```csharp
reporter.Start();
```

### Report progress
```csharp
reporter.ReportSuccess()
reporter.ReportFailure()
```
## Report progress for background jobs
In case your workload happens in the background and you only pretend to collect progression status during certain moments of the execution, or export the results at the end, the BackgroundReporter may help you to remove all the boilerplate.

### Collect stats while running
```csharp
var onProgress = (Stats stats) =>
{
// TODO: Do something useful
};

var onCompletion = (Stats stats) =>
{
// TODO: Do something useful
};

var reporter = new ReporterBuilder()
.NotifyingProgress(onProgress)
.NotifyingCompletion(onCompletion)
_reporter = new BackgroundReporterBuilder()
.NotifyingProgress((stats) =>
{
logger.LogDebug("Getting stats on progress {percent}", stats.CurrentPercent);
})
.NotifyingCompletion((stats) =>
{
logger.LogDebug("Getting stats on completion");
})
.Build(Worker.AllItems);

_worker.OnSuccess = () => _reporter.ReportSuccess();
_worker.OnFailure = () => _reporter.ReportFailure();
```
80 changes: 80 additions & 0 deletions src/Progress.UnitTest/Builders/BackgroundReporterBuilderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using Progress.Builders;
using Progress.Settings;

namespace Progress.UnitTest.Builders;

public class BackgroundReporterBuilderTests
{
[Fact]
public void GivenProgressNotifications_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var callback = (Stats stats) => { };
var builder = new BackgroundReporterBuilder()
.NotifyingProgress(callback);

// Act
var actual = builder.Build(100);

// Arrange
actual.OnProgress.Should().NotBeNull();
actual.OnProgress.Should().Be(callback);
actual.Configuration.StatsFrequency.Should().Be(TimeSpan.FromSeconds(5));
}

[Fact]
public void GivenProgressNotificationsWithFrequency_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var callback = (Stats stats) => { };
var frequency = TimeSpan.FromSeconds(20);
var builder = new BackgroundReporterBuilder()
.NotifyingProgress(callback, frequency);

// Act
var actual = builder.Build(100);

// Arrange
actual.OnProgress.Should().NotBeNull();
actual.OnProgress.Should().Be(callback);
actual.Configuration.StatsFrequency.Should().Be(frequency);
actual.Configuration.Options.NotifyProgressStats.Should().BeTrue();
}

[Fact]
public void GivenCompletionNotifications_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var callback = (Stats stats) => { };
var builder = new BackgroundReporterBuilder()
.NotifyingCompletion(callback);

// Act
var actual = builder.Build(100);

// Arrange
actual.OnCompletion.Should().NotBeNull();
actual.OnCompletion.Should().Be(callback);
actual.Configuration.Options.NotifyCompletionStats.Should().BeTrue();
}

[Fact]
public void GivenExporting_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
const string fileName = "output.txt";
const FileType type = FileType.Text;
var builder = new BackgroundReporterBuilder()
.ExportingTo(fileName, type);

// Act
var actual = builder.Build(100);

// Assert
actual.Configuration.ExportSettings.Should().NotBeNull();
actual.Configuration.ExportSettings.FileName.Should().Be(fileName);
actual.Configuration.ExportSettings.FileType.Should().Be(FileType.Text);
actual.Configuration.Options.ExportCompletionStats.Should().BeTrue();
}

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using Progress.Settings;
using Progress.Builders;
using Progress.Settings;

namespace Progress.UnitTest;
namespace Progress.UnitTest.Builders;

public class ReporterBuilderTests
public class ConsoleReporterBuilderTests
{
[Fact]
public void GivenNothingToComplete_WhenBuilding_ThenThrowsException()
{
// Arrange
var builder = new ReporterBuilder();
var builder = new ConsoleReporterBuilder();

// Act
var action = () => builder.Build(0);
Expand All @@ -21,7 +22,7 @@ public void GivenNothingToComplete_WhenBuilding_ThenThrowsException()
public void GivenSomethingToComplete_WhenBuilding_ThenReturnsReporter()
{
// Arrange
var builder = new ReporterBuilder();
var builder = new ConsoleReporterBuilder();

// Act
var actual = builder.Build(100);
Expand All @@ -35,7 +36,7 @@ public void GivenReportingFrequency_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var expectedFrequency = TimeSpan.FromSeconds(20);
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.UsingReportingFrequency(expectedFrequency);

// Act
Expand All @@ -49,7 +50,7 @@ public void GivenReportingFrequency_WhenBuilding_ThenReporterIsSetUp()
public void GivenElapsedTime_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.DisplayingElapsedTime();

// Act
Expand All @@ -63,7 +64,7 @@ public void GivenElapsedTime_WhenBuilding_ThenReporterIsSetUp()
public void GivenStartingTime_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.DisplayingStartingTime();

// Act
Expand All @@ -77,7 +78,7 @@ public void GivenStartingTime_WhenBuilding_ThenReporterIsSetUp()
public void GivenItemsOverview_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.DisplayingItemsOverview();

// Act
Expand All @@ -91,7 +92,7 @@ public void GivenItemsOverview_WhenBuilding_ThenReporterIsSetUp()
public void GivenSummary_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.DisplayingItemsSummary();

// Act
Expand All @@ -106,7 +107,7 @@ public void GivenProgressNotifications_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var callback = (Stats stats) => { };
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.NotifyingProgress(callback);

// Act
Expand All @@ -124,7 +125,7 @@ public void GivenProgressNotificationsWithFrequency_WhenBuilding_ThenReporterIsS
// Arrange
var callback = (Stats stats) => { };
var frequency = TimeSpan.FromSeconds(20);
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.NotifyingProgress(callback, frequency);

// Act
Expand All @@ -142,7 +143,7 @@ public void GivenCompletionNotifications_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var callback = (Stats stats) => { };
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.NotifyingCompletion(callback);

// Act
Expand All @@ -160,7 +161,7 @@ public void GivenExporting_WhenBuilding_ThenReporterIsSetUp()
// Arrange
const string fileName = "output.txt";
const FileType type = FileType.Text;
var builder = new ReporterBuilder()
var builder = new ConsoleReporterBuilder()
.ExportingTo(fileName, type);

// Act
Expand Down
2 changes: 1 addition & 1 deletion src/Progress.UnitTest/PrinterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class PrinterTests
public void GivenDefaults_WhenPrinting_ThenGetsOutput()
{
// Arrange
Printer printer = new(new Settings.ReportingOptions(), BarDescriptor.Default.Build());
Printer printer = new(new Settings.Console.ReportingOptions(), BarDescriptor.Default.Build());
Stats stats = new();

// Act
Expand Down
2 changes: 1 addition & 1 deletion src/Progress.UnitTest/Progress.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

<ItemGroup>
<Using Include="Xunit" />
<Using Include="FluentAssertions"/>
<Using Include="FluentAssertions" />
</ItemGroup>

</Project>
74 changes: 74 additions & 0 deletions src/Progress.UnitTest/Reporters/BackgroundReporterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Progress.Descriptors;
using Progress.Reporters;

namespace Progress.UnitTest.Reporters;

public class BackgroundReporterTests
{
[Fact]
public void GivenNothingToComplete_WhenInitializing_ThenThrowsException()
{
// Act
var action = () => new BackgroundReporter(0);

// Assert
action.Should().Throw<ArgumentException>();
}

[Fact]
public void GivenOperationNotStarted_WhenStopping_ThenThrowsException()
{
// Arrange
var reporter = new BackgroundReporter(100);

// Act
var action = () => reporter.Stop();

// Assert
action.Should().Throw<InvalidOperationException>();
}

[Fact]
public async Task GivenProgressNotifications_WhenRunning_ThenCallbackIsCalled()
{
// Arrange
bool isCalled = false;
var reporter = new BackgroundReporter(100)
{
OnProgress = (stats) => isCalled = true
};

reporter.Configuration.Options.NotifyProgressStats = true;
reporter.Configuration.StatsFrequency = TimeSpan.FromSeconds(1);


// Act
reporter.Start();
await Task.Delay(TimeSpan.FromMilliseconds(1500));

// Assert
isCalled.Should().BeTrue();
}

[Fact]
public async Task GivenCompletionNotifications_WhenFinished_ThenCallbackIsCalled()
{
// Arrange
bool isCalled = false;
var reporter = new BackgroundReporter(1)
{
OnCompletion = (stats) => isCalled = true
};

reporter.Configuration.Options.NotifyCompletionStats = true;
reporter.Configuration.StatsFrequency = TimeSpan.FromMilliseconds(500);
reporter.Start();

// Act
reporter.ReportSuccess();
await Task.Delay(TimeSpan.FromMilliseconds(1500));

// Assert
isCalled.Should().BeTrue();
}
}
Loading