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

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