cannam@242: /* cannam@242: * Define JSON11_TEST_CUSTOM_CONFIG to 1 if you want to build this tester into cannam@242: * your own unit-test framework rather than a stand-alone program. By setting cannam@242: * The values of the variables included below, you can insert your own custom cannam@242: * code into this file as it builds, in order to make it into a test case for cannam@242: * your favorite framework. cannam@242: */ cannam@242: #if !JSON11_TEST_CUSTOM_CONFIG cannam@242: #define JSON11_TEST_CPP_PREFIX_CODE cannam@242: #define JSON11_TEST_CPP_SUFFIX_CODE cannam@242: #define JSON11_TEST_STANDALONE_MAIN 1 cannam@242: #define JSON11_TEST_CASE(name) static void name() cannam@242: #define JSON11_TEST_ASSERT(b) assert(b) cannam@242: #ifdef NDEBUG cannam@242: #undef NDEBUG//at now assert will work even in Release build cannam@242: #endif cannam@242: #endif // JSON11_TEST_CUSTOM_CONFIG cannam@242: cannam@242: /* cannam@242: * Enable or disable code which demonstrates the behavior change in Xcode 7 / Clang 3.7, cannam@242: * introduced by DR1467 and described here: https://github.com/dropbox/json11/issues/86 cannam@242: * Defaults to off since it doesn't appear the standards committee is likely to act cannam@242: * on this, so it needs to be considered normal behavior. cannam@242: */ cannam@242: #ifndef JSON11_ENABLE_DR1467_CANARY cannam@242: #define JSON11_ENABLE_DR1467_CANARY 0 cannam@242: #endif cannam@242: cannam@242: /* cannam@242: * Beginning of standard source file, which makes use of the customizations above. cannam@242: */ cannam@242: #include cannam@150: #include cannam@150: #include cannam@150: #include cannam@150: #include cannam@150: #include cannam@150: #include "json11.hpp" cannam@150: #include cannam@150: #include cannam@150: #include cannam@242: #include cannam@242: #include cannam@242: cannam@242: // Insert user-defined prefix code (includes, function declarations, etc) cannam@242: // to set up a custom test suite cannam@242: JSON11_TEST_CPP_PREFIX_CODE cannam@150: cannam@150: using namespace json11; cannam@150: using std::string; cannam@150: cannam@150: // Check that Json has the properties we want. cannam@150: #define CHECK_TRAIT(x) static_assert(std::x::value, #x) cannam@150: CHECK_TRAIT(is_nothrow_constructible); cannam@150: CHECK_TRAIT(is_nothrow_default_constructible); cannam@150: CHECK_TRAIT(is_copy_constructible); cannam@150: CHECK_TRAIT(is_nothrow_move_constructible); cannam@150: CHECK_TRAIT(is_copy_assignable); cannam@150: CHECK_TRAIT(is_nothrow_move_assignable); cannam@150: CHECK_TRAIT(is_nothrow_destructible); cannam@150: cannam@242: JSON11_TEST_CASE(json11_test) { cannam@242: const string simple_test = cannam@242: R"({"k1":"v1", "k2":42, "k3":["a",123,true,false,null]})"; cannam@242: cannam@242: string err; cannam@242: const auto json = Json::parse(simple_test, err); cannam@242: cannam@242: std::cout << "k1: " << json["k1"].string_value() << "\n"; cannam@242: std::cout << "k3: " << json["k3"].dump() << "\n"; cannam@242: cannam@242: for (auto &k : json["k3"].array_items()) { cannam@242: std::cout << " - " << k.dump() << "\n"; cannam@242: } cannam@242: cannam@242: string comment_test = R"({ cannam@242: // comment /* with nested comment */ cannam@242: "a": 1, cannam@242: // comment cannam@242: // continued cannam@242: "b": "text", cannam@242: /* multi cannam@242: line cannam@242: comment cannam@242: // line-comment-inside-multiline-comment cannam@242: */ cannam@242: // and single-line comment cannam@242: // and single-line comment /* multiline inside single line */ cannam@242: "c": [1, 2, 3] cannam@242: // and single-line comment at end of object cannam@242: })"; cannam@242: cannam@242: string err_comment; cannam@242: auto json_comment = Json::parse( cannam@242: comment_test, err_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(!json_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(err_comment.empty()); cannam@242: cannam@242: comment_test = "{\"a\": 1}//trailing line comment"; cannam@242: json_comment = Json::parse( cannam@242: comment_test, err_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(!json_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(err_comment.empty()); cannam@242: cannam@242: comment_test = "{\"a\": 1}/*trailing multi-line comment*/"; cannam@242: json_comment = Json::parse( cannam@242: comment_test, err_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(!json_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(err_comment.empty()); cannam@242: cannam@242: string failing_comment_test = "{\n/* unterminated comment\n\"a\": 1,\n}"; cannam@242: string err_failing_comment; cannam@242: auto json_failing_comment = Json::parse( cannam@242: failing_comment_test, err_failing_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(json_failing_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(!err_failing_comment.empty()); cannam@242: cannam@242: failing_comment_test = "{\n/* unterminated trailing comment }"; cannam@242: json_failing_comment = Json::parse( cannam@242: failing_comment_test, err_failing_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(json_failing_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(!err_failing_comment.empty()); cannam@242: cannam@242: failing_comment_test = "{\n/ / bad comment }"; cannam@242: json_failing_comment = Json::parse( cannam@242: failing_comment_test, err_failing_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(json_failing_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(!err_failing_comment.empty()); cannam@242: cannam@242: failing_comment_test = "{// bad comment }"; cannam@242: json_failing_comment = Json::parse( cannam@242: failing_comment_test, err_failing_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(json_failing_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(!err_failing_comment.empty()); cannam@242: cannam@242: failing_comment_test = "{\n\"a\": 1\n}/"; cannam@242: json_failing_comment = Json::parse( cannam@242: failing_comment_test, err_failing_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(json_failing_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(!err_failing_comment.empty()); cannam@242: cannam@242: failing_comment_test = "{/* bad\ncomment *}"; cannam@242: json_failing_comment = Json::parse( cannam@242: failing_comment_test, err_failing_comment, JsonParse::COMMENTS); cannam@242: JSON11_TEST_ASSERT(json_failing_comment.is_null()); cannam@242: JSON11_TEST_ASSERT(!err_failing_comment.empty()); cannam@242: cannam@242: std::list l1 { 1, 2, 3 }; cannam@242: std::vector l2 { 1, 2, 3 }; cannam@242: std::set l3 { 1, 2, 3 }; cannam@242: JSON11_TEST_ASSERT(Json(l1) == Json(l2)); cannam@242: JSON11_TEST_ASSERT(Json(l2) == Json(l3)); cannam@242: cannam@242: std::map m1 { { "k1", "v1" }, { "k2", "v2" } }; cannam@242: std::unordered_map m2 { { "k1", "v1" }, { "k2", "v2" } }; cannam@242: JSON11_TEST_ASSERT(Json(m1) == Json(m2)); cannam@242: cannam@242: // Json literals cannam@242: const Json obj = Json::object({ cannam@242: { "k1", "v1" }, cannam@242: { "k2", 42.0 }, cannam@242: { "k3", Json::array({ "a", 123.0, true, false, nullptr }) }, cannam@242: }); cannam@242: cannam@242: std::cout << "obj: " << obj.dump() << "\n"; cannam@242: JSON11_TEST_ASSERT(obj.dump() == "{\"k1\": \"v1\", \"k2\": 42, \"k3\": [\"a\", 123, true, false, null]}"); cannam@242: cannam@242: JSON11_TEST_ASSERT(Json("a").number_value() == 0); cannam@242: JSON11_TEST_ASSERT(Json("a").string_value() == "a"); cannam@242: JSON11_TEST_ASSERT(Json().number_value() == 0); cannam@242: cannam@242: JSON11_TEST_ASSERT(obj == json); cannam@242: JSON11_TEST_ASSERT(Json(42) == Json(42.0)); cannam@242: JSON11_TEST_ASSERT(Json(42) != Json(42.1)); cannam@242: cannam@242: const string unicode_escape_test = cannam@242: R"([ "blah\ud83d\udca9blah\ud83dblah\udca9blah\u0000blah\u1234" ])"; cannam@242: cannam@242: const char utf8[] = "blah" "\xf0\x9f\x92\xa9" "blah" "\xed\xa0\xbd" "blah" cannam@242: "\xed\xb2\xa9" "blah" "\0" "blah" "\xe1\x88\xb4"; cannam@242: cannam@242: Json uni = Json::parse(unicode_escape_test, err); cannam@242: JSON11_TEST_ASSERT(uni[0].string_value().size() == (sizeof utf8) - 1); cannam@242: JSON11_TEST_ASSERT(std::memcmp(uni[0].string_value().data(), utf8, sizeof utf8) == 0); cannam@242: cannam@242: // Demonstrates the behavior change in Xcode 7 / Clang 3.7, introduced by DR1467 cannam@242: // and described here: https://llvm.org/bugs/show_bug.cgi?id=23812 cannam@242: if (JSON11_ENABLE_DR1467_CANARY) { cannam@242: Json nested_array = Json::array { Json::array { 1, 2, 3 } }; cannam@242: JSON11_TEST_ASSERT(nested_array.is_array()); cannam@242: JSON11_TEST_ASSERT(nested_array.array_items().size() == 1); cannam@242: JSON11_TEST_ASSERT(nested_array.array_items()[0].is_array()); cannam@242: JSON11_TEST_ASSERT(nested_array.array_items()[0].array_items().size() == 3); cannam@242: } cannam@242: cannam@242: { cannam@242: const std::string good_json = R"( {"k1" : "v1"})"; cannam@242: const std::string bad_json1 = good_json + " {"; cannam@242: const std::string bad_json2 = good_json + R"({"k2":"v2", "k3":[)"; cannam@242: struct TestMultiParse { cannam@242: std::string input; cannam@242: std::string::size_type expect_parser_stop_pos; cannam@242: size_t expect_not_empty_elms_count; cannam@242: Json expect_parse_res; cannam@242: } tests[] = { cannam@242: {" {", 0, 0, {}}, cannam@242: {good_json, good_json.size(), 1, Json(std::map{ { "k1", "v1" } })}, cannam@242: {bad_json1, good_json.size() + 1, 1, Json(std::map{ { "k1", "v1" } })}, cannam@242: {bad_json2, good_json.size(), 1, Json(std::map{ { "k1", "v1" } })}, cannam@242: {"{}", 2, 1, Json::object{}}, cannam@242: }; cannam@242: for (const auto &tst : tests) { cannam@242: std::string::size_type parser_stop_pos; cannam@242: std::string err; cannam@242: auto res = Json::parse_multi(tst.input, parser_stop_pos, err); cannam@242: JSON11_TEST_ASSERT(parser_stop_pos == tst.expect_parser_stop_pos); cannam@242: JSON11_TEST_ASSERT( cannam@242: (size_t)std::count_if(res.begin(), res.end(), cannam@242: [](const Json& j) { return !j.is_null(); }) cannam@242: == tst.expect_not_empty_elms_count); cannam@242: if (!res.empty()) { cannam@242: JSON11_TEST_ASSERT(tst.expect_parse_res == res[0]); cannam@242: } cannam@242: } cannam@242: } cannam@242: cannam@242: Json my_json = Json::object { cannam@242: { "key1", "value1" }, cannam@242: { "key2", false }, cannam@242: { "key3", Json::array { 1, 2, 3 } }, cannam@242: }; cannam@242: std::string json_obj_str = my_json.dump(); cannam@242: std::cout << "json_obj_str: " << json_obj_str << "\n"; cannam@242: JSON11_TEST_ASSERT(json_obj_str == "{\"key1\": \"value1\", \"key2\": false, \"key3\": [1, 2, 3]}"); cannam@242: cannam@242: class Point { cannam@242: public: cannam@242: int x; cannam@242: int y; cannam@242: Point (int x, int y) : x(x), y(y) {} cannam@242: Json to_json() const { return Json::array { x, y }; } cannam@242: }; cannam@242: cannam@242: std::vector points = { { 1, 2 }, { 10, 20 }, { 100, 200 } }; cannam@242: std::string points_json = Json(points).dump(); cannam@242: std::cout << "points_json: " << points_json << "\n"; cannam@242: JSON11_TEST_ASSERT(points_json == "[[1, 2], [10, 20], [100, 200]]"); cannam@242: } cannam@242: cannam@242: #if JSON11_TEST_STANDALONE_MAIN cannam@242: cannam@242: static void parse_from_stdin() { cannam@150: string buf; cannam@150: string line; cannam@150: while (std::getline(std::cin, line)) { cannam@150: buf += line + "\n"; cannam@150: } cannam@150: cannam@150: string err; cannam@150: auto json = Json::parse(buf, err); cannam@150: if (!err.empty()) { cannam@150: printf("Failed: %s\n", err.c_str()); cannam@150: } else { cannam@150: printf("Result: %s\n", json.dump().c_str()); cannam@150: } cannam@150: } cannam@150: cannam@150: int main(int argc, char **argv) { cannam@150: if (argc == 2 && argv[1] == string("--stdin")) { cannam@150: parse_from_stdin(); cannam@150: return 0; cannam@150: } cannam@150: cannam@242: json11_test(); cannam@242: } cannam@150: cannam@242: #endif // JSON11_TEST_STANDALONE_MAIN cannam@150: cannam@242: // Insert user-defined suffix code (function definitions, etc) cannam@242: // to set up a custom test suite cannam@242: JSON11_TEST_CPP_SUFFIX_CODE