1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <unittest/unittest.h>
6 
7 #include <fidl/flat_ast.h>
8 #include <fidl/lexer.h>
9 #include <fidl/parser.h>
10 #include <fidl/source_file.h>
11 
12 #include <fstream>
13 
14 #include "examples.h"
15 #include "test_library.h"
16 
17 namespace {
18 
19 // We repeat each test in a loop in order to catch situations where memory layout
20 // determines what JSON is produced (this is often manifested due to using a std::map<Foo*,...>
21 // in compiler source code).
22 static const int kRepeatTestCount = 100;
23 
trim(std::string & s)24 static inline void trim(std::string& s) {
25     s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
26                 return !std::isspace(ch) && ch != '\n';
27             }));
28     s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
29                 return !std::isspace(ch) && ch != '\n';
30             }).base(),
31             s.end());
32 }
33 
checkJSONGenerator(std::string raw_source_code,std::string expected_json)34 bool checkJSONGenerator(std::string raw_source_code, std::string expected_json) {
35     TestLibrary library("json.fidl", raw_source_code);
36     EXPECT_TRUE(library.Compile());
37 
38     // actual
39     auto actual = library.GenerateJSON();
40     trim(actual);
41 
42     // expected
43     trim(expected_json);
44 
45     if (actual.compare(expected_json) == 0) {
46         return true;
47     }
48 
49     // On error, we output both the actual and expected to allow simple
50     // diffing to debug the test.
51 
52     std::ofstream output_actual("json_generator_tests_actual.txt");
53     output_actual << actual;
54     output_actual.close();
55 
56     std::ofstream output_expected("json_generator_tests_expected.txt");
57     output_expected << expected_json;
58     output_expected.close();
59 
60     return false;
61 }
62 
json_generator_test_struct()63 bool json_generator_test_struct() {
64     BEGIN_TEST;
65 
66     for (int i = 0; i < kRepeatTestCount; i++) {
67         EXPECT_TRUE(checkJSONGenerator(R"FIDL(
68 library fidl.test.json;
69 
70 struct Simple {
71     uint8 f1;
72     bool f2;
73 };
74 
75 )FIDL",
76                                        R"JSON(
77 {
78   "version": "0.0.1",
79   "name": "fidl.test.json",
80   "library_dependencies": [],
81   "const_declarations": [],
82   "enum_declarations": [],
83   "interface_declarations": [],
84   "struct_declarations": [
85     {
86       "name": "fidl.test.json/Simple",
87       "anonymous": false,
88       "members": [
89         {
90           "type": {
91             "kind": "primitive",
92             "subtype": "uint8"
93           },
94           "name": "f1",
95           "size": 1,
96           "max_out_of_line": 0,
97           "alignment": 1,
98           "offset": 0,
99           "max_handles": 0
100         },
101         {
102           "type": {
103             "kind": "primitive",
104             "subtype": "bool"
105           },
106           "name": "f2",
107           "size": 1,
108           "max_out_of_line": 0,
109           "alignment": 1,
110           "offset": 1,
111           "max_handles": 0
112         }
113       ],
114       "size": 2,
115       "max_out_of_line": 0,
116       "alignment": 1,
117       "max_handles": 0
118     }
119   ],
120   "table_declarations": [],
121   "union_declarations": [],
122   "declaration_order": [
123     "fidl.test.json/Simple"
124   ],
125   "declarations": {
126     "fidl.test.json/Simple": "struct"
127   }
128 }
129 )JSON"));
130     }
131 
132     END_TEST;
133 }
134 
json_generator_test_empty_struct()135 bool json_generator_test_empty_struct() {
136     BEGIN_TEST;
137 
138     for (int i = 0; i < kRepeatTestCount; i++) {
139         EXPECT_TRUE(checkJSONGenerator(R"FIDL(
140 library fidl.test.json;
141 
142 struct Empty {
143 };
144 
145 interface EmptyInterface {
146   Send(Empty e);
147   -> Receive (Empty e);
148   SendAndReceive(Empty e) -> (Empty e);
149 };
150 )FIDL",
151                                        R"JSON(
152 {
153   "version": "0.0.1",
154   "name": "fidl.test.json",
155   "library_dependencies": [],
156   "const_declarations": [],
157   "enum_declarations": [],
158   "interface_declarations": [
159     {
160       "name": "fidl.test.json/EmptyInterface",
161       "methods": [
162         {
163           "ordinal": 296942602,
164           "name": "Send",
165           "has_request": true,
166           "maybe_request": [
167             {
168               "type": {
169                 "kind": "identifier",
170                 "identifier": "fidl.test.json/Empty",
171                 "nullable": false
172               },
173               "name": "e",
174               "size": 1,
175               "max_out_of_line": 0,
176               "alignment": 1,
177               "offset": 16,
178               "max_handles": 0
179             }
180           ],
181           "maybe_request_size": 24,
182           "maybe_request_alignment": 8,
183           "has_response": false
184         },
185         {
186           "ordinal": 939543845,
187           "name": "Receive",
188           "has_request": false,
189           "has_response": true,
190           "maybe_response": [
191             {
192               "type": {
193                 "kind": "identifier",
194                 "identifier": "fidl.test.json/Empty",
195                 "nullable": false
196               },
197               "name": "e",
198               "size": 1,
199               "max_out_of_line": 0,
200               "alignment": 1,
201               "offset": 16,
202               "max_handles": 0
203             }
204           ],
205           "maybe_response_size": 24,
206           "maybe_response_alignment": 8
207         },
208         {
209           "ordinal": 556045674,
210           "name": "SendAndReceive",
211           "has_request": true,
212           "maybe_request": [
213             {
214               "type": {
215                 "kind": "identifier",
216                 "identifier": "fidl.test.json/Empty",
217                 "nullable": false
218               },
219               "name": "e",
220               "size": 1,
221               "max_out_of_line": 0,
222               "alignment": 1,
223               "offset": 16,
224               "max_handles": 0
225             }
226           ],
227           "maybe_request_size": 24,
228           "maybe_request_alignment": 8,
229           "has_response": true,
230           "maybe_response": [
231             {
232               "type": {
233                 "kind": "identifier",
234                 "identifier": "fidl.test.json/Empty",
235                 "nullable": false
236               },
237               "name": "e",
238               "size": 1,
239               "max_out_of_line": 0,
240               "alignment": 1,
241               "offset": 16,
242               "max_handles": 0
243             }
244           ],
245           "maybe_response_size": 24,
246           "maybe_response_alignment": 8
247         }
248       ]
249     }
250   ],
251   "struct_declarations": [
252     {
253       "name": "fidl.test.json/Empty",
254       "anonymous": false,
255       "members": [],
256       "size": 1,
257       "max_out_of_line": 0,
258       "alignment": 1,
259       "max_handles": 0
260     }
261   ],
262   "table_declarations": [],
263   "union_declarations": [],
264   "declaration_order": [
265     "fidl.test.json/Empty",
266     "fidl.test.json/EmptyInterface"
267   ],
268   "declarations": {
269     "fidl.test.json/EmptyInterface": "interface",
270     "fidl.test.json/Empty": "struct"
271   }
272 }
273 )JSON"));
274     }
275 
276     END_TEST;
277 }
278 
json_generator_test_table()279 bool json_generator_test_table() {
280     BEGIN_TEST;
281 
282     for (int i = 0; i < kRepeatTestCount; i++) {
283         EXPECT_TRUE(checkJSONGenerator(R"FIDL(
284 library fidl.test.json;
285 
286 table Simple {
287     1: uint8 f1;
288     2: bool f2;
289     3: reserved;
290 };
291 
292 )FIDL",
293                                        R"JSON(
294 {
295   "version": "0.0.1",
296   "name": "fidl.test.json",
297   "library_dependencies": [],
298   "const_declarations": [],
299   "enum_declarations": [],
300   "interface_declarations": [],
301   "struct_declarations": [],
302   "table_declarations": [
303     {
304       "name": "fidl.test.json/Simple",
305       "members": [
306         {
307           "ordinal": 1,
308           "reserved": false,
309           "type": {
310             "kind": "primitive",
311             "subtype": "uint8"
312           },
313           "name": "f1",
314           "size": 1,
315           "max_out_of_line": 0,
316           "alignment": 1,
317           "max_handles": 0
318         },
319         {
320           "ordinal": 2,
321           "reserved": false,
322           "type": {
323             "kind": "primitive",
324             "subtype": "bool"
325           },
326           "name": "f2",
327           "size": 1,
328           "max_out_of_line": 0,
329           "alignment": 1,
330           "max_handles": 0
331         },
332         {
333           "ordinal": 3,
334           "reserved": true
335         }
336       ],
337       "size": 16,
338       "max_out_of_line": 48,
339       "alignment": 8,
340       "max_handles": 0
341     }
342   ],
343   "union_declarations": [],
344   "declaration_order": [
345     "fidl.test.json/Simple"
346   ],
347   "declarations": {
348     "fidl.test.json/Simple": "table"
349   }
350 }
351 )JSON"));
352     }
353 
354     END_TEST;
355 }
356 
json_generator_test_union()357 bool json_generator_test_union() {
358     BEGIN_TEST;
359 
360     for (int i = 0; i < kRepeatTestCount; i++) {
361         EXPECT_TRUE(checkJSONGenerator(R"FIDL(
362 library fidl.test.json;
363 
364 struct Pizza {
365     vector<string:16> toppings;
366 };
367 
368 struct Pasta {
369     string:16 sauce;
370 };
371 
372 union PizzaOrPasta {
373     Pizza pizza;
374     Pasta pasta;
375 };
376 
377 )FIDL",
378                                        R"JSON(
379 {
380   "version": "0.0.1",
381   "name": "fidl.test.json",
382   "library_dependencies": [],
383   "const_declarations": [],
384   "enum_declarations": [],
385   "interface_declarations": [],
386   "struct_declarations": [
387     {
388       "name": "fidl.test.json/Pizza",
389       "anonymous": false,
390       "members": [
391         {
392           "type": {
393             "kind": "vector",
394             "element_type": {
395               "kind": "string",
396               "maybe_element_count": 16,
397               "nullable": false
398             },
399             "nullable": false
400           },
401           "name": "toppings",
402           "size": 16,
403           "max_out_of_line": 4294967295,
404           "alignment": 8,
405           "offset": 0,
406           "max_handles": 0
407         }
408       ],
409       "size": 16,
410       "max_out_of_line": 4294967295,
411       "alignment": 8,
412       "max_handles": 0
413     },
414     {
415       "name": "fidl.test.json/Pasta",
416       "anonymous": false,
417       "members": [
418         {
419           "type": {
420             "kind": "string",
421             "maybe_element_count": 16,
422             "nullable": false
423           },
424           "name": "sauce",
425           "size": 16,
426           "max_out_of_line": 16,
427           "alignment": 8,
428           "offset": 0,
429           "max_handles": 0
430         }
431       ],
432       "size": 16,
433       "max_out_of_line": 16,
434       "alignment": 8,
435       "max_handles": 0
436     }
437   ],
438   "table_declarations": [],
439   "union_declarations": [
440     {
441       "name": "fidl.test.json/PizzaOrPasta",
442       "members": [
443         {
444           "type": {
445             "kind": "identifier",
446             "identifier": "fidl.test.json/Pizza",
447             "nullable": false
448           },
449           "name": "pizza",
450           "size": 16,
451           "max_out_of_line": 4294967295,
452           "alignment": 8,
453           "offset": 8
454         },
455         {
456           "type": {
457             "kind": "identifier",
458             "identifier": "fidl.test.json/Pasta",
459             "nullable": false
460           },
461           "name": "pasta",
462           "size": 16,
463           "max_out_of_line": 16,
464           "alignment": 8,
465           "offset": 8
466         }
467       ],
468       "size": 24,
469       "max_out_of_line": 4294967295,
470       "alignment": 8,
471       "max_handles": 0
472     }
473   ],
474   "declaration_order": [
475     "fidl.test.json/Pizza",
476     "fidl.test.json/Pasta",
477     "fidl.test.json/PizzaOrPasta"
478   ],
479   "declarations": {
480     "fidl.test.json/Pizza": "struct",
481     "fidl.test.json/Pasta": "struct",
482     "fidl.test.json/PizzaOrPasta": "union"
483   }
484 }
485 )JSON"));
486     }
487 
488     END_TEST;
489 }
490 
491 // This test ensures that inherited methods have the same ordinal / signature /
492 // etc as the method from which they are inheriting.
json_generator_test_inheritance()493 bool json_generator_test_inheritance() {
494     BEGIN_TEST;
495 
496     for (int i = 0; i < kRepeatTestCount; i++) {
497         EXPECT_TRUE(checkJSONGenerator(R"FIDL(
498 library fidl.test.json;
499 
500 [FragileBase]
501 interface super {
502    foo(string s) -> (int64 y);
503 };
504 
505 interface sub : super {
506 };
507 
508 )FIDL",
509                                        R"JSON({
510   "version": "0.0.1",
511   "name": "fidl.test.json",
512   "library_dependencies": [],
513   "const_declarations": [],
514   "enum_declarations": [],
515   "interface_declarations": [
516     {
517       "name": "fidl.test.json/super",
518       "maybe_attributes": [
519         {
520           "name": "FragileBase",
521           "value": ""
522         }
523       ],
524       "methods": [
525         {
526           "ordinal": 790020540,
527           "name": "foo",
528           "has_request": true,
529           "maybe_request": [
530             {
531               "type": {
532                 "kind": "string",
533                 "nullable": false
534               },
535               "name": "s",
536               "size": 16,
537               "max_out_of_line": 4294967295,
538               "alignment": 8,
539               "offset": 16,
540               "max_handles": 0
541             }
542           ],
543           "maybe_request_size": 32,
544           "maybe_request_alignment": 8,
545           "has_response": true,
546           "maybe_response": [
547             {
548               "type": {
549                 "kind": "primitive",
550                 "subtype": "int64"
551               },
552               "name": "y",
553               "size": 8,
554               "max_out_of_line": 0,
555               "alignment": 8,
556               "offset": 16,
557               "max_handles": 0
558             }
559           ],
560           "maybe_response_size": 24,
561           "maybe_response_alignment": 8
562         }
563       ]
564     },
565     {
566       "name": "fidl.test.json/sub",
567       "methods": [
568         {
569           "ordinal": 790020540,
570           "name": "foo",
571           "has_request": true,
572           "maybe_request": [
573             {
574               "type": {
575                 "kind": "string",
576                 "nullable": false
577               },
578               "name": "s",
579               "size": 16,
580               "max_out_of_line": 4294967295,
581               "alignment": 8,
582               "offset": 16,
583               "max_handles": 0
584             }
585           ],
586           "maybe_request_size": 32,
587           "maybe_request_alignment": 8,
588           "has_response": true,
589           "maybe_response": [
590             {
591               "type": {
592                 "kind": "primitive",
593                 "subtype": "int64"
594               },
595               "name": "y",
596               "size": 8,
597               "max_out_of_line": 0,
598               "alignment": 8,
599               "offset": 16,
600               "max_handles": 0
601             }
602           ],
603           "maybe_response_size": 24,
604           "maybe_response_alignment": 8
605         }
606       ]
607     }
608   ],
609   "struct_declarations": [],
610   "table_declarations": [],
611   "union_declarations": [],
612   "declaration_order": [
613     "fidl.test.json/super",
614     "fidl.test.json/sub"
615   ],
616   "declarations": {
617     "fidl.test.json/super": "interface",
618     "fidl.test.json/sub": "interface"
619   }
620 })JSON"));
621     }
622 
623     END_TEST;
624 }
625 } // namespace
626 
627 BEGIN_TEST_CASE(json_generator_tests);
628 RUN_TEST(json_generator_test_empty_struct);
629 RUN_TEST(json_generator_test_struct);
630 RUN_TEST(json_generator_test_table);
631 RUN_TEST(json_generator_test_union);
632 RUN_TEST(json_generator_test_inheritance);
633 END_TEST_CASE(json_generator_tests);
634