Beispiel #1
0
        private static void ValidateFormatter <T>(FormatterTestData <T> testData)
        {
            // It's useful to do the over-sized buffer test first. If the formatter api has a bug that generates output text that's longer than
            // the expected string, this test will fail with an "Expected/Actual" diff while the exact-sized buffer test would say "Oops. The api returned 'false' and no output"."
            FormatterTestData <T> oversizedTestData = new FormatterTestData <T>(testData.Value, testData.Format, testData.Precision, testData.ExpectedOutput)
            {
                PassedInBufferLength = testData.ExpectedOutput.Length + 200,
            };

            ValidateFormatterHelper(oversizedTestData);

            // Now run the test with a buffer that's exactly the right size.
            ValidateFormatterHelper(testData);

            // Now run the test with buffers that are too short.
            for (int truncatedBufferLength = 0; truncatedBufferLength < testData.ExpectedOutput.Length; truncatedBufferLength++)
            {
                FormatterTestData <T> newTestData = new FormatterTestData <T>(testData.Value, testData.Format, testData.Precision, testData.ExpectedOutput)
                {
                    PassedInBufferLength = truncatedBufferLength,
                };

                ValidateFormatterHelper(newTestData);
            }
        }
Beispiel #2
0
 private static void CanaryCheck <T>(FormatterTestData <T> testData, ReadOnlySpan <byte> canaries, string pastWhat)
 {
     for (int i = 0; i < canaries.Length; i++)
     {
         if (canaries[i] != 0xcc)
         {
             throw new TestException($"BUFFER OVERRUN! This format attempt ({testData}) wrote past {pastWhat}!");
         }
     }
 }
Beispiel #3
0
        private static void ValidateFormatterHelper <T>(FormatterTestData <T> testData)
        {
            int    spanLength           = testData.PassedInBufferLength;
            string expectedOutput       = testData.ExpectedOutput;
            int    expectedOutputLength = expectedOutput.Length;

            bool           expectedSuccess = spanLength >= expectedOutputLength;
            StandardFormat format          = new StandardFormat(testData.FormatSymbol, testData.Precision);
            T value = testData.Value;

            const int CanarySize = 100;

            byte[]      backingArray = new byte[spanLength + CanarySize];
            Span <byte> span         = new Span <byte>(backingArray, 0, spanLength);

            CanaryFill(backingArray);

            bool success = TryFormatUtf8 <T>(value, span, out int bytesWritten, format);

            if (success)
            {
                if (!expectedSuccess)
                {
                    if (bytesWritten >= 0 && bytesWritten <= span.Length)
                    {
                        string unexpectedOutput = span.Slice(0, bytesWritten).ToUtf16String();
                        throw new TestException($"This format attempt ({testData}) was expected to fail due to an undersized buffer. Instead, it generated \"{unexpectedOutput}\"");
                    }
                    else
                    {
                        throw new TestException($"This format attempt ({testData}) was expected to fail due to an undersized buffer. Instead, it succeeded but set 'bytesWritten' to an out of range value: {bytesWritten}");
                    }
                }

                if (bytesWritten < 0 || bytesWritten > span.Length)
                {
                    throw new TestException($"This format attempt ({testData}) succeeded as expected but set 'bytesWritten' to an out of range value: {bytesWritten}");
                }

                string actual   = span.Slice(0, bytesWritten).ToUtf16String();
                string expected = testData.ExpectedOutput;
                if (actual != expected)
                {
                    // We'll allocate (and not throw) the TestException (so that someone with a breakpoint inside TestException's constructor will break as desired) but
                    // use Assert.Equals() to trigger the actual failure so we get XUnit's more useful comparison output into the log.
                    new TestException($"This format attempt ({testData}) succeeded as expected but generated the wrong text:\n  Expected: \"{expected}\"\n  Actual:   \"{actual}\"\n");
                    Assert.Equal(expected, actual);
                }

                // If the api scribbled into the Span past the reported 'bytesWritten' (but still within the bounds of the Span itself), this should raise eyebrows at least.
                CanaryCheck(testData, new ReadOnlySpan <byte>(backingArray, span.Length, CanarySize), "the end of the Span itself");

                // If the api scribbled beyond the range of the Span itself, it's panic time.
                CanaryCheck(testData, new ReadOnlySpan <byte>(backingArray, expectedOutputLength, span.Length - expectedOutputLength), "'bytesWritten'");
            }
            else
            {
                if (expectedSuccess)
                {
                    throw new TestException($"This format attempt ({testData}) was expected to succeed. Instead, it failed.");
                }

                if (bytesWritten != 0)
                {
                    throw new TestException($"This format attempt ({testData}) failed as expected but did not set `bytesWritten` to 0");
                }

                // Note: It's not guaranteed that partial (and useless) results will be written to the buffer despite
                // byteWritten being 0. (In particular, ulong values that are larger than long.MaxValue using the "N" format.)
                // We can only check the canary portion for overwrites.
                CanaryCheck(testData, new ReadOnlySpan <byte>(backingArray, span.Length, CanarySize), "the end of the Span itself");
            }
        }
Beispiel #4
0
 public static void TestFormatterSingle(FormatterTestData <float> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #5
0
 public static void TestFormatterGuid(FormatterTestData <Guid> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #6
0
 public static void TestFormatterDecimal(FormatterTestData <decimal> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #7
0
 public static void TestFormatterDouble(FormatterTestData <double> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #8
0
 public static void TestFormatterUInt32(FormatterTestData <uint> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #9
0
 public static void TestFormatterUInt64(FormatterTestData <ulong> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #10
0
 public static void TestFormatterUInt16(FormatterTestData <ushort> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #11
0
 public static void TestFormatterByte(FormatterTestData <byte> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #12
0
 public static void TestFormatterTimeSpan(FormatterTestData <TimeSpan> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #13
0
 public static void TestFormatterBoolean(FormatterTestData <bool> testData)
 {
     ValidateFormatter(testData);
 }
Beispiel #14
0
 public static void TestFormatterDateTimeOffset(FormatterTestData <DateTimeOffset> testData)
 {
     ValidateFormatter(testData);
 }