[fidl] Add JSON generation tests

Test: make -j 12 HOST_USE_ASAN=true tools && ./build-x64/host_tests/fidl-compiler-test
Change-Id: I1a8f6cb83ed479acc1202b7265846d658fdc6ddf
diff --git a/system/utest/fidl-compiler/json_generator_tests.cpp b/system/utest/fidl-compiler/json_generator_tests.cpp
new file mode 100644
index 0000000..fb5af64
--- /dev/null
+++ b/system/utest/fidl-compiler/json_generator_tests.cpp
@@ -0,0 +1,247 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <unittest/unittest.h>
+
+#include <fidl/flat_ast.h>
+#include <fidl/lexer.h>
+#include <fidl/parser.h>
+#include <fidl/source_file.h>
+
+#include <fstream>
+
+#include "examples.h"
+#include "test_library.h"
+
+namespace {
+
+static inline void trim(std::string &s) {
+    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
+        return !std::isspace(ch) && ch != '\n';
+    }));
+    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
+        return !std::isspace(ch) && ch != '\n';
+    }).base(), s.end());
+}
+
+bool checkJSONGenerator(std::string raw_source_code, std::string expected_json) {
+    TestLibrary library("json.fidl", raw_source_code);
+    EXPECT_TRUE(library.Compile());
+
+    // actual
+    auto actual = library.GenerateJSON();
+    trim(actual);
+
+    // expected
+    trim(expected_json);
+
+    if (actual.compare(expected_json) == 0) {
+        return true;
+    }
+
+    // On error, we output both the actual and expected to allow simple
+    // diffing to debug the test.
+
+    std::ofstream output_actual("json_generator_tests_actual.txt");
+    output_actual << actual;
+    output_actual.close();
+
+    std::ofstream output_expected("json_generator_tests_expected.txt");
+    output_expected << expected_json;
+    output_expected.close();
+
+    return false;
+}
+
+bool json_generator_test_simple() {
+    BEGIN_TEST;
+
+    EXPECT_TRUE(checkJSONGenerator(R"FIDL(
+library fidl.test.json;
+
+struct Simple {
+    uint8 f1;
+    bool f2;
+};
+
+)FIDL", R"JSON(
+{
+  "version": "0.0.1",
+  "name": "fidl.test.json",
+  "library_dependencies": [],
+  "const_declarations": [],
+  "enum_declarations": [],
+  "interface_declarations": [],
+  "struct_declarations": [
+    {
+      "name": "fidl.test.json/Simple",
+      "members": [
+        {
+          "type": {
+            "kind": "primitive",
+            "subtype": "uint8"
+          },
+          "name": "f1",
+          "size": 1,
+          "alignment": 1,
+          "offset": 0,
+          "max_handles": 0
+        },
+        {
+          "type": {
+            "kind": "primitive",
+            "subtype": "bool"
+          },
+          "name": "f2",
+          "size": 1,
+          "alignment": 1,
+          "offset": 1,
+          "max_handles": 0
+        }
+      ],
+      "size": 2,
+      "alignment": 1,
+      "max_handles": 0
+    }
+  ],
+  "union_declarations": [],
+  "declaration_order": [
+    "fidl.test.json/Simple"
+  ],
+  "declarations": {
+    "fidl.test.json/Simple": "struct"
+  }
+}
+)JSON"));
+
+    END_TEST;
+}
+
+bool json_generator_test_union() {
+    BEGIN_TEST;
+
+    EXPECT_TRUE(checkJSONGenerator(R"FIDL(
+library fidl.test.json;
+
+struct Pizza {
+    vector<string:16> toppings;
+};
+
+struct Pasta {
+    string:16 sauce;
+};
+
+union PizzaOrPasta {
+    Pizza pizza;
+    Pasta pasta;
+};
+
+)FIDL", R"JSON(
+{
+  "version": "0.0.1",
+  "name": "fidl.test.json",
+  "library_dependencies": [],
+  "const_declarations": [],
+  "enum_declarations": [],
+  "interface_declarations": [],
+  "struct_declarations": [
+    {
+      "name": "fidl.test.json/Pizza",
+      "members": [
+        {
+          "type": {
+            "kind": "vector",
+            "element_type": {
+              "kind": "string",
+              "maybe_element_count": 16,
+              "nullable": false
+            },
+            "nullable": false
+          },
+          "name": "toppings",
+          "size": 16,
+          "alignment": 8,
+          "offset": 0,
+          "max_handles": 0
+        }
+      ],
+      "size": 16,
+      "alignment": 8,
+      "max_handles": 0
+    },
+    {
+      "name": "fidl.test.json/Pasta",
+      "members": [
+        {
+          "type": {
+            "kind": "string",
+            "maybe_element_count": 16,
+            "nullable": false
+          },
+          "name": "sauce",
+          "size": 16,
+          "alignment": 8,
+          "offset": 0,
+          "max_handles": 0
+        }
+      ],
+      "size": 16,
+      "alignment": 8,
+      "max_handles": 0
+    }
+  ],
+  "union_declarations": [
+    {
+      "name": "fidl.test.json/PizzaOrPasta",
+      "members": [
+        {
+          "type": {
+            "kind": "identifier",
+            "identifier": "fidl.test.json/Pizza",
+            "nullable": false
+          },
+          "name": "pizza",
+          "size": 16,
+          "alignment": 8,
+          "offset": 8
+        },
+        {
+          "type": {
+            "kind": "identifier",
+            "identifier": "fidl.test.json/Pasta",
+            "nullable": false
+          },
+          "name": "pasta",
+          "size": 16,
+          "alignment": 8,
+          "offset": 8
+        }
+      ],
+      "size": 24,
+      "alignment": 8,
+      "max_handles": 0
+    }
+  ],
+  "declaration_order": [
+    "fidl.test.json/Pasta",
+    "fidl.test.json/Pizza",
+    "fidl.test.json/PizzaOrPasta"
+  ],
+  "declarations": {
+    "fidl.test.json/Pizza": "struct",
+    "fidl.test.json/Pasta": "struct",
+    "fidl.test.json/PizzaOrPasta": "union"
+  }
+}
+)JSON"));
+
+    END_TEST;
+}
+
+} // namespace
+
+BEGIN_TEST_CASE(json_generator_tests);
+RUN_TEST(json_generator_test_simple);
+RUN_TEST(json_generator_test_union);
+END_TEST_CASE(json_generator_tests);
diff --git a/system/utest/fidl-compiler/rules.mk b/system/utest/fidl-compiler/rules.mk
index 74b8f45..e6370b9 100644
--- a/system/utest/fidl-compiler/rules.mk
+++ b/system/utest/fidl-compiler/rules.mk
@@ -65,6 +65,7 @@
     $(LOCAL_DIR)/bad_compound_identifier_tests.cpp \
     $(LOCAL_DIR)/dup_attributes_tests.cpp \
     $(LOCAL_DIR)/formatter_unittests.cpp \
+    $(LOCAL_DIR)/json_generator_tests.cpp \
     $(LOCAL_DIR)/max_bytes_tests.cpp \
     $(LOCAL_DIR)/max_handle_tests.cpp \
     $(LOCAL_DIR)/superinterface_tests.cpp \
diff --git a/system/utest/fidl-compiler/test_library.h b/system/utest/fidl-compiler/test_library.h
index 0a59953..ddfaf6e 100644
--- a/system/utest/fidl-compiler/test_library.h
+++ b/system/utest/fidl-compiler/test_library.h
@@ -6,6 +6,7 @@
 #define ZIRCON_SYSTEM_UTEST_FIDL_COMPILER_TEST_LIBRARY_H_
 
 #include <fidl/flat_ast.h>
+#include <fidl/json_generator.h>
 #include <fidl/lexer.h>
 #include <fidl/parser.h>
 #include <fidl/source_file.h>
@@ -51,6 +52,12 @@
                library_->Compile();
     }
 
+    std::string GenerateJSON() {
+        auto json_generator = fidl::JSONGenerator(library_.get());
+        auto out = json_generator.Produce();
+        return out.str();
+    }
+
     bool AddSourceFile(const std::string& filename, const std::string& raw_source_code) {
         auto source_file = MakeSourceFile(filename, raw_source_code);
         fidl::IdentifierTable identifier_table;