Reduce the amount of memory allocated by System.IO.Tests (#66387)
authorAdam Sitnik <adam.sitnik@gmail.com>
Wed, 9 Mar 2022 14:46:58 +0000 (15:46 +0100)
committerGitHub <noreply@github.com>
Wed, 9 Mar 2022 14:46:58 +0000 (15:46 +0100)
* use 2.1 GB instead 6.5 GB to verify lack of Int32 overflow:

reduce the size
use same memory for input and output

* use SkipTestException to indicate that the test has been skipped

* don't run WriteChars_VeryLargeArray_DoesNotOverflow with other tests in parallel, as it can cause OOM

* move the test to Outerloop as suggested by Stephen

src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.EncodingTests.cs
src/libraries/System.IO/tests/System.IO.Tests.csproj

index 6a41e67..632aac0 100644 (file)
@@ -5,10 +5,14 @@ using System.Numerics;
 using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Text;
+using Microsoft.DotNet.XUnitExtensions;
 using Xunit;
 
 namespace System.IO.Tests
 {
+    // WriteChars_VeryLargeArray_DoesNotOverflow allocates a lot of memory and can cause OOM,
+    // it should not be executed in parallel with other tests
+    [Collection(nameof(DisableParallelization))]
     public class BinaryWriter_EncodingTests
     {
         [Fact]
@@ -187,40 +191,40 @@ namespace System.IO.Tests
             Assert.Equal(expectedBytes, stream.GetBuffer()[Get7BitEncodedIntByteLength((uint)expectedBytes.Length)..(int)stream.Length]);
         }
 
+        [OuterLoop("Allocates a lot of memory")]
         [Fact]
         [SkipOnPlatform(TestPlatforms.Android, "OOM on Android could be uncatchable & kill the test runner")]
         public unsafe void WriteChars_VeryLargeArray_DoesNotOverflow()
         {
-            const nuint INPUT_LEN_IN_CHARS = 1_500_000_000;
-            const nuint OUTPUT_LEN_IN_BYTES = 3_500_000_000; // overallocate
+            const nuint INT32_OVERFLOW_SIZE = (nuint)int.MaxValue + 3;
 
-            SafeBuffer unmanagedInputBuffer = null;
-            SafeBuffer unmanagedOutputBufer = null;
+            SafeBuffer unmanagedBuffer = null;
             try
             {
                 try
                 {
-                    unmanagedInputBuffer = SafeBufferUtil.CreateSafeBuffer(INPUT_LEN_IN_CHARS * sizeof(char));
-                    unmanagedOutputBufer = SafeBufferUtil.CreateSafeBuffer(OUTPUT_LEN_IN_BYTES * sizeof(byte));
+                    unmanagedBuffer = SafeBufferUtil.CreateSafeBuffer(INT32_OVERFLOW_SIZE * sizeof(byte));
                 }
                 catch (OutOfMemoryException)
                 {
-                    return; // skip test in low-mem conditions
+                    throw new SkipTestException($"Unable to execute {nameof(WriteChars_VeryLargeArray_DoesNotOverflow)} due to OOM"); // skip test in low-mem conditions
                 }
 
-                Span<char> inputSpan = new Span<char>((char*)unmanagedInputBuffer.DangerousGetHandle(), (int)INPUT_LEN_IN_CHARS);
-                inputSpan.Fill('\u0224'); // LATIN CAPITAL LETTER Z WITH HOOK
-                Stream outStream = new UnmanagedMemoryStream(unmanagedOutputBufer, 0, (long)unmanagedOutputBufer.ByteLength, FileAccess.ReadWrite);
+                Assert.True((long)unmanagedBuffer.ByteLength > int.MaxValue);
+
+                // reuse same memory for input and output to avoid allocating more memory and OOMs
+                Span<char> span = new Span<char>((char*)unmanagedBuffer.DangerousGetHandle(), (int)(INT32_OVERFLOW_SIZE / sizeof(char)));
+                span.Fill('\u0224'); // LATIN CAPITAL LETTER Z WITH HOOK
+                Stream outStream = new UnmanagedMemoryStream(unmanagedBuffer, 0, (long)unmanagedBuffer.ByteLength, FileAccess.ReadWrite);
                 BinaryWriter writer = new BinaryWriter(outStream);
 
-                writer.Write(inputSpan); // will write 3 billion bytes to the output
+                writer.Write(span); // will write slightly more than int.MaxValue bytes to the output
 
-                Assert.Equal(3_000_000_000, outStream.Position);
+                Assert.Equal((long)INT32_OVERFLOW_SIZE, outStream.Position);
             }
             finally
             {
-                unmanagedInputBuffer?.Dispose();
-                unmanagedOutputBufer?.Dispose();
+                unmanagedBuffer?.Dispose();
             }
         }
 
index aa8928e..9014ff2 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <RootNamespace>System.IO</RootNamespace>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -44,6 +44,7 @@
     <Compile Include="Stream\Stream.ValidateArguments.cs" />
     <Compile Include="StringReader\StringReader.CtorTests.cs" />
     <Compile Include="StringWriter\StringWriterTests.cs" />
+    <Compile Include="$(CommonTestPath)TestUtilities\System\DisableParallelization.cs" Link="Common\TestUtilities\System\DisableParallelization.cs" />
     <Compile Include="$(CommonTestPath)System\Buffers\NativeMemoryManager.cs" Link="Common\System\Buffers\NativeMemoryManager.cs" />
     <Compile Include="$(CommonTestPath)System\IO\CallTrackingStream.cs" Link="Common\System\IO\CallTrackingStream.cs" />
     <Compile Include="$(CommonTestPath)System\IO\DelegateStream.cs" Link="Common\System\IO\DelegateStream.cs" />