Allow for static initialisation.
[libeze] / test-blob.c
1
2 #include <stdio.h>
3 #include <stdint.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <assert.h>
8 #include <errno.h>
9
10 #include "ez-blob.h"
11 #include "ez-list.h"
12
13 #include "ez-blob-tagz.h"
14 #include "ez-blob-xdrn.h"
15
16 #include "ez-blob-io.h"
17
18 /*
19   TODO: some bad data tests.
20
21   This depends a lot on the structure and the format.
22   
23   * lengths outside of data
24   * counts that will exhaust memory
25   * field id's out of range
26   * data corruption (wrong output)
27   * meta-data corruption (wrong/invalid structure)
28   * truncated data
29   * too much data
30
31   Primarily it shouldn't crash or corrupt memory.
32   With explicit knowledge of format, it should fail on corrupt meta-data.
33  */
34
35 static int doprint = 1;
36 static int dolarge = 0;
37
38 struct simple {
39         char b;
40         short s;
41         int a;
42         int64_t l;
43         float c;
44         double d;
45 };
46
47 static const ez_blob_desc simple_DESC[] = {
48         EZ_BLOB_START(struct simple, 0, 6),
49         EZ_BLOB_INT8(struct simple, 1, b),
50         EZ_BLOB_INT16(struct simple, 2, s),
51         EZ_BLOB_INT32(struct simple, 3, a),
52         EZ_BLOB_INT64(struct simple, 4, l),
53         EZ_BLOB_FLOAT32(struct simple, 5, c),
54         EZ_BLOB_FLOAT64(struct simple, 6, d),
55 };
56
57 struct arrays {
58         uint32_t count;
59         char *string;
60         ez_blob array;
61 };
62
63 static const ez_blob_desc array_DESC[] = {
64         EZ_BLOB_START(struct arrays, 0, 3),
65         EZ_BLOB_INT32(struct arrays, 1, count),
66         EZ_BLOB_STRING(struct arrays, 2, string),
67         EZ_BLOB_INT8V(struct arrays, 3, array),
68 };
69
70 struct embedded {
71         struct simple s;
72         struct arrays a;
73 };
74
75 static const ez_blob_desc embed_DESC[] = {
76         EZ_BLOB_START(struct embedded, 0, 2),
77         EZ_BLOB_STRUCT(struct embedded, 1, s, simple_DESC),
78         EZ_BLOB_STRUCT(struct embedded, 2, a, array_DESC),
79 };
80
81 struct tree {
82         struct tree *left;
83         struct tree *right;
84
85         char *s;
86 };
87
88 static const ez_blob_desc tree_DESC[] = {
89         EZ_BLOB_START(struct tree, 0, 3),
90         EZ_BLOB_STRING(struct tree, 1, s),
91         EZ_BLOB_STRUCTPN(struct tree, 2, left, tree_DESC),
92         EZ_BLOB_STRUCTPN(struct tree, 3, right, tree_DESC),
93 };
94
95 struct listnode {
96         ez_node ln;
97         char *name;
98         ez_blob value;
99 };
100
101 static const ez_blob_desc listnode_DESC[] = {
102         EZ_BLOB_START(struct listnode, 0, 2),
103         EZ_BLOB_STRING(struct listnode, 1, name),
104         EZ_BLOB_INT8V(struct listnode, 2, value),
105 };
106
107 struct list {
108         char *name;
109         ez_list list;
110 };
111
112 static const ez_blob_desc list_DESC[] = {
113         EZ_BLOB_START(struct list, 0, 2),
114         EZ_BLOB_STRING(struct list, 1, name),
115         EZ_BLOB_LIST(struct list, 2, list, listnode_DESC),
116 };
117
118 struct intsize {
119         int16_t value16;
120         int32_t value32;
121         int64_t value64;
122 };
123
124 static const ez_blob_desc intsize_DESC[] = {
125         EZ_BLOB_START(struct intsize, 7, 3),
126         EZ_BLOB_INT16(struct intsize, 1, value16),
127         EZ_BLOB_INT32(struct intsize, 2, value32),
128         EZ_BLOB_INT64(struct intsize, 3, value64),
129 };
130
131 struct stringsize {
132         char *value;
133 };
134
135 static const ez_blob_desc stringsize_DESC[] = {
136         EZ_BLOB_START(struct stringsize, 8, 1),
137         EZ_BLOB_STRING(struct stringsize, 1, value),
138 };
139
140 static void dumphex(const char *data, size_t size, const char *prefix) {
141         ez_blob blob = { .eb_size = size, .eb_data = (void *)data };
142         ez_blob_dump(&blob, prefix);
143 }
144
145 static void blob_print(const ez_blob_desc *d, const void *a, int depth) {
146         ez_blob_print(d, a, depth);
147 }
148
149 static int blob_equals(const ez_blob_desc *desc, const void *a, const void *b) {
150         int e = 1;
151         for (int i=0, dlen=desc->bd_length; e && i < dlen; i++) {
152                 const ez_blob_desc *d = &desc[i+1];
153                 const void *u = a + d->bd_offset;
154                 const void *v = b + d->bd_offset;
155
156                 switch (d->bd_type) {
157                 case EZ_BLOB_INT8:
158                         e &= (*(uint8_t *)u) == (*(uint8_t *)v);
159                         break;
160                 case EZ_BLOB_INT16:
161                         e &= (*(uint16_t *)u) == (*(uint16_t *)v);
162                         break;
163                 case EZ_BLOB_INT32:
164                         e &= (*(uint32_t *)u) == (*(uint32_t *)v);
165                         break;
166                 case EZ_BLOB_INT64:
167                         e &= (*(uint64_t *)u) == (*(uint64_t *)v);
168                         break;
169                 case EZ_BLOB_FLOAT32:
170                         e &= (*(float *)u) == (*(float *)v);
171                         break;
172                 case EZ_BLOB_FLOAT64:
173                         e &= (*(double *)u) == (*(double *)v);
174                         break;
175
176                 case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE:
177                         if (!*(char **)u || !*(char **)v) {
178                                 e &= (*(char **)u) == (*(char **)v);
179                                 break;
180                         }
181                 case EZ_BLOB_CSTRING | EZ_BLOB_INT8:
182                         if (!*(char **)u || !*(char **)v) {
183                                 printf("strings can't be null\n");
184                                 e = 0;
185                         } else {
186                                 e &= strcmp(*(char **)u, *(char **)v) == 0;
187                         }
188                         break;
189
190                 case EZ_BLOB_VECTOR | EZ_BLOB_INT8:
191                 case EZ_BLOB_VECTOR | EZ_BLOB_INT16:
192                 case EZ_BLOB_VECTOR | EZ_BLOB_INT32:
193                 case EZ_BLOB_VECTOR | EZ_BLOB_INT64:
194                 case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32:
195                 case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64: {
196                         const struct ez_blob *ua = u;
197                         const struct ez_blob *va = v;
198
199                         e &= ua->eb_size == va->eb_size
200                                 && memcmp(ua->eb_data, va->eb_data, ua->eb_size) == 0;
201                         break;
202                 }
203                         
204                 case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE:
205                         if (!*(void **)u || !*(void **)v) {
206                                 e &= *(void **)u == *(void **)v;
207                                 break;
208                         }
209                 case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP:
210                         u = *(void **)u;
211                         v = *(void **)v;
212                 case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE:
213                         if (!u || !v) {
214                                 printf("structs p can't be null\n");
215                                 e = 0;
216                         } else {
217                                 e &= blob_equals(d->bd_table, u, v);
218                         }
219                         break;
220
221
222                 case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE:
223                         if (!*(void **)u || !*(void **)v) {
224                                 e &= *(void **)u == *(void **)v;
225                                 break;
226                         }
227                 case EZ_BLOB_STRUCT | EZ_BLOB_LISTP:
228                         u = *(void **)u;
229                         v = *(void **)v;
230                 case EZ_BLOB_STRUCT | EZ_BLOB_LIST:
231                         if (!u || !v) {
232                                 printf("lists p can't be null\n");
233                                 e = 0;
234                         } else {
235                                 ez_list *ul = (void *)u;
236                                 ez_list *vl = (void *)v;
237
238                         
239                                 e &= ez_list_size(ul) == ez_list_size(vl);
240                                 for (ez_node *uw = ez_list_head(ul), *un = ez_node_succ(uw),
241                                              *vw = ez_list_head(vl), *vn = ez_node_succ(uw);
242                                      e && un && vn;
243                                      uw = un, un = ez_node_succ(un),
244                                              vw = vn, vn = ez_node_succ(vn)) {
245                                         e &= blob_equals(d->bd_table, uw, vw);
246                                 }
247                         }
248                         break;
249
250                 default:
251                         printf("cmp unknown field type=%d id=%d\n", d->bd_type, d->bd_id);
252                         e = 0;
253                         break;
254                 }
255
256                 if (!e) {
257                         printf("cmp failed in struct %d at field %d type %04x\n", desc->bd_id, d->bd_id, d->bd_type);
258                         abort();
259                 }
260         }
261
262         return e;
263 }
264
265 struct test_funcs {
266         const char *name;
267         int (*blob_encode)(const ez_blob_desc *d, const void *p, ez_blob *blob);
268         void *(*blob_decode)(const ez_blob_desc *desc, const ez_blob *blob);
269         int (*blob_decode_raw)(const ez_blob_desc *desc, const ez_blob *blob, void *p);
270 };
271
272 static void test_basics(const ez_blob_desc *d, void *src, struct test_funcs *funcs) {
273         ez_blob blob, blob2;
274         void *t;
275         size_t dsize = d->bd_offset;
276         char tmp[dsize + 64] __attribute__ ((aligned(64)));
277         int res;
278
279         if (doprint) {
280                 printf("source\n");
281                 blob_print(d, src, 4);
282         }
283         
284         res = funcs->blob_encode(d, src, &blob);
285         if (res == ENOMEM) {
286                 printf(" encode failed: probably out of memory, skipped testing this\n");
287                 return;
288         }
289         assert(res == 0);
290
291         if (doprint)
292                 dumphex(blob.eb_data, blob.eb_size, "serial: ");
293         else
294                 dumphex(blob.eb_data, blob.eb_size < 256 ? blob.eb_size : 256, "header: ");
295                 
296         // check decode equals
297         t = funcs->blob_decode(d, &blob);
298         assert(t != NULL);
299
300         if (doprint) {
301                 printf("decoded\n");
302                 blob_print(d, t, 4);
303         }
304         assert(blob_equals(d, t, src));
305
306         // check encode-decoded equals source
307         res = funcs->blob_encode(d, t, &blob2);
308         assert(res == 0);
309         assert(blob.eb_size == blob2.eb_size);
310         assert(memcmp(blob.eb_data, blob2.eb_data, blob2.eb_size) == 0);
311         free(blob2.eb_data);
312
313         ez_blob_free(d, t);
314
315         // check raw decode stays within bounds of struct
316         memset(tmp, 0xbe, sizeof(tmp));
317         funcs->blob_decode_raw(d, &blob, &tmp[32]);
318         //assert(t != NULL);
319         //assert(blob_equals(d, tmp+32, src));
320         if (doprint)
321                 dumphex(tmp, sizeof(tmp), "object: ");
322
323         for (int i=0;i<32;i++) {
324                 assert((tmp[i] & 0xff) == 0xbe);
325                 assert((tmp[i + 32 + dsize] & 0xff) == 0xbe);
326         }
327         ez_blob_free_raw(d, tmp+32);
328         free(blob.eb_data);
329 }
330
331 static void test_simple(struct test_funcs *funcs) {
332         struct simple src = {
333                 'z', 0xff0f, 1, 0xf01040a04010dULL, 3.14159f, 5644941221133
334         };
335         const ez_blob_desc *d = simple_DESC;
336
337         printf("test simple\n");
338         test_basics(d, &src, funcs);
339 }
340
341 static void test_arrays(struct test_funcs *funcs) {
342         char data[7] = { 1, 2, 3, 4, 5, 6, 7 };
343         struct arrays src = {
344                 32,
345                 "Bob was here.",
346                 .array.eb_size = 7,
347                 .array.eb_data = data
348         };
349         const ez_blob_desc *d = array_DESC;
350
351         printf("test arrays\n");
352         test_basics(d, &src, funcs);
353 }
354
355 static void test_struct(struct test_funcs *funcs) {
356         char data[10] = { 1, 2, 3, 4, 5, 6, 7, 1, 2, 3 };
357         struct embedded src = {
358                 {
359                         -1, -1, -1, -1, 1337.4004, 6.022E23,
360                 },
361                 {
362                         55, "Jane was there.", .array.eb_size = 10, .array.eb_data = data
363                 }
364         };
365         const ez_blob_desc *d = embed_DESC;
366
367         printf("test embedded structures\n");
368         test_basics(d, &src, funcs);
369 }
370
371 static void test_rawtree(struct test_funcs *funcs) {
372         struct tree src[6] = {
373                 { &src[1], &src[3], "root" },
374                 { NULL, &src[2], "left" },
375                 { NULL, NULL, "left-right" },
376                 { &src[4], &src[5], "right" },
377                 { NULL, NULL, "right-left" },
378                 { NULL, NULL, "right-right" },
379         };
380         const ez_blob_desc *d = tree_DESC;
381
382         printf("test raw tree\n");
383         test_basics(d, &src, funcs);
384 }
385
386 static void test_list(struct test_funcs *funcs) {
387         struct list list = {
388                 .name = "a list",
389                 .list = EZ_INIT_LIST(list.list)
390         };
391         struct listnode nodes[3] = {
392                 { .name = "node 0", .value.eb_size = 5, .value.eb_data = "data0" },
393                 { .name = "node 1", .value.eb_size = 5, .value.eb_data = "data1" },
394                 { .name = "node 2", .value.eb_size = 5, .value.eb_data = "data2" },
395         };
396
397         list.name = "a list";
398         //      ez_list_init(&list.
399         for (int i=0;i<3;i++)
400                 ez_list_addtail(&list.list, &nodes[i]);
401         
402         const ez_blob_desc *d = list_DESC;
403
404         printf("test list\n");
405         test_basics(d, &list, funcs);
406 }
407
408 static void test_intsize(struct test_funcs *funcs) {
409         uint64_t sizes[8] = { 0x00, 0xff, 0x101, 0xffff, 0x10002, 0xffffffff, 0x100000003ULL, ~0ULL };
410         const ez_blob_desc *d = intsize_DESC;
411         struct intsize src;
412
413         // Checks that the sizes reconstruct to their original values
414         
415         for (int i=0;i<8;i++) {
416                 uint64_t size = sizes[i];
417
418                 src.value16 = size;
419                 src.value32 = size;
420                 src.value64 = size;
421                 
422                 printf("test int value %016lx\n", size);
423                 test_basics(d, &src, funcs);
424         }
425 }
426
427 static void test_stringsize(struct test_funcs *funcs) {
428         size_t sizes[7] = { 0, 1, 255, 256, 65535, 65536, 0x100000000ULL };
429         const ez_blob_desc *d = stringsize_DESC;
430         struct stringsize src;
431         int sprint = doprint;
432         
433         for (int i=0;i<7;i++) {
434                 size_t size = sizes[i];
435
436                 // only tagz supports >32 bit counts
437                 if (!dolarge
438                     || (size >= 0x100000000ULL
439                         && strcmp(funcs->name, "tagz") != 0)) {
440                         printf("skip string size %zd\n", size);
441                         continue;
442                 }
443                 
444                 src.value = malloc(size+1);
445                 if (src.value) {
446                         memset(src.value, ' ', size);
447                         src.value[size] = 0;
448                         printf("test string size %zd\n", size);
449                         doprint = size <= 256;
450                         test_basics(d, &src, funcs);
451                         free(src.value);
452                 } else {
453                         printf("skip string size %zd: memory allocation failed\n", size);
454                 }
455         }
456         doprint = sprint;
457 }
458
459 // the corruption tests don't know the format details so can't tell what a given
460 // change should cause.
461 // however they should not crash, cause leaks, or valgrind oddness.
462
463 static void test_corrupt_bit(struct test_funcs *funcs) {
464         char data[10] = { 1, 2, 3, 4, 5, 6, 7, 1, 2, 3 };
465         struct embedded src = {
466                 {
467                         -1, -1, -1, -1, 1337.4004, 6.022E23,
468                 },
469                 {
470                         55, "Jane was there.", .array.eb_size = 10, .array.eb_data = data
471                 }
472         };
473         const ez_blob_desc *d = embed_DESC;
474         struct embedded *dst;
475         int res;
476         ez_blob blob;
477
478         // test 1 corrupt bit in every position
479         
480         // we don't know the internals so which ones would fail, but
481         // can check for bad memory accesses with valgrind or crash
482         
483         int good = 0, bad = 0;
484         res = funcs->blob_encode(d, &src, &blob);
485         assert(res == 0);
486         for (int i=0;i<blob.eb_size;i++) {
487                 uint8_t *data = blob.eb_data;
488                 for (int j=0;j<8;j++) {
489                         data[i] ^= (1<<j);
490                         dst = funcs->blob_decode(d, &blob);
491                         data[i] ^= (1<<j);
492                         
493                         if (dst) {
494                                 good++;
495                                 ez_blob_free(d, dst);
496                         } else {
497                                 bad++;
498                         }
499                 }
500         }
501         free(blob.eb_data);
502         printf(" info: %d failed, %d decoded - but not important\n", bad, good);
503 }
504
505 static void test_corrupt_byte(struct test_funcs *funcs) {
506         char data[10] = { 1, 2, 3, 4, 5, 6, 7, 1, 2, 3 };
507         struct embedded src = {
508                 {
509                         -1, -1, -1, -1, 1337.4004, 6.022E23,
510                 },
511                 {
512                         55, "Jane was there.", .array.eb_size = 10, .array.eb_data = data
513                 }
514         };
515         const ez_blob_desc *d = embed_DESC;
516         struct embedded *dst;
517         int res;
518         ez_blob blob;
519
520         int good = 0, bad = 0;
521         res = funcs->blob_encode(d, &src, &blob);
522         assert(res == 0);
523         for (int i=0;i<blob.eb_size;i++) {
524                 uint8_t *data = blob.eb_data;
525                 uint8_t save = data[i];
526
527                 for (int j=0;j<256;j++) {
528                         data[i] = j;
529                         dst = funcs->blob_decode(d, &blob);
530                         if (dst) {
531                                 good++;
532                                 ez_blob_free(d, dst);
533                         } else {
534                                 bad++;
535                         }
536                 }
537                 data[i] = save;
538         }
539         free(blob.eb_data);
540         printf(" info: %d failed, %d decoded - but not important\n", bad, good);
541 }
542
543 struct test_funcs backends[] = {
544         {
545                 "tagz",
546                 ez_tagz_encode,
547                 ez_tagz_decode,
548                 ez_tagz_decode_raw
549         },
550         {
551                 "xdrn",
552                 ez_xdrn_encode,
553                 ez_xdrn_decode,
554                 ez_xdrn_decode_raw
555         },      
556 };
557
558 struct {
559         const char *name;
560         void (*test)(struct test_funcs *);
561 } tests[] = {
562         { "primitives", test_simple },
563         { "arrays", test_arrays },
564         { "structs", test_struct },
565         { "tree as links", test_rawtree },
566         { "lists", test_list },
567         { "string sizes", test_stringsize },
568         { "int sizes", test_intsize },
569         { "corrupted bit", test_corrupt_bit },
570         { "corrupted byte", test_corrupt_byte },
571 };
572
573 int main(int argc, char **argv) {
574         int nfunc = sizeof(backends)/sizeof(backends[0]);
575         int ntest = sizeof(tests)/sizeof(tests[0]);
576
577         dolarge = (argc >= 2);
578         
579         for (int i=0;i<nfunc;i++) {
580                 struct test_funcs *f = &backends[i];
581
582                 for (int j=0;j<ntest;j++) {
583                         printf("* **********************************************************************\n");
584                         printf("* test: %s / %s\n", f->name, tests[j].name);
585                         fflush(stdout);
586
587                         tests[j].test(f);
588                 }
589         }
590         return 0;
591 }