// Copyright 2017-2019 Alexander Luzgarev using System; using System.IO; using System.Numerics; using NUnit.Framework; namespace MatFileHandler.Tests { /// /// Tests of file writing API. /// [TestFixture] public class MatFileWriterTests { private const string TestDirectory = "test-data"; /// /// Test writing a simple Double array. /// [Test] public void TestWrite() { var builder = new DataBuilder(); var array = builder.NewArray(1, 2); array[0] = -13.5; array[1] = 17.0; var variable = builder.NewVariable("test", array); var actual = builder.NewFile(new[] { variable }); MatCompareWithLevel5TestData("double-array", actual); } /// /// Test writing a large file. /// [Test] public void TestHuge() { var builder = new DataBuilder(); var array = builder.NewArray(1000, 10000); array[0] = -13.5; array[1] = 17.0; var variable = builder.NewVariable("test", array); var matFile = builder.NewFile(new[] { variable }); using (var stream = new MemoryStream()) { var writer = new MatFileWriter(stream); writer.Write(matFile); } } /// /// Test writing lower and upper limits of integer data types. /// [Test] public void TestLimits() { var builder = new DataBuilder(); var int8 = builder.NewVariable("int8_", builder.NewArray(CommonData.Int8Limits, 1, 2)); var uint8 = builder.NewVariable("uint8_", builder.NewArray(CommonData.UInt8Limits, 1, 2)); var int16 = builder.NewVariable("int16_", builder.NewArray(CommonData.Int16Limits, 1, 2)); var uint16 = builder.NewVariable("uint16_", builder.NewArray(CommonData.UInt16Limits, 1, 2)); var int32 = builder.NewVariable("int32_", builder.NewArray(CommonData.Int32Limits, 1, 2)); var uint32 = builder.NewVariable("uint32_", builder.NewArray(CommonData.UInt32Limits, 1, 2)); var int64 = builder.NewVariable("int64_", builder.NewArray(CommonData.Int64Limits, 1, 2)); var uint64 = builder.NewVariable("uint64_", builder.NewArray(CommonData.UInt64Limits, 1, 2)); var actual = builder.NewFile(new[] { int16, int32, int64, int8, uint16, uint32, uint64, uint8 }); MatCompareWithLevel5TestData("limits", actual); } /// /// Test writing lower and upper limits of integer-based complex data types. /// [Test] public void TestLimitsComplex() { var builder = new DataBuilder(); var int8Complex = builder.NewVariable( "int8_complex", builder.NewArray(CreateComplexLimits(CommonData.Int8Limits), 1, 2)); var uint8Complex = builder.NewVariable( "uint8_complex", builder.NewArray(CreateComplexLimits(CommonData.UInt8Limits), 1, 2)); var int16Complex = builder.NewVariable( "int16_complex", builder.NewArray(CreateComplexLimits(CommonData.Int16Limits), 1, 2)); var uint16Complex = builder.NewVariable( "uint16_complex", builder.NewArray(CreateComplexLimits(CommonData.UInt16Limits), 1, 2)); var int32Complex = builder.NewVariable( "int32_complex", builder.NewArray(CreateComplexLimits(CommonData.Int32Limits), 1, 2)); var uint32Complex = builder.NewVariable( "uint32_complex", builder.NewArray(CreateComplexLimits(CommonData.UInt32Limits), 1, 2)); var int64Complex = builder.NewVariable( "int64_complex", builder.NewArray(CreateComplexLimits(CommonData.Int64Limits), 1, 2)); var uint64Complex = builder.NewVariable( "uint64_complex", builder.NewArray(CreateComplexLimits(CommonData.UInt64Limits), 1, 2)); var actual = builder.NewFile(new[] { int16Complex, int32Complex, int64Complex, int8Complex, uint16Complex, uint32Complex, uint64Complex, uint8Complex, }); MatCompareWithLevel5TestData("limits_complex", actual); } /// /// Test writing a wide-Unicode symbol. /// [Test] public void TestUnicodeWide() { var builder = new DataBuilder(); var s = builder.NewVariable("s", builder.NewCharArray("🍆")); var actual = builder.NewFile(new[] { s }); MatCompareWithLevel5TestData("unicode-wide", actual); } /// /// Test writing a sparse array. /// [Test] public void TestSparseArray() { var builder = new DataBuilder(); var sparseArray = builder.NewSparseArray(4, 5); sparseArray[1, 1] = 1; sparseArray[1, 2] = 2; sparseArray[2, 1] = 3; sparseArray[2, 3] = 4; var sparse = builder.NewVariable("sparse_", sparseArray); var actual = builder.NewFile(new[] { sparse }); MatCompareWithLevel5TestData("sparse", actual); } /// /// Test writing a structure array. /// [Test] public void TestStructure() { var builder = new DataBuilder(); var structure = builder.NewStructureArray(new[] { "x", "y" }, 2, 3); structure["x", 0, 0] = builder.NewArray(new[] { 12.345 }, 1, 1); structure["y", 0, 0] = builder.NewCharArray("abc"); structure["x", 1, 0] = builder.NewCharArray("xyz"); structure["y", 1, 0] = builder.NewEmpty(); structure["x", 0, 1] = builder.NewArray(new[] { 2.0 }, 1, 1); structure["y", 0, 1] = builder.NewArray(new[] { 13.0 }, 1, 1); structure["x", 1, 1] = builder.NewEmpty(); structure["y", 1, 1] = builder.NewCharArray("acbd", 2, 2); var cellArray = builder.NewCellArray(1, 2); cellArray[0] = builder.NewCharArray("x"); cellArray[1] = builder.NewCharArray("yz"); structure["x", 0, 2] = cellArray; structure["y", 0, 2] = builder.NewArray(new[] { 1.0, 4.0, 2.0, 5.0, 3.0, 6.0 }, 2, 3); structure["x", 1, 2] = builder.NewArray(new[] { 1.5f }, 1, 1); structure["y", 1, 2] = builder.NewEmpty(); var struct_ = builder.NewVariable("struct_", structure); var actual = builder.NewFile(new[] { struct_ }); MatCompareWithLevel5TestData("struct", actual); } /// /// Test writing a logical array. /// [Test] public void TestLogical() { var builder = new DataBuilder(); var logical = builder.NewArray(new[] { true, false, true, true, false, true }, 2, 3); var logicalVariable = builder.NewVariable("logical_", logical); var actual = builder.NewFile(new[] { logicalVariable }); MatCompareWithLevel5TestData("logical", actual); } /// /// Test writing a sparse logical array. /// [Test] public void TestSparseLogical() { var builder = new DataBuilder(); var array = builder.NewSparseArray(2, 3); array[0, 0] = true; array[0, 1] = true; array[1, 1] = true; array[1, 2] = true; var sparseLogical = builder.NewVariable("sparse_logical", array); var actual = builder.NewFile(new[] { sparseLogical }); MatCompareWithLevel5TestData("sparse_logical", actual); } /// /// Test writing a sparse complex array. /// [Test] public void TestSparseComplex() { var builder = new DataBuilder(); var array = builder.NewSparseArray(2, 2); array[0, 0] = -1.5 + (2.5 * Complex.ImaginaryOne); array[1, 0] = 2 - (3 * Complex.ImaginaryOne); array[1, 1] = 0.5 + Complex.ImaginaryOne; var sparseComplex = builder.NewVariable("sparse_complex", array); var actual = builder.NewFile(new[] { sparseComplex }); MatCompareWithLevel5TestData("sparse_complex", actual); } /// /// Test writing a global variable. /// [Test] public void TestGlobal() { var builder = new DataBuilder(); var array = builder.NewArray(new double[] { 1, 3, 5 }, 1, 3); var global = builder.NewVariable("global_", array, true); var actual = builder.NewFile(new[] { global }); MatCompareWithLevel5TestData("global", actual); } private static AbstractTestDataFactory GetMatTestData(string factoryName) => new MatTestDataFactory(Path.Combine(TestDirectory, factoryName)); private void CompareSparseArrays(ISparseArrayOf expected, ISparseArrayOf actual) where T : struct { Assert.That(actual, Is.Not.Null); Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions)); Assert.That(expected.Data, Is.EquivalentTo(actual.Data)); } private void CompareStructureArrays(IStructureArray expected, IStructureArray actual) { Assert.That(actual, Is.Not.Null); Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions)); Assert.That(expected.FieldNames, Is.EquivalentTo(actual.FieldNames)); foreach (var name in expected.FieldNames) { for (var i = 0; i < expected.Count; i++) { CompareMatArrays(expected[name, i], actual[name, i]); } } } private void CompareCellArrays(ICellArray expected, ICellArray actual) { Assert.That(actual, Is.Not.Null); Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions)); for (var i = 0; i < expected.Count; i++) { CompareMatArrays(expected[i], actual[i]); } } private void CompareNumericalArrays(IArrayOf expected, IArrayOf actual) { Assert.That(actual, Is.Not.Null); Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions)); Assert.That(expected.Data, Is.EqualTo(actual.Data)); } private void CompareCharArrays(ICharArray expected, ICharArray actual) { Assert.That(actual, Is.Not.Null); Assert.That(expected.Dimensions, Is.EqualTo(actual.Dimensions)); Assert.That(expected.String, Is.EqualTo(actual.String)); } private void CompareMatArrays(IArray expected, IArray actual) { switch (expected) { case ISparseArrayOf expectedSparseArrayOfDouble: CompareSparseArrays(expectedSparseArrayOfDouble, actual as ISparseArrayOf); return; case ISparseArrayOf expectedSparseArrayOfComplex: CompareSparseArrays(expectedSparseArrayOfComplex, actual as ISparseArrayOf); return; case IStructureArray expectedStructureArray: CompareStructureArrays(expectedStructureArray, actual as IStructureArray); return; case ICharArray expectedCharArray: CompareCharArrays(expectedCharArray, actual as ICharArray); return; case IArrayOf byteArray: CompareNumericalArrays(byteArray, actual as IArrayOf); return; case IArrayOf sbyteArray: CompareNumericalArrays(sbyteArray, actual as IArrayOf); return; case IArrayOf shortArray: CompareNumericalArrays(shortArray, actual as IArrayOf); return; case IArrayOf ushortArray: CompareNumericalArrays(ushortArray, actual as IArrayOf); return; case IArrayOf intArray: CompareNumericalArrays(intArray, actual as IArrayOf); return; case IArrayOf uintArray: CompareNumericalArrays(uintArray, actual as IArrayOf); return; case IArrayOf longArray: CompareNumericalArrays(longArray, actual as IArrayOf); return; case IArrayOf ulongArray: CompareNumericalArrays(ulongArray, actual as IArrayOf); return; case IArrayOf floatArray: CompareNumericalArrays(floatArray, actual as IArrayOf); return; case IArrayOf doubleArray: CompareNumericalArrays(doubleArray, actual as IArrayOf); return; case IArrayOf> byteArray: CompareNumericalArrays(byteArray, actual as IArrayOf>); return; case IArrayOf> sbyteArray: CompareNumericalArrays(sbyteArray, actual as IArrayOf>); return; case IArrayOf> shortArray: CompareNumericalArrays(shortArray, actual as IArrayOf>); return; case IArrayOf> ushortArray: CompareNumericalArrays(ushortArray, actual as IArrayOf>); return; case IArrayOf> intArray: CompareNumericalArrays(intArray, actual as IArrayOf>); return; case IArrayOf> uintArray: CompareNumericalArrays(uintArray, actual as IArrayOf>); return; case IArrayOf> longArray: CompareNumericalArrays(longArray, actual as IArrayOf>); return; case IArrayOf> ulongArray: CompareNumericalArrays(ulongArray, actual as IArrayOf>); return; case IArrayOf> floatArray: CompareNumericalArrays(floatArray, actual as IArrayOf>); return; case IArrayOf doubleArray: CompareNumericalArrays(doubleArray, actual as IArrayOf); return; case IArrayOf boolArray: CompareNumericalArrays(boolArray, actual as IArrayOf); return; case ICellArray cellArray: CompareCellArrays(cellArray, actual as ICellArray); return; } if (expected.IsEmpty) { Assert.That(actual.IsEmpty, Is.True); return; } throw new NotSupportedException(); } private void CompareMatFiles(IMatFile expected, IMatFile actual) { Assert.That(expected.Variables.Length, Is.EqualTo(actual.Variables.Length)); for (var i = 0; i < expected.Variables.Length; i++) { var expectedVariable = expected.Variables[i]; var actualVariable = actual.Variables[i]; Assert.That(expectedVariable.Name, Is.EqualTo(actualVariable.Name)); Assert.That(expectedVariable.IsGlobal, Is.EqualTo(actualVariable.IsGlobal)); CompareMatArrays(expectedVariable.Value, actualVariable.Value); } } private void CompareTestDataWithWritingOptions( IMatFile expected, IMatFile actual, MatFileWriterOptions? maybeOptions) { byte[] buffer; using (var stream = new MemoryStream()) { var writer = maybeOptions is MatFileWriterOptions options ? new MatFileWriter(stream, options) : new MatFileWriter(stream); writer.Write(actual); buffer = stream.ToArray(); } using (var stream = new MemoryStream(buffer)) { var reader = new MatFileReader(stream); var actualRead = reader.Read(); CompareMatFiles(expected, actualRead); } } private void MatCompareWithLevel5TestData(string testName, IMatFile actual) { MatCompareWithTestData("level5", testName, actual); } private void MatCompareWithTestData(string factoryName, string testName, IMatFile actual) { var expected = GetMatTestData(factoryName)[testName]; CompareTestDataWithWritingOptions(expected, actual, null); CompareTestDataWithWritingOptions( expected, actual, new MatFileWriterOptions { UseCompression = CompressionUsage.Always }); CompareTestDataWithWritingOptions( expected, actual, new MatFileWriterOptions { UseCompression = CompressionUsage.Never }); } private ComplexOf[] CreateComplexLimits(T[] limits) where T : struct { return new[] { new ComplexOf(limits[0], limits[1]), new ComplexOf(limits[1], limits[0]) }; } } }