|  | // Copyright 2018 The Chromium 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 "json.h" | 
|  |  | 
|  | #include <array> | 
|  | #include <clocale> | 
|  | #include <cmath> | 
|  | #include <cstdlib> | 
|  | #include <cstring> | 
|  | #include <iomanip> | 
|  | #include <iostream> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  |  | 
|  | #include "cbor.h" | 
|  | #include "parser_handler.h" | 
|  | #include "span.h" | 
|  | #include "status.h" | 
|  | #include "test_platform.h" | 
|  |  | 
|  | namespace crdtp { | 
|  | namespace json { | 
|  | // ============================================================================= | 
|  | // json::NewJSONEncoder - for encoding streaming parser events as JSON | 
|  | // ============================================================================= | 
|  |  | 
|  | void WriteUTF8AsUTF16(ParserHandler* writer, const std::string& utf8) { | 
|  | writer->HandleString16(SpanFrom(UTF8ToUTF16(SpanFrom(utf8)))); | 
|  | } | 
|  |  | 
|  | TEST(JsonEncoder, OverlongEncodings) { | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  |  | 
|  | // We encode 0x7f, which is the DEL ascii character, as a 4 byte UTF8 | 
|  | // sequence. This is called an overlong encoding, because only 1 byte | 
|  | // is needed to represent 0x7f as UTF8. | 
|  | std::vector<uint8_t> chars = { | 
|  | 0xf0,  // Starts 4 byte utf8 sequence | 
|  | 0x80,  // continuation byte | 
|  | 0x81,  // continuation byte w/ payload bit 7 set to 1. | 
|  | 0xbf,  // continuation byte w/ payload bits 0-6 set to 11111. | 
|  | }; | 
|  | writer->HandleString8(SpanFrom(chars)); | 
|  | EXPECT_EQ("\"\"", out);  // Empty string means that 0x7f was rejected (good). | 
|  | } | 
|  |  | 
|  | TEST(JsonEncoder, IncompleteUtf8Sequence) { | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  |  | 
|  | writer->HandleArrayBegin();  // This emits [, which starts an array. | 
|  |  | 
|  | {  // 🌎 takes four bytes to encode in UTF-8. We test with the first three; | 
|  | // This means we're trying to emit a string that consists solely of an | 
|  | // incomplete UTF-8 sequence. So the string in the JSON output is empty. | 
|  | std::string world_utf8 = "🌎"; | 
|  | ASSERT_EQ(4u, world_utf8.size()); | 
|  | std::vector<uint8_t> chars(world_utf8.begin(), world_utf8.begin() + 3); | 
|  | writer->HandleString8(SpanFrom(chars)); | 
|  | EXPECT_EQ("[\"\"", out);  // Incomplete sequence rejected: empty string. | 
|  | } | 
|  |  | 
|  | {  // This time, the incomplete sequence is at the end of the string. | 
|  | std::string msg = "Hello, \xF0\x9F\x8C"; | 
|  | std::vector<uint8_t> chars(msg.begin(), msg.end()); | 
|  | writer->HandleString8(SpanFrom(chars)); | 
|  | EXPECT_EQ("[\"\",\"Hello, \"", out);  // Incomplete sequence dropped at end. | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(JsonStdStringWriterTest, HelloWorld) { | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleMapBegin(); | 
|  | WriteUTF8AsUTF16(writer.get(), "msg1"); | 
|  | WriteUTF8AsUTF16(writer.get(), "Hello, 🌎."); | 
|  | std::string key = "msg1-as-utf8"; | 
|  | std::string value = "Hello, 🌎."; | 
|  | writer->HandleString8(SpanFrom(key)); | 
|  | writer->HandleString8(SpanFrom(value)); | 
|  | WriteUTF8AsUTF16(writer.get(), "msg2"); | 
|  | WriteUTF8AsUTF16(writer.get(), "\\\b\r\n\t\f\""); | 
|  | WriteUTF8AsUTF16(writer.get(), "nested"); | 
|  | writer->HandleMapBegin(); | 
|  | WriteUTF8AsUTF16(writer.get(), "double"); | 
|  | writer->HandleDouble(3.1415); | 
|  | WriteUTF8AsUTF16(writer.get(), "int"); | 
|  | writer->HandleInt32(-42); | 
|  | WriteUTF8AsUTF16(writer.get(), "bool"); | 
|  | writer->HandleBool(false); | 
|  | WriteUTF8AsUTF16(writer.get(), "null"); | 
|  | writer->HandleNull(); | 
|  | writer->HandleMapEnd(); | 
|  | WriteUTF8AsUTF16(writer.get(), "array"); | 
|  | writer->HandleArrayBegin(); | 
|  | writer->HandleInt32(1); | 
|  | writer->HandleInt32(2); | 
|  | writer->HandleInt32(3); | 
|  | writer->HandleArrayEnd(); | 
|  | writer->HandleMapEnd(); | 
|  | EXPECT_TRUE(status.ok()); | 
|  | EXPECT_EQ( | 
|  | "{\"msg1\":\"Hello, \\ud83c\\udf0e.\"," | 
|  | "\"msg1-as-utf8\":\"Hello, \\ud83c\\udf0e.\"," | 
|  | "\"msg2\":\"\\\\\\b\\r\\n\\t\\f\\\"\"," | 
|  | "\"nested\":{\"double\":3.1415,\"int\":-42," | 
|  | "\"bool\":false,\"null\":null},\"array\":[1,2,3]}", | 
|  | out); | 
|  | } | 
|  |  | 
|  | TEST(JsonStdStringWriterTest, RepresentingNonFiniteValuesAsNull) { | 
|  | // JSON can't represent +Infinity, -Infinity, or NaN. | 
|  | // So in practice it's mapped to null. | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleMapBegin(); | 
|  | writer->HandleString8(SpanFrom("Infinity")); | 
|  | writer->HandleDouble(std::numeric_limits<double>::infinity()); | 
|  | writer->HandleString8(SpanFrom("-Infinity")); | 
|  | writer->HandleDouble(-std::numeric_limits<double>::infinity()); | 
|  | writer->HandleString8(SpanFrom("NaN")); | 
|  | writer->HandleDouble(std::numeric_limits<double>::quiet_NaN()); | 
|  | writer->HandleMapEnd(); | 
|  | EXPECT_TRUE(status.ok()); | 
|  | EXPECT_EQ("{\"Infinity\":null,\"-Infinity\":null,\"NaN\":null}", out); | 
|  | } | 
|  |  | 
|  | TEST(JsonStdStringWriterTest, BinaryEncodedAsJsonString) { | 
|  | // The encoder emits binary submitted to ParserHandler::HandleBinary | 
|  | // as base64. The following three examples are taken from | 
|  | // https://en.wikipedia.org/wiki/Base64. | 
|  | { | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleBinary(SpanFrom(std::vector<uint8_t>({'M', 'a', 'n'}))); | 
|  | EXPECT_TRUE(status.ok()); | 
|  | EXPECT_EQ("\"TWFu\"", out); | 
|  | } | 
|  | { | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleBinary(SpanFrom(std::vector<uint8_t>({'M', 'a'}))); | 
|  | EXPECT_TRUE(status.ok()); | 
|  | EXPECT_EQ("\"TWE=\"", out); | 
|  | } | 
|  | { | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleBinary(SpanFrom(std::vector<uint8_t>({'M'}))); | 
|  | EXPECT_TRUE(status.ok()); | 
|  | EXPECT_EQ("\"TQ==\"", out); | 
|  | } | 
|  | {  // "Hello, world.", verified with base64decode.org. | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleBinary(SpanFrom(std::vector<uint8_t>( | 
|  | {'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.'}))); | 
|  | EXPECT_TRUE(status.ok()); | 
|  | EXPECT_EQ("\"SGVsbG8sIHdvcmxkLg==\"", out); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(JsonStdStringWriterTest, HandlesErrors) { | 
|  | // When an error is sent via HandleError, it saves it in the provided | 
|  | // status and clears the output. | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleMapBegin(); | 
|  | WriteUTF8AsUTF16(writer.get(), "msg1"); | 
|  | writer->HandleError(Status{Error::JSON_PARSER_VALUE_EXPECTED, 42}); | 
|  | EXPECT_EQ(Error::JSON_PARSER_VALUE_EXPECTED, status.error); | 
|  | EXPECT_EQ(42u, status.pos); | 
|  | EXPECT_EQ("", out); | 
|  | } | 
|  |  | 
|  | TEST(JsonStdStringWriterTest, DoubleToString_LeadingZero) { | 
|  | // In JSON, .1 must be rendered as 0.1, and -.7 must be rendered as -0.7. | 
|  | std::string out; | 
|  | Status status; | 
|  | std::unique_ptr<ParserHandler> writer = NewJSONEncoder(&out, &status); | 
|  | writer->HandleArrayBegin(); | 
|  | writer->HandleDouble(.1); | 
|  | writer->HandleDouble(-.7); | 
|  | writer->HandleArrayEnd(); | 
|  | EXPECT_EQ("[0.1,-0.7]", out); | 
|  | } | 
|  |  | 
|  | // ============================================================================= | 
|  | // json::ParseJSON - for receiving streaming parser events for JSON | 
|  | // ============================================================================= | 
|  |  | 
|  | class Log : public ParserHandler { | 
|  | public: | 
|  | void HandleMapBegin() override { log_ << "map begin\n"; } | 
|  |  | 
|  | void HandleMapEnd() override { log_ << "map end\n"; } | 
|  |  | 
|  | void HandleArrayBegin() override { log_ << "array begin\n"; } | 
|  |  | 
|  | void HandleArrayEnd() override { log_ << "array end\n"; } | 
|  |  | 
|  | void HandleString8(span<uint8_t> chars) override { | 
|  | log_ << "string8: " << std::string(chars.begin(), chars.end()) << "\n"; | 
|  | } | 
|  |  | 
|  | void HandleString16(span<uint16_t> chars) override { | 
|  | log_ << "string16: " << UTF16ToUTF8(chars) << "\n"; | 
|  | } | 
|  |  | 
|  | void HandleBinary(span<uint8_t> bytes) override { | 
|  | // JSON doesn't have native support for arbitrary bytes, so our parser will | 
|  | // never call this. | 
|  | CHECK(false); | 
|  | } | 
|  |  | 
|  | void HandleDouble(double value) override { | 
|  | log_ << "double: " << value << "\n"; | 
|  | } | 
|  |  | 
|  | void HandleInt32(int32_t value) override { log_ << "int: " << value << "\n"; } | 
|  |  | 
|  | void HandleBool(bool value) override { log_ << "bool: " << value << "\n"; } | 
|  |  | 
|  | void HandleNull() override { log_ << "null\n"; } | 
|  |  | 
|  | void HandleError(Status status) override { status_ = status; } | 
|  |  | 
|  | std::string str() const { return status_.ok() ? log_.str() : ""; } | 
|  |  | 
|  | Status status() const { return status_; } | 
|  |  | 
|  | private: | 
|  | std::ostringstream log_; | 
|  | Status status_; | 
|  | }; | 
|  |  | 
|  | class JsonParserTest : public ::testing::Test { | 
|  | protected: | 
|  | Log log_; | 
|  | }; | 
|  |  | 
|  | TEST_F(JsonParserTest, SimpleDictionary) { | 
|  | std::string json = "{\"foo\": 42}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: foo\n" | 
|  | "int: 42\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, UsAsciiDelCornerCase) { | 
|  | // DEL (0x7f) is a 7 bit US-ASCII character, and while it is a control | 
|  | // character according to Unicode, it's not considered a control | 
|  | // character in https://tools.ietf.org/html/rfc7159#section-7, so | 
|  | // it can be placed directly into the JSON string, without JSON escaping. | 
|  | std::string json = "{\"foo\": \"a\x7f\"}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: foo\n" | 
|  | "string16: a\x7f\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  |  | 
|  | // We've seen an implementation of UTF16ToUTF8 which would replace the DEL | 
|  | // character with ' ', so this simple roundtrip tests the routines in | 
|  | // encoding_test_helper.h, to make test failures of the above easier to | 
|  | // diagnose. | 
|  | std::vector<uint16_t> utf16 = UTF8ToUTF16(SpanFrom(json)); | 
|  | EXPECT_EQ(json, UTF16ToUTF8(SpanFrom(utf16))); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, Whitespace) { | 
|  | std::string json = "\n  {\n\"msg\"\n: \v\"Hello, world.\"\t\r}\t"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: msg\n" | 
|  | "string16: Hello, world.\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, NestedDictionary) { | 
|  | std::string json = "{\"foo\": {\"bar\": {\"baz\": 1}, \"bar2\": 2}}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: foo\n" | 
|  | "map begin\n" | 
|  | "string16: bar\n" | 
|  | "map begin\n" | 
|  | "string16: baz\n" | 
|  | "int: 1\n" | 
|  | "map end\n" | 
|  | "string16: bar2\n" | 
|  | "int: 2\n" | 
|  | "map end\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, Doubles) { | 
|  | std::string json = "{\"foo\": 3.1415, \"bar\": 31415e-4}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: foo\n" | 
|  | "double: 3.1415\n" | 
|  | "string16: bar\n" | 
|  | "double: 3.1415\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, Unicode) { | 
|  | // Globe character. 0xF0 0x9F 0x8C 0x8E in utf8, 0xD83C 0xDF0E in utf16. | 
|  | std::string json = "{\"msg\": \"Hello, \\uD83C\\uDF0E.\"}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: msg\n" | 
|  | "string16: Hello, 🌎.\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, Unicode_ParseUtf16) { | 
|  | // Globe character. utf8: 0xF0 0x9F 0x8C 0x8E; utf16: 0xD83C 0xDF0E. | 
|  | // Crescent moon character. utf8: 0xF0 0x9F 0x8C 0x99; utf16: 0xD83C 0xDF19. | 
|  |  | 
|  | // We provide the moon with json escape, but the earth as utf16 input. | 
|  | // Either way they arrive as utf8 (after decoding in log_.str()). | 
|  | std::vector<uint16_t> json = | 
|  | UTF8ToUTF16(SpanFrom("{\"space\": \"🌎 \\uD83C\\uDF19.\"}")); | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: space\n" | 
|  | "string16: 🌎 🌙.\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, Unicode_ParseUtf8) { | 
|  | // Used below: | 
|  | // гласность - example for 2 byte utf8, Russian word "glasnost" | 
|  | // 屋 - example for 3 byte utf8, Chinese word for "house" | 
|  | // 🌎 - example for 4 byte utf8: 0xF0 0x9F 0x8C 0x8E; utf16: 0xD83C 0xDF0E. | 
|  | // 🌙 - example for escapes: utf8: 0xF0 0x9F 0x8C 0x99; utf16: 0xD83C 0xDF19. | 
|  |  | 
|  | // We provide the moon with json escape, but the earth as utf8 input. | 
|  | // Either way they arrive as utf8 (after decoding in log_.str()). | 
|  | std::string json = | 
|  | "{" | 
|  | "\"escapes\": \"\\uD83C\\uDF19\"," | 
|  | "\"2 byte\":\"гласность\"," | 
|  | "\"3 byte\":\"屋\"," | 
|  | "\"4 byte\":\"🌎\"" | 
|  | "}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: escapes\n" | 
|  | "string16: 🌙\n" | 
|  | "string16: 2 byte\n" | 
|  | "string16: гласность\n" | 
|  | "string16: 3 byte\n" | 
|  | "string16: 屋\n" | 
|  | "string16: 4 byte\n" | 
|  | "string16: 🌎\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, UnprocessedInputRemainsError) { | 
|  | // Trailing junk after the valid JSON. | 
|  | std::string json = "{\"foo\": 3.1415} junk"; | 
|  | size_t junk_idx = json.find("junk"); | 
|  | EXPECT_NE(junk_idx, std::string::npos); | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_UNPROCESSED_INPUT_REMAINS, log_.status().error); | 
|  | EXPECT_EQ(junk_idx, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | std::string MakeNestedJson(int depth) { | 
|  | std::string json; | 
|  | for (int ii = 0; ii < depth; ++ii) | 
|  | json += "{\"foo\":"; | 
|  | json += "42"; | 
|  | for (int ii = 0; ii < depth; ++ii) | 
|  | json += "}"; | 
|  | return json; | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, StackLimitExceededError_BelowLimit) { | 
|  | // kStackLimit is 300 (see json_parser.cc). First let's | 
|  | // try with a small nested example. | 
|  | std::string json_3 = MakeNestedJson(3); | 
|  | ParseJSON(SpanFrom(json_3), &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | EXPECT_EQ( | 
|  | "map begin\n" | 
|  | "string16: foo\n" | 
|  | "map begin\n" | 
|  | "string16: foo\n" | 
|  | "map begin\n" | 
|  | "string16: foo\n" | 
|  | "int: 42\n" | 
|  | "map end\n" | 
|  | "map end\n" | 
|  | "map end\n", | 
|  | log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, StackLimitExceededError_AtLimit) { | 
|  | // Now with kStackLimit (300). | 
|  | std::string json_limit = MakeNestedJson(300); | 
|  | ParseJSON(span<uint8_t>(reinterpret_cast<const uint8_t*>(json_limit.data()), | 
|  | json_limit.size()), | 
|  | &log_); | 
|  | EXPECT_TRUE(log_.status().ok()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, StackLimitExceededError_AboveLimit) { | 
|  | // Now with kStackLimit + 1 (301) - it exceeds in the innermost instance. | 
|  | std::string exceeded = MakeNestedJson(301); | 
|  | ParseJSON(SpanFrom(exceeded), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_STACK_LIMIT_EXCEEDED, log_.status().error); | 
|  | EXPECT_EQ(strlen("{\"foo\":") * 301, log_.status().pos); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, StackLimitExceededError_WayAboveLimit) { | 
|  | // Now way past the limit. Still, the point of exceeding is 301. | 
|  | std::string far_out = MakeNestedJson(320); | 
|  | ParseJSON(SpanFrom(far_out), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_STACK_LIMIT_EXCEEDED, log_.status().error); | 
|  | EXPECT_EQ(strlen("{\"foo\":") * 301, log_.status().pos); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, NoInputError) { | 
|  | std::string json = ""; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_NO_INPUT, log_.status().error); | 
|  | EXPECT_EQ(0u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, InvalidTokenError) { | 
|  | std::string json = "|"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_INVALID_TOKEN, log_.status().error); | 
|  | EXPECT_EQ(0u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, InvalidNumberError) { | 
|  | // Mantissa exceeds max (the constant used here is int64_t max). | 
|  | std::string json = "1E9223372036854775807"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_INVALID_NUMBER, log_.status().error); | 
|  | EXPECT_EQ(0u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, InvalidStringError) { | 
|  | // \x22 is an unsupported escape sequence | 
|  | std::string json = "\"foo\\x22\""; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_INVALID_STRING, log_.status().error); | 
|  | EXPECT_EQ(0u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, UnexpectedArrayEndError) { | 
|  | std::string json = "[1,2,]"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_UNEXPECTED_ARRAY_END, log_.status().error); | 
|  | EXPECT_EQ(5u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, CommaOrArrayEndExpectedError) { | 
|  | std::string json = "[1,2 2"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED, | 
|  | log_.status().error); | 
|  | EXPECT_EQ(5u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, StringLiteralExpectedError) { | 
|  | // There's an error because the key bar, a string, is not terminated. | 
|  | std::string json = "{\"foo\": 3.1415, \"bar: 31415e-4}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_STRING_LITERAL_EXPECTED, log_.status().error); | 
|  | EXPECT_EQ(16u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, ColonExpectedError) { | 
|  | std::string json = "{\"foo\", 42}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_COLON_EXPECTED, log_.status().error); | 
|  | EXPECT_EQ(6u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, UnexpectedMapEndError) { | 
|  | std::string json = "{\"foo\": 42, }"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_UNEXPECTED_MAP_END, log_.status().error); | 
|  | EXPECT_EQ(12u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, CommaOrMapEndExpectedError) { | 
|  | // The second separator should be a comma. | 
|  | std::string json = "{\"foo\": 3.1415: \"bar\": 0}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_COMMA_OR_MAP_END_EXPECTED, log_.status().error); | 
|  | EXPECT_EQ(14u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | TEST_F(JsonParserTest, ValueExpectedError) { | 
|  | std::string json = "}"; | 
|  | ParseJSON(SpanFrom(json), &log_); | 
|  | EXPECT_EQ(Error::JSON_PARSER_VALUE_EXPECTED, log_.status().error); | 
|  | EXPECT_EQ(0u, log_.status().pos); | 
|  | EXPECT_EQ("", log_.str()); | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | class ConvertJSONToCBORTest : public ::testing::Test {}; | 
|  |  | 
|  | using ContainerTestTypes = ::testing::Types<std::vector<uint8_t>, std::string>; | 
|  | TYPED_TEST_SUITE(ConvertJSONToCBORTest, ContainerTestTypes); | 
|  |  | 
|  | TYPED_TEST(ConvertJSONToCBORTest, RoundTripValidJson) { | 
|  | std::string json_in = "{\"msg\":\"Hello, world.\",\"lst\":[1,2,3]}"; | 
|  | TypeParam json(json_in.begin(), json_in.end()); | 
|  | TypeParam cbor; | 
|  | { | 
|  | Status status = ConvertJSONToCBOR(SpanFrom(json), &cbor); | 
|  | EXPECT_EQ(Error::OK, status.error); | 
|  | EXPECT_EQ(Status::npos(), status.pos); | 
|  | } | 
|  | TypeParam roundtrip_json; | 
|  | { | 
|  | Status status = ConvertCBORToJSON(SpanFrom(cbor), &roundtrip_json); | 
|  | EXPECT_EQ(Error::OK, status.error); | 
|  | EXPECT_EQ(Status::npos(), status.pos); | 
|  | } | 
|  | EXPECT_EQ(json, roundtrip_json); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(ConvertJSONToCBORTest, RoundTripValidJson16) { | 
|  | std::vector<uint16_t> json16 = { | 
|  | '{', '"', 'm', 's',    'g',    '"', ':', '"', 'H', 'e', 'l', 'l', | 
|  | 'o', ',', ' ', 0xd83c, 0xdf0e, '.', '"', ',', '"', 'l', 's', 't', | 
|  | '"', ':', '[', '1',    ',',    '2', ',', '3', ']', '}'}; | 
|  | TypeParam cbor; | 
|  | { | 
|  | Status status = | 
|  | ConvertJSONToCBOR(span<uint16_t>(json16.data(), json16.size()), &cbor); | 
|  | EXPECT_EQ(Error::OK, status.error); | 
|  | EXPECT_EQ(Status::npos(), status.pos); | 
|  | } | 
|  | TypeParam roundtrip_json; | 
|  | { | 
|  | Status status = ConvertCBORToJSON(SpanFrom(cbor), &roundtrip_json); | 
|  | EXPECT_EQ(Error::OK, status.error); | 
|  | EXPECT_EQ(Status::npos(), status.pos); | 
|  | } | 
|  | std::string json = "{\"msg\":\"Hello, \\ud83c\\udf0e.\",\"lst\":[1,2,3]}"; | 
|  | TypeParam expected_json(json.begin(), json.end()); | 
|  | EXPECT_EQ(expected_json, roundtrip_json); | 
|  | } | 
|  | }  // namespace json | 
|  | }  // namespace crdtp |