From b76184325d02c0ceb6b166d77bc04664cdc35c6b Mon Sep 17 00:00:00 2001 From: Jeremy Powell Date: Thu, 10 Oct 2024 11:39:00 +1300 Subject: [PATCH 1/3] Truncate StreamDecorator reads Fixes: #184 --- sources/OpenMcdf.Extensions/StreamDecorator.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sources/OpenMcdf.Extensions/StreamDecorator.cs b/sources/OpenMcdf.Extensions/StreamDecorator.cs index ee3b04a1..b79202b8 100644 --- a/sources/OpenMcdf.Extensions/StreamDecorator.cs +++ b/sources/OpenMcdf.Extensions/StreamDecorator.cs @@ -66,6 +66,11 @@ public override int Read(byte[] buffer, int offset, int count) if (position >= cfStream.Size) return 0; + int maxReadableLength = (int)Math.Min(int.MaxValue, Length - Position); + count = Math.Max(0, Math.Min(maxReadableLength, count)); + if (count == 0) + return 0; + count = cfStream.Read(buffer, position, offset, count); position += count; return count; From 0fa4d4177094bb1355bd7c420a1fe39f2a3295d0 Mon Sep 17 00:00:00 2001 From: Jeremy Powell Date: Mon, 7 Oct 2024 14:09:38 +1300 Subject: [PATCH 2/3] Add test for StreamDecorator.CopyTo Test for buffer lengths at various sector boundaries, for v3 and v4 --- .../CFSStreamExtensionsTest.cs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/sources/Test/OpenMcdf.Extensions.Test/CFSStreamExtensionsTest.cs b/sources/Test/OpenMcdf.Extensions.Test/CFSStreamExtensionsTest.cs index 098dd1be..a63931e7 100644 --- a/sources/Test/OpenMcdf.Extensions.Test/CFSStreamExtensionsTest.cs +++ b/sources/Test/OpenMcdf.Extensions.Test/CFSStreamExtensionsTest.cs @@ -92,7 +92,6 @@ public void Test_AS_IOSTREAM_MULTISECTOR_WRITE() cf.SaveAs("$ACFFile2.cfs"); } - // Works using (CompoundFile cf = new("$ACFFile2.cfs")) { using Stream s = cf.RootStorage.GetStream("ANewStream").AsIOStream(); @@ -102,15 +101,42 @@ public void Test_AS_IOSTREAM_MULTISECTOR_WRITE() Assert.AreEqual(readData.Length, readCount); CollectionAssert.AreEqual(data, readData); } + } - // Won't work until #88 is fixed. - using (CompoundFile cf = new("$ACFFile2.cfs")) - { - using Stream readStream = cf.RootStorage.GetStream("ANewStream").AsIOStream(); - using MemoryStream ms = new(); - readStream.CopyTo(ms); - CollectionAssert.AreEqual(data, ms.ToArray()); - } + [TestMethod] + [DataRow(CFSVersion.Ver_3, 0)] + [DataRow(CFSVersion.Ver_3, 63)] + [DataRow(CFSVersion.Ver_3, 64)] + [DataRow(CFSVersion.Ver_3, 65)] + [DataRow(CFSVersion.Ver_3, 511)] + [DataRow(CFSVersion.Ver_3, 512)] + [DataRow(CFSVersion.Ver_3, 513)] + [DataRow(CFSVersion.Ver_3, 4095)] + [DataRow(CFSVersion.Ver_3, 4096)] + [DataRow(CFSVersion.Ver_3, 409)] + [DataRow(CFSVersion.Ver_4, 0)] + [DataRow(CFSVersion.Ver_4, 63)] + [DataRow(CFSVersion.Ver_4, 64)] + [DataRow(CFSVersion.Ver_4, 65)] + [DataRow(CFSVersion.Ver_4, 511)] + [DataRow(CFSVersion.Ver_4, 512)] + [DataRow(CFSVersion.Ver_4, 513)] + [DataRow(CFSVersion.Ver_4, 4095)] + [DataRow(CFSVersion.Ver_4, 4096)] + [DataRow(CFSVersion.Ver_4, 4097)] + public void Test_STREAMDECORATOR_COPY(CFSVersion version, int length) + { + using CompoundFile cf = new(version, CFSConfiguration.Default); + CFStream cfStream = cf.RootStorage.AddStream("MyStream"); + using StreamDecorator stream = new(cfStream); + var buffer = Helpers.GetBuffer(length); + stream.Write(buffer, 0, buffer.Length); + stream.Position = 0; + Assert.AreEqual(length, cfStream.Size); + + using MemoryStream memoryStream = new(); + stream.CopyTo(memoryStream); + CollectionAssert.AreEqual(buffer, memoryStream.ToArray()); } } } From 6e712f475d181797695c633240ae362caad6f6ec Mon Sep 17 00:00:00 2001 From: Jeremy Powell Date: Thu, 10 Oct 2024 13:01:34 +1300 Subject: [PATCH 3/3] Improve CFStream tests - Test v3 and v4 at sector boundaries - Allow failures to be debugged repeatably - Allow test parallelization --- sources/Test/OpenMcdf.Test/CFStreamTest.cs | 110 +++++++++++++-------- 1 file changed, 69 insertions(+), 41 deletions(-) diff --git a/sources/Test/OpenMcdf.Test/CFStreamTest.cs b/sources/Test/OpenMcdf.Test/CFStreamTest.cs index dcaf05ad..0f4bfa4d 100644 --- a/sources/Test/OpenMcdf.Test/CFStreamTest.cs +++ b/sources/Test/OpenMcdf.Test/CFStreamTest.cs @@ -448,51 +448,79 @@ public void Test_DELETE_STREAM_2() } [TestMethod] - public void Test_WRITE_AND_READ_CFS() + [DataRow(CFSVersion.Ver_3, 0)] + [DataRow(CFSVersion.Ver_3, 63)] + [DataRow(CFSVersion.Ver_3, 64)] + [DataRow(CFSVersion.Ver_3, 65)] + [DataRow(CFSVersion.Ver_3, 511)] + [DataRow(CFSVersion.Ver_3, 512)] + [DataRow(CFSVersion.Ver_3, 513)] + [DataRow(CFSVersion.Ver_3, 4095)] + [DataRow(CFSVersion.Ver_3, 4096)] + [DataRow(CFSVersion.Ver_3, 409)] + [DataRow(CFSVersion.Ver_4, 0)] + [DataRow(CFSVersion.Ver_4, 63)] + [DataRow(CFSVersion.Ver_4, 64)] + [DataRow(CFSVersion.Ver_4, 65)] + [DataRow(CFSVersion.Ver_4, 511)] + [DataRow(CFSVersion.Ver_4, 512)] + [DataRow(CFSVersion.Ver_4, 513)] + [DataRow(CFSVersion.Ver_4, 4095)] + [DataRow(CFSVersion.Ver_4, 4096)] + [DataRow(CFSVersion.Ver_4, 4097)] + public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS(CFSVersion version, int bufferSize) { - string filename = "WRITE_AND_READ_CFS.cfs"; - - using CompoundFile cf = new(); - - CFStorage st = cf.RootStorage.AddStorage("MyStorage"); - CFStream sm = st.AddStream("MyStream"); - byte[] b = Helpers.GetBuffer(220, 0x0A); - sm.SetData(b); - - cf.SaveAs(filename); - cf.Close(); - - using CompoundFile cf2 = new(filename); - CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); - CFStream sm2 = st2.GetStream("MyStream"); - cf2.Close(); - - Assert.IsNotNull(sm2); - Assert.AreEqual(220, sm2.Size); - - File.Delete(filename); + SingleWriteReadMatching(version, bufferSize); } [TestMethod] - public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS() + [DataRow(CFSVersion.Ver_3, 0)] + [DataRow(CFSVersion.Ver_3, 63)] + [DataRow(CFSVersion.Ver_3, 64)] + [DataRow(CFSVersion.Ver_3, 65)] + [DataRow(CFSVersion.Ver_3, 511)] + [DataRow(CFSVersion.Ver_3, 512)] + [DataRow(CFSVersion.Ver_3, 513)] + [DataRow(CFSVersion.Ver_3, 4095)] + [DataRow(CFSVersion.Ver_3, 4096)] + [DataRow(CFSVersion.Ver_3, 409)] + [DataRow(CFSVersion.Ver_4, 0)] + [DataRow(CFSVersion.Ver_4, 63)] + [DataRow(CFSVersion.Ver_4, 64)] + [DataRow(CFSVersion.Ver_4, 65)] + [DataRow(CFSVersion.Ver_4, 511)] + [DataRow(CFSVersion.Ver_4, 512)] + [DataRow(CFSVersion.Ver_4, 513)] + [DataRow(CFSVersion.Ver_4, 4095)] + [DataRow(CFSVersion.Ver_4, 4096)] + [DataRow(CFSVersion.Ver_4, 4097)] + public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS_STREAM(CFSVersion version, int bufferLength) { - Random r = new Random(); + SingleWriteReadMatchingSTREAMED(version, bufferLength); + } - for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) + static IEnumerable FuzzyBufferLengths + { + get { - SingleWriteReadMatching(i + r.Next(0, 3)); + Random r = new Random(); + + foreach (CFSVersion version in Enum.GetValues(typeof(CFSVersion))) + { + for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) + { + yield return new object[] { version, i + r.Next(0, 3) }; + } + } } } [TestMethod] - public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS_STREAM() + [Ignore("Run fuzzing tests manually")] + [DynamicData(nameof(FuzzyBufferLengths), DynamicDataSourceType.Property)] + public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS_STREAM_FUZZING(CFSVersion version, int bufferLength) { - Random r = new Random(); - - for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) - { - SingleWriteReadMatchingSTREAMED(i + r.Next(0, 3)); - } + SingleWriteReadMatchingSTREAMED(version, bufferLength); } [TestMethod] @@ -554,13 +582,13 @@ public static void SingleTransactedChange(int size) CollectionAssert.AreEqual(b, sm2.GetData()); } - private static void SingleWriteReadMatching(int size) + private static void SingleWriteReadMatching(CFSVersion version, int bufferLength) { - string filename = "INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS.cfs"; + string filename = $"{nameof(SingleWriteReadMatching)}_{version}_{bufferLength}.cfs"; File.Delete(filename); - byte[] b = Helpers.GetBuffer(size); + byte[] b = Helpers.GetBuffer(bufferLength); using (CompoundFile cf = new()) { @@ -576,17 +604,17 @@ private static void SingleWriteReadMatching(int size) CFStream sm2 = st2.GetStream("MyStream"); Assert.IsNotNull(sm2); - Assert.AreEqual(size, sm2.Size); + Assert.AreEqual(bufferLength, sm2.Size); CollectionAssert.AreEqual(b, sm2.GetData()); } - private static void SingleWriteReadMatchingSTREAMED(int size) + private static void SingleWriteReadMatchingSTREAMED(CFSVersion version, int bufferLength) { - byte[] b = Helpers.GetBuffer(size); + byte[] b = Helpers.GetBuffer(bufferLength); - using MemoryStream ms = new(size); + using MemoryStream ms = new(bufferLength); - using (CompoundFile cf = new()) + using (CompoundFile cf = new(version, CFSConfiguration.Default)) { CFStorage st = cf.RootStorage.AddStorage("MyStorage"); CFStream sm = st.AddStream("MyStream");