OILS / mycpp / gc_builtins_test.cc View on Github | oils.pub

460 lines, 287 significant
1#include "mycpp/gc_builtins.h"
2
3#include <assert.h>
4#include <inttypes.h> // PRId64
5#include <limits.h> // INT_MAX
6#include <math.h> // INFINITY
7#include <stdarg.h> // va_list, etc.
8#include <stdio.h> // vprintf
9
10#include "mycpp/gc_dict.h"
11#include "mycpp/gc_list.h"
12#include "mycpp/gc_tuple.h"
13#include "mycpp/test_common.h"
14#include "vendor/greatest.h"
15
16GLOBAL_STR(kStrFood, "food");
17GLOBAL_STR(kWithNull, "foo\0bar");
18GLOBAL_STR(kSpace, " ");
19
20TEST print_test() {
21 print(kStrFood);
22 print(kWithNull); // truncates
23
24 PASS();
25}
26
27TEST repr_test() {
28 print(repr(StrFromC("")));
29 print(repr(StrFromC("hi\n")));
30
31 // Hm we're not printing \y00 here, could do that I suppose.
32 // This function is used for error messages.
33 print(repr(StrFromC("\x02 foo bar \xff \xfe \t")));
34
35 // Uses double quotes
36 print(repr(StrFromC("this isn't cool")));
37
38 PASS();
39}
40
41TEST bool_test() {
42 ASSERT_EQ(false, to_bool(kEmptyString));
43 ASSERT_EQ(true, to_bool(StrFromC("a")));
44
45 ASSERT_EQ(true, to_bool(42));
46 ASSERT_EQ(true, to_bool(1));
47 ASSERT_EQ(false, to_bool(0));
48 ASSERT_EQ(true, to_bool(-42));
49
50 PASS();
51}
52
53TEST int_test() {
54 ASSERT_EQ(1, to_int(true));
55 ASSERT_EQ(0, to_int(false));
56
57 PASS();
58}
59
60TEST float_test() {
61 ASSERT_EQ(0.0f, to_float(0));
62 ASSERT_EQ(1.0f, to_float(1));
63 ASSERT_EQ(42.0f, to_float(42));
64 ASSERT_EQ(-42.0f, to_float(-42));
65
66 ASSERT_EQ(0.0f, to_float(StrFromC("0.0")));
67
68 ASSERT_EQ(0.25f, to_float(StrFromC("0.25")));
69 ASSERT_EQ(0.5f, to_float(StrFromC("0.5")));
70 ASSERT_EQ(99.0f, to_float(StrFromC("99")));
71
72 ASSERT_EQ(-0.25f, to_float(StrFromC("-0.25")));
73 ASSERT_EQ(-0.5f, to_float(StrFromC("-0.5")));
74 ASSERT_EQ(-99.0f, to_float(StrFromC("-99")));
75
76 // Now with spaces
77 ASSERT_EQ(-99.0f, to_float(StrFromC("-99 ")));
78 ASSERT_EQ(-99.0f, to_float(StrFromC(" -99")));
79
80 // Note: strtod supports hexadecimal and NaN
81
82 bool caught;
83
84 caught = false;
85 try {
86 (void)to_float(kEmptyString);
87 } catch (ValueError* e) {
88 caught = true;
89 }
90 ASSERT(caught);
91
92 caught = false;
93 try {
94 (void)to_float(StrFromC("x"));
95 } catch (ValueError* e) {
96 caught = true;
97 }
98 ASSERT(caught);
99
100 // Bug fixed in sleep 5s - trailing 's' is not allowed
101 caught = false;
102 try {
103 (void)to_float(StrFromC("5s"));
104 } catch (ValueError* e) {
105 caught = true;
106 }
107 ASSERT(caught);
108
109 BigStr* huge = str_repeat(StrFromC("123456789"), 100);
110 double d = to_float(huge);
111 ASSERT_EQ(INFINITY, d);
112
113 double d2 = to_float(StrFromC("-1e309"));
114 ASSERT_EQ(-INFINITY, d2);
115
116 BigStr* zeros = str_repeat(StrFromC("00000000"), 100);
117 BigStr* tiny = str_concat3(StrFromC("0."), zeros, StrFromC("1"));
118 double d3 = to_float(tiny);
119 log("d3 = %.17g", d3);
120 ASSERT_EQ(0.0f, d3);
121
122 BigStr* neg_tiny = str_concat3(StrFromC("-0."), zeros, StrFromC("1"));
123 double d4 = to_float(neg_tiny);
124 log("d4 = %.17g", d4);
125 ASSERT_EQ(-0.0f, d4);
126
127 PASS();
128}
129
130// Wrapper for testing
131bool _StringToInt64(BigStr* s, int64_t* result, int base) {
132 return StringToInt64(s->data_, len(s), base, result);
133}
134
135TEST StringToInteger_test() {
136 int64_t i;
137 bool ok;
138
139 // Empirically this is 4 4 8 on 32-bit and 4 8 8 on 64-bit
140 // We want the bigger numbers
141#if 0
142 log("sizeof(int) = %d", sizeof(int));
143 log("sizeof(long) = %ld", sizeof(long));
144 log("sizeof(long long) = %ld", sizeof(long long));
145 log("");
146 log("LONG_MAX = %ld", LONG_MAX);
147 log("LLONG_MAX = %lld", LLONG_MAX);
148#endif
149
150 ok = _StringToInt64(StrFromC("345"), &i, 10);
151 ASSERT(ok);
152 ASSERT_EQ_FMT((int64_t)345, i, "%" PRId64);
153
154 // Hack to test slicing. Truncated "345" at "34".
155 ok = _StringToInt64(StrFromC("345", 2), &i, 10);
156 ASSERT(ok);
157 ASSERT_EQ_FMT((int64_t)34, i, "%" PRId64);
158
159 ok = _StringToInt64(StrFromC("12345678909"), &i, 10);
160 ASSERT(ok);
161 ASSERT_EQ_FMT((int64_t)12345678909, i, "%" PRId64);
162
163 // overflow
164 ok = _StringToInt64(StrFromC("12345678901234567890"), &i, 10);
165 ASSERT(!ok);
166
167 // underflow
168 ok = _StringToInt64(StrFromC("-12345678901234567890"), &i, 10);
169 ASSERT(!ok);
170
171 // negative
172 ok = _StringToInt64(StrFromC("-123"), &i, 10);
173 ASSERT(ok);
174 ASSERT(i == -123);
175
176 // Leading space is OK!
177 ok = _StringToInt64(StrFromC("\n\t -123"), &i, 10);
178 ASSERT(ok);
179 ASSERT(i == -123);
180
181 // Trailing space is OK!
182 ok = _StringToInt64(StrFromC(" -123 \t\n"), &i, 10);
183 ASSERT(ok);
184 ASSERT(i == -123);
185
186 // \v is not space
187 ok = _StringToInt64(StrFromC(" -123 \v"), &i, 10);
188 ASSERT(!ok);
189
190 // Empty string isn't an integer
191 ok = _StringToInt64(StrFromC(""), &i, 10);
192 ASSERT(!ok);
193
194 ok = _StringToInt64(StrFromC("xx"), &i, 10);
195 ASSERT(!ok);
196
197 // Trailing garbage
198 ok = _StringToInt64(StrFromC("42a"), &i, 10);
199 ASSERT(!ok);
200
201 PASS();
202}
203
204TEST str_to_int_test() {
205 int i;
206
207 i = to_int(StrFromC("ff"), 16);
208 ASSERT(i == 255);
209
210 // strtol allows 0x prefix
211 i = to_int(StrFromC("0xff"), 16);
212 ASSERT(i == 255);
213
214 // TODO: test ValueError here
215 // i = to_int(StrFromC("0xz"), 16);
216
217 i = to_int(StrFromC("0"), 16);
218 ASSERT(i == 0);
219
220 i = to_int(StrFromC("077"), 8);
221 ASSERT_EQ_FMT(63, i, "%d");
222
223 bool caught = false;
224 try {
225 i = to_int(StrFromC("zzz"));
226 } catch (ValueError* e) {
227 caught = true;
228 }
229 ASSERT(caught);
230
231 PASS();
232}
233
234TEST int_to_str_test() {
235 BigStr* int_str;
236 int_str = str(INT_MAX);
237 ASSERT(str_equals0("2147483647", int_str));
238
239 int_str = str(-INT_MAX);
240 ASSERT(str_equals0("-2147483647", int_str));
241
242 int int_min = INT_MIN;
243 int_str = str(int_min);
244 ASSERT(str_equals0("-2147483648", int_str));
245
246 // Wraps with - sign. Is this well-defined behavior?
247 int_str = str(1 << 31);
248 log("i = %s", int_str->data_);
249
250 PASS();
251}
252
253TEST float_to_str_test() {
254 BigStr* s = str(3.0);
255 ASSERT(str_equals0("3.0", s));
256 log("s = %s", s->data_);
257
258 double f = 3.5;
259 s = str(f);
260 ASSERT(str_equals0("3.5", s));
261 log("s = %s", s->data_);
262
263 PASS();
264}
265
266TEST comparators_test() {
267 log("maybe_str_equals()");
268 ASSERT(maybe_str_equals(kEmptyString, kEmptyString));
269 ASSERT(!maybe_str_equals(kEmptyString, nullptr));
270 ASSERT(maybe_str_equals(nullptr, nullptr));
271
272 // Compare by VALUE, not by pointer.
273 // TODO: check for this bug elsewhere
274 log("Tuple2<BigStr*, int> items_equal()");
275 auto t1 = Alloc<Tuple2<BigStr*, int>>(StrFromC("42"), 42);
276 auto t2 = Alloc<Tuple2<BigStr*, int>>(StrFromC("42"), 42);
277 auto t3 = Alloc<Tuple2<BigStr*, int>>(StrFromC("99"), 99);
278
279 ASSERT(items_equal(t1, t2));
280 ASSERT(!items_equal(t2, t3));
281
282 PASS();
283}
284
285TEST container_test() {
286 //
287 // User-defined class
288 //
289
290 // We used Dict<Token*, V> to get rid of the span_id, e.g. for --tool ysh-ify
291 auto* dp = Alloc<Dict<Point*, BigStr*>>();
292 for (int i = 0; i < 32; ++i) {
293 Point* p2 = Alloc<Point>(42, 43);
294 dp->set(p2, kEmptyString);
295 }
296 ASSERT_EQ_FMT(32, len(dp), "%d");
297
298 // For now, we're not allowed to compare lists by pointers.
299#if 0
300 auto* lp = Alloc<List<Point*>>();
301 lp->append(Alloc<Point>(0, 1));
302 lp->append(Alloc<Point>(2, 3));
303 ASSERT(!list_contains(lp, Alloc<Point>(4, 5)));
304#endif
305
306 //
307 // int
308 //
309 auto* di = Alloc<Dict<int, BigStr*>>();
310 for (int i = 0; i < 32; ++i) {
311 int p2 = 1 << i;
312 di->set(p2, kEmptyString);
313 }
314 ASSERT_EQ_FMT(32, len(di), "%d");
315
316 auto* li = Alloc<List<int>>();
317 li->append(1 << 30);
318 li->append(1 << 31);
319 ASSERT(!list_contains(li, 0));
320
321 //
322 // mops::BigInt
323 //
324
325 // Failed before we had keys_equal() for mops::BigInt
326 auto* d = Alloc<Dict<mops::BigInt, BigStr*>>();
327 for (int i = 0; i < 64; ++i) {
328 mops::BigInt p2 = mops::BigInt{1} << i;
329 d->set(p2, kEmptyString);
330 }
331 ASSERT_EQ_FMT(64, len(d), "%d");
332
333 // Failed before we had items_equal() for mops::BigInt
334 auto* lb = Alloc<List<mops::BigInt>>();
335 lb->append(mops::BigInt{1} << 32);
336 lb->append(mops::BigInt{1} << 33);
337 ASSERT(!list_contains(lb, mops::BigInt{0}));
338
339 PASS();
340}
341
342TEST exceptions_test() {
343 auto v1 = Alloc<ValueError>();
344 ASSERT_EQ(HeapTag::FixedSize, ObjHeader::FromObject(v1)->heap_tag);
345
346 auto v2 = Alloc<ValueError>(kEmptyString);
347 ASSERT_EQ(HeapTag::FixedSize, ObjHeader::FromObject(v2)->heap_tag);
348
349 IndexError* other;
350 bool caught = false;
351 try {
352 throw Alloc<IndexError>();
353 } catch (IndexError* e) {
354 log("e %p", e);
355 other = e;
356 caught = true;
357 }
358
359 log("other %p", other);
360 ASSERT(caught);
361
362 caught = false;
363 try {
364 throw Alloc<OSError>(99);
365 } catch (IOError_OSError* e) {
366 caught = true;
367 }
368 ASSERT(caught);
369
370 // TODO: Make this work with return value rooting
371 RuntimeError* r = nullptr;
372 BigStr* message = nullptr;
373 StackRoots _roots2({&r, &message});
374 message = StrFromC("libc::regex_match");
375
376 caught = false;
377 try {
378 r = Alloc<RuntimeError>(message);
379 throw r;
380
381 } catch (RuntimeError* e) {
382 caught = true;
383
384 log("RuntimeError %s", e->message->data());
385 }
386 ASSERT(caught);
387
388 auto u = Alloc<UnicodeError>(StrFromC("libc"));
389 (void)u;
390
391 auto i = Alloc<IOError>(0);
392 (void)i;
393
394 PASS();
395}
396
397TEST hash_str_test() {
398 // two strings known not to collide ahead of time
399 BigStr* a = StrFromC("foobarbaz");
400 BigStr* b = StrFromC("123456789");
401 ASSERT(hash(a) != hash(b));
402
403 PASS();
404}
405
406TEST intern_test() {
407 BigStr* s = StrFromC("foo");
408 BigStr* t = intern(s);
409
410 ASSERT(str_equals(s, t));
411
412 PASS();
413}
414
415TEST max_test() {
416 ASSERT(max(-1, 0) == 0);
417 ASSERT(max(0, -1) == max(-1, 0));
418 ASSERT(max(42, 13) == 42);
419
420 auto* ints = NewList<int>(std::initializer_list<int>{13, 0, 42, -1});
421 ASSERT(max(ints) == 42);
422
423 PASS();
424}
425
426GREATEST_MAIN_DEFS();
427
428int main(int argc, char** argv) {
429 gHeap.Init();
430
431 GREATEST_MAIN_BEGIN();
432
433 RUN_TEST(print_test);
434 RUN_TEST(repr_test);
435
436 RUN_TEST(bool_test);
437 RUN_TEST(int_test);
438 RUN_TEST(float_test);
439
440 RUN_TEST(StringToInteger_test);
441 RUN_TEST(str_to_int_test);
442 RUN_TEST(int_to_str_test);
443 RUN_TEST(float_to_str_test);
444
445 RUN_TEST(comparators_test);
446 RUN_TEST(container_test);
447
448 RUN_TEST(exceptions_test);
449
450 RUN_TEST(hash_str_test);
451 RUN_TEST(intern_test);
452
453 RUN_TEST(max_test);
454
455 gHeap.CleanProcessExit();
456
457 GREATEST_MAIN_END(); /* display results */
458
459 return 0;
460}