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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bld/
[Ll]og/
[Ll]ogs/
.vs/
.vscode/

# .NET Core
project.lock.json
Expand Down
91 changes: 29 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,42 @@
[![NuGet version (Progress)](https://img.shields.io/nuget/v/Progress.svg?style=square)](https://www.nuget.org/packages/Progress/)


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.
This library provides a set of features that help you spin up reporting tasks, either in the form of console apps that show workloads progression or via background jobs providing access to stats during the lifespan of the workload.

![Progress](img/logo512x512.png)
Please read the documentation for further info about how to interact with the library's API here:
[Library API](./src/Progress/Content/README.md)

## Report progress for console apps
Report progression with ease by using multilple components such as Bars, Spinners, Pulse and more.
## Reporters
The reporter is the key component that orchestrates, provides and display information about the workloads progression. The following reporters are currently supported:

```csharp
using var reporter = new ConsoleReporterBuilder()
.UsingReportingFrequency(TimeSpan.FromMilliseconds(50))
.UsingComponentDescriptor(BarDescriptor.Default)
.Build(Worker.AllItems);
|Reporter| Description|
|---|---|
| **ConsoleReporter** | Reports progression of a single workload. Console based |
| **ConsoleAggregateReporter** | Reports progression of many workloads. Console based |
| **BackgroundReporter** | Reports progression of a single workload in background. Used in HostedService or other background jobs that don't require a std output |

var worker = new Worker()
{
OnSuccess = () => reporter.ReportSuccess(),
OnFailure = () => reporter.ReportFailure(),
};
## Supported features
The following are the supported features:

reporter.Start();
await worker.DoMywork();
```
| Feature | Description | Reporters |
| ---| --- | --- |
| On progress stats | Provides stats info during the reporting process | ConsoleReporter, ConsoleAggregateReporter, BackgroundRepoerter |
| On completeion stats | Provides stats info once the reporting finishes | ConsoleReporter, ConsoleAggregateReporter, BackgroundRepoerter |
| Exports | Exports the final stats to: json, txt, csv or xml | ConsoleReporter, ConsoleAggregateReporter, BackgroundRepoerter |
| Display data points | Display important data points such as: starting time, ETA, elaspsed time, remaining time, success, failures and total hits | ConsoleReporter, ConsoleAggregateReporter |
| Display workload progression | Display progress with different types of components | ConsoleReporter, ConsoleAggregateReporter |

### Define the component to show progress
Components are subjected to the ConsoleReporter.
## Components
The components are meant for console-based reporters, aiming to display progression during the lifespan of the workload. When using the *ConsoleAggregateReporter* one can choose and use different components or use the same for all workloads.

```csharp
var descriptor = new BarDescriptor()
.UsingProgressSymbol('#')
.UsingWidth(50)
.DisplayingPercent();
```
### Progress bars
![Progress bars](img/consoleAggregateBarReporter.gif)

or use the default one
```csharp
var descriptor = BarDescriptor.Default;
```
### Spinners
![Spinners](img/consoleAggregateSpinnerReporter.gif)

### Define the reporter to use the component
```csharp
var reporter = new ConsoleReporterBuilder()
.DisplayingStartingTime()
.DisplayingElapsedTime()
.DisplayingTimeOfArrival()
.DisplayingRemainingTime()
.DisplayingItemsSummary()
.DisplayingItemsOverview()
.NotifyingProgress(onProgress)
.NotifyingCompletion(onCompletion)
.UsingReportingFrequency(TimeSpan.FromMilliseconds(50))
.UsingComponentDescriptor(descriptor)
.Build(Worker.AllItems);
```
### Pulse
![Progress bars](img/consolePulseReporter.gif)

## 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.

```csharp
_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();
```
### Hearthbeat
![Hearthbeat](img/consoleHearthbeatReporter.gif)
Binary file added img/consoleAggregateBarReporter.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/consoleAggregateSpinnerReporter.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/consoleHearthbeatReporter.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/consolePulseReporter.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
203 changes: 203 additions & 0 deletions src/Progress.UnitTest/Builders/ConsoleAggregateReporterBuilderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
using Progress.Builders;
using Progress.Descriptors;
using Progress.Reporters;
using Progress.Settings;

namespace Progress.UnitTest.Builders;

public class ConsoleAggregateReporterBuilderTests
{
[Fact]
public void GivenNothingToComplete_WhenBuilding_ThenThrowsException()
{
// Arrange
var builder = new ConsoleAggregateReporterBuilder();

// Act
var action = () => builder.Build();

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

[Fact]
public void GivenSomethingToComplete_WhenBuilding_ThenReturnsReporter()
{
// Arrange
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default);

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

// Assert
actual.Should().NotBeNull();
}

[Fact]
public void GivenReportingFrequency_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var expectedFrequency = TimeSpan.FromSeconds(20);
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.UsingReportingFrequency(expectedFrequency);

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

// Assert
actual.ReportFrequency.Should().Be(expectedFrequency);
}

[Fact]
public void GivenElapsedTime_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.DisplayingElapsedTime();

// Act
var actual = builder.Build().Configuration.Options;

// Assert
actual.DisplayElapsedTime.Should().BeTrue();
}

[Fact]
public void GivenStartingTime_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.DisplayingStartingTime();

// Act
var actual = builder.Build().Configuration.Options;

// Assert
actual.DisplayStartingTime.Should().BeTrue();
}

[Fact]
public void GivenItemsOverview_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.DisplayingItemsOverview();

// Act
var actual = builder.Build().Configuration.Options;

// Assert
actual.DisplayItemsOverview.Should().BeTrue();
}

[Fact]
public void GivenSummary_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.DisplayingItemsSummary();

// Act
var actual = builder.Build().Configuration.Options;

// Assert
actual.DisplayItemsSummary.Should().BeTrue();
}

[Fact]
public void GivenProgressNotifications_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var callback = (Stats stats) => { };
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.NotifyingProgress(callback);

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

// 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 ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.NotifyingProgress(callback, frequency);

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

// 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 ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.NotifyingCompletion(callback);

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

// 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 ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.ExportingTo(fileName, type);

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

// 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();
}

[Fact]
public void GivenHideOnComplete_WhenBuilding_ThenReporterIsSetUp()
{
// Arrange
var builder = new ConsoleAggregateReporterBuilder()
.UsingWorkload(Workload.Default(100), BarDescriptor.Default)
.HideWorkloadOnComplete(true);

// Act
var actual = builder.Build().Configuration.Options;

// Assert
actual.HideWorkflowOnComplete.Should().BeTrue();
}
}
Loading