Skip to content

Commit

Permalink
Fix unit test failure with console output by making IAnsiConsole user…
Browse files Browse the repository at this point in the history
…-providable
  • Loading branch information
paulirwin committed Aug 10, 2024
1 parent dc77d1f commit 22e255b
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 119 deletions.
142 changes: 46 additions & 96 deletions Rucksack.Tests/BasicIntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Rucksack.LoadStrategies;
using Rucksack.Tests.Util;
using Xunit.Abstractions;
using static System.TimeSpan;

Expand All @@ -18,14 +19,7 @@ await LoadTestRunner.Run(() =>
{
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
}, new LoadTestOptions
{
LoadStrategy = new OneShotLoadStrategy(count),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
}, LoadTestOptionsFactory.Create(new OneShotLoadStrategy(count), testOutputHelper));

executionCount.Should().Be(count);
}
Expand All @@ -45,14 +39,7 @@ await LoadTestRunner.Run(() =>
{
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
}, new LoadTestOptions
{
LoadStrategy = new RepeatBurstLoadStrategy(count, interval, duration),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
}, LoadTestOptionsFactory.Create(new RepeatBurstLoadStrategy(count, interval, duration), testOutputHelper));

executionCount.Should().Be(count * durationSeconds / intervalSeconds);
}
Expand All @@ -67,17 +54,12 @@ public async Task BasicSteppedBurstIntegrationTest()
const int expected = 75; // 5 + 10 + 15 + 20 + 25

await LoadTestRunner.Run(() =>
{
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
}, new LoadTestOptions
{
LoadStrategy = new SteppedBurstLoadStrategy(step, from, to, FromSeconds(1)),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
},
LoadTestOptionsFactory.Create(new SteppedBurstLoadStrategy(step, from, to, FromSeconds(1)),
testOutputHelper));

executionCount.Should().Be(expected);
}
Expand All @@ -93,17 +75,12 @@ public async Task BasicConstantUserLoadIntegrationTest()
const int expected = 25;

await LoadTestRunner.Run(() =>
{
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
}, new LoadTestOptions
{
LoadStrategy = new ConstantUserLoadStrategy(count, FromSeconds(1), FromSeconds(5)),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
},
LoadTestOptionsFactory.Create(new ConstantUserLoadStrategy(count, FromSeconds(1), FromSeconds(5)),
testOutputHelper));

executionCount.Should().Be(expected);
}
Expand All @@ -119,21 +96,16 @@ public async Task BasicConstantUserLoadIntegrationTest_WithLongRunningTask()
const int expected = 21;

await LoadTestRunner.Run(async () =>
{
int value = Interlocked.Increment(ref executionCount);

if (value == 1)
{
await Task.Delay(5000);
}
}, new LoadTestOptions
{
LoadStrategy = new ConstantUserLoadStrategy(count, FromSeconds(1), FromSeconds(5)),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
int value = Interlocked.Increment(ref executionCount);

if (value == 1)
{
await Task.Delay(5000);
}
},
LoadTestOptionsFactory.Create(new ConstantUserLoadStrategy(count, FromSeconds(1), FromSeconds(5)),
testOutputHelper));

executionCount.Should().Be(expected);
}
Expand All @@ -151,17 +123,13 @@ public async Task BasicSteppedUserLoadIntegrationTest()
const int expected = 75;

await LoadTestRunner.Run(() =>
{
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
}, new LoadTestOptions
{
LoadStrategy = new SteppedUserLoadStrategy(step, from, to, FromSeconds(1), FromSeconds(1), FromSeconds(5)),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
},
LoadTestOptionsFactory.Create(
new SteppedUserLoadStrategy(step, from, to, FromSeconds(1), FromSeconds(1), FromSeconds(5)),
testOutputHelper));

executionCount.Should().Be(expected);
}
Expand All @@ -179,21 +147,17 @@ public async Task BasicSteppedUserLoadIntegrationTest_WithLongRunningTask()
const int expected = 71;

await LoadTestRunner.Run(async () =>
{
int value = Interlocked.Increment(ref executionCount);

if (value == 1)
{
await Task.Delay(5000);
}
}, new LoadTestOptions
{
LoadStrategy = new SteppedUserLoadStrategy(step, from, to, FromSeconds(1), FromSeconds(1), FromSeconds(5)),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
int value = Interlocked.Increment(ref executionCount);

if (value == 1)
{
await Task.Delay(5000);
}
},
LoadTestOptionsFactory.Create(
new SteppedUserLoadStrategy(step, from, to, FromSeconds(1), FromSeconds(1), FromSeconds(5)),
testOutputHelper));

executionCount.Should().Be(expected);
}
Expand All @@ -209,21 +173,14 @@ await LoadTestRunner.Run(() =>
{
Interlocked.Increment(ref executionCount);
return Task.CompletedTask;
}, new LoadTestOptions
}, LoadTestOptionsFactory.Create(new SequentialLoadStrategy(FromSeconds(1))
{
LoadStrategy = new SequentialLoadStrategy(FromSeconds(1))
{
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
},
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
new OneShotLoadStrategy(count),
}, testOutputHelper));

executionCount.Should().Be(expected);
}
Expand All @@ -245,14 +202,7 @@ await LoadTestRunner.Run(async () =>
var delay = random.Next(1000, 5000);
await Task.Delay(delay);
Interlocked.Increment(ref executionCount);
}, new LoadTestOptions
{
LoadStrategy = new RepeatBurstLoadStrategy(count, interval, duration),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
}, LoadTestOptionsFactory.Create(new RepeatBurstLoadStrategy(count, interval, duration), testOutputHelper));

executionCount.Should().Be(count * durationSeconds / intervalSeconds);
}
Expand Down
20 changes: 4 additions & 16 deletions Rucksack.Tests/EmptyIntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Rucksack.LoadStrategies;
using Rucksack.Tests.Util;
using Spectre.Console;
using Xunit.Abstractions;

namespace Rucksack.Tests;
Expand All @@ -15,14 +17,7 @@ public void EmptyIntegrationTest()
LoadTestRunner.Run(() =>
{
executed = true;
}, new LoadTestOptions
{
LoadStrategy = new OneShotLoadStrategy(1),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
}, LoadTestOptionsFactory.Create(new OneShotLoadStrategy(1), testOutputHelper));

executed.Should().BeTrue();
}
Expand All @@ -36,14 +31,7 @@ await LoadTestRunner.Run(async () =>
{
await Task.Delay(100);
executed = true;
}, new LoadTestOptions
{
LoadStrategy = new OneShotLoadStrategy(1),
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
});
}, LoadTestOptionsFactory.Create(new OneShotLoadStrategy(1), testOutputHelper));

executed.Should().BeTrue();
}
Expand Down
24 changes: 24 additions & 0 deletions Rucksack.Tests/Util/LoadTestOptionsFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Xunit.Abstractions;

namespace Rucksack.Tests.Util;

public static class LoadTestOptionsFactory
{
public static LoadTestOptions Create(ILoadStrategy strategy, ITestOutputHelper testOutputHelper)
=> new()
{
LoadStrategy = strategy,
LoggerFactory = LoggerFactory.Create(builder =>
{
builder.AddXUnit(testOutputHelper);
}),
Console = AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.No,
ColorSystem = ColorSystemSupport.NoColors,
Out = new AnsiConsoleOutput(new TestOutputHelperTextWriterAdapter(testOutputHelper)),
})
};
}
40 changes: 40 additions & 0 deletions Rucksack.Tests/Util/TestOutputHelperTextWriterAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Text;
using Xunit.Abstractions;

namespace Rucksack.Tests.Util;

public class TestOutputHelperTextWriterAdapter(ITestOutputHelper output)
: TextWriter
{
private string currentLine = "";

public override void Write(char value)
{
if (value == '\n')
{
WriteCurrentLine();
}
else
{
currentLine += value;
}
}

public override Encoding Encoding => Encoding.Default;

private void WriteCurrentLine()
{
output.WriteLine(currentLine);
currentLine = "";
}

protected override void Dispose(bool disposing)
{
if (currentLine != "")
{
WriteCurrentLine();
}

base.Dispose(disposing);
}
}
3 changes: 3 additions & 0 deletions Rucksack/LoadTestOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using Spectre.Console;

namespace Rucksack;

Expand All @@ -7,4 +8,6 @@ public class LoadTestOptions
public required ILoadStrategy LoadStrategy { get; init; }

public ILoggerFactory? LoggerFactory { get; init; }

public IAnsiConsole Console { get; init; } = AnsiConsole.Console;
}
15 changes: 8 additions & 7 deletions Rucksack/LoadTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@ public static void Run(Action action, LoadTestOptions options) =>
public static async Task Run(Func<Task> action, LoadTestOptions options)
{
var logger = options.LoggerFactory?.CreateLogger(nameof(LoadTestRunner));
var ansiConsole = options.Console;

logger?.LogInformation("Rucksack is running...");
AnsiConsole.Write(new FigletText("Rucksack")
ansiConsole.Write(new FigletText("Rucksack")
.Color(Color.Green));
AnsiConsole.WriteLine("Starting load test...");
ansiConsole.WriteLine("Starting load test...");

LoadStrategyResult? result = null;
List<Task<LoadTaskResult>> allTasks = [];

List<LoadTaskResult> results = new(allTasks.Count);

await AnsiConsole.Progress()
await ansiConsole.Progress()
.StartAsync(async (ctx) =>
{
var progressTask = ctx.AddTask("[green]Running Tasks...[/]", maxValue: allTasks.Count);
Expand Down Expand Up @@ -80,7 +81,7 @@ await AnsiConsole.Progress()
var passRate = successCount / (double)totalCount;
logger?.LogInformation("Passed: {SuccessCount}/{TotalCount} ({PassRate:P1})", successCount, totalCount,
passRate);
AnsiConsole.MarkupLineInterpolated($"[green]Passed: {successCount}/{totalCount} ({passRate:P1})[/]");
ansiConsole.MarkupLineInterpolated($"[green]Passed: {successCount}/{totalCount} ({passRate:P1})[/]");

if (successCount != totalCount)
{
Expand All @@ -103,18 +104,18 @@ await AnsiConsole.Progress()
new Markup(count.ToString(), new Style(foreground: Color.Red)));
}

AnsiConsole.Write(table);
ansiConsole.Write(table);
}

var avgDuration = TimeSpan.FromMilliseconds(results
.Select(r => r.Duration.TotalMilliseconds)
.Average());

logger?.LogInformation("Average duration: {AvgDuration}", avgDuration);
AnsiConsole.MarkupLineInterpolated($"[green]Average duration: {avgDuration}[/]");
ansiConsole.MarkupLineInterpolated($"[green]Average duration: {avgDuration}[/]");

logger?.LogInformation("Rucksack has finished");
AnsiConsole.MarkupLine("Rucksack has finished");
ansiConsole.MarkupLine("Rucksack has finished");

return;

Expand Down

0 comments on commit 22e255b

Please sign in to comment.