AiDotNet icon indicating copy to clipboard operation
AiDotNet copied to clipboard

[Test Coverage] Implement Tests for Utility Helpers

Open ooples opened this issue 2 months ago • 1 comments

Problem: Utility helpers have 0% test coverage. Files: SerializationHelper.cs, DeserializationHelper.cs, ConversionsHelper.cs, ParallelProcessingHelper.cs, TextProcessingHelper.cs, EnumHelper.cs. Goal: 80%+ coverage.

ooples avatar Nov 07 '25 02:11 ooples

Issue #378: Junior Developer Implementation Guide

Unit Tests for Utility Helpers


Overview

Create comprehensive unit tests for utility helper classes in src/Helpers/ that provide cross-cutting functionality.

Files to Test:

  • SerializationHelper.cs - Serialization operations (558 lines)
  • DeserializationHelper.cs - Deserialization operations
  • ConversionsHelper.cs - Type conversions
  • ParallelProcessingHelper.cs - Parallel execution utilities
  • TextProcessingHelper.cs - Text and string operations
  • EnumHelper.cs - Enum utilities

Target: 0% → 80% test coverage


SerializationHelper Testing

Key Methods to Test

  1. SerializeNode() - Serialize decision tree nodes
  2. SerializeMatrix() - Serialize matrix to binary
  3. SerializeVector() - Serialize vector to binary
  4. SerializeTensor() - Serialize tensor to binary
  5. SerializeInterface() - Serialize interface instance
  6. WriteValue() - Write typed value to stream

Test Examples

[TestClass]
public class SerializationHelperTests
{
    [TestMethod]
    public void SerializeMatrix_ThenDeserialize_MatchesOriginal()
    {
        // Arrange
        var original = new Matrix<double>(3, 2);
        original[0, 0] = 1.0; original[0, 1] = 2.0;
        original[1, 0] = 3.0; original[1, 1] = 4.0;
        original[2, 0] = 5.0; original[2, 1] = 6.0;

        // Act
        byte[] serialized = SerializationHelper<double>.SerializeMatrix(original);
        var deserialized = SerializationHelper<double>.DeserializeMatrix(serialized);

        // Assert
        Assert.AreEqual(3, deserialized.Rows);
        Assert.AreEqual(2, deserialized.Columns);
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 2; j++)
                Assert.AreEqual(original[i, j], deserialized[i, j]);
    }

    [TestMethod]
    public void SerializeVector_PreservesAllValues()
    {
        // Arrange
        var original = new Vector<double>(new[] { 1.0, 2.5, 3.7, -4.2 });

        // Act
        byte[] serialized = SerializationHelper<double>.SerializeVector(original);
        var deserialized = SerializationHelper<double>.DeserializeVector(serialized);

        // Assert
        Assert.AreEqual(4, deserialized.Length);
        for (int i = 0; i < 4; i++)
            Assert.AreEqual(original[i], deserialized[i], 1e-10);
    }

    [TestMethod]
    public void SerializeNode_WithChildren_PreservesTreeStructure()
    {
        // Arrange
        var root = new DecisionTreeNode<double>
        {
            IsLeaf = false,
            FeatureIndex = 0,
            SplitValue = 5.0,
            Left = new DecisionTreeNode<double> { IsLeaf = true, Prediction = 1.0 },
            Right = new DecisionTreeNode<double> { IsLeaf = true, Prediction = 0.0 }
        };

        using var ms = new MemoryStream();
        using var writer = new BinaryWriter(ms);

        // Act
        SerializationHelper<double>.SerializeNode(root, writer);
        writer.Flush();
        ms.Position = 0;

        using var reader = new BinaryReader(ms);
        var deserialized = SerializationHelper<double>.DeserializeNode(reader);

        // Assert
        Assert.IsNotNull(deserialized);
        Assert.IsFalse(deserialized.IsLeaf);
        Assert.AreEqual(0, deserialized.FeatureIndex);
        Assert.AreEqual(5.0, deserialized.SplitValue);
        Assert.IsTrue(deserialized.Left.IsLeaf);
        Assert.AreEqual(1.0, deserialized.Left.Prediction);
    }

    [TestMethod]
    public void WriteValue_ForDifferentTypes_SerializesCorrectly()
    {
        using var ms = new MemoryStream();
        using var writer = new BinaryWriter(ms);

        // Act
        SerializationHelper<double>.WriteValue(writer, 3.14);
        SerializationHelper<float>.WriteValue(writer, 2.71f);
        SerializationHelper<int>.WriteValue(writer, 42);

        writer.Flush();
        ms.Position = 0;

        using var reader = new BinaryReader(ms);

        // Assert
        Assert.AreEqual(3.14, SerializationHelper<double>.ReadValue(reader));
        Assert.AreEqual(2.71f, SerializationHelper<float>.ReadValue(reader));
        Assert.AreEqual(42, SerializationHelper<int>.ReadValue(reader));
    }
}

Test Checklist

  • [ ] Matrix serialization/deserialization
  • [ ] Vector serialization/deserialization
  • [ ] Tensor serialization/deserialization
  • [ ] Decision tree node serialization
  • [ ] All numeric types (double, float, int, etc.)
  • [ ] Null handling
  • [ ] Empty collections
  • [ ] Large data structures

DeserializationHelper Testing

Test Pattern

Deserialization tests mirror serialization - the key is round-trip testing:

[TestMethod]
public void RoundTrip_Matrix_PreservesData()
{
    var original = CreateTestMatrix();
    var serialized = SerializationHelper<double>.SerializeMatrix(original);
    var deserialized = DeserializationHelper<double>.DeserializeMatrix(serialized);
    AssertMatricesEqual(original, deserialized);
}

ConversionsHelper Testing

Key Methods to Test

  1. ToDouble() - Convert T to double
  2. ToFloat() - Convert T to float
  3. ToInt() - Convert T to int
  4. FromDouble() - Convert double to T
  5. ConvertArray() - Convert array types
  6. ConvertMatrix() - Convert matrix types
  7. ConvertVector() - Convert vector types

Test Examples

[TestClass]
public class ConversionsHelperTests
{
    [TestMethod]
    public void ToDouble_FromFloat_ConvertsCorrectly()
    {
        // Arrange
        float value = 3.14f;

        // Act
        double result = ConversionsHelper.ToDouble(value);

        // Assert
        Assert.AreEqual(3.14, result, 1e-6);
    }

    [TestMethod]
    public void ConvertMatrix_FromIntToDouble_PreservesValues()
    {
        // Arrange
        var intMatrix = new Matrix<int>(2, 2);
        intMatrix[0, 0] = 1; intMatrix[0, 1] = 2;
        intMatrix[1, 0] = 3; intMatrix[1, 1] = 4;

        // Act
        var doubleMatrix = ConversionsHelper.ConvertMatrix<int, double>(intMatrix);

        // Assert
        Assert.AreEqual(1.0, doubleMatrix[0, 0]);
        Assert.AreEqual(4.0, doubleMatrix[1, 1]);
    }

    [TestMethod]
    public void ConvertVector_HandlesLargePrecisionLoss()
    {
        // Arrange
        var doubleVector = new Vector<double>(new[] { 1.99999, 2.00001 });

        // Act
        var intVector = ConversionsHelper.ConvertVector<double, int>(doubleVector);

        // Assert
        Assert.AreEqual(1, intVector[0]);  // Truncated
        Assert.AreEqual(2, intVector[1]);  // Truncated
    }
}

Test Checklist

  • [ ] All numeric type conversions
  • [ ] Array conversions
  • [ ] Matrix conversions
  • [ ] Vector conversions
  • [ ] Precision loss handling
  • [ ] Overflow handling
  • [ ] Null handling

ParallelProcessingHelper Testing

Key Methods to Test

  1. ParallelFor() - Parallel loop execution
  2. ParallelForEach() - Parallel collection iteration
  3. ParallelMap() - Map operation in parallel
  4. ParallelReduce() - Reduce operation in parallel
  5. GetOptimalThreadCount() - Determine thread count
  6. CreatePartitions() - Partition data for parallel processing

Test Examples

[TestClass]
public class ParallelProcessingHelperTests
{
    [TestMethod]
    public void ParallelFor_ProcessesAllElements()
    {
        // Arrange
        var results = new int[100];

        // Act
        ParallelProcessingHelper.ParallelFor(0, 100, i =>
        {
            results[i] = i * 2;
        });

        // Assert
        for (int i = 0; i < 100; i++)
            Assert.AreEqual(i * 2, results[i]);
    }

    [TestMethod]
    public void ParallelMap_TransformsAllElements()
    {
        // Arrange
        var input = Enumerable.Range(0, 100).ToList();

        // Act
        var output = ParallelProcessingHelper.ParallelMap(
            input, x => x * x);

        // Assert
        for (int i = 0; i < 100; i++)
            Assert.AreEqual(i * i, output[i]);
    }

    [TestMethod]
    public void ParallelReduce_ComputesSum()
    {
        // Arrange
        var data = Enumerable.Range(1, 100).ToList();

        // Act
        int sum = ParallelProcessingHelper.ParallelReduce(
            data, (a, b) => a + b, identity: 0);

        // Assert
        Assert.AreEqual(5050, sum);  // Sum of 1..100 = 5050
    }

    [TestMethod]
    public void GetOptimalThreadCount_ReturnsReasonableValue()
    {
        // Act
        int threadCount = ParallelProcessingHelper.GetOptimalThreadCount();

        // Assert
        Assert.IsTrue(threadCount > 0);
        Assert.IsTrue(threadCount <= Environment.ProcessorCount);
    }

    [TestMethod]
    public void CreatePartitions_DividesDataEvenly()
    {
        // Arrange
        var data = Enumerable.Range(0, 100).ToList();
        int partitionCount = 4;

        // Act
        var partitions = ParallelProcessingHelper.CreatePartitions(
            data, partitionCount);

        // Assert
        Assert.AreEqual(4, partitions.Count);
        Assert.AreEqual(100, partitions.Sum(p => p.Count));
        Assert.IsTrue(partitions.All(p => p.Count >= 24 && p.Count <= 26),
            "Partitions should be roughly equal");
    }
}

Test Checklist

  • [ ] Parallel loops complete all iterations
  • [ ] Thread safety (no race conditions)
  • [ ] Correct results vs sequential
  • [ ] Performance improvement (optional)
  • [ ] Exception handling
  • [ ] Partitioning algorithms
  • [ ] Thread count optimization

TextProcessingHelper Testing

Key Methods to Test

  1. Tokenize() - Split text into tokens
  2. RemoveStopWords() - Filter common words
  3. Stem() - Reduce words to stems
  4. Lemmatize() - Reduce words to lemmas
  5. CalculateTFIDF() - Term frequency-inverse document frequency
  6. NormalizeText() - Clean and normalize text
  7. ExtractNGrams() - Extract n-grams

Test Examples

[TestClass]
public class TextProcessingHelperTests
{
    [TestMethod]
    public void Tokenize_SplitsOnWhitespace()
    {
        // Arrange
        string text = "The quick brown fox";

        // Act
        var tokens = TextProcessingHelper.Tokenize(text);

        // Assert
        Assert.AreEqual(4, tokens.Count);
        Assert.AreEqual("The", tokens[0]);
        Assert.AreEqual("fox", tokens[3]);
    }

    [TestMethod]
    public void RemoveStopWords_FiltersCommonWords()
    {
        // Arrange
        var tokens = new List<string> { "the", "cat", "is", "on", "mat" };

        // Act
        var filtered = TextProcessingHelper.RemoveStopWords(tokens);

        // Assert
        Assert.AreEqual(2, filtered.Count);
        Assert.IsTrue(filtered.Contains("cat"));
        Assert.IsTrue(filtered.Contains("mat"));
        Assert.IsFalse(filtered.Contains("the"));
        Assert.IsFalse(filtered.Contains("is"));
    }

    [TestMethod]
    public void ExtractNGrams_GeneratesBigrams()
    {
        // Arrange
        var tokens = new List<string> { "the", "quick", "brown", "fox" };

        // Act
        var bigrams = TextProcessingHelper.ExtractNGrams(tokens, n: 2);

        // Assert
        Assert.AreEqual(3, bigrams.Count);
        Assert.IsTrue(bigrams.Contains("the quick"));
        Assert.IsTrue(bigrams.Contains("quick brown"));
        Assert.IsTrue(bigrams.Contains("brown fox"));
    }

    [TestMethod]
    public void NormalizeText_ConvertsToLowerAndRemovesPunctuation()
    {
        // Arrange
        string text = "Hello, World! This is a TEST.";

        // Act
        string normalized = TextProcessingHelper.NormalizeText(text);

        // Assert
        Assert.AreEqual("hello world this is a test", normalized);
    }
}

Test Checklist

  • [ ] Tokenization (various delimiters)
  • [ ] Stop word removal
  • [ ] Stemming algorithms
  • [ ] Lemmatization
  • [ ] TF-IDF calculation
  • [ ] Text normalization
  • [ ] N-gram extraction
  • [ ] Unicode handling
  • [ ] Edge cases (empty, punctuation-only)

EnumHelper Testing

Key Methods to Test

  1. Parse() - Parse string to enum
  2. TryParse() - Safe parse with output
  3. GetValues() - Get all enum values
  4. GetNames() - Get all enum names
  5. GetDescription() - Get description attribute
  6. ToInt() - Convert enum to int
  7. FromInt() - Convert int to enum

Test Examples

[TestClass]
public class EnumHelperTests
{
    public enum TestEnum
    {
        [Description("First Value")]
        First = 1,

        [Description("Second Value")]
        Second = 2,

        [Description("Third Value")]
        Third = 3
    }

    [TestMethod]
    public void Parse_ValidString_ReturnsEnum()
    {
        // Act
        var result = EnumHelper.Parse<TestEnum>("Second");

        // Assert
        Assert.AreEqual(TestEnum.Second, result);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentException))]
    public void Parse_InvalidString_ThrowsException()
    {
        // Act
        EnumHelper.Parse<TestEnum>("Invalid");
    }

    [TestMethod]
    public void TryParse_ValidString_ReturnsTrue()
    {
        // Act
        bool success = EnumHelper.TryParse<TestEnum>("Third", out var result);

        // Assert
        Assert.IsTrue(success);
        Assert.AreEqual(TestEnum.Third, result);
    }

    [TestMethod]
    public void GetValues_ReturnsAllEnumValues()
    {
        // Act
        var values = EnumHelper.GetValues<TestEnum>();

        // Assert
        Assert.AreEqual(3, values.Count());
        Assert.IsTrue(values.Contains(TestEnum.First));
        Assert.IsTrue(values.Contains(TestEnum.Third));
    }

    [TestMethod]
    public void GetDescription_ReturnsAttribute()
    {
        // Act
        string description = EnumHelper.GetDescription(TestEnum.Second);

        // Assert
        Assert.AreEqual("Second Value", description);
    }

    [TestMethod]
    public void ToInt_ConvertsCorrectly()
    {
        // Act
        int value = EnumHelper.ToInt(TestEnum.Second);

        // Assert
        Assert.AreEqual(2, value);
    }

    [TestMethod]
    public void FromInt_ConvertsCorrectly()
    {
        // Act
        var result = EnumHelper.FromInt<TestEnum>(3);

        // Assert
        Assert.AreEqual(TestEnum.Third, result);
    }
}

Test Checklist

  • [ ] Parse valid strings
  • [ ] Parse invalid strings (exception)
  • [ ] TryParse with valid/invalid strings
  • [ ] Get all values
  • [ ] Get all names
  • [ ] Get descriptions from attributes
  • [ ] Int conversions (to/from)
  • [ ] Case-insensitive parsing
  • [ ] Edge cases (null, empty string)

Test File Structure

tests/Helpers/
├── SerializationHelperTests.cs
├── DeserializationHelperTests.cs
├── ConversionsHelperTests.cs
├── ParallelProcessingHelperTests.cs
├── TextProcessingHelperTests.cs
└── EnumHelperTests.cs

Success Criteria

Definition of Done

  • [ ] 6 test files created
  • [ ] 90+ tests total (15 per helper minimum)
  • [ ] All tests passing
  • [ ] Code coverage >= 75% for each helper
  • [ ] Round-trip serialization tested
  • [ ] All type conversions tested
  • [ ] Parallel operations verified

Quality Checklist

  • [ ] Serialization round-trip for all types
  • [ ] Type conversion edge cases (overflow, precision loss)
  • [ ] Parallel processing thread safety
  • [ ] Text processing handles Unicode
  • [ ] Enum helper handles all enum types
  • [ ] Null/empty input handling

Resources

  • See ISSUE_349_JUNIOR_DEV_GUIDE.md for helper testing patterns
  • Serialization reference: BinaryReader/BinaryWriter documentation
  • Parallel patterns: Task Parallel Library (TPL) documentation

Target: Create tests ensuring utility helpers provide reliable cross-cutting functionality for the entire library.

ooples avatar Nov 07 '25 02:11 ooples