1 | #pragma GCC diagnostic ignored "-Wmissing-declarations" |
2 | #include <gtest/gtest.h> |
3 | |
4 | #include <IO/ReadHelpers.h> |
5 | #include <IO/WriteHelpers.h> |
6 | #include <IO/parseDateTimeBestEffort.h> |
7 | |
8 | #include <Common/PODArray.h> |
9 | |
10 | /** Test formatting and parsing predefined DateTime64 values to/from string |
11 | */ |
12 | |
13 | using namespace DB; |
14 | |
15 | struct DateTime64StringsTestParam |
16 | { |
17 | const std::string_view ; |
18 | const std::string_view string; |
19 | DateTime64 dt64; |
20 | UInt32 scale; |
21 | const DateLUTImpl & timezone = DateLUT::instance(); |
22 | }; |
23 | |
24 | static std::ostream & operator << (std::ostream & ostr, const DateTime64StringsTestParam & param) |
25 | { |
26 | return ostr << param.comment; |
27 | } |
28 | |
29 | class DateTime64StringsTest : public ::testing::TestWithParam<DateTime64StringsTestParam> {}; |
30 | class DateTime64StringParseTest : public DateTime64StringsTest{}; |
31 | class DateTime64StringParseBestEffortTest : public DateTime64StringsTest{}; |
32 | class DateTime64StringWriteTest : public DateTime64StringsTest {}; |
33 | |
34 | |
35 | TEST_P(DateTime64StringParseTest, readDateTime64Text) |
36 | { |
37 | const auto & param = GetParam(); |
38 | ReadBufferFromMemory read_buffer(param.string.data(), param.string.size()); |
39 | |
40 | DateTime64 actual; |
41 | EXPECT_TRUE(tryReadDateTime64Text(actual, param.scale, read_buffer)); |
42 | |
43 | EXPECT_EQ(param.dt64, actual); |
44 | } |
45 | |
46 | TEST_P(DateTime64StringParseTest, parseDateTime64BestEffort) |
47 | { |
48 | const auto & param = GetParam(); |
49 | ReadBufferFromMemory read_buffer(param.string.data(), param.string.size()); |
50 | |
51 | DateTime64 actual; |
52 | EXPECT_TRUE(tryParseDateTime64BestEffort(actual, param.scale, read_buffer, param.timezone, DateLUT::instance("UTC" ))); |
53 | |
54 | EXPECT_EQ(param.dt64, actual); |
55 | } |
56 | |
57 | TEST_P(DateTime64StringWriteTest, WriteText) |
58 | { |
59 | const auto & param = GetParam(); |
60 | |
61 | PaddedPODArray<char> actual_string(param.string.size() * 2, '\0'); // TODO: detect overflows |
62 | |
63 | WriteBuffer write_buffer(actual_string.data(), actual_string.size()); |
64 | EXPECT_NO_THROW(writeDateTimeText(param.dt64, param.scale, write_buffer)); |
65 | |
66 | EXPECT_STREQ(param.string.data(), actual_string.data()); |
67 | } |
68 | |
69 | TEST_P(DateTime64StringParseBestEffortTest, parse) |
70 | { |
71 | const auto & param = GetParam(); |
72 | ReadBufferFromMemory read_buffer(param.string.data(), param.string.size()); |
73 | |
74 | DateTime64 actual; |
75 | EXPECT_TRUE(tryParseDateTime64BestEffort(actual, param.scale, read_buffer, param.timezone, DateLUT::instance("UTC" ))); |
76 | |
77 | EXPECT_EQ(param.dt64, actual); |
78 | } |
79 | |
80 | |
81 | // YYYY-MM-DD HH:MM:SS.NNNNNNNNN |
82 | INSTANTIATE_TEST_CASE_P(Basic, |
83 | DateTime64StringParseTest, |
84 | ::testing::ValuesIn(std::initializer_list<DateTime64StringsTestParam>{ |
85 | { |
86 | "When subsecond part is missing from string it is set to zero." , |
87 | "2019-09-16 19:20:17" , |
88 | 1568650817'000, |
89 | 3 |
90 | }, |
91 | { |
92 | "When subsecond part is present in string, but it is zero, it is set to zero." , |
93 | "2019-09-16 19:20:17.0" , |
94 | 1568650817'000, |
95 | 3 |
96 | }, |
97 | { |
98 | "When scale is 0, subsecond part is not set." , |
99 | "2019-09-16 19:20:17" , |
100 | 1568650817ULL, |
101 | 0 |
102 | }, |
103 | { |
104 | "When scale is 0, subsecond part is 0 despite beeing present in string." , |
105 | "2019-09-16 19:20:17.123" , |
106 | 1568650817ULL, |
107 | 0 |
108 | }, |
109 | { |
110 | "When subsecond part is present in string, it is set correctly to DateTime64 value of scale 3." , |
111 | "2019-09-16 19:20:17.123" , |
112 | 1568650817'123, |
113 | 3 |
114 | }, |
115 | { |
116 | "When subsecond part is present in string (and begins with 0), it is set correctly to DateTime64 value of scale 3." , |
117 | "2019-09-16 19:20:17.012" , |
118 | 1568650817'012, |
119 | 3 |
120 | }, |
121 | { |
122 | "When subsecond part scale is smaller than DateTime64 scale, subsecond part is properly adjusted (as if padded from right with zeroes)." , |
123 | "2019-09-16 19:20:17.123" , |
124 | 1568650817'12300ULL, |
125 | 5 |
126 | }, |
127 | { |
128 | "When subsecond part scale is larger than DateTime64 scale, subsecond part is truncated." , |
129 | "2019-09-16 19:20:17.123" , |
130 | 1568650817'1ULL, |
131 | 1 |
132 | } |
133 | }), |
134 | ); |
135 | |
136 | INSTANTIATE_TEST_CASE_P(BestEffort, |
137 | DateTime64StringParseBestEffortTest, |
138 | ::testing::ValuesIn(std::initializer_list<DateTime64StringsTestParam>{ |
139 | { |
140 | "When subsecond part is unreasonably large, it fals to parse" , |
141 | "2019-09-16 19:20:17.12345678910111213141516171819202122233435363738393031323334353637383940414243444546474849505152535455565758596061626364" , |
142 | 1568650817'123456ULL, |
143 | 6 |
144 | } |
145 | }), |
146 | ); |
147 | |
148 | |
149 | // TODO: add negative test cases for invalid strings, verifying that error is reported properly |
150 | |
151 | INSTANTIATE_TEST_CASE_P(Basic, |
152 | DateTime64StringWriteTest, |
153 | ::testing::ValuesIn(std::initializer_list<DateTime64StringsTestParam>{ |
154 | { |
155 | "non-zero subsecond part on DateTime64 with scale of 3" , |
156 | "2019-09-16 19:20:17.123" , |
157 | 1568650817'123, |
158 | 3 |
159 | }, |
160 | { |
161 | "non-zero subsecond part on DateTime64 with scale of 5" , |
162 | "2019-09-16 19:20:17.12345" , |
163 | 1568650817'12345ULL, |
164 | 5 |
165 | }, |
166 | { |
167 | "Zero subsecond part is written to string" , |
168 | "2019-09-16 19:20:17.000" , |
169 | 1568650817'000ULL, |
170 | 3 |
171 | }, |
172 | { |
173 | "When scale is 0, subsecond part (and separtor) is missing from string" , |
174 | "2019-09-16 19:20:17" , |
175 | 1568650817ULL, |
176 | 0 |
177 | }, |
178 | { |
179 | "Subsecond part with leading zeroes is written to string correctly" , |
180 | "2019-09-16 19:20:17.001" , |
181 | 1568650817'001ULL, |
182 | 3 |
183 | } |
184 | }), |
185 | ); |
186 | |
187 | |