// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "json.h"
#include <capnp/test-util.h>
#include <capnp/compat/json.capnp.h>
#include <capnp/compat/json-test.capnp.h>
#include <kj/debug.h>
#include <kj/string.h>
#include <kj/test.h>

namespace capnp {
namespace _ {  // private
namespace {

KJ_TEST("basic json encoding") {
  JsonCodec json;

  KJ_EXPECT(json.encode(VOID) == "null");
  KJ_EXPECT(json.encode(true) == "true");
  KJ_EXPECT(json.encode(false) == "false");
  KJ_EXPECT(json.encode(123) == "123");
  KJ_EXPECT(json.encode(-5.5) == "-5.5");
  KJ_EXPECT(json.encode(Text::Reader("foo")) == "\"foo\"");
  KJ_EXPECT(json.encode(Text::Reader("ab\"cd\\ef\x03")) == "\"ab\\\"cd\\\\ef\\u0003\"");
  KJ_EXPECT(json.encode(test::TestEnum::CORGE) == "\"corge\"");

  byte bytes[] = {12, 34, 56};
  KJ_EXPECT(json.encode(Data::Reader(bytes, 3)) == "[12,34,56]");

  json.setPrettyPrint(true);
  KJ_EXPECT(json.encode(Data::Reader(bytes, 3)) == "[12, 34, 56]");
}

KJ_TEST("raw encoding") {
  JsonCodec json;

  auto text = kj::str("{\"field\":\"value\"}");
  MallocMessageBuilder message;
  auto value = message.initRoot<JsonValue>();
  value.setRaw(text);

  KJ_EXPECT(json.encodeRaw(value) == text);
}

const char ALL_TYPES_JSON[] =
    "{ \"voidField\": null,\n"
    "  \"boolField\": true,\n"
    "  \"int8Field\": -123,\n"
    "  \"int16Field\": -12345,\n"
    "  \"int32Field\": -12345678,\n"
    "  \"int64Field\": \"-123456789012345\",\n"
    "  \"uInt8Field\": 234,\n"
    "  \"uInt16Field\": 45678,\n"
    "  \"uInt32Field\": 3456789012,\n"
    "  \"uInt64Field\": \"12345678901234567890\",\n"
    "  \"float32Field\": 1234.5,\n"
    "  \"float64Field\": -1.23e47,\n"
    "  \"textField\": \"foo\",\n"
    "  \"dataField\": [98, 97, 114],\n"
    "  \"structField\": {\n"
    "    \"voidField\": null,\n"
    "    \"boolField\": true,\n"
    "    \"int8Field\": -12,\n"
    "    \"int16Field\": 3456,\n"
    "    \"int32Field\": -78901234,\n"
    "    \"int64Field\": \"56789012345678\",\n"
    "    \"uInt8Field\": 90,\n"
    "    \"uInt16Field\": 1234,\n"
    "    \"uInt32Field\": 56789012,\n"
    "    \"uInt64Field\": \"345678901234567890\",\n"
    "    \"float32Field\": -1.2499999646475857e-10,\n"
    "    \"float64Field\": 345,\n"
    "    \"textField\": \"baz\",\n"
    "    \"dataField\": [113, 117, 120],\n"
    "    \"structField\": {\n"
    "      \"voidField\": null,\n"
    "      \"boolField\": false,\n"
    "      \"int8Field\": 0,\n"
    "      \"int16Field\": 0,\n"
    "      \"int32Field\": 0,\n"
    "      \"int64Field\": \"0\",\n"
    "      \"uInt8Field\": 0,\n"
    "      \"uInt16Field\": 0,\n"
    "      \"uInt32Field\": 0,\n"
    "      \"uInt64Field\": \"0\",\n"
    "      \"float32Field\": 0,\n"
    "      \"float64Field\": 0,\n"
    "      \"textField\": \"nested\",\n"
    "      \"structField\": {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"really nested\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
    "      \"enumField\": \"foo\",\n"
    "      \"interfaceField\": null },\n"
    "    \"enumField\": \"baz\",\n"
    "    \"interfaceField\": null,\n"
    "    \"voidList\": [null, null, null],\n"
    "    \"boolList\": [false, true, false, true, true],\n"
    "    \"int8List\": [12, -34, -128, 127],\n"
    "    \"int16List\": [1234, -5678, -32768, 32767],\n"
    "    \"int32List\": [12345678, -90123456, -2147483648, 2147483647],\n"
    "    \"int64List\": [\"123456789012345\", \"-678901234567890\", \"-9223372036854775808\", \"9223372036854775807\"],\n"
    "    \"uInt8List\": [12, 34, 0, 255],\n"
    "    \"uInt16List\": [1234, 5678, 0, 65535],\n"
    "    \"uInt32List\": [12345678, 90123456, 0, 4294967295],\n"
    "    \"uInt64List\": [\"123456789012345\", \"678901234567890\", \"0\", \"18446744073709551615\"],\n"
    "    \"float32List\": [0, 1234567, 9.9999999338158125e36, -9.9999999338158125e36, 9.99999991097579e-38, -9.99999991097579e-38],\n"
    "    \"float64List\": [0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306],\n"
    "    \"textList\": [\"quux\", \"corge\", \"grault\"],\n"
    "    \"dataList\": [[103, 97, 114, 112, 108, 121], [119, 97, 108, 100, 111], [102, 114, 101, 100]],\n"
    "    \"structList\": [\n"
    "      {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"x structlist 1\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
    "      {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"x structlist 2\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
    "      {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"x structlist 3\", \"enumField\": \"foo\", \"interfaceField\": null} ],\n"
    "    \"enumList\": [\"qux\", \"bar\", \"grault\"] },\n"
    "  \"enumField\": \"corge\",\n"
    "  \"interfaceField\": null,\n"
    "  \"voidList\": [null, null, null, null, null, null],\n"
    "  \"boolList\": [true, false, false, true],\n"
    "  \"int8List\": [111, -111],\n"
    "  \"int16List\": [11111, -11111],\n"
    "  \"int32List\": [111111111, -111111111],\n"
    "  \"int64List\": [\"1111111111111111111\", \"-1111111111111111111\"],\n"
    "  \"uInt8List\": [111, 222],\n"
    "  \"uInt16List\": [33333, 44444],\n"
    "  \"uInt32List\": [3333333333],\n"
    "  \"uInt64List\": [\"11111111111111111111\"],\n"
    "  \"float32List\": [5555.5, \"Infinity\", \"-Infinity\", \"NaN\"],\n"
    "  \"float64List\": [7777.75, \"Infinity\", \"-Infinity\", \"NaN\"],\n"
    "  \"textList\": [\"plugh\", \"xyzzy\", \"thud\"],\n"
    "  \"dataList\": [[111, 111, 112, 115], [101, 120, 104, 97, 117, 115, 116, 101, 100], [114, 102, 99, 51, 48, 57, 50]],\n"
    "  \"structList\": [\n"
    "    {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"structlist 1\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
    "    {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"structlist 2\", \"enumField\": \"foo\", \"interfaceField\": null},\n"
    "    {\"voidField\": null, \"boolField\": false, \"int8Field\": 0, \"int16Field\": 0, \"int32Field\": 0, \"int64Field\": \"0\", \"uInt8Field\": 0, \"uInt16Field\": 0, \"uInt32Field\": 0, \"uInt64Field\": \"0\", \"float32Field\": 0, \"float64Field\": 0, \"textField\": \"structlist 3\", \"enumField\": \"foo\", \"interfaceField\": null} ],\n"
    "  \"enumList\": [\"foo\", \"garply\"] }";

KJ_TEST("encode all types") {
  MallocMessageBuilder message;
  auto root = message.getRoot<TestAllTypes>();
  initTestMessage(root);

  JsonCodec json;
  json.setPrettyPrint(true);
  KJ_EXPECT(json.encode(root) == ALL_TYPES_JSON);

  // Verify that if we strip out the non-string spaces, we get the non-pretty-print version.
  kj::Vector<char> chars;
  bool inQuotes = false;
  for (char c: ALL_TYPES_JSON) {
    if (c == '\"') inQuotes = !inQuotes;

    if ((c == '\n' || c == ' ') && !inQuotes) {
      // skip space
    } else {
      chars.add(c);
    }
  }
  kj::String nospaces(chars.releaseAsArray());

  json.setPrettyPrint(false);
  KJ_EXPECT(json.encode(root) == nospaces);
}

KJ_TEST("encode union") {
  MallocMessageBuilder message;
  auto root = message.getRoot<test::TestUnnamedUnion>();

  root.setBefore("a");
  root.setMiddle(44);
  root.setAfter("c");

  JsonCodec json;

  root.setFoo(123);
  KJ_EXPECT(json.encode(root) == "{\"before\":\"a\",\"foo\":123,\"middle\":44,\"after\":\"c\"}");

  root.setBar(321);
  KJ_EXPECT(json.encode(root) == "{\"before\":\"a\",\"middle\":44,\"bar\":321,\"after\":\"c\"}");
}

KJ_TEST("decode all types") {
  JsonCodec json;
  json.setHasMode(HasMode::NON_DEFAULT);

#define CASE_MAYBE_ROUNDTRIP(s, f, roundtrip) \
  { \
    MallocMessageBuilder message; \
    auto root = message.initRoot<TestAllTypes>(); \
    kj::StringPtr input = s; \
    json.decode(input, root); \
    KJ_EXPECT((f), input, root); \
    auto reencoded = json.encode(root); \
    KJ_EXPECT(roundtrip == (input == reencoded), roundtrip, input, reencoded); \
  }
#define CASE_NO_ROUNDTRIP(s, f) CASE_MAYBE_ROUNDTRIP(s, f, false)
#define CASE(s, f) CASE_MAYBE_ROUNDTRIP(s, f, true)
#define CASE_THROW(s, errorMessage) \
  { \
    MallocMessageBuilder message; \
    auto root = message.initRoot<TestAllTypes>(); \
    KJ_EXPECT_THROW_MESSAGE(errorMessage, json.decode(s, root)); \
  }
#define CASE_THROW_RECOVERABLE(s, errorMessage) \
  { \
    MallocMessageBuilder message; \
    auto root = message.initRoot<TestAllTypes>(); \
    KJ_EXPECT_THROW_RECOVERABLE_MESSAGE(errorMessage, json.decode(s, root)); \
  }

  CASE(R"({})", root.getBoolField() == false);
  CASE_NO_ROUNDTRIP(R"({"unknownField":7})", root.getBoolField() == false);
  CASE(R"({"boolField":true})", root.getBoolField() == true);
  CASE(R"({"int8Field":-128})", root.getInt8Field() == -128);
  CASE_NO_ROUNDTRIP(R"({"int8Field":"127"})", root.getInt8Field() == 127);
  CASE_THROW_RECOVERABLE(R"({"int8Field":"-129"})", "Value out-of-range");
  CASE_THROW_RECOVERABLE(R"({"int8Field":128})", "Value out-of-range");
  CASE(R"({"int16Field":-32768})", root.getInt16Field() == -32768);
  CASE_NO_ROUNDTRIP(R"({"int16Field":"32767"})", root.getInt16Field() == 32767);
  CASE_THROW_RECOVERABLE(R"({"int16Field":"-32769"})", "Value out-of-range");
  CASE_THROW_RECOVERABLE(R"({"int16Field":32768})", "Value out-of-range");
  CASE(R"({"int32Field":-2147483648})", root.getInt32Field() == -2147483648);
  CASE_NO_ROUNDTRIP(R"({"int32Field":"2147483647"})", root.getInt32Field() == 2147483647);
  CASE_NO_ROUNDTRIP(R"({"int64Field":-9007199254740992})", root.getInt64Field() == -9007199254740992LL);
  CASE_NO_ROUNDTRIP(R"({"int64Field":9007199254740991})", root.getInt64Field() == 9007199254740991LL);
  CASE(R"({"int64Field":"-9223372036854775808"})", root.getInt64Field() == -9223372036854775808ULL);
  CASE(R"({"int64Field":"9223372036854775807"})", root.getInt64Field() == 9223372036854775807LL);
  CASE_THROW_RECOVERABLE(R"({"int64Field":"-9223372036854775809"})", "Value out-of-range");
  CASE_THROW_RECOVERABLE(R"({"int64Field":"9223372036854775808"})", "Value out-of-range");
  CASE(R"({"uInt8Field":255})", root.getUInt8Field() == 255);
  CASE_NO_ROUNDTRIP(R"({"uInt8Field":"0"})", root.getUInt8Field() == 0);
  CASE_THROW_RECOVERABLE(R"({"uInt8Field":"256"})", "Value out-of-range");
  CASE_THROW_RECOVERABLE(R"({"uInt8Field":-1})", "Value out-of-range");
  CASE(R"({"uInt16Field":65535})", root.getUInt16Field() == 65535);
  CASE_NO_ROUNDTRIP(R"({"uInt16Field":"0"})", root.getUInt16Field() == 0);
  CASE_THROW_RECOVERABLE(R"({"uInt16Field":"655356"})", "Value out-of-range");
  CASE_THROW_RECOVERABLE(R"({"uInt16Field":-1})", "Value out-of-range");
  CASE(R"({"uInt32Field":4294967295})", root.getUInt32Field() == 4294967295);
  CASE_NO_ROUNDTRIP(R"({"uInt32Field":"0"})", root.getUInt32Field() == 0);
  CASE_THROW_RECOVERABLE(R"({"uInt32Field":"42949672956"})", "Value out-of-range");
  CASE_THROW_RECOVERABLE(R"({"uInt32Field":-1})", "Value out-of-range");
  CASE_NO_ROUNDTRIP(R"({"uInt64Field":9007199254740991})", root.getUInt64Field() == 9007199254740991ULL);
  CASE(R"({"uInt64Field":"18446744073709551615"})", root.getUInt64Field() == 18446744073709551615ULL);
  CASE_NO_ROUNDTRIP(R"({"uInt64Field":"0"})", root.getUInt64Field() == 0);
  CASE_THROW_RECOVERABLE(R"({"uInt64Field":"18446744073709551616"})", "Value out-of-range");
  CASE_NO_ROUNDTRIP(R"({"float32Field":0})", root.getFloat32Field() == 0);
  CASE(R"({"float32Field":4.5})", root.getFloat32Field() == 4.5);
  CASE_NO_ROUNDTRIP(R"({"float32Field":null})", kj::isNaN(root.getFloat32Field()));
  CASE(R"({"float32Field":"NaN"})", kj::isNaN(root.getFloat32Field()));
  CASE_NO_ROUNDTRIP(R"({"float32Field":"nan"})", kj::isNaN(root.getFloat32Field()));
  CASE(R"({"float32Field":"Infinity"})", root.getFloat32Field() == kj::inf());
  CASE(R"({"float32Field":"-Infinity"})", root.getFloat32Field() == -kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float32Field":"infinity"})", root.getFloat32Field() == kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float32Field":"-infinity"})", root.getFloat32Field() == -kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float32Field":"INF"})", root.getFloat32Field() == kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float32Field":"-INF"})", root.getFloat32Field() == -kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float32Field":1e39})", root.getFloat32Field() == kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float32Field":-1e39})", root.getFloat32Field() == -kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float64Field":0})", root.getFloat64Field() == 0);
  CASE(R"({"float64Field":4.5})", root.getFloat64Field() == 4.5);
  CASE_NO_ROUNDTRIP(R"({"float64Field":null})", kj::isNaN(root.getFloat64Field()));
  CASE(R"({"float64Field":"NaN"})", kj::isNaN(root.getFloat64Field()));
  CASE_NO_ROUNDTRIP(R"({"float64Field":"nan"})", kj::isNaN(root.getFloat64Field()));
  CASE(R"({"float64Field":"Infinity"})", root.getFloat64Field() == kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float64Field":"infinity"})", root.getFloat64Field() == kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float64Field":"-infinity"})", root.getFloat64Field() == -kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float64Field":"INF"})", root.getFloat64Field() == kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float64Field":"-INF"})", root.getFloat64Field() == -kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float64Field":1e309})", root.getFloat64Field() == kj::inf());
  CASE_NO_ROUNDTRIP(R"({"float64Field":-1e309})", root.getFloat64Field() == -kj::inf());
  CASE(R"({"textField":"hello"})", kj::str("hello") == root.getTextField());
  CASE(R"({"dataField":[7,0,122]})",
      kj::heapArray<byte>({7,0,122}).asPtr() == root.getDataField());
  CASE(R"({"structField":{}})", root.hasStructField() == true);
  CASE(R"({"structField":{}})", root.getStructField().getBoolField() == false);
  CASE_NO_ROUNDTRIP(R"({"structField":{"boolField":false}})", root.getStructField().getBoolField() == false);
  CASE(R"({"structField":{"boolField":true}})", root.getStructField().getBoolField() == true);
  CASE(R"({"enumField":"bar"})", root.getEnumField() == TestEnum::BAR);

  CASE_NO_ROUNDTRIP(R"({"textField":"foo\u1234bar"})",
      kj::str(u8"foo\u1234bar") == root.getTextField());

  CASE_THROW_RECOVERABLE(R"({"structField":null})", "Expected object value");
  CASE_THROW_RECOVERABLE(R"({"structList":null})", "Expected list value");
  CASE_THROW_RECOVERABLE(R"({"boolList":null})", "Expected list value");
  CASE_THROW_RECOVERABLE(R"({"structList":[null]})", "Expected object value");
  CASE_THROW_RECOVERABLE(R"({"int64Field":"177a"})", "String does not contain valid");
  CASE_THROW_RECOVERABLE(R"({"uInt64Field":"177a"})", "String does not contain valid");
  CASE_THROW_RECOVERABLE(R"({"float64Field":"177a"})", "String does not contain valid");

  CASE(R"({})", root.hasBoolList() == false);
  CASE(R"({"boolList":[]})", root.hasBoolList() == true);
  CASE(R"({"boolList":[]})", root.getBoolList().size() == 0);
  CASE(R"({"boolList":[false]})", root.getBoolList().size() == 1);
  CASE(R"({"boolList":[false]})", root.getBoolList()[0] == false);
  CASE(R"({"boolList":[true]})", root.getBoolList()[0] == true);
  CASE(R"({"int8List":[7]})", root.getInt8List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"int8List":["7"]})", root.getInt8List()[0] == 7);
  CASE(R"({"int16List":[7]})", root.getInt16List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"int16List":["7"]})", root.getInt16List()[0] == 7);
  CASE(R"({"int32List":[7]})", root.getInt32List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"int32List":["7"]})", root.getInt32List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"int64List":[7]})", root.getInt64List()[0] == 7);
  CASE(R"({"int64List":["7"]})", root.getInt64List()[0] == 7);
  CASE(R"({"uInt8List":[7]})", root.getUInt8List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"uInt8List":["7"]})", root.getUInt8List()[0] == 7);
  CASE(R"({"uInt16List":[7]})", root.getUInt16List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"uInt16List":["7"]})", root.getUInt16List()[0] == 7);
  CASE(R"({"uInt32List":[7]})", root.getUInt32List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"uInt32List":["7"]})", root.getUInt32List()[0] == 7);
  CASE_NO_ROUNDTRIP(R"({"uInt64List":[7]})", root.getUInt64List()[0] == 7);
  CASE(R"({"uInt64List":["7"]})", root.getUInt64List()[0] == 7);
  CASE(R"({"float32List":[4.5]})", root.getFloat32List()[0] == 4.5);
  CASE_NO_ROUNDTRIP(R"({"float32List":["4.5"]})", root.getFloat32List()[0] == 4.5);
  CASE_NO_ROUNDTRIP(R"({"float32List":[null]})", kj::isNaN(root.getFloat32List()[0]));
  CASE(R"({"float32List":["NaN"]})", kj::isNaN(root.getFloat32List()[0]));
  CASE(R"({"float32List":["Infinity"]})", root.getFloat32List()[0] == kj::inf());
  CASE(R"({"float32List":["-Infinity"]})", root.getFloat32List()[0] == -kj::inf());
  CASE(R"({"float64List":[4.5]})", root.getFloat64List()[0] == 4.5);
  CASE_NO_ROUNDTRIP(R"({"float64List":["4.5"]})", root.getFloat64List()[0] == 4.5);
  CASE_NO_ROUNDTRIP(R"({"float64List":[null]})", kj::isNaN(root.getFloat64List()[0]));
  CASE(R"({"float64List":["NaN"]})", kj::isNaN(root.getFloat64List()[0]));
  CASE(R"({"float64List":["Infinity"]})", root.getFloat64List()[0] == kj::inf());
  CASE(R"({"float64List":["-Infinity"]})", root.getFloat64List()[0] == -kj::inf());
  CASE(R"({"textList":["hello"]})", kj::str("hello") == root.getTextList()[0]);
  CASE(R"({"dataList":[[7,0,122]]})",
      kj::heapArray<byte>({7,0,122}).asPtr() == root.getDataList()[0]);
  CASE(R"({"structList":[{}]})", root.hasStructList() == true);
  CASE(R"({"structList":[{}]})", root.getStructList()[0].getBoolField() == false);
  CASE_NO_ROUNDTRIP(R"({"structList":[{"boolField":false}]})", root.getStructList()[0].getBoolField() == false);
  CASE(R"({"structList":[{"boolField":true}]})", root.getStructList()[0].getBoolField() == true);
  CASE(R"({"enumList":["bar"]})", root.getEnumList()[0] == TestEnum::BAR);
#undef CASE_MAYBE_ROUNDTRIP
#undef CASE_NO_ROUNDTRIP
#undef CASE
#undef CASE_THROW
#undef CASE_THROW_RECOVERABLE
}

KJ_TEST("decode test message") {
  MallocMessageBuilder message;
  auto root = message.getRoot<TestAllTypes>();
  initTestMessage(root);

  JsonCodec json;
  auto encoded = json.encode(root);

  MallocMessageBuilder decodedMessage;
  auto decodedRoot = decodedMessage.initRoot<TestAllTypes>();
  json.decode(encoded, decodedRoot);

  KJ_EXPECT(root.toString().flatten() == decodedRoot.toString().flatten());
}

KJ_TEST("basic json decoding") {
  // TODO(cleanup): this test is a mess!
  JsonCodec json;
  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();
    json.decodeRaw("null", root);

    KJ_EXPECT(root.which() == JsonValue::NULL_);
    KJ_EXPECT(root.getNull() == VOID);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("false", root);
    KJ_EXPECT(root.which() == JsonValue::BOOLEAN);
    KJ_EXPECT(root.getBoolean() == false);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("true", root);
    KJ_EXPECT(root.which() == JsonValue::BOOLEAN);
    KJ_EXPECT(root.getBoolean() == true);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("\"foo\"", root);
    KJ_EXPECT(root.which() == JsonValue::STRING);
    KJ_EXPECT(kj::str("foo") == root.getString());
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(R"("\"")", root);
    KJ_EXPECT(root.which() == JsonValue::STRING);
    KJ_EXPECT(kj::str("\"") == root.getString());
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(R"("\\abc\"d\\e")", root);
    KJ_EXPECT(root.which() == JsonValue::STRING);
    KJ_EXPECT(kj::str("\\abc\"d\\e") == root.getString());
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(R"("\"\\\/\b\f\n\r\t\u0003abc\u0064\u0065f")", root);
    KJ_EXPECT(root.which() == JsonValue::STRING);
    KJ_EXPECT(kj::str("\"\\/\b\f\n\r\t\x03""abcdef") == root.getString(), root.getString());
  }
  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("[]", root);
    KJ_EXPECT(root.which() == JsonValue::ARRAY, (uint)root.which());
    KJ_EXPECT(root.getArray().size() == 0);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("[true]", root);
    KJ_EXPECT(root.which() == JsonValue::ARRAY);
    auto array = root.getArray();
    KJ_EXPECT(array.size() == 1, array.size());
    KJ_EXPECT(root.getArray()[0].which() == JsonValue::BOOLEAN);
    KJ_EXPECT(root.getArray()[0].getBoolean() == true);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("  [  true  , false\t\n , null]", root);
    KJ_EXPECT(root.which() == JsonValue::ARRAY);
    auto array = root.getArray();
    KJ_EXPECT(array.size() == 3);
    KJ_EXPECT(array[0].which() == JsonValue::BOOLEAN);
    KJ_EXPECT(array[0].getBoolean() == true);
    KJ_EXPECT(array[1].which() == JsonValue::BOOLEAN);
    KJ_EXPECT(array[1].getBoolean() == false);
    KJ_EXPECT(array[2].which() == JsonValue::NULL_);
    KJ_EXPECT(array[2].getNull() == VOID);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("{}", root);
    KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
    KJ_EXPECT(root.getObject().size() == 0);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("\r\n\t {\r\n\t }\r\n\t ", root);
    KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
    KJ_EXPECT(root.getObject().size() == 0);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(R"({"some": null})", root);
    KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
    auto object = root.getObject();
    KJ_EXPECT(object.size() == 1);
    KJ_EXPECT(kj::str("some") == object[0].getName());
    KJ_EXPECT(object[0].getValue().which() == JsonValue::NULL_);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(R"({"foo\n\tbaz": "a val", "bar": ["a", -5.5e11,  { "z": {}}]})", root);
    KJ_EXPECT(root.which() == JsonValue::OBJECT, (uint)root.which());
    auto object = root.getObject();
    KJ_EXPECT(object.size() == 2);
    KJ_EXPECT(kj::str("foo\n\tbaz") == object[0].getName());
    KJ_EXPECT(object[0].getValue().which() == JsonValue::STRING);
    KJ_EXPECT(kj::str("a val") == object[0].getValue().getString());

    KJ_EXPECT(kj::str("bar") == object[1].getName());
    KJ_EXPECT(object[1].getValue().which() == JsonValue::ARRAY);
    auto array = object[1].getValue().getArray();
    KJ_EXPECT(array.size() == 3, array.size());
    KJ_EXPECT(array[0].which() == JsonValue::STRING);
    KJ_EXPECT(kj::str("a") == array[0].getString());
    KJ_EXPECT(array[1].which() == JsonValue::NUMBER);
    KJ_EXPECT(array[1].getNumber() == -5.5e11);
    KJ_EXPECT(array[2].which() == JsonValue::OBJECT);
    KJ_EXPECT(array[2].getObject().size() == 1);
    KJ_EXPECT(array[2].getObject()[0].getValue().which() == JsonValue::OBJECT);
    KJ_EXPECT(array[2].getObject()[0].getValue().getObject().size() == 0);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("123", root);
    KJ_EXPECT(root.which() == JsonValue::NUMBER);
    KJ_EXPECT(root.getNumber() == 123);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    KJ_EXPECT_THROW_MESSAGE("input", json.decodeRaw("z", root));
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    // Leading + not allowed in numbers.
    KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("+123", root));
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("[00]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("[01]", root));
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("-", root));
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("-5", root);
    KJ_EXPECT(root.which() == JsonValue::NUMBER);
    KJ_EXPECT(root.getNumber() == -5);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw("-5.5", root);
    KJ_EXPECT(root.which() == JsonValue::NUMBER);
    KJ_EXPECT(root.getNumber() == -5.5);
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("a", root));
    KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("[", root));
    KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("{", root));
    KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("\"\\u\"", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[}", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[}]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1, , ]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[,]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[true,]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[, 1]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1\"\"]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1,, \"\"]", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{\"a\"1: 0}", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw(R"({"some": null,})", root));
    KJ_EXPECT_THROW_MESSAGE("Input remains", json.decodeRaw("11a", root));
    KJ_EXPECT_THROW_MESSAGE("Invalid escape", json.decodeRaw(R"("\z")", root));
    KJ_EXPECT_THROW_MESSAGE("Invalid escape", json.decodeRaw(R"("\z")", root));
    {
      // TODO(msvc):  This raw string literal currently confuses MSVC's preprocessor, so I hoisted
      // it outside the macro.
      auto f = [&] { json.decodeRaw(R"(["\n\", 3])", root); };
      KJ_EXPECT_THROW_MESSAGE("ends prematurely", f());
    }
    KJ_EXPECT_THROW_MESSAGE("Invalid hex", json.decodeRaw(R"("\u12zz")", root));
    KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("-", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("--", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("\f{}", root));
    KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{\v}", root));
  }

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(R"("\u007f")", root);
    KJ_EXPECT(root.which() == JsonValue::STRING);

    char utf_buffer[] = {127, 0};
    KJ_EXPECT(kj::str(utf_buffer) == root.getString());
  }
}

KJ_TEST("maximum nesting depth") {
  JsonCodec json;
  auto input = kj::str(R"({"foo": "a", "bar": ["b", { "baz": [-5.5e11] }, [ [ 1 ], {  "z": 2 }]]})");
  // `input` has a maximum nesting depth of 4, reached 3 times.

  {
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(input, root);
  }

  {
    json.setMaxNestingDepth(0);
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    KJ_EXPECT_THROW_MESSAGE("nest",
        json.decodeRaw(input, root));
  }

  {
    json.setMaxNestingDepth(3);
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    KJ_EXPECT_THROW_MESSAGE("nest",
        json.decodeRaw(input, root));
  }

  {
    json.setMaxNestingDepth(4);
    MallocMessageBuilder message;
    auto root = message.initRoot<JsonValue>();

    json.decodeRaw(input, root);
  }
}

KJ_TEST("unknown fields") {
  JsonCodec json;
  MallocMessageBuilder message;
  auto root = message.initRoot<TestJsonAnnotations2>();
  auto valid = R"({"foo": "a"})"_kj;
  auto unknown = R"({"foo": "a", "unknown-field": "b"})"_kj;
  json.decode(valid, root);
  json.decode(unknown, root);
  json.setRejectUnknownFields(true);
  json.decode(valid, root);
  KJ_EXPECT_THROW_MESSAGE("Unknown field", json.decode(unknown, root));

  // Verify unknown field rejection still works when handling by annotation.
  json.handleByAnnotation<TestJsonAnnotations2>();
  KJ_EXPECT_THROW_MESSAGE("Unknown field", json.decode(unknown, root));
}

class TestCallHandler: public JsonCodec::Handler<Text> {
public:
  void encode(const JsonCodec& codec, Text::Reader input,
              JsonValue::Builder output) const override {
    auto call = output.initCall();
    call.setFunction("Frob");
    auto params = call.initParams(2);
    params[0].setNumber(123);
    params[1].setString(input);
  }

  Orphan<Text> decode(const JsonCodec& codec, JsonValue::Reader input,
                      Orphanage orphanage) const override {
    KJ_UNIMPLEMENTED("TestHandler::decode");
  }
};

class TestDynamicStructHandler: public JsonCodec::Handler<DynamicStruct> {
public:
  void encode(const JsonCodec& codec, DynamicStruct::Reader input,
              JsonValue::Builder output) const override {
    auto fields = input.getSchema().getFields();
    auto items = output.initArray(fields.size());
    for (auto field: fields) {
      KJ_REQUIRE(field.getIndex() < items.size());
      auto item = items[field.getIndex()];
      if (input.has(field)) {
        codec.encode(input.get(field), field.getType(), item);
      } else {
        item.setNull();
      }
    }
  }

  void decode(const JsonCodec& codec, JsonValue::Reader input,
              DynamicStruct::Builder output) const override {
    auto orphanage = Orphanage::getForMessageContaining(output);
    auto fields = output.getSchema().getFields();
    auto items = input.getArray();
    for (auto field: fields) {
      KJ_REQUIRE(field.getIndex() < items.size());
      auto item = items[field.getIndex()];
      if (!item.isNull()) {
        output.adopt(field, codec.decode(item, field.getType(), orphanage));
      }
    }
  }
};

class TestStructHandler: public JsonCodec::Handler<test::TestOldVersion> {
public:
  void encode(const JsonCodec& codec, test::TestOldVersion::Reader input, JsonValue::Builder output) const override {
    dynamicHandler.encode(codec, input, output);
  }

  void decode(const JsonCodec& codec, JsonValue::Reader input, test::TestOldVersion::Builder output) const override {
    dynamicHandler.decode(codec, input, output);
  }

private:
  TestDynamicStructHandler dynamicHandler;
};

KJ_TEST("register custom encoding handlers") {
  JsonCodec json;

  TestStructHandler structHandler;
  json.addTypeHandler(structHandler);

  // JSON decoder can't parse calls back, so test only encoder here
  TestCallHandler callHandler;
  json.addTypeHandler(callHandler);

  MallocMessageBuilder message;
  auto root = message.getRoot<test::TestOldVersion>();
  root.setOld1(123);
  root.setOld2("foo");

  KJ_EXPECT(json.encode(root) == "[\"123\",Frob(123,\"foo\"),null]");
}

KJ_TEST("register custom roundtrip handler") {
  for (auto i = 1; i <= 2; i++) {
    JsonCodec json;
    TestStructHandler staticHandler;
    TestDynamicStructHandler dynamicHandler;
    kj::String encoded;

    if (i == 1) {
      // first iteration: test with explicit struct handler
      json.addTypeHandler(staticHandler);
    } else {
      // second iteration: same checks, but with DynamicStruct handler
      json.addTypeHandler(StructSchema::from<test::TestOldVersion>(), dynamicHandler);
    }

    {
      MallocMessageBuilder message;
      auto root = message.getRoot<test::TestOldVersion>();
      root.setOld1(123);
      root.initOld3().setOld2("foo");

      encoded = json.encode(root);

      KJ_EXPECT(encoded == "[\"123\",null,[\"0\",\"foo\",null]]");
    }

    {
      MallocMessageBuilder message;
      auto root = message.getRoot<test::TestOldVersion>();
      json.decode(encoded, root);

      KJ_EXPECT(root.getOld1() == 123);
      KJ_EXPECT(!root.hasOld2());
      auto nested = root.getOld3();
      KJ_EXPECT(nested.getOld1() == 0);
      KJ_EXPECT("foo" == nested.getOld2());
      KJ_EXPECT(!nested.hasOld3());
    }
  }
}

KJ_TEST("register field handler") {
  TestStructHandler handler;
  JsonCodec json;
  json.addFieldHandler(StructSchema::from<test::TestOldVersion>().getFieldByName("old3"),
                       handler);

  kj::String encoded;

  {
    MallocMessageBuilder message;
    auto root = message.getRoot<test::TestOldVersion>();
    root.setOld1(123);
    root.setOld2("foo");
    auto nested = root.initOld3();
    nested.setOld2("bar");

    encoded = json.encode(root);

    KJ_EXPECT(encoded == "{\"old1\":\"123\",\"old2\":\"foo\",\"old3\":[\"0\",\"bar\",null]}")
  }

  {
    MallocMessageBuilder message;
    auto root = message.getRoot<test::TestOldVersion>();
    json.decode(encoded, root);

    KJ_EXPECT(root.getOld1() == 123);
    KJ_EXPECT("foo" == root.getOld2());
    auto nested = root.getOld3();
    KJ_EXPECT(nested.getOld1() == 0);
    KJ_EXPECT("bar" == nested.getOld2());
    KJ_EXPECT(!nested.hasOld3());
  }
}

class TestCapabilityHandler: public JsonCodec::Handler<test::TestInterface> {
public:
  void encode(const JsonCodec& codec, test::TestInterface::Client input,
              JsonValue::Builder output) const override {
    KJ_UNIMPLEMENTED("TestCapabilityHandler::encode");
  }

  test::TestInterface::Client decode(
      const JsonCodec& codec, JsonValue::Reader input) const override {
    return nullptr;
  }
};

KJ_TEST("register capability handler") {
  // This test currently only checks that this compiles, which at one point wasn't the caes.
  // TODO(test): Actually run some code here.

  TestCapabilityHandler handler;
  JsonCodec json;
  json.addTypeHandler(handler);
}

static constexpr kj::StringPtr GOLDEN_ANNOTATED =
R"({ "names-can_contain!anything Really": "foo",
  "flatFoo": 123,
  "flatBar": "abc",
  "renamed-flatBaz": {"hello": true},
  "flatQux": "cba",
  "pfx.foo": "this is a long string in order to force multi-line pretty printing",
  "pfx.renamed-bar": 321,
  "pfx.baz": {"hello": true},
  "pfx.xfp.qux": "fed",
  "union-type": "renamed-bar",
  "barMember": 789,
  "multiMember": "ghi",
  "dependency": {"renamed-foo": "corge"},
  "simpleGroup": {"renamed-grault": "garply"},
  "enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
  "innerJson": [123, "hello", {"object": true}],
  "customFieldHandler": "add-prefix-waldo",
  "testBase64": "ZnJlZA==",
  "testHex": "706c756768",
  "bUnion": "renamed-bar",
  "bValue": 678,
  "externalUnion": {"type": "bar", "value": "cba"},
  "unionWithVoid": {"type": "voidValue"} })"_kj;

static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE =
R"({
  "unionWithVoid": {"type": "voidValue"},
  "externalUnion": {"type": "bar", "value": "cba"},
  "bValue": 678,
  "bUnion": "renamed-bar",
  "testHex": "706c756768",
  "testBase64": "ZnJlZA==",
  "customFieldHandler": "add-prefix-waldo",
  "innerJson": [123, "hello", {"object": true}],
  "enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
  "simpleGroup": { "renamed-grault": "garply" },
  "dependency": { "renamed-foo": "corge" },
  "multiMember": "ghi",
  "barMember": 789,
  "union-type": "renamed-bar",
  "pfx.xfp.qux": "fed",
  "pfx.baz": {"hello": true},
  "pfx.renamed-bar": 321,
  "pfx.foo": "this is a long string in order to force multi-line pretty printing",
  "flatQux": "cba",
  "renamed-flatBaz": {"hello": true},
  "flatBar": "abc",
  "flatFoo": 123,
  "names-can_contain!anything Really": "foo"
})"_kj;

class PrefixAdder: public JsonCodec::Handler<capnp::Text> {
public:
  void encode(const JsonCodec& codec, capnp::Text::Reader input, JsonValue::Builder output) const {
    output.setString(kj::str("add-prefix-", input));
  }

  Orphan<capnp::Text> decode(const JsonCodec& codec, JsonValue::Reader input,
                             Orphanage orphanage) const {
    return orphanage.newOrphanCopy(capnp::Text::Reader(input.getString().slice(11)));
  }
};

KJ_TEST("rename fields") {
  JsonCodec json;
  json.handleByAnnotation<TestJsonAnnotations>();
  json.setPrettyPrint(true);

  PrefixAdder customHandler;
  json.addFieldHandler(Schema::from<TestJsonAnnotations>().getFieldByName("customFieldHandler"),
                       customHandler);

  kj::String goldenText;

  {
    MallocMessageBuilder message;
    auto root = message.getRoot<TestJsonAnnotations>();
    root.setSomeField("foo");

    auto aGroup = root.getAGroup();
    aGroup.setFlatFoo(123);
    aGroup.setFlatBar("abc");
    aGroup.getFlatBaz().setHello(true);
    aGroup.getDoubleFlat().setFlatQux("cba");

    auto prefixedGroup = root.getPrefixedGroup();
    prefixedGroup.setFoo("this is a long string in order to force multi-line pretty printing");
    prefixedGroup.setBar(321);
    prefixedGroup.getBaz().setHello(true);
    prefixedGroup.getMorePrefix().setQux("fed");

    auto unionBar = root.getAUnion().initBar();
    unionBar.setBarMember(789);
    unionBar.setMultiMember("ghi");

    root.initDependency().setFoo("corge");
    root.initSimpleGroup().setGrault("garply");

    root.setEnums({
      TestJsonAnnotatedEnum::QUX,
      TestJsonAnnotatedEnum::BAR,
      TestJsonAnnotatedEnum::FOO,
      TestJsonAnnotatedEnum::BAZ
    });

    auto val = root.initInnerJson();
    auto arr = val.initArray(3);
    arr[0].setNumber(123);
    arr[1].setString("hello");
    auto field = arr[2].initObject(1)[0];
    field.setName("object");
    field.initValue().setBoolean(true);

    root.setCustomFieldHandler("waldo");

    root.setTestBase64("fred"_kj.asBytes());
    root.setTestHex("plugh"_kj.asBytes());

    root.getBUnion().setBar(678);

    root.initExternalUnion().initBar().setValue("cba");

    root.initUnionWithVoid().setVoidValue();

    auto encoded = json.encode(root.asReader());
    KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded);

    goldenText = kj::str(root);
  }

  {
    MallocMessageBuilder message;
    auto root = message.getRoot<TestJsonAnnotations>();
    json.decode(GOLDEN_ANNOTATED, root);

    KJ_EXPECT(kj::str(root) == goldenText, root, goldenText);
  }

  {
    // Try parsing in reverse, mostly to test that union tags can come after content.
    MallocMessageBuilder message;
    auto root = message.getRoot<TestJsonAnnotations>();
    json.decode(GOLDEN_ANNOTATED_REVERSE, root);

    KJ_EXPECT(kj::str(root) == goldenText, root, goldenText);
  }
}

KJ_TEST("base64 union encoded correctly") {
  // At one point field handlers were not correctly applied when the field was a member of a union
  // in a type that was handled by annotation.

  JsonCodec json;
  json.handleByAnnotation<TestBase64Union>();
  json.setPrettyPrint(true);

  MallocMessageBuilder message;
  auto root = message.getRoot<TestBase64Union>();
  root.initFoo(5);

  KJ_EXPECT(json.encode(root) == "{\"foo\": \"AAAAAAA=\"}", json.encode(root));
}

}  // namespace
}  // namespace _ (private)
}  // namespace capnp
