1 | // Licensed to the Apache Software Foundation (ASF) under one |
2 | // or more contributor license agreements. See the NOTICE file |
3 | // distributed with this work for additional information |
4 | // regarding copyright ownership. The ASF licenses this file |
5 | // to you under the Apache License, Version 2.0 (the |
6 | // "License"); you may not use this file except in compliance |
7 | // with the License. You may obtain a copy of the License at |
8 | // |
9 | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | // |
11 | // Unless required by applicable law or agreed to in writing, |
12 | // software distributed under the License is distributed on an |
13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | // KIND, either express or implied. See the License for the |
15 | // specific language governing permissions and limitations |
16 | // under the License. |
17 | |
18 | #include <locale> |
19 | #include <stdexcept> |
20 | #include <string> |
21 | |
22 | #include <gtest/gtest.h> |
23 | |
24 | #include "arrow/type.h" |
25 | #include "arrow/util/parsing.h" |
26 | |
27 | namespace arrow { |
28 | |
29 | using internal::StringConverter; |
30 | |
31 | template <typename ConverterType, typename C_TYPE> |
32 | void AssertConversion(ConverterType& converter, const std::string& s, C_TYPE expected) { |
33 | typename ConverterType::value_type out; |
34 | ASSERT_TRUE(converter(s.data(), s.length(), &out)) |
35 | << "Conversion failed for '" << s << "' (expected to return " << expected << ")" ; |
36 | ASSERT_EQ(out, expected) << "Conversion failed for '" << s << "'" ; |
37 | } |
38 | |
39 | template <typename ConverterType> |
40 | void AssertConversionFails(ConverterType& converter, const std::string& s) { |
41 | typename ConverterType::value_type out; |
42 | ASSERT_FALSE(converter(s.data(), s.length(), &out)) |
43 | << "Conversion should have failed for '" << s << "' (returned " << out << ")" ; |
44 | } |
45 | |
46 | class LocaleGuard { |
47 | public: |
48 | explicit LocaleGuard(const char* new_locale) : global_locale_(std::locale()) { |
49 | try { |
50 | std::locale::global(std::locale(new_locale)); |
51 | } catch (std::runtime_error&) { |
52 | // Locale unavailable, ignore |
53 | } |
54 | } |
55 | |
56 | ~LocaleGuard() { std::locale::global(global_locale_); } |
57 | |
58 | protected: |
59 | std::locale global_locale_; |
60 | }; |
61 | |
62 | TEST(StringConversion, ToBoolean) { |
63 | StringConverter<BooleanType> converter; |
64 | |
65 | AssertConversion(converter, "true" , true); |
66 | AssertConversion(converter, "tRuE" , true); |
67 | AssertConversion(converter, "FAlse" , false); |
68 | AssertConversion(converter, "false" , false); |
69 | AssertConversion(converter, "1" , true); |
70 | AssertConversion(converter, "0" , false); |
71 | |
72 | AssertConversionFails(converter, "" ); |
73 | } |
74 | |
75 | TEST(StringConversion, ToFloat) { |
76 | StringConverter<FloatType> converter; |
77 | |
78 | AssertConversion(converter, "1.5" , 1.5f); |
79 | AssertConversion(converter, "0" , 0.0f); |
80 | // XXX ASSERT_EQ doesn't distinguish signed zeros |
81 | AssertConversion(converter, "-0.0" , -0.0f); |
82 | AssertConversion(converter, "-1e20" , -1e20f); |
83 | |
84 | AssertConversionFails(converter, "" ); |
85 | AssertConversionFails(converter, "e" ); |
86 | } |
87 | |
88 | TEST(StringConversion, ToDouble) { |
89 | StringConverter<DoubleType> converter; |
90 | |
91 | AssertConversion(converter, "1.5" , 1.5); |
92 | AssertConversion(converter, "0" , 0); |
93 | // XXX ASSERT_EQ doesn't distinguish signed zeros |
94 | AssertConversion(converter, "-0.0" , -0.0); |
95 | AssertConversion(converter, "-1e100" , -1e100); |
96 | |
97 | AssertConversionFails(converter, "" ); |
98 | AssertConversionFails(converter, "e" ); |
99 | } |
100 | |
101 | TEST(StringConversion, ToFloatLocale) { |
102 | // French locale uses the comma as decimal point |
103 | LocaleGuard locale_guard("fr_FR.UTF-8" ); |
104 | |
105 | StringConverter<FloatType> converter; |
106 | AssertConversion(converter, "1.5" , 1.5f); |
107 | } |
108 | |
109 | TEST(StringConversion, ToDoubleLocale) { |
110 | // French locale uses the comma as decimal point |
111 | LocaleGuard locale_guard("fr_FR.UTF-8" ); |
112 | |
113 | StringConverter<DoubleType> converter; |
114 | AssertConversion(converter, "1.5" , 1.5f); |
115 | } |
116 | |
117 | TEST(StringConversion, ToInt8) { |
118 | StringConverter<Int8Type> converter; |
119 | |
120 | AssertConversion(converter, "0" , 0); |
121 | AssertConversion(converter, "127" , 127); |
122 | AssertConversion(converter, "0127" , 127); |
123 | AssertConversion(converter, "-128" , -128); |
124 | AssertConversion(converter, "-00128" , -128); |
125 | |
126 | // Non-representable values |
127 | AssertConversionFails(converter, "128" ); |
128 | AssertConversionFails(converter, "-129" ); |
129 | |
130 | AssertConversionFails(converter, "" ); |
131 | AssertConversionFails(converter, "-" ); |
132 | AssertConversionFails(converter, "0.0" ); |
133 | AssertConversionFails(converter, "e" ); |
134 | } |
135 | |
136 | TEST(StringConversion, ToUInt8) { |
137 | StringConverter<UInt8Type> converter; |
138 | |
139 | AssertConversion(converter, "0" , 0); |
140 | AssertConversion(converter, "26" , 26); |
141 | AssertConversion(converter, "255" , 255); |
142 | AssertConversion(converter, "0255" , 255); |
143 | |
144 | // Non-representable values |
145 | AssertConversionFails(converter, "-1" ); |
146 | AssertConversionFails(converter, "256" ); |
147 | AssertConversionFails(converter, "260" ); |
148 | AssertConversionFails(converter, "1234" ); |
149 | |
150 | AssertConversionFails(converter, "" ); |
151 | AssertConversionFails(converter, "-" ); |
152 | AssertConversionFails(converter, "0.0" ); |
153 | AssertConversionFails(converter, "e" ); |
154 | } |
155 | |
156 | TEST(StringConversion, ToInt16) { |
157 | StringConverter<Int16Type> converter; |
158 | |
159 | AssertConversion(converter, "0" , 0); |
160 | AssertConversion(converter, "32767" , 32767); |
161 | AssertConversion(converter, "032767" , 32767); |
162 | AssertConversion(converter, "-32768" , -32768); |
163 | AssertConversion(converter, "-0032768" , -32768); |
164 | |
165 | // Non-representable values |
166 | AssertConversionFails(converter, "32768" ); |
167 | AssertConversionFails(converter, "-32769" ); |
168 | |
169 | AssertConversionFails(converter, "" ); |
170 | AssertConversionFails(converter, "-" ); |
171 | AssertConversionFails(converter, "0.0" ); |
172 | AssertConversionFails(converter, "e" ); |
173 | } |
174 | |
175 | TEST(StringConversion, ToUInt16) { |
176 | StringConverter<UInt16Type> converter; |
177 | |
178 | AssertConversion(converter, "0" , 0); |
179 | AssertConversion(converter, "6660" , 6660); |
180 | AssertConversion(converter, "65535" , 65535); |
181 | AssertConversion(converter, "065535" , 65535); |
182 | |
183 | // Non-representable values |
184 | AssertConversionFails(converter, "-1" ); |
185 | AssertConversionFails(converter, "65536" ); |
186 | AssertConversionFails(converter, "123456" ); |
187 | |
188 | AssertConversionFails(converter, "" ); |
189 | AssertConversionFails(converter, "-" ); |
190 | AssertConversionFails(converter, "0.0" ); |
191 | AssertConversionFails(converter, "e" ); |
192 | } |
193 | |
194 | TEST(StringConversion, ToInt32) { |
195 | StringConverter<Int32Type> converter; |
196 | |
197 | AssertConversion(converter, "0" , 0); |
198 | AssertConversion(converter, "2147483647" , 2147483647); |
199 | AssertConversion(converter, "02147483647" , 2147483647); |
200 | AssertConversion(converter, "-2147483648" , -2147483648LL); |
201 | AssertConversion(converter, "-002147483648" , -2147483648LL); |
202 | |
203 | // Non-representable values |
204 | AssertConversionFails(converter, "2147483648" ); |
205 | AssertConversionFails(converter, "-2147483649" ); |
206 | |
207 | AssertConversionFails(converter, "" ); |
208 | AssertConversionFails(converter, "-" ); |
209 | AssertConversionFails(converter, "0.0" ); |
210 | AssertConversionFails(converter, "e" ); |
211 | } |
212 | |
213 | TEST(StringConversion, ToUInt32) { |
214 | StringConverter<UInt32Type> converter; |
215 | |
216 | AssertConversion(converter, "0" , 0); |
217 | AssertConversion(converter, "432198765" , 432198765UL); |
218 | AssertConversion(converter, "4294967295" , 4294967295UL); |
219 | AssertConversion(converter, "04294967295" , 4294967295UL); |
220 | |
221 | // Non-representable values |
222 | AssertConversionFails(converter, "-1" ); |
223 | AssertConversionFails(converter, "4294967296" ); |
224 | AssertConversionFails(converter, "12345678901" ); |
225 | |
226 | AssertConversionFails(converter, "" ); |
227 | AssertConversionFails(converter, "-" ); |
228 | AssertConversionFails(converter, "0.0" ); |
229 | AssertConversionFails(converter, "e" ); |
230 | } |
231 | |
232 | TEST(StringConversion, ToInt64) { |
233 | StringConverter<Int64Type> converter; |
234 | |
235 | AssertConversion(converter, "0" , 0); |
236 | AssertConversion(converter, "9223372036854775807" , 9223372036854775807LL); |
237 | AssertConversion(converter, "09223372036854775807" , 9223372036854775807LL); |
238 | AssertConversion(converter, "-9223372036854775808" , -9223372036854775807LL - 1); |
239 | AssertConversion(converter, "-009223372036854775808" , -9223372036854775807LL - 1); |
240 | |
241 | // Non-representable values |
242 | AssertConversionFails(converter, "9223372036854775808" ); |
243 | AssertConversionFails(converter, "-9223372036854775809" ); |
244 | |
245 | AssertConversionFails(converter, "" ); |
246 | AssertConversionFails(converter, "-" ); |
247 | AssertConversionFails(converter, "0.0" ); |
248 | AssertConversionFails(converter, "e" ); |
249 | } |
250 | |
251 | TEST(StringConversion, ToUInt64) { |
252 | StringConverter<UInt64Type> converter; |
253 | |
254 | AssertConversion(converter, "0" , 0); |
255 | AssertConversion(converter, "18446744073709551615" , 18446744073709551615ULL); |
256 | |
257 | // Non-representable values |
258 | AssertConversionFails(converter, "-1" ); |
259 | AssertConversionFails(converter, "18446744073709551616" ); |
260 | |
261 | AssertConversionFails(converter, "" ); |
262 | AssertConversionFails(converter, "-" ); |
263 | AssertConversionFails(converter, "0.0" ); |
264 | AssertConversionFails(converter, "e" ); |
265 | } |
266 | |
267 | TEST(StringConversion, ToTimestamp1) { |
268 | { |
269 | StringConverter<TimestampType> converter(timestamp(TimeUnit::SECOND)); |
270 | |
271 | AssertConversion(converter, "1970-01-01" , 0); |
272 | AssertConversion(converter, "1989-07-14" , 616377600); |
273 | AssertConversion(converter, "2000-02-29" , 951782400); |
274 | AssertConversion(converter, "3989-07-14" , 63730281600LL); |
275 | AssertConversion(converter, "1900-02-28" , -2203977600LL); |
276 | |
277 | AssertConversionFails(converter, "" ); |
278 | AssertConversionFails(converter, "1970" ); |
279 | AssertConversionFails(converter, "19700101" ); |
280 | AssertConversionFails(converter, "1970/01/01" ); |
281 | AssertConversionFails(converter, "1970-01-01 " ); |
282 | AssertConversionFails(converter, "1970-01-01Z" ); |
283 | |
284 | // Invalid dates |
285 | AssertConversionFails(converter, "1970-00-01" ); |
286 | AssertConversionFails(converter, "1970-13-01" ); |
287 | AssertConversionFails(converter, "1970-01-32" ); |
288 | AssertConversionFails(converter, "1970-02-29" ); |
289 | AssertConversionFails(converter, "2100-02-29" ); |
290 | } |
291 | { |
292 | StringConverter<TimestampType> converter(timestamp(TimeUnit::MILLI)); |
293 | |
294 | AssertConversion(converter, "1970-01-01" , 0); |
295 | AssertConversion(converter, "1989-07-14" , 616377600000LL); |
296 | AssertConversion(converter, "3989-07-14" , 63730281600000LL); |
297 | AssertConversion(converter, "1900-02-28" , -2203977600000LL); |
298 | } |
299 | { |
300 | StringConverter<TimestampType> converter(timestamp(TimeUnit::MICRO)); |
301 | |
302 | AssertConversion(converter, "1970-01-01" , 0); |
303 | AssertConversion(converter, "1989-07-14" , 616377600000000LL); |
304 | AssertConversion(converter, "3989-07-14" , 63730281600000000LL); |
305 | AssertConversion(converter, "1900-02-28" , -2203977600000000LL); |
306 | } |
307 | { |
308 | StringConverter<TimestampType> converter(timestamp(TimeUnit::NANO)); |
309 | |
310 | AssertConversion(converter, "1970-01-01" , 0); |
311 | AssertConversion(converter, "1989-07-14" , 616377600000000000LL); |
312 | AssertConversion(converter, "2018-11-13" , 1542067200000000000LL); |
313 | AssertConversion(converter, "1900-02-28" , -2203977600000000000LL); |
314 | } |
315 | } |
316 | |
317 | TEST(StringConversion, ToTimestamp2) { |
318 | { |
319 | StringConverter<TimestampType> converter(timestamp(TimeUnit::SECOND)); |
320 | |
321 | AssertConversion(converter, "1970-01-01 00:00:00" , 0); |
322 | AssertConversion(converter, "2018-11-13 17:11:10" , 1542129070); |
323 | AssertConversion(converter, "2018-11-13T17:11:10" , 1542129070); |
324 | AssertConversion(converter, "2018-11-13 17:11:10Z" , 1542129070); |
325 | AssertConversion(converter, "2018-11-13T17:11:10Z" , 1542129070); |
326 | AssertConversion(converter, "1900-02-28 12:34:56" , -2203932304LL); |
327 | |
328 | // Invalid dates |
329 | AssertConversionFails(converter, "1970-02-29 00:00:00" ); |
330 | AssertConversionFails(converter, "2100-02-29 00:00:00" ); |
331 | // Invalid times |
332 | AssertConversionFails(converter, "1970-01-01 24:00:00" ); |
333 | AssertConversionFails(converter, "1970-01-01 00:60:00" ); |
334 | AssertConversionFails(converter, "1970-01-01 00:00:60" ); |
335 | } |
336 | { |
337 | StringConverter<TimestampType> converter(timestamp(TimeUnit::MILLI)); |
338 | |
339 | AssertConversion(converter, "2018-11-13 17:11:10" , 1542129070000LL); |
340 | AssertConversion(converter, "2018-11-13T17:11:10Z" , 1542129070000LL); |
341 | AssertConversion(converter, "3989-07-14T11:22:33Z" , 63730322553000LL); |
342 | AssertConversion(converter, "1900-02-28 12:34:56" , -2203932304000LL); |
343 | } |
344 | { |
345 | StringConverter<TimestampType> converter(timestamp(TimeUnit::MICRO)); |
346 | |
347 | AssertConversion(converter, "2018-11-13 17:11:10" , 1542129070000000LL); |
348 | AssertConversion(converter, "2018-11-13T17:11:10Z" , 1542129070000000LL); |
349 | AssertConversion(converter, "3989-07-14T11:22:33Z" , 63730322553000000LL); |
350 | AssertConversion(converter, "1900-02-28 12:34:56" , -2203932304000000LL); |
351 | } |
352 | { |
353 | StringConverter<TimestampType> converter(timestamp(TimeUnit::NANO)); |
354 | |
355 | AssertConversion(converter, "2018-11-13 17:11:10" , 1542129070000000000LL); |
356 | AssertConversion(converter, "2018-11-13T17:11:10Z" , 1542129070000000000LL); |
357 | AssertConversion(converter, "1900-02-28 12:34:56" , -2203932304000000000LL); |
358 | } |
359 | } |
360 | |
361 | } // namespace arrow |
362 | |