Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Coverage #219

Merged
merged 6 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions OpenMcdf.Tests/BinaryReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ public sealed class BinaryReaderTests
[TestMethod]
public void ReadGuid()
{
byte[] bytes = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };
byte[] bytes = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10];
using MemoryStream stream = new(bytes);
using CfbBinaryReader reader = new(stream);
Guid guid = reader.ReadGuid();
Assert.AreEqual(new Guid(bytes), guid);

Assert.ThrowsException<EndOfStreamException>(() => reader.ReadGuid());
}

[TestMethod]
public void ReadFileTime()
{
byte[] bytes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
using MemoryStream stream = new(bytes);
using CfbBinaryReader reader = new(stream);
DateTime actual = reader.ReadFileTime();
Expand All @@ -29,7 +31,39 @@ public void ReadFileTime()
public void ReadHeader(string fileName)
{
using FileStream stream = File.OpenRead(fileName);
using CfbBinaryReader reader = new(stream);
using MemoryStream memoryStream = new();
stream.CopyAllTo(memoryStream);

using CfbBinaryReader reader = new(memoryStream);
Header header = reader.ReadHeader();

stream.CopyAllTo(memoryStream);
memoryStream.WriteByte(1); // Corrupt signature
Assert.ThrowsException<FormatException>(() => reader.ReadHeader());

stream.CopyAllTo(memoryStream);
memoryStream.Position = 24;
memoryStream.WriteByte(1); // Corrupt CLSID
Assert.ThrowsException<FormatException>(() => reader.ReadHeader());

stream.CopyAllTo(memoryStream);
memoryStream.Position = 26;
memoryStream.WriteByte(1); // Corrupt Major version
Assert.ThrowsException<FormatException>(() => reader.ReadHeader());

stream.CopyAllTo(memoryStream);
memoryStream.Position = 28;
memoryStream.WriteByte(1); // Corrupt byte order
Assert.ThrowsException<FormatException>(() => reader.ReadHeader());

stream.CopyAllTo(memoryStream);
memoryStream.Position = 32;
memoryStream.WriteByte(1); // Corrupt mini sector shift
Assert.ThrowsException<FormatException>(() => reader.ReadHeader());

stream.CopyAllTo(memoryStream);
memoryStream.Position = 32;
memoryStream.WriteByte(1); // Corrupt mini sector shift
Assert.ThrowsException<FormatException>(() => reader.ReadHeader());
}
}
27 changes: 22 additions & 5 deletions OpenMcdf.Tests/EntryInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,30 @@
public sealed class EntryInfoTests
{
[TestMethod]
[DataRow("MultipleStorage.cfs", 1)]
[DataRow("TestStream_v3_0.cfs", 1)]
[DataRow("TestStream_v4_0.cfs", 1)]
public void EnumerateEntryInfos(string fileName, int count)
[DataRow("MultipleStorage.cfs", EntryType.Storage, "MyStorage", "/Root Entry")]
[DataRow("TestStream_v3_0.cfs", EntryType.Stream, "TestStream", "/Root Entry")]
[DataRow("TestStream_v4_0.cfs", EntryType.Stream, "TestStream", "/Root Entry")]
public void EnumerateEntryInfos(string fileName, EntryType type, string name, string path)
{
using var rootStorage = RootStorage.OpenRead(fileName);
IEnumerable<EntryInfo> entries = rootStorage.EnumerateEntries();
Assert.AreEqual(count, entries.Count());
Assert.AreEqual(1, entries.Count());

EntryInfo entry = entries.First();
Assert.AreEqual(type, entry.Type);
Assert.AreEqual(name, entry.Name);
Assert.AreEqual(path, entry.Path);
Assert.AreEqual(Guid.Empty, entry.CLSID);
Assert.AreEqual(0, entry.Length);
if (type is EntryType.Storage)
{
Assert.AreNotEqual(DirectoryEntry.ZeroFileTime, entry.CreationTime);
Assert.AreNotEqual(DirectoryEntry.ZeroFileTime, entry.ModifiedTime);
}
else
{
Assert.AreEqual(DirectoryEntry.ZeroFileTime, entry.CreationTime);
Assert.AreEqual(DirectoryEntry.ZeroFileTime, entry.ModifiedTime);
}
}
}
9 changes: 7 additions & 2 deletions OpenMcdf.Tests/OpenMcdf.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net48;net8.0-windows</TargetFrameworks>
Expand All @@ -10,12 +10,17 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="GitHubActionsTestLogger" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="MSTest" Version="3.6.2" />
<PackageReference Include="MSTest" Version="3.6.3" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-windows'">
Expand Down
118 changes: 110 additions & 8 deletions OpenMcdf.Tests/StreamTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace OpenMcdf.Tests;
using System;

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Debug)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Debug)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Debug)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Debug)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Debug)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Debug)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check warning on line 1 in OpenMcdf.Tests/StreamTests.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

namespace OpenMcdf.Tests;

[TestClass]
public sealed class StreamTests
Expand Down Expand Up @@ -41,24 +43,66 @@
[DataRow(Version.V4, 4097)]
public void ReadViaCopyTo(Version version, int length)
{
// Test files are filled with bytes equal to their position modulo 256
using MemoryStream expectedStream = new(length);
for (int i = 0; i < length; i++)
expectedStream.WriteByte((byte)i);

string fileName = $"TestStream_v{(int)version}_{length}.cfs";
using var rootStorage = RootStorage.OpenRead(fileName);
rootStorage.Validate();

using Stream stream = rootStorage.OpenStream("TestStream");
Assert.AreEqual(length, stream.Length);

// Test files are filled with bytes equal to their position modulo 256
using MemoryStream expectedStream = new(length);
for (int i = 0; i < length; i++)
expectedStream.WriteByte((byte)i);

using MemoryStream actualStream = new();
stream.CopyTo(actualStream);

StreamAssert.AreEqual(expectedStream, actualStream);
}

#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
[TestMethod]
[DataRow(Version.V3, 0)]
[DataRow(Version.V3, 63)]
[DataRow(Version.V3, 64)]
[DataRow(Version.V3, 65)]
[DataRow(Version.V3, 511)]
[DataRow(Version.V3, 512)]
[DataRow(Version.V3, 513)]
[DataRow(Version.V3, 4095)]
[DataRow(Version.V3, 4096)]
[DataRow(Version.V3, 4097)]
[DataRow(Version.V3, 65536)]
[DataRow(Version.V4, 0)]
[DataRow(Version.V4, 63)]
[DataRow(Version.V4, 64)]
[DataRow(Version.V4, 65)]
[DataRow(Version.V4, 511)]
[DataRow(Version.V4, 512)]
[DataRow(Version.V4, 513)]
[DataRow(Version.V4, 4095)]
[DataRow(Version.V4, 4096)]
[DataRow(Version.V4, 4097)]
public void ReadSpan(Version version, int length)
{
// Test files are filled with bytes equal to their position modulo 256
byte[] expectedBuffer = new byte[length];
for (int i = 0; i < length; i++)
expectedBuffer[i] = ((byte)i);

string fileName = $"TestStream_v{(int)version}_{length}.cfs";
using var rootStorage = RootStorage.OpenRead(fileName);

using Stream stream = rootStorage.OpenStream("TestStream");
Assert.AreEqual(length, stream.Length);

byte[] actualBuffer = new byte[length];
stream.Read(actualBuffer);

CollectionAssert.AreEqual(expectedBuffer, actualBuffer);
}
#endif

[TestMethod]
[DataRow(Version.V3, 0)]
[DataRow(Version.V3, 63)]
Expand Down Expand Up @@ -104,6 +148,22 @@
StreamAssert.AreEqual(expectedStream, actualStream);
}

[TestMethod]
[DataRow(Version.V3, 64)] // Mini-stream
[DataRow(Version.V4, 4096)] // Regular stream
public void Seek(Version version, int length)
{
string fileName = $"TestStream_v{(int)version}_{length}.cfs";
using var rootStorage = RootStorage.OpenRead(fileName);
using Stream stream = rootStorage.OpenStream("TestStream");

stream.Seek(0, SeekOrigin.Begin);
Assert.ThrowsException<IOException>(() => stream.Seek(-1, SeekOrigin.Begin));
Assert.ThrowsException<IOException>(() => stream.Seek(-1, SeekOrigin.Current));
Assert.ThrowsException<IOException>(() => stream.Seek(length + 1, SeekOrigin.End));
Assert.ThrowsException<ArgumentException>(() => stream.Seek(length, (SeekOrigin)3));
}

[TestMethod]
[DataRow(Version.V3, 0)]
[DataRow(Version.V3, 63)]
Expand Down Expand Up @@ -132,7 +192,41 @@
[DataRow(Version.V4, 1024 * 4096)] // Multiple FAT sectors (1024 * 4096)
[DataRow(Version.V4, 7087616 * 4)] // First DIFAT chain
[DataRow(Version.V4, 2 * 7087616 * 4)] // Long DIFAT chain
public void Write(Version version, int length)
public void Write(Version version, int length) => WriteCore(version, length, false);

#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
[TestMethod]
[DataRow(Version.V3, 0)]
[DataRow(Version.V3, 63)]
[DataRow(Version.V3, 64)] // Mini-stream sector size
[DataRow(Version.V3, 65)]
[DataRow(Version.V3, 511)]
[DataRow(Version.V3, 512)] // Multiple stream sectors
[DataRow(Version.V3, 513)]
[DataRow(Version.V3, 4095)]
[DataRow(Version.V3, 4096)]
[DataRow(Version.V3, 4097)]
[DataRow(Version.V3, 128 * 512)] // Multiple FAT sectors
[DataRow(Version.V3, 1024 * 4096)] // Multiple FAT sectors
[DataRow(Version.V3, 7087616)] // First DIFAT chain
[DataRow(Version.V3, 2 * 7087616)] // Long DIFAT chain
[DataRow(Version.V4, 0)]
[DataRow(Version.V4, 63)]
[DataRow(Version.V4, 64)] // Mini-stream sector size
[DataRow(Version.V4, 65)]
[DataRow(Version.V4, 511)]
[DataRow(Version.V4, 512)]
[DataRow(Version.V4, 513)]
[DataRow(Version.V4, 4095)]
[DataRow(Version.V4, 4096)] // Multiple stream sectors
[DataRow(Version.V4, 4097)]
[DataRow(Version.V4, 1024 * 4096)] // Multiple FAT sectors (1024 * 4096)
[DataRow(Version.V4, 7087616 * 4)] // First DIFAT chain
[DataRow(Version.V4, 2 * 7087616 * 4)] // Long DIFAT chain
public void WriteSpan(Version version, int length) => WriteCore(version, length, true);
#endif

static void WriteCore(Version version, int length, bool preferSpan)
{
using MemoryStream memoryStream = new();
using var rootStorage = RootStorage.Create(memoryStream, version);
Expand All @@ -144,7 +238,15 @@
for (int i = 0; i < length; i++)
expectedBuffer[i] = (byte)i;

#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
if (preferSpan)
stream.Write(expectedBuffer);
else
stream.Write(expectedBuffer, 0, expectedBuffer.Length);
#else
stream.Write(expectedBuffer, 0, expectedBuffer.Length);
#endif

Assert.AreEqual(length, stream.Length);
Assert.AreEqual(length, stream.Position);

Expand Down
2 changes: 1 addition & 1 deletion OpenMcdf/Header.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal sealed class Header : IEquatable<Header?>
/// <summary>
/// Identification signature for the compound file structure.
/// </summary>
internal static readonly byte[] Signature = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
internal static readonly byte[] Signature = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];

internal static readonly byte[] Unused = new byte[6];

Expand Down
10 changes: 9 additions & 1 deletion OpenMcdf/StreamExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,13 @@ public static void WriteByteCore(this Stream stream, byte value)
}

#endif
}

public static void CopyAllTo(this Stream source, Stream destination)
{
source.Position = 0;
destination.Position = 0;
destination.SetLength(source.Length);
source.CopyTo(destination);
destination.Position = 0;
}
}
2 changes: 1 addition & 1 deletion OpenMcdf/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static void ThrowIfNotWritable(this Stream stream)
throw new NotSupportedException("Stream does not support writing.");
}

public static void ThrowSeekBeforeOrigin() => throw new IOException("Seek before origin.");
public static void ThrowSeekBeforeOrigin() => throw new IOException("An attempt was made to move the position before the beginning of the stream.");

public static void ThrowIfNameIsInvalid(string value)
{
Expand Down