OILS / mycpp / gc_builtins_test.cc View on Github | oilshell.org

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