diff --git a/Huffman.Tree.Benchmarks/Huffman.Tree.Benchmarks.csproj b/Huffman.Tree.Benchmarks/Huffman.Tree.Benchmarks.csproj
new file mode 100644
index 0000000..31cd0eb
--- /dev/null
+++ b/Huffman.Tree.Benchmarks/Huffman.Tree.Benchmarks.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Huffman.Tree.Benchmarks/Program.cs b/Huffman.Tree.Benchmarks/Program.cs
new file mode 100644
index 0000000..ce31231
--- /dev/null
+++ b/Huffman.Tree.Benchmarks/Program.cs
@@ -0,0 +1,5 @@
+// See https://aka.ms/new-console-template for more information
+using BenchmarkDotNet.Running;
+using Huffman.Tree.Tests;
+
+BenchmarkRunner.Run();
\ No newline at end of file
diff --git a/Huffman.Tree.Tests/Huffman.Tree.Tests.csproj b/Huffman.Tree.Tests/Huffman.Tree.Tests.csproj
new file mode 100644
index 0000000..bb5f413
--- /dev/null
+++ b/Huffman.Tree.Tests/Huffman.Tree.Tests.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net6.0
+ net6.0;net7.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/Huffman.Tree.Tests/HuffmanTreeTests.cs b/Huffman.Tree.Tests/HuffmanTreeTests.cs
new file mode 100644
index 0000000..c9f121c
--- /dev/null
+++ b/Huffman.Tree.Tests/HuffmanTreeTests.cs
@@ -0,0 +1,93 @@
+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 TestCases => EnumerateTestCases().SelectMany(x => x.OfType());
+
+ private IEnumerable>? _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