Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added WithProgressIncrements extension methods #23

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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: 48 additions & 0 deletions src/Hangfire.Console/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,53 @@ public static IEnumerable WithProgress(this IEnumerable enumerable, IProgressBar

return new ProgressEnumerable(enumerable, progressBar, count);
}

/// <summary>
/// Returns a <see cref="IEnumerable{T}"/> reporting enumeration progress.
/// </summary>
/// <typeparam name="T">Item type</typeparam>
/// <param name="enumerable">Source enumerable</param>
/// <param name="progressBar">Progress bar</param>
/// <param name="percentageIncrementStep">Minimum percentage step, controls frequency of updates</param>
/// <param name="count">Item count</param>
/// <returns></returns>
public static IEnumerable<T> WithProgressIncrements<T>(this IEnumerable<T> enumerable, IProgressBar progressBar, int percentageIncrementStep, int count = -1)
{
if (enumerable is ICollection<T>)
{
count = ((ICollection<T>)enumerable).Count;
}
else if (enumerable is IReadOnlyCollection<T>)
{
count = ((IReadOnlyCollection<T>)enumerable).Count;
}
else if (count < 0)
{
throw new ArgumentException("Count is required when enumerable is not a collection", nameof(count));
}

return new ProgressEnumerable<T>(enumerable, progressBar, count, percentageIncrementStep);
}

/// <summary>
/// Returns a <see cref="IEnumerable"/> reporting enumeration progress.
/// </summary>
/// <param name="enumerable">Source enumerable</param>
/// <param name="progressBar">Progress bar</param>
/// <param name="percentageIncrementStep">Minimum percentage step, controls frequency of updates</param>
/// <param name="count">Item count</param>
public static IEnumerable WithProgressIncrements(this IEnumerable enumerable, IProgressBar progressBar, int percentageIncrementStep, int count = -1)
{
if (enumerable is ICollection)
{
count = ((ICollection)enumerable).Count;
}
else if (count < 0)
{
throw new ArgumentException("Count is required when enumerable is not a collection", nameof(count));
}

return new ProgressEnumerable(enumerable, progressBar, count, percentageIncrementStep);
}
}
}
72 changes: 62 additions & 10 deletions src/Hangfire.Console/Progress/ProgressEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal class ProgressEnumerable : IEnumerable
private readonly IEnumerable _enumerable;
private readonly IProgressBar _progressBar;
private readonly int _count;
private readonly int _percentageIncrementStep;

public ProgressEnumerable(IEnumerable enumerable, IProgressBar progressBar, int count)
{
Expand All @@ -27,25 +28,35 @@ public ProgressEnumerable(IEnumerable enumerable, IProgressBar progressBar, int
_count = count;
}

public ProgressEnumerable(IEnumerable enumerable, IProgressBar progressBar, int count, int percentageIncrementStep) : this(enumerable, progressBar, count)
{
if (percentageIncrementStep <= 0)
throw new ArgumentOutOfRangeException(nameof(percentageIncrementStep));

_percentageIncrementStep = percentageIncrementStep;
}

public IEnumerator GetEnumerator()
{
return new Enumerator(_enumerable.GetEnumerator(), _progressBar, _count);
return new Enumerator(_enumerable.GetEnumerator(), _progressBar, _count, _percentageIncrementStep);
}

private class Enumerator : IEnumerator, IDisposable
{
private readonly IEnumerator _enumerator;
private readonly IProgressBar _progressBar;
private int _count, _index;
private int _count, _index, _percentageIncrementStep, _segmentThreshold;

public Enumerator(IEnumerator enumerator, IProgressBar progressBar, int count)
public Enumerator(IEnumerator enumerator, IProgressBar progressBar, int count, int percentageIncrementStep)
{
_enumerator = enumerator;
_progressBar = progressBar;
_count = count;
_index = -1;
_percentageIncrementStep = percentageIncrementStep;
CalculateSegmentThreshold();
}

public object Current => _enumerator.Current;

public void Dispose()
Expand All @@ -71,9 +82,19 @@ public bool MoveNext()
{
// adjust maxCount if overrunned
_count = _index + 1;
CalculateSegmentThreshold();
}

_progressBar.SetValue(_index * 100.0 / _count);
if (_percentageIncrementStep > 0)
{
// update only when current index has hit next progress threshold
int remainder = _index % _segmentThreshold;

if (remainder == 0)
_progressBar.SetValue((_index / _segmentThreshold) * _percentageIncrementStep);
}
else
_progressBar.SetValue(_index * 100.0 / _count);
}
return r;
}
Expand All @@ -83,6 +104,11 @@ public void Reset()
_enumerator.Reset();
_index = -1;
}

private void CalculateSegmentThreshold()
{
_segmentThreshold = (int)((_percentageIncrementStep / 100.0) * _count);
}
}
}

Expand All @@ -95,6 +121,7 @@ internal class ProgressEnumerable<T> : IEnumerable<T>
private readonly IEnumerable<T> _enumerable;
private readonly IProgressBar _progressBar;
private readonly int _count;
private readonly int _percentageIncrementStep;

public ProgressEnumerable(IEnumerable<T> enumerable, IProgressBar progressBar, int count)
{
Expand All @@ -110,28 +137,38 @@ public ProgressEnumerable(IEnumerable<T> enumerable, IProgressBar progressBar, i
_count = count;
}

public ProgressEnumerable(IEnumerable<T> enumerable, IProgressBar progressBar, int count, int percentageIncrementStep) : this(enumerable, progressBar, count)
{
if (percentageIncrementStep <= 0)
throw new ArgumentOutOfRangeException(nameof(percentageIncrementStep));

_percentageIncrementStep = percentageIncrementStep;
}

public IEnumerator<T> GetEnumerator()
{
return new Enumerator(_enumerable.GetEnumerator(), _progressBar, _count);
return new Enumerator(_enumerable.GetEnumerator(), _progressBar, _count, _percentageIncrementStep);
}

IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator(_enumerable.GetEnumerator(), _progressBar, _count);
return new Enumerator(_enumerable.GetEnumerator(), _progressBar, _count, _percentageIncrementStep);
}

private class Enumerator : IEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
private readonly IProgressBar _progressBar;
private int _count, _index;
private int _count, _index, _percentageIncrementStep, _segmentThreshold;

public Enumerator(IEnumerator<T> enumerator, IProgressBar progressBar, int count)
public Enumerator(IEnumerator<T> enumerator, IProgressBar progressBar, int count, int percentageIncrementStep)
{
_enumerator = enumerator;
_progressBar = progressBar;
_count = count;
_index = -1;
_percentageIncrementStep = percentageIncrementStep;
CalculateSegmentThreshold();
}

public T Current => _enumerator.Current;
Expand Down Expand Up @@ -161,9 +198,19 @@ public bool MoveNext()
{
// adjust maxCount if overrunned
_count = _index + 1;
CalculateSegmentThreshold();
}

_progressBar.SetValue(_index * 100.0 / _count);
if (_percentageIncrementStep > 0)
{
// update only when current index has hit next progress threshold
int remainder = _index % _segmentThreshold;

if (remainder == 0)
_progressBar.SetValue((_index / _segmentThreshold) * _percentageIncrementStep);
}
else
_progressBar.SetValue(_index * 100.0 / _count);
}
return r;
}
Expand All @@ -173,6 +220,11 @@ public void Reset()
_enumerator.Reset();
_index = -1;
}

private void CalculateSegmentThreshold()
{
_segmentThreshold = (int)((_percentageIncrementStep / 100.0) * _count);
}
}
}
}
20 changes: 20 additions & 0 deletions tests/Hangfire.Console.Tests/ConsoleExtensionsFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Hangfire.Storage;
using Moq;
using System;
using System.Linq;
using Xunit;

namespace Hangfire.Console.Tests
Expand Down Expand Up @@ -76,6 +77,25 @@ public void WriteProgressBar_ReturnsProgressBar_IfConsoleCreated()
_transaction.Verify(x => x.Commit());
}

[Fact]
public void WithProgressBarIncrements_CommitsGivenTimes_IfConsoleCreated()
{
var context = CreatePerformContext();
context.Items["ConsoleContext"] = CreateConsoleContext(context);

var progressBar = ConsoleExtensions.WriteProgressBar(context);
int[] numbers = Enumerable.Repeat(1, 1000).ToArray();

foreach (int num in numbers.WithProgressIncrements(progressBar, 5))
{
// do something, don't want the loop being optimised out
System.Diagnostics.Debug.Write(num);
}

Assert.IsType<DefaultProgressBar>(progressBar);
_transaction.Verify(x => x.Commit(), Times.Exactly(21));
}

[Fact]
public void WriteProgressBar_ReturnsNoOp_IfConsoleNotCreated()
{
Expand Down