/** * Unit tests for Matrix * * Copyright 2015, Klaus Post * Copyright 2015, Backblaze, Inc. All rights reserved. */ package reedsolomon import ( "testing" ) // TestNewMatrix - Tests validate the result for invalid input and the allocations made by newMatrix method. func TestNewMatrix(t *testing.T) { testCases := []struct { rows int columns int // flag to indicate whether the test should pass. shouldPass bool expectedResult matrix expectedErr error }{ // Test case - 1. // Test case with a negative row size. {-1, 10, false, nil, errInvalidRowSize}, // Test case - 2. // Test case with a negative column size. {10, -1, false, nil, errInvalidColSize}, // Test case - 3. // Test case with negative value for both row and column size. {-1, -1, false, nil, errInvalidRowSize}, // Test case - 4. // Test case with 0 value for row size. {0, 10, false, nil, errInvalidRowSize}, // Test case - 5. // Test case with 0 value for column size. {-1, 0, false, nil, errInvalidRowSize}, // Test case - 6. // Test case with 0 value for both row and column size. {0, 0, false, nil, errInvalidRowSize}, } for i, testCase := range testCases { actualResult, actualErr := newMatrix(testCase.rows, testCase.columns) if actualErr != nil && testCase.shouldPass { t.Errorf("Test %d: Expected to pass, but failed with: %s", i+1, actualErr.Error()) } if actualErr == nil && !testCase.shouldPass { t.Errorf("Test %d: Expected to fail with \"%s\", but passed instead.", i+1, testCase.expectedErr) } // Failed as expected, but does it fail for the expected reason. if actualErr != nil && !testCase.shouldPass { if testCase.expectedErr != actualErr { t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead.", i+1, testCase.expectedErr, actualErr) } } // Test passes as expected, but the output values // are verified for correctness here. if actualErr == nil && testCase.shouldPass { if testCase.rows != len(actualResult) { // End the tests here if the the size doesn't match number of rows. t.Fatalf("Test %d: Expected the size of the row of the new matrix to be `%d`, but instead found `%d`", i+1, testCase.rows, len(actualResult)) } // Iterating over each row and validating the size of the column. for j, row := range actualResult { // If the row check passes, verify the size of each columns. if testCase.columns != len(row) { t.Errorf("Test %d: Row %d: Expected the size of the column of the new matrix to be `%d`, but instead found `%d`", i+1, j+1, testCase.columns, len(row)) } } } } } // TestMatrixIdentity - validates the method for returning identity matrix of given size. func TestMatrixIdentity(t *testing.T) { m, err := identityMatrix(3) if err != nil { t.Fatal(err) } str := m.String() expect := "[[1, 0, 0], [0, 1, 0], [0, 0, 1]]" if str != expect { t.Fatal(str, "!=", expect) } } // Tests validate the output of matix multiplication method. func TestMatrixMultiply(t *testing.T) { m1, err := newMatrixData( [][]byte{ []byte{1, 2}, []byte{3, 4}, }) if err != nil { t.Fatal(err) } m2, err := newMatrixData( [][]byte{ []byte{5, 6}, []byte{7, 8}, }) if err != nil { t.Fatal(err) } actual, err := m1.Multiply(m2) if err != nil { t.Fatal(err) } str := actual.String() expect := "[[11, 22], [19, 42]]" if str != expect { t.Fatal(str, "!=", expect) } } // Tests validate the output of the method with computes inverse of matrix. func TestMatrixInverse(t *testing.T) { testCases := []struct { matrixData [][]byte // expected inverse matrix. expectedResult string // flag indicating whether the test should pass. shouldPass bool expectedErr error }{ // Test case - 1. // Test case validating inverse of the input Matrix. { // input data to construct the matrix. [][]byte{ []byte{56, 23, 98}, []byte{3, 100, 200}, []byte{45, 201, 123}, }, // expected Inverse matrix. "[[175, 133, 33], [130, 13, 245], [112, 35, 126]]", // test is expected to pass. true, nil, }, // Test case - 2. // Test case validating inverse of the input Matrix. { // input data to contruct the matrix. [][]byte{ []byte{1, 0, 0, 0, 0}, []byte{0, 1, 0, 0, 0}, []byte{0, 0, 0, 1, 0}, []byte{0, 0, 0, 0, 1}, []byte{7, 7, 6, 6, 1}, }, // expectedInverse matrix. "[[1, 0, 0, 0, 0]," + " [0, 1, 0, 0, 0]," + " [123, 123, 1, 122, 122]," + " [0, 0, 1, 0, 0]," + " [0, 0, 0, 1, 0]]", // test is expected to pass. true, nil, }, // Test case with a non-square matrix. // expected to fail with errNotSquare. { [][]byte{ []byte{56, 23}, []byte{3, 100}, []byte{45, 201}, }, "", false, errNotSquare, }, // Test case with singular matrix. // expected to fail with error errSingular. { [][]byte{ []byte{4, 2}, []byte{12, 6}, }, "", false, errSingular, }, } for i, testCase := range testCases { m, err := newMatrixData(testCase.matrixData) if err != nil { t.Fatalf("Test %d: Failed initializing new Matrix : %s", i+1, err) } actualResult, actualErr := m.Invert() if actualErr != nil && testCase.shouldPass { t.Errorf("Test %d: Expected to pass, but failed with: %s", i+1, actualErr.Error()) } if actualErr == nil && !testCase.shouldPass { t.Errorf("Test %d: Expected to fail with \"%s\", but passed instead.", i+1, testCase.expectedErr) } // Failed as expected, but does it fail for the expected reason. if actualErr != nil && !testCase.shouldPass { if testCase.expectedErr != actualErr { t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead.", i+1, testCase.expectedErr, actualErr) } } // Test passes as expected, but the output values // are verified for correctness here. if actualErr == nil && testCase.shouldPass { if testCase.expectedResult != actualResult.String() { t.Errorf("Test %d: The inverse matrix doesnt't match the expected result", i+1) } } } }