/* * Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2011-2014 Kazuho Oku, Yasuhiro Matsumoto, Shigeo Mitsunari * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "picojson.h" #include "picotest/picotest.h" #ifdef _MSC_VER #pragma warning(disable : 4127) // conditional expression is constant #endif using namespace std; #define is(x, y, name) _ok((x) == (y), name) #include #include #include #include int main(void) { #if PICOJSON_USE_LOCALE setlocale(LC_ALL, ""); #endif // constructors #define TEST(expr, expected) \ is(picojson::value expr .serialize(), string(expected), "picojson::value" #expr) TEST( (true), "true"); TEST( (false), "false"); TEST( (42.0), "42"); TEST( (string("hello")), "\"hello\""); TEST( ("hello"), "\"hello\""); TEST( ("hello", 4), "\"hell\""); { double a = 1; for (int i = 0; i < 1024; i++) { picojson::value vi(a); std::stringstream ss; ss << vi; picojson::value vo; ss >> vo; double b = vo.get(); if ((i < 53 && a != b) || fabs(a - b) / b > 1e-8) { printf("ng i=%d a=%.18e b=%.18e\n", i, a, b); } a *= 2; } } #undef TEST #define TEST(in, type, cmp, serialize_test) { \ picojson::value v; \ const char* s = in; \ string err = picojson::parse(v, s, s + strlen(s)); \ _ok(err.empty(), in " no error"); \ _ok(v.is(), in " check type"); \ is(v.get(), static_cast(cmp), in " correct output"); \ is(*s, '\0', in " read to eof"); \ if (serialize_test) { \ is(v.serialize(), string(in), in " serialize"); \ } \ } TEST("false", bool, false, true); TEST("true", bool, true, true); TEST("90.5", double, 90.5, false); TEST("1.7976931348623157e+308", double, DBL_MAX, false); TEST("\"hello\"", string, string("hello"), true); TEST("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", string, string("\"\\/\b\f\n\r\t"), true); TEST("\"\\u0061\\u30af\\u30ea\\u30b9\"", string, string("a\xe3\x82\xaf\xe3\x83\xaa\xe3\x82\xb9"), false); TEST("\"\\ud840\\udc0b\"", string, string("\xf0\xa0\x80\x8b"), false); #ifdef PICOJSON_USE_INT64 TEST("0", int64_t, 0, true); TEST("-9223372036854775808", int64_t, std::numeric_limits::min(), true); TEST("9223372036854775807", int64_t, std::numeric_limits::max(), true); #endif #undef TEST #define TEST(type, expr) { \ picojson::value v; \ const char *s = expr; \ string err = picojson::parse(v, s, s + strlen(s)); \ _ok(err.empty(), "empty " #type " no error"); \ _ok(v.is(), "empty " #type " check type"); \ _ok(v.get().empty(), "check " #type " array size"); \ } TEST(array, "[]"); TEST(object, "{}"); #undef TEST { picojson::value v; const char *s = "[1,true,\"hello\"]"; string err = picojson::parse(v, s, s + strlen(s)); _ok(err.empty(), "array no error"); _ok(v.is(), "array check type"); is(v.get().size(), size_t(3), "check array size"); _ok(v.contains(0), "check contains array[0]"); _ok(v.get(0).is(), "check array[0] type"); is(v.get(0).get(), 1.0, "check array[0] value"); _ok(v.contains(1), "check contains array[1]"); _ok(v.get(1).is(), "check array[1] type"); _ok(v.get(1).get(), "check array[1] value"); _ok(v.contains(2), "check contains array[2]"); _ok(v.get(2).is(), "check array[2] type"); is(v.get(2).get(), string("hello"), "check array[2] value"); _ok(!v.contains(3), "check not contains array[3]"); } { picojson::value v; const char *s = "{ \"a\": true }"; string err = picojson::parse(v, s, s + strlen(s)); _ok(err.empty(), "object no error"); _ok(v.is(), "object check type"); is(v.get().size(), size_t(1), "check object size"); _ok(v.contains("a"), "check contains property"); _ok(v.get("a").is(), "check bool property exists"); is(v.get("a").get(), true, "check bool property value"); is(v.serialize(), string("{\"a\":true}"), "serialize object"); _ok(!v.contains("z"), "check not contains property"); } #define TEST(json, msg) do { \ picojson::value v; \ const char *s = json; \ string err = picojson::parse(v, s, s + strlen(s)); \ is(err, string("syntax error at line " msg), msg); \ } while (0) TEST("falsoa", "1 near: oa"); TEST("{]", "1 near: ]"); TEST("\n\bbell", "2 near: bell"); TEST("\"abc\nd\"", "1 near: "); #undef TEST { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); s = "{ \"d\": 2.0, \"b\": true, \"a\": [1,2,\"three\"] }"; err = picojson::parse(v2, s, s + strlen(s)); _ok((v1 == v2), "check == operator in deep comparison"); } { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); s = "{ \"d\": 2.0, \"a\": [1,\"three\"], \"b\": true }"; err = picojson::parse(v2, s, s + strlen(s)); _ok((v1 != v2), "check != operator for array in deep comparison"); } { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); s = "{ \"d\": 2.0, \"a\": [1,2,\"three\"], \"b\": false }"; err = picojson::parse(v2, s, s + strlen(s)); _ok((v1 != v2), "check != operator for object in deep comparison"); } { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); picojson::object& o = v1.get(); o.erase("b"); picojson::array& a = o["a"].get(); picojson::array::iterator i; i = std::remove(a.begin(), a.end(), picojson::value(std::string("three"))); a.erase(i, a.end()); s = "{ \"a\": [1,2], \"d\": 2 }"; err = picojson::parse(v2, s, s + strlen(s)); _ok((v1 == v2), "check erase()"); } _ok(picojson::value(3.0).serialize() == "3", "integral number should be serialized as a integer"); { const char* s = "{ \"a\": [1,2], \"d\": 2 }"; picojson::null_parse_context ctx; string err; picojson::_parse(ctx, s, s + strlen(s), &err); _ok(err.empty(), "null_parse_context"); } { picojson::value v1, v2; v1 = picojson::value(true); swap(v1, v2); _ok(v1.is(), "swap (null)"); _ok(v2.get() == true, "swap (bool)"); v1 = picojson::value("a"); v2 = picojson::value(1.0); swap(v1, v2); _ok(v1.get() == 1.0, "swap (dobule)"); _ok(v2.get() == "a", "swap (string)"); v1 = picojson::value(picojson::object()); v2 = picojson::value(picojson::array()); swap(v1, v2); _ok(v1.is(), "swap (array)"); _ok(v2.is(), "swap (object)"); } { picojson::value v; const char *s = "{ \"a\": 1, \"b\": [ 2, { \"b1\": \"abc\" } ], \"c\": {}, \"d\": [] }"; string err; err = picojson::parse(v, s, s + strlen(s)); _ok(err.empty(), "parse test data for prettifying output"); _ok(v.serialize() == "{\"a\":1,\"b\":[2,{\"b1\":\"abc\"}],\"c\":{},\"d\":[]}", "non-prettifying output"); _ok(v.serialize(true) == "{\n \"a\": 1,\n \"b\": [\n 2,\n {\n \"b1\": \"abc\"\n }\n ],\n \"c\": {},\n \"d\": []\n}\n", "prettifying output"); } try { picojson::value v(std::numeric_limits::quiet_NaN()); _ok(false, "should not accept NaN"); } catch (std::overflow_error e) { _ok(true, "should not accept NaN"); } try { picojson::value v(std::numeric_limits::infinity()); _ok(false, "should not accept infinity"); } catch (std::overflow_error e) { _ok(true, "should not accept infinity"); } try { picojson::value v(123.); _ok(! v.is(), "is() should return false"); v.get(); _ok(false, "get() should raise an error"); } catch (std::runtime_error e) { _ok(true, "get() should raise an error"); } #ifdef PICOJSON_USE_INT64 { picojson::value v1((int64_t)123); _ok(v1.is(), "is int64_t"); _ok(v1.is(), "is double as well"); _ok(v1.serialize() == "123", "serialize the value"); _ok(v1.get() == 123, "value is correct as int64_t"); _ok(v1.get(), "value is correct as double"); _ok(! v1.is(), "is no more int64_type once get() is called"); _ok(v1.is(), "and is still a double"); const char *s = "-9223372036854775809"; _ok(picojson::parse(v1, s, s + strlen(s)).empty(), "parse underflowing int64_t"); _ok(! v1.is(), "underflowing int is not int64_t"); _ok(v1.is(), "underflowing int is double"); _ok(v1.get() + 9.22337203685478e+18 < 65536, "double value is somewhat correct"); } #endif { picojson::value v; std::string err = picojson::parse(v, "[ 1, \"abc\" ]"); _ok(err.empty(), "simple API no error"); _ok(v.is(), "simple API return type is array"); is(v.get().size(), 2, "simple API array size"); _ok(v.get()[0].is(), "simple API type #0"); is(v.get()[0].get(), 1, "simple API value #0"); _ok(v.get()[1].is(), "simple API type #1"); is(v.get()[1].get(), "abc", "simple API value #1"); } { picojson::value v1((double) 0); _ok(! v1.evaluate_as_boolean(), "((double) 0) is false"); picojson::value v2((double) 1); _ok(v2.evaluate_as_boolean(), "((double) 1) is true"); #ifdef PICOJSON_USE_INT64 picojson::value v3((int64_t) 0); _ok(! v3.evaluate_as_boolean(), "((int64_t) 0) is false"); picojson::value v4((int64_t) 1); _ok(v4.evaluate_as_boolean(), "((int64_t) 1) is true"); #endif } return done_testing(); }