diff options
Diffstat (limited to 'tests/json/test_json.cc')
-rw-r--r-- | tests/json/test_json.cc | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/tests/json/test_json.cc b/tests/json/test_json.cc new file mode 100644 index 0000000..1444081 --- /dev/null +++ b/tests/json/test_json.cc @@ -0,0 +1,244 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2014 Google Inc. See LICENSE for details. + * + * A set of tests for JSON parsing and serialization. + */ + +#include "tests/upb_test.h" +#include "upb/handlers.h" +#include "upb/symtab.h" +#include "upb/json/printer.h" +#include "upb/json/parser.h" +#include "upb/upb.h" + +#include <string> + +// Macros for readability in test case list: allows us to give TEST("...") / +// EXPECT("...") pairs. +#define TEST(x) x +#define EXPECT_SAME NULL +#define EXPECT(x) x +#define TEST_SENTINEL { NULL, NULL } + +struct TestCase { + const char* input; + const char* expected; +}; + +static TestCase kTestRoundtripMessages[] = { + // Test most fields here. + { + TEST("{\"optional_int32\":-42,\"optional_string\":\"Test\\u0001Message\"," + "\"optional_msg\":{\"foo\":42}," + "\"optional_bool\":true,\"repeated_msg\":[{\"foo\":1}," + "{\"foo\":2}]}"), + EXPECT_SAME + }, + // Test special escapes in strings. + { + TEST("{\"repeated_string\":[\"\\b\",\"\\r\",\"\\n\",\"\\f\",\"\\t\"," + "\"\uFFFF\"]}"), + EXPECT_SAME + }, + // Test enum symbolic names. + { + // The common case: parse and print the symbolic name. + TEST("{\"optional_enum\":\"A\"}"), + EXPECT_SAME + }, + { + // Unknown enum value: will be printed as an integer. + TEST("{\"optional_enum\":42}"), + EXPECT_SAME + }, + { + // Known enum value: we're happy to parse an integer but we will re-emit the + // symbolic name. + TEST("{\"optional_enum\":1}"), + EXPECT("{\"optional_enum\":\"B\"}") + }, + // UTF-8 tests: escapes -> literal UTF8 in output. + { + // Note double escape on \uXXXX: we want the escape to be processed by the + // JSON parser, not by the C++ compiler! + TEST("{\"optional_string\":\"\\u007F\"}"), + EXPECT("{\"optional_string\":\"\x7F\"}") + }, + { + TEST("{\"optional_string\":\"\\u0080\"}"), + EXPECT("{\"optional_string\":\"\xC2\x80\"}") + }, + { + TEST("{\"optional_string\":\"\\u07FF\"}"), + EXPECT("{\"optional_string\":\"\xDF\xBF\"}") + }, + { + TEST("{\"optional_string\":\"\\u0800\"}"), + EXPECT("{\"optional_string\":\"\xE0\xA0\x80\"}") + }, + { + TEST("{\"optional_string\":\"\\uFFFF\"}"), + EXPECT("{\"optional_string\":\"\xEF\xBF\xBF\"}") + }, + TEST_SENTINEL +}; + +static void AddField(upb::MessageDef* message, + int number, + const char* name, + upb_fieldtype_t type, + bool is_repeated, + const upb::Def* subdef = NULL) { + upb::reffed_ptr<upb::FieldDef> field(upb::FieldDef::New()); + upb::Status st; + field->set_name(name, &st); + field->set_type(type); + field->set_label(is_repeated ? UPB_LABEL_REPEATED : UPB_LABEL_OPTIONAL); + field->set_number(number, &st); + if (subdef) { + field->set_subdef(subdef, &st); + } + message->AddField(field, &st); +} + +static const upb::MessageDef* BuildTestMessage( + upb::reffed_ptr<upb::SymbolTable> symtab) { + upb::Status st; + + // Create SubMessage. + upb::reffed_ptr<upb::MessageDef> submsg(upb::MessageDef::New()); + submsg->set_full_name("SubMessage", &st); + AddField(submsg.get(), 1, "foo", UPB_TYPE_INT32, false); + + // Create MyEnum. + upb::reffed_ptr<upb::EnumDef> myenum(upb::EnumDef::New()); + myenum->set_full_name("MyEnum", &st); + myenum->AddValue("A", 0, &st); + myenum->AddValue("B", 1, &st); + myenum->AddValue("C", 2, &st); + + // Create TestMessage. + upb::reffed_ptr<upb::MessageDef> md(upb::MessageDef::New()); + md->set_full_name("TestMessage", &st); + + AddField(md.get(), 1, "optional_int32", UPB_TYPE_INT32, false); + AddField(md.get(), 2, "optional_int64", UPB_TYPE_INT64, false); + AddField(md.get(), 3, "optional_uint32", UPB_TYPE_UINT32, false); + AddField(md.get(), 4, "optional_uint64", UPB_TYPE_UINT64, false); + AddField(md.get(), 5, "optional_string", UPB_TYPE_STRING, false); + AddField(md.get(), 6, "optional_bytes", UPB_TYPE_BYTES, false); + AddField(md.get(), 7, "optional_bool" , UPB_TYPE_BOOL, false); + AddField(md.get(), 8, "optional_msg" , UPB_TYPE_MESSAGE, false, + upb::upcast(submsg.get())); + AddField(md.get(), 9, "optional_enum", UPB_TYPE_ENUM, false, + upb::upcast(myenum.get())); + + AddField(md.get(), 11, "repeated_int32", UPB_TYPE_INT32, true); + AddField(md.get(), 12, "repeated_int64", UPB_TYPE_INT64, true); + AddField(md.get(), 13, "repeated_uint32", UPB_TYPE_UINT32, true); + AddField(md.get(), 14, "repeated_uint64", UPB_TYPE_UINT64, true); + AddField(md.get(), 15, "repeated_string", UPB_TYPE_STRING, true); + AddField(md.get(), 16, "repeated_bytes", UPB_TYPE_BYTES, true); + AddField(md.get(), 17, "repeated_bool" , UPB_TYPE_BOOL, true); + AddField(md.get(), 18, "repeated_msg" , UPB_TYPE_MESSAGE, true, + upb::upcast(submsg.get())); + AddField(md.get(), 19, "optional_enum", UPB_TYPE_ENUM, true, + upb::upcast(myenum.get())); + + // Add both to our symtab. + upb::Def* defs[3] = { + upb::upcast(submsg.ReleaseTo(&defs)), + upb::upcast(myenum.ReleaseTo(&defs)), + upb::upcast(md.ReleaseTo(&defs)), + }; + symtab->Add(defs, 3, &defs, &st); + + // Return TestMessage. + return symtab->LookupMessage("TestMessage"); +} + +class StringSink { + public: + StringSink() { + upb_byteshandler_init(&byteshandler_); + upb_byteshandler_setstring(&byteshandler_, &str_handler, NULL); + upb_bytessink_reset(&bytessink_, &byteshandler_, &s_); + } + ~StringSink() { } + + upb_bytessink* Sink() { return &bytessink_; } + + const std::string& Data() { return s_; } + + private: + + static size_t str_handler(void* _closure, const void* hd, + const char* data, size_t len, + const upb_bufhandle* handle) { + UPB_UNUSED(hd); + UPB_UNUSED(handle); + std::string* s = static_cast<std::string*>(_closure); + std::string appended(data, len); + s->append(data, len); + return len; + } + + upb_byteshandler byteshandler_; + upb_bytessink bytessink_; + std::string s_; +}; + +// Starts with a message in JSON format, parses and directly serializes again, +// and compares the result. +void test_json_roundtrip() { + upb::reffed_ptr<upb::SymbolTable> symtab(upb::SymbolTable::New()); + const upb::MessageDef* md = BuildTestMessage(symtab.get()); + upb::reffed_ptr<const upb::Handlers> serialize_handlers( + upb::json::Printer::NewHandlers(md)); + + for (const TestCase* test_case = kTestRoundtripMessages; + test_case->input != NULL; test_case++) { + + const char *json_src = test_case->input; + const char *json_expected = test_case->expected; + if (json_expected == EXPECT_SAME) { + json_expected = json_src; + } + + upb::Status st; + upb::json::Parser parser(&st); + upb::json::Printer printer(serialize_handlers.get()); + StringSink data_sink; + + parser.ResetOutput(printer.input()); + printer.ResetOutput(data_sink.Sink()); + + bool ok = upb::BufferSource::PutBuffer(json_src, strlen(json_src), + parser.input()); + if (!ok) { + fprintf(stderr, "upb parse error: %s\n", st.error_message()); + } + ASSERT(ok); + + if (memcmp(json_expected, + data_sink.Data().data(), + data_sink.Data().size())) { + fprintf(stderr, + "JSON parse/serialize roundtrip result differs:\n" + "Original:\n%s\nParsed/Serialized:\n%s\n", + json_src, data_sink.Data().c_str()); + abort(); + } + } +} + +extern "C" { +int run_tests(int argc, char *argv[]) { + UPB_UNUSED(argc); + UPB_UNUSED(argv); + test_json_roundtrip(); + return 0; +} +} |