Files
2023-01-29 17:40:20 +01:00

94 lines
2.9 KiB
C#

using AutoFixture;
using BenchmarkDotNet.Attributes;
using FluentAssertions;
using MoreLinq;
namespace Huffman.Tree.Tests
{
[TestClass]
[MemoryDiagnoser]
public class HuffmanTreeTests
{
public readonly record struct TestCase
{
public (string token, long frequency)[] Tokens { get; init; }
public (string token, int[] bits)[] Expected { get; init; }
}
private static IEnumerable<TestCase> TestCases => EnumerateTestCases().SelectMany(x => x.OfType<TestCase>());
private IEnumerable<HuffmanToken<string>>? _codes;
private (string token, long frequency)[] _tokens;
public HuffmanTreeTests()
{
_tokens = TestCases.First().Tokens;
}
[TestMethod]
[DynamicData(nameof(EnumerateTestCases), DynamicDataSourceType.Method)]
public void BuildTreeShouldSuccess(TestCase testCase)
{
// This is to overcome the requirement for BuildTree() to be argument- & deferred-result-free. It's required by BenchmarkDotNet.
_tokens = testCase.Tokens;
BuildTree();
PrintCodesToOutput();
ValidateCodes(testCase.Expected);
}
private void ValidateCodes((string token, int[] bits)[] expected)
{
var expectedBits = expected.Select(x => (x.token, x.bits.Select(bit => Convert.ToBoolean(bit))));
_ = _codes.Should().NotBeNull();
_ = _codes!.Select(x => (x.Token, x.Code))
.Should()
.NotBeNull().And
.BeEquivalentTo(expectedBits);
}
private void PrintCodesToOutput()
{
foreach (var node in _codes!)
{
string bits = node.Code!.AsString();
Console.WriteLine($"{node.Token} => {bits}");
}
}
public static IEnumerable<object[]> EnumerateTestCases()
{
yield return new object[]
{
new TestCase
{
// test case
Tokens = new[] { ("a", 10L), ("b", 5L), ("c", 2L), ("d", 14L), ("e", 15L), (";", long.MaxValue) },
// expected result
Expected = new[] { (";", new[] { 1 }), ("a", new[] { 0, 0, 1 }), ("b", new[] { 0, 0, 0, 1 }), ("c", new[] { 0, 0, 0, 0 }), ("d", new[] { 0, 1, 0 }), ("e", new[] { 0, 1, 1 }) }
}
};
}
[Benchmark]
public void BuildTree()
{
_codes = _tokens?.AsHuffmanTree().Nodes.OfType<HuffmanToken<string>>();
// We keep this assertions here because we want to build all the codes. For this we need to access each of them.
_ = _codes.Should().NotBeNull().And.AllSatisfy(node => node.Code!.AsString().Should().NotBeNull().And.NotBe(string.Empty));
}
[Benchmark]
[TestMethod]
public void BuildTree100()
{
var fixture = new Fixture();
var collection = fixture.CreateMany<(string token, long frequency)>(100);
_tokens = collection.ToArray();
_codes = _tokens?.AsHuffmanTree().Nodes.OfType<HuffmanToken<string>>();
_codes.ForEach(x => Console.WriteLine($"{x.Token} => {x.Code.AsString()}"));
// We keep this assertions here because we want to build all the codes. For this we need to access each of them.
_ = _codes.Should().NotBeNull().And.AllSatisfy(node => node.Code!.AsString().Should().NotBeNull().And.NotBe(string.Empty));
}
}
}