425 lines
20 KiB
C#
Executable File
425 lines
20 KiB
C#
Executable File
// Copyright 2017-2018 Alexander Luzgarev
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Numerics;
|
|
using Xunit;
|
|
|
|
namespace MatFileHandler.Tests
|
|
{
|
|
/// <summary>
|
|
/// Tests of file writing API.
|
|
/// </summary>
|
|
public class MatFileWriterTests
|
|
{
|
|
private const string TestDirectory = "test-data";
|
|
|
|
/// <summary>
|
|
/// Test writing a simple Double array.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestWrite(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
var builder = new DataBuilder();
|
|
var array = builder.NewArray<double>(1, 2);
|
|
array[0] = -13.5;
|
|
array[1] = 17.0;
|
|
var variable = builder.NewVariable("test", array);
|
|
var actual = builder.NewFile(new[] { variable });
|
|
MatCompareWithTestData("good", "double-array", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a large file.
|
|
/// </summary>
|
|
[Fact]
|
|
public void TestHuge()
|
|
{
|
|
var builder = new DataBuilder();
|
|
var array = builder.NewArray<double>(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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing lower and upper limits of integer data types.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestLimits(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
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 });
|
|
MatCompareWithTestData("good", "limits", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing lower and upper limits of integer-based complex data types.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestLimitsComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
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,
|
|
});
|
|
MatCompareWithTestData("good", "limits_complex", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a wide-Unicode symbol.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestUnicodeWide(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
var builder = new DataBuilder();
|
|
var s = builder.NewVariable("s", builder.NewCharArray("🍆"));
|
|
var actual = builder.NewFile(new[] { s });
|
|
MatCompareWithTestData("good", "unicode-wide", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a sparse array.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestSparseArray(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
var builder = new DataBuilder();
|
|
var sparseArray = builder.NewSparseArray<double>(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 });
|
|
MatCompareWithTestData("good", "sparse", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a structure array.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestStructure(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
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_ });
|
|
MatCompareWithTestData("good", "struct", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a logical array.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
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 });
|
|
MatCompareWithTestData("good", "logical", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a sparse logical array.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestSparseLogical(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
var builder = new DataBuilder();
|
|
var array = builder.NewSparseArray<bool>(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 });
|
|
MatCompareWithTestData("good", "sparse_logical", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a sparse complex array.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestSparseComplex(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
var builder = new DataBuilder();
|
|
var array = builder.NewSparseArray<Complex>(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 });
|
|
MatCompareWithTestData("good", "sparse_complex", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test writing a global variable.
|
|
/// </summary>
|
|
[Theory, MemberData(nameof(MatFileWritingTestData))]
|
|
public void TestGlobal(MatFileWritingMethod method, MatFileWriterOptionsForTests options)
|
|
{
|
|
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 });
|
|
MatCompareWithTestData("good", "global", actual, method, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Various writing methods for testing writing of .mat files.
|
|
/// </summary>
|
|
public static TheoryData<MatFileWritingMethod, MatFileWriterOptionsForTests> MatFileWritingTestData
|
|
{
|
|
get
|
|
{
|
|
var always = new MatFileWriterOptions { UseCompression = CompressionUsage.Always};
|
|
var never = new MatFileWriterOptions { UseCompression = CompressionUsage.Never };
|
|
var data = new TheoryData<MatFileWritingMethod, MatFileWriterOptionsForTests>
|
|
{
|
|
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.None },
|
|
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.Always },
|
|
{ MatFileWritingMethod.NormalStream, MatFileWriterOptionsForTests.Never },
|
|
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.None },
|
|
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.Always },
|
|
{ MatFileWritingMethod.UnseekableStream, MatFileWriterOptionsForTests.Never },
|
|
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.None },
|
|
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.Always },
|
|
{ MatFileWritingMethod.UnalignedStream, MatFileWriterOptionsForTests.Never },
|
|
};
|
|
return data;
|
|
}
|
|
}
|
|
|
|
private static AbstractTestDataFactory<IMatFile> GetMatTestData(string factoryName) =>
|
|
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
|
|
|
private void CompareSparseArrays<T>(ISparseArrayOf<T> expected, ISparseArrayOf<T> actual)
|
|
where T : struct
|
|
{
|
|
Assert.NotNull(actual);
|
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
|
Assert.Equal(expected.Data, actual.Data);
|
|
}
|
|
|
|
private void CompareStructureArrays(IStructureArray expected, IStructureArray actual)
|
|
{
|
|
Assert.NotNull(actual);
|
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
|
Assert.Equal(expected.FieldNames, 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.NotNull(actual);
|
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
|
for (var i = 0; i < expected.Count; i++)
|
|
{
|
|
CompareMatArrays(expected[i], actual[i]);
|
|
}
|
|
}
|
|
|
|
private void CompareNumericalArrays<T>(IArrayOf<T> expected, IArrayOf<T> actual)
|
|
{
|
|
Assert.NotNull(actual);
|
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
|
Assert.Equal(expected.Data, actual.Data);
|
|
}
|
|
|
|
private void CompareCharArrays(ICharArray expected, ICharArray actual)
|
|
{
|
|
Assert.NotNull(actual);
|
|
Assert.Equal(expected.Dimensions, actual.Dimensions);
|
|
Assert.Equal(expected.String, actual.String);
|
|
}
|
|
|
|
private void CompareMatArrays(IArray expected, IArray actual)
|
|
{
|
|
switch (expected)
|
|
{
|
|
case ISparseArrayOf<double> expectedSparseArrayOfDouble:
|
|
CompareSparseArrays(expectedSparseArrayOfDouble, actual as ISparseArrayOf<double>);
|
|
return;
|
|
case ISparseArrayOf<Complex> expectedSparseArrayOfComplex:
|
|
CompareSparseArrays(expectedSparseArrayOfComplex, actual as ISparseArrayOf<Complex>);
|
|
return;
|
|
case IStructureArray expectedStructureArray:
|
|
CompareStructureArrays(expectedStructureArray, actual as IStructureArray);
|
|
return;
|
|
case ICharArray expectedCharArray:
|
|
CompareCharArrays(expectedCharArray, actual as ICharArray);
|
|
return;
|
|
case IArrayOf<byte> byteArray:
|
|
CompareNumericalArrays(byteArray, actual as IArrayOf<byte>);
|
|
return;
|
|
case IArrayOf<sbyte> sbyteArray:
|
|
CompareNumericalArrays(sbyteArray, actual as IArrayOf<sbyte>);
|
|
return;
|
|
case IArrayOf<short> shortArray:
|
|
CompareNumericalArrays(shortArray, actual as IArrayOf<short>);
|
|
return;
|
|
case IArrayOf<ushort> ushortArray:
|
|
CompareNumericalArrays(ushortArray, actual as IArrayOf<ushort>);
|
|
return;
|
|
case IArrayOf<int> intArray:
|
|
CompareNumericalArrays(intArray, actual as IArrayOf<int>);
|
|
return;
|
|
case IArrayOf<uint> uintArray:
|
|
CompareNumericalArrays(uintArray, actual as IArrayOf<uint>);
|
|
return;
|
|
case IArrayOf<long> longArray:
|
|
CompareNumericalArrays(longArray, actual as IArrayOf<long>);
|
|
return;
|
|
case IArrayOf<ulong> ulongArray:
|
|
CompareNumericalArrays(ulongArray, actual as IArrayOf<ulong>);
|
|
return;
|
|
case IArrayOf<float> floatArray:
|
|
CompareNumericalArrays(floatArray, actual as IArrayOf<float>);
|
|
return;
|
|
case IArrayOf<double> doubleArray:
|
|
CompareNumericalArrays(doubleArray, actual as IArrayOf<double>);
|
|
return;
|
|
case IArrayOf<ComplexOf<byte>> byteArray:
|
|
CompareNumericalArrays(byteArray, actual as IArrayOf<ComplexOf<byte>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<sbyte>> sbyteArray:
|
|
CompareNumericalArrays(sbyteArray, actual as IArrayOf<ComplexOf<sbyte>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<short>> shortArray:
|
|
CompareNumericalArrays(shortArray, actual as IArrayOf<ComplexOf<short>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<ushort>> ushortArray:
|
|
CompareNumericalArrays(ushortArray, actual as IArrayOf<ComplexOf<ushort>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<int>> intArray:
|
|
CompareNumericalArrays(intArray, actual as IArrayOf<ComplexOf<int>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<uint>> uintArray:
|
|
CompareNumericalArrays(uintArray, actual as IArrayOf<ComplexOf<uint>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<long>> longArray:
|
|
CompareNumericalArrays(longArray, actual as IArrayOf<ComplexOf<long>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<ulong>> ulongArray:
|
|
CompareNumericalArrays(ulongArray, actual as IArrayOf<ComplexOf<ulong>>);
|
|
return;
|
|
case IArrayOf<ComplexOf<float>> floatArray:
|
|
CompareNumericalArrays(floatArray, actual as IArrayOf<ComplexOf<float>>);
|
|
return;
|
|
case IArrayOf<Complex> doubleArray:
|
|
CompareNumericalArrays(doubleArray, actual as IArrayOf<Complex>);
|
|
return;
|
|
case IArrayOf<bool> boolArray:
|
|
CompareNumericalArrays(boolArray, actual as IArrayOf<bool>);
|
|
return;
|
|
case ICellArray cellArray:
|
|
CompareCellArrays(cellArray, actual as ICellArray);
|
|
return;
|
|
}
|
|
if (expected.IsEmpty)
|
|
{
|
|
Assert.True(actual.IsEmpty);
|
|
return;
|
|
}
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
private void CompareMatFiles(IMatFile expected, IMatFile actual)
|
|
{
|
|
Assert.Equal(expected.Variables.Length, actual.Variables.Length);
|
|
for (var i = 0; i < expected.Variables.Length; i++)
|
|
{
|
|
var expectedVariable = expected.Variables[i];
|
|
var actualVariable = actual.Variables[i];
|
|
Assert.Equal(expectedVariable.Name, actualVariable.Name);
|
|
Assert.Equal(expectedVariable.IsGlobal, actualVariable.IsGlobal);
|
|
CompareMatArrays(expectedVariable.Value, actualVariable.Value);
|
|
}
|
|
}
|
|
|
|
private void MatCompareWithTestData(
|
|
string factoryName,
|
|
string testName,
|
|
IMatFile actual,
|
|
MatFileWritingMethod method,
|
|
MatFileWriterOptionsForTests options)
|
|
{
|
|
var expected = GetMatTestData(factoryName)[testName];
|
|
var buffer = MatFileWritingMethods.WriteMatFile(method, options, actual);
|
|
using var stream = new MemoryStream(buffer);
|
|
var reader = new MatFileReader(stream);
|
|
var actualRead = reader.Read();
|
|
CompareMatFiles(expected, actualRead);
|
|
}
|
|
|
|
private ComplexOf<T>[] CreateComplexLimits<T>(T[] limits)
|
|
where T : struct
|
|
{
|
|
return new[] { new ComplexOf<T>(limits[0], limits[1]), new ComplexOf<T>(limits[1], limits[0]) };
|
|
}
|
|
}
|
|
}
|