Add more DateTime ParseExact tests for "r" (dotnet/corefx#30818)
authorStephen Toub <stoub@microsoft.com>
Wed, 4 Jul 2018 00:25:16 +0000 (20:25 -0400)
committerDan Moseley <danmose@microsoft.com>
Wed, 4 Jul 2018 00:25:16 +0000 (17:25 -0700)
Commit migrated from https://github.com/dotnet/corefx/commit/a02e71d274d0f44613d7b3f2f8590f5b17cfaca3

src/libraries/System.Runtime/tests/System/DateTimeTests.cs

index 73b3d79..ba446cc 100644 (file)
@@ -708,20 +708,25 @@ namespace System.Tests
         [MemberData(nameof(StandardFormatSpecifiers))]
         public static void ParseExact_ToStringThenParseExactRoundtrip_Success(string standardFormat)
         {
-            DateTime dt = DateTime.Now;
-            string expected = dt.ToString(standardFormat);
+            var r = new Random(42);
+            for (int i = 0; i < 200; i++) // test with a bunch of random dates
+            {
+                DateTime dt = new DateTime(DateTime.MinValue.Ticks + (long)(r.NextDouble() * (DateTime.MaxValue.Ticks - DateTime.MinValue.Ticks)), DateTimeKind.Unspecified);
+                string expected = dt.ToString(standardFormat);
 
-            Assert.Equal(expected, DateTime.ParseExact(expected, standardFormat, null).ToString(standardFormat));
-            Assert.Equal(expected, DateTime.ParseExact(expected, standardFormat, null, DateTimeStyles.None).ToString(standardFormat));
-            Assert.Equal(expected, DateTime.ParseExact(expected, new[] { standardFormat }, null, DateTimeStyles.None).ToString(standardFormat));
+                Assert.Equal(expected, DateTime.ParseExact(expected, standardFormat, null).ToString(standardFormat));
+                Assert.Equal(expected, DateTime.ParseExact(expected, standardFormat, null, DateTimeStyles.None).ToString(standardFormat));
+                Assert.Equal(expected, DateTime.ParseExact(expected, new[] { standardFormat }, null, DateTimeStyles.None).ToString(standardFormat));
+                Assert.Equal(expected, DateTime.ParseExact(expected, new[] { standardFormat }, null, DateTimeStyles.AllowWhiteSpaces).ToString(standardFormat));
 
-            Assert.True(DateTime.TryParseExact(expected, standardFormat, null, DateTimeStyles.None, out DateTime actual));
-            Assert.Equal(expected, actual.ToString(standardFormat));
-            Assert.True(DateTime.TryParseExact(expected, new[] { standardFormat }, null, DateTimeStyles.None, out actual));
-            Assert.Equal(expected, actual.ToString(standardFormat));
+                Assert.True(DateTime.TryParseExact(expected, standardFormat, null, DateTimeStyles.None, out DateTime actual));
+                Assert.Equal(expected, actual.ToString(standardFormat));
+                Assert.True(DateTime.TryParseExact(expected, new[] { standardFormat }, null, DateTimeStyles.None, out actual));
+                Assert.Equal(expected, actual.ToString(standardFormat));
 
-            // Should also parse with Parse, though may not round trip exactly
-            DateTime.Parse(expected);
+                // Should also parse with Parse, though may not round trip exactly
+                DateTime.Parse(expected);
+            }
         }
 
         public static IEnumerable<object[]> InvalidFormatSpecifierRoundtripPairs()
@@ -753,22 +758,127 @@ namespace System.Tests
             yield return new object[] { new DateTime(1906, 8, 15, 7, 24, 5, 300), "1906-08-15T07:24:05.3000000" };
         }
 
-        [Theory] 
-        [MemberData(nameof(Format_String_TestData_R))]
-        public static void ParseExact_String_String_FormatProvider_DateTimeStyles_R(DateTime dt, string expected)
+        [Theory]
+        [MemberData(nameof(ParseExact_TestData_R))]
+        public static void ParseExact_String_String_FormatProvider_DateTimeStyles_R(DateTime dt, string input)
         {
-            string actual = dt.ToString("r");
-            Assert.Equal(expected, actual);
+            Assert.Equal(dt.ToString("r"), DateTime.ParseExact(input, "r", null).ToString("r"));
+            Assert.Equal(dt.ToString("r"), DateTime.ParseExact(input, "r", null, DateTimeStyles.None).ToString("r"));
 
-            DateTime result = DateTime.ParseExact(actual, "r", null, DateTimeStyles.None);
-            Assert.Equal(expected, result.ToString("r"));
+            const string Whitespace = " \t\r\n ";
+            Assert.Equal(dt.ToString("r"), DateTime.ParseExact(Whitespace + input, "r", null, DateTimeStyles.AllowLeadingWhite).ToString("r"));
+            Assert.Equal(dt.ToString("r"), DateTime.ParseExact(input + Whitespace, "r", null, DateTimeStyles.AllowTrailingWhite).ToString("r"));
+            Assert.Equal(dt.ToString("r"), DateTime.ParseExact(Whitespace + input + Whitespace, "r", null, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite).ToString("r"));
+            Assert.Equal(dt.ToString("r"), DateTime.ParseExact(input.Substring(0, 4) + Whitespace + input.Substring(4), "r", null, DateTimeStyles.AllowInnerWhite).ToString("r"));
         }
 
-        public static IEnumerable<object[]> Format_String_TestData_R()
+        public static IEnumerable<object[]> ParseExact_TestData_R()
         {
-            yield return new object[] { DateTime.MaxValue, "Fri, 31 Dec 9999 23:59:59 GMT" };
-            yield return new object[] { DateTime.MinValue, "Mon, 01 Jan 0001 00:00:00 GMT" };
-            yield return new object[] { new DateTime(1906, 8, 15, 7, 24, 5, 300), "Wed, 15 Aug 1906 07:24:05 GMT" };
+            // Lowest, highest, and random DateTime in lower, upper, and normal casing
+            var pairs = new(DateTime, string)[]
+            {
+                (DateTime.MaxValue, "Fri, 31 Dec 9999 23:59:59"),
+                (DateTime.MinValue, "Mon, 01 Jan 0001 00:00:00"),
+                (new DateTime(1906, 8, 15, 7, 24, 5, 300), "Wed, 15 Aug 1906 07:24:05"),
+            };
+            foreach ((DateTime, string) pair in pairs)
+            {
+                yield return new object[] { pair.Item1, pair.Item2 + " GMT" };
+                yield return new object[] { pair.Item1, pair.Item2.ToLowerInvariant() + " GMT" };
+                yield return new object[] { pair.Item1, pair.Item2.ToUpperInvariant() + " GMT" };
+            }
+
+            // All months
+            DateTime dt = DateTime.UtcNow;
+            for (int i = 0; i < 12; i++)
+            {
+                dt = dt.AddMonths(1);
+                yield return new object[] { dt, dt.ToString("R") };
+            }
+
+            // All days
+            for (int i = 0; i < 7; i++)
+            {
+                dt = dt.AddDays(1);
+                yield return new object[] { dt, dt.ToString("R") };
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(ParseExact_TestData_InvalidData_R))]
+        public static void ParseExact_InvalidData_R(string invalidString)
+        {
+            Assert.Throws<FormatException>(() => DateTime.ParseExact(invalidString, "r", null));
+            Assert.Throws<FormatException>(() => DateTime.ParseExact(invalidString, "r", null, DateTimeStyles.None));
+            Assert.Throws<FormatException>(() => DateTime.ParseExact(invalidString, new string[] { "r" }, null, DateTimeStyles.None));
+        }
+
+        public static IEnumerable<object[]> ParseExact_TestData_InvalidData_R()
+        {
+            yield return new object[] { "Thu, 15 Aug 1906 07:24:05 GMT" }; // invalid day of week
+            yield return new object[] { "Ste, 15 Aug 1906 07:24:05 GMT" }; // invalid day of week
+            yield return new object[] { "We, 15 Aug 1906 07:24:05 GMT" }; // too short day of week
+            yield return new object[] { "Wedn, 15 Aug 1906 07:24:05 GMT" }; // too long day of week
+
+            yield return new object[] { "Wed, 32 Aug 1906 07:24:05 GMT" }; // too large day
+            yield return new object[] { "Wed, -1 Aug 1906 07:24:05 GMT" }; // too small day
+
+            yield return new object[] { "Wed, 15 Au 1906 07:24:05 GMT" }; // too small month
+            yield return new object[] { "Wed, 15 August 1906 07:24:05 GMT" }; // too large month
+
+            yield return new object[] { "Wed, 15 Aug -1 07:24:05 GMT" }; // too small year
+            yield return new object[] { "Wed, 15 Aug 10000 07:24:05 GMT" }; // too large year
+
+            yield return new object[] { "Wed, 15 Aug 1906 24:24:05 GMT" }; // too large hour
+            yield return new object[] { "Wed, 15 Aug 1906 07:60:05 GMT" }; // too large minute
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:60 GMT" }; // too large second
+
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 STE" }; // invalid timezone
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 GM" }; // too short timezone
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 GMTT" }; // too long timezone
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 gmt" }; // wrong casing
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 Z" }; // zulu invalid
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 UTC" }; // UTC invalid
+
+            yield return new object[] { " Wed, 15 Aug 1906 07:24:05 GMT" }; // whitespace before
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 GMT " }; // whitespace after
+            yield return new object[] { "Wed, 15 Aug 1906  07:24:05 GMT" }; // extra whitespace middle
+            yield return new object[] { "Wed, 15 Aug 1906 07: 24:05 GMT" }; // extra whitespace middle
+
+            yield return new object[] { "Wed,\t15 Aug 1906 07:24:05 GMT" }; // wrong whitespace for first space
+            yield return new object[] { "Wed, 15\tAug 1906 07:24:05 GMT" }; // wrong whitespace for second space
+            yield return new object[] { "Wed, 15 Aug\t1906 07:24:05 GMT" }; // wrong whitespace for third space
+            yield return new object[] { "Wed, 15 Aug 1906\t07:24:05 GMT" }; // wrong whitespace for fourth space
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05\tGMT" }; // wrong whitespace for fifth space
+            yield return new object[] { "Wed; 15 Aug 1906 07:24:05 GMT" }; // wrong comma
+            yield return new object[] { "Wed\x642C 15 Aug 1906 07:24:05 GMT" }; // wrong comma
+            yield return new object[] { "Wed, 15 Aug 1906 07;24:05 GMT" }; // wrong first colon
+            yield return new object[] { "Wed, 15 Aug 1906 07:24;05 GMT" }; // wrong second colon
+
+            yield return new object[] { "\x2057ed, 15 Aug 1906 07:24:05 GMT" }; // invalid characters to validate ASCII checks on day of week
+            yield return new object[] { "W\x5765d, 15 Aug 1906 07:24:05 GMT" }; // invalid characters to validate ASCII checks on day of week
+            yield return new object[] { "We\x6564, 15 Aug 1906 07:24:05 GMT" }; // invalid characters to validate ASCII checks on day of week
+
+            yield return new object[] { "Wed, 15 \x2041ug 1906 07:24:05 GMT" }; // invalid characters to validate ASCII checks on month
+            yield return new object[] { "Wed, 15 A\x4175g 1906 07:24:05 GMT" }; // invalid characters to validate ASCII checks on month
+            yield return new object[] { "Wed, 15 Au\x7567 1906 07:24:05 GMT" }; // invalid characters to validate ASCII checks on month
+
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 \x2047MT" }; // invalid characters to validate ASCII checks on GMT
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 G\x474DT" }; // invalid characters to validate ASCII checks on GMT
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:05 GM\x4D54" }; // invalid characters to validate ASCII checks on GMT
+
+            yield return new object[] { "Wed, A5 Aug 1906 07:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 1A Aug 1906 07:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug A906 07:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 1A06 07:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 19A6 07:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 190A 07:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 1906 A7:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 1906 0A:24:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 1906 07:A4:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 1906 07:2A:05 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:A5 GMT" }; // invalid digits
+            yield return new object[] { "Wed, 15 Aug 1906 07:24:0A GMT" }; // invalid digits
         }
 
         [Fact]