#include "json/serialization.h" #ifdef JSON_GOOGLE_TEST #include "gtest/gtest.h" #include #include #else #include #endif using namespace NSJSBase; using namespace NSJSON; #ifdef JSON_GOOGLE_TEST class CJSONTest : public testing::Test { public: void SetUp() override { // create and enter context m_pContext = new CJSContext(); m_pContext->Enter(); } void TearDown() override { m_pContext->Exit(); } bool compare(const CValue& value, JSSmart jsValue, bool makeExpects = true) { if (value.IsUndefined()) { if (makeExpects) EXPECT_TRUE(jsValue->isUndefined()); return jsValue->isUndefined(); } if (value.IsNull()) { if (makeExpects) EXPECT_TRUE(jsValue->isNull()); return jsValue->isNull(); } if (value.IsArray()) { if (makeExpects) EXPECT_TRUE(jsValue->isArray()); if (!jsValue->isArray()) return false; JSSmart jsArr = jsValue->toArray(); const int len = value.GetCount(); if (makeExpects) EXPECT_EQ(len, jsArr->getCount()); if (len != jsArr->getCount()) return false; for (int i = 0; i < len; i++) { if (!compare(value[i], jsArr->get(i), makeExpects)) { if (makeExpects) ADD_FAILURE() << "Array values at index [" << i << "] are different!"; return false; } } } else if (value.IsTypedArray()) { if (makeExpects) EXPECT_TRUE(jsValue->isTypedArray()); if (!jsValue->isTypedArray()) return false; JSSmart jsTypedArr = jsValue->toTypedArray(); if (makeExpects) EXPECT_EQ(value.GetCount(), jsTypedArr->getCount()); if (value.GetCount() != jsTypedArr->getCount()) return false; // compare pointers, not values if (makeExpects) EXPECT_EQ(value.GetData(), jsTypedArr->getData().Data); if (value.GetData() != jsTypedArr->getData().Data) return false; } else if (value.IsObject()) { if (makeExpects) EXPECT_TRUE(jsValue->isObject()); if (!jsValue->isObject()) return false; JSSmart jsObj = jsValue->toObject(); std::vector properties = value.GetPropertyNames(); std::vector jsProperties = jsObj->getPropertyNames(); const int len = properties.size(); if (makeExpects) EXPECT_EQ(len, jsProperties.size()); if (len != jsProperties.size()) return false; // sort both vectors since the order of properties may vary in them std::sort(properties.begin(), properties.end()); std::sort(jsProperties.begin(), jsProperties.end()); for (int i = 0; i < len; i++) { if (makeExpects) EXPECT_EQ(properties[i], jsProperties[i]); if (properties[i] != jsProperties[i]) return false; const char* sProperty = properties[i].c_str(); if (!compare(value[sProperty], jsObj->get(sProperty), makeExpects)) { if (makeExpects) ADD_FAILURE() << "Object property values for property \"" << sProperty << "\" are different!"; return false; } } } else { // primitive types if (value.IsBool()) { if (makeExpects) EXPECT_TRUE(jsValue->isBool()); if (!jsValue->isBool()) return false; bool val = (bool)value; bool jsVal = jsValue->toBool(); if (makeExpects) EXPECT_EQ(val, jsVal); if (val != jsVal) return false; } else if (value.IsInt()) { if (makeExpects) EXPECT_TRUE(jsValue->isNumber()); if (!jsValue->isNumber()) return false; int val = (int)value; int jsVal = jsValue->toInt32(); if (makeExpects) EXPECT_EQ(val, jsVal); if (val != jsVal) return false; } else if (value.IsDouble()) { if (makeExpects) EXPECT_TRUE(jsValue->isNumber()); if (!jsValue->isNumber()) return false; double val = (double)value; double jsVal = jsValue->toDouble(); if (!std::isnan(val)) { // strict check without tolerance if (makeExpects) EXPECT_EQ(val, jsVal); if (val != jsVal) return false; } else { if (makeExpects) EXPECT_TRUE(std::isnan(jsVal)); if (!std::isnan(jsVal)) return false; } } else { if (makeExpects) { EXPECT_TRUE(value.IsString()); EXPECT_TRUE(jsValue->isString()); } if (!jsValue->isString()) return false; std::string val = value.ToStringA(); std::string jsVal = jsValue->toStringA(); if (makeExpects) EXPECT_EQ(val, jsVal); if (val != jsVal) return false; } } return true; } public: JSSmart m_pContext; }; // --------- CValue basic functinality tests ---------- TEST_F(CJSONTest, undefined_from_default_constructor) { CValue val; JSSmart jsVal = CJSContext::createUndefined(); EXPECT_TRUE(compare(val, jsVal)); val = 0; EXPECT_FALSE(compare(val, jsVal, false)); } TEST_F(CJSONTest, undefined_from_static_method) { CValue val = CValue::CreateUndefined(); JSSmart jsVal = CJSContext::createUndefined(); EXPECT_TRUE(compare(val, jsVal)); val = 5; EXPECT_FALSE(compare(val, jsVal, false)); } TEST_F(CJSONTest, null_) { CValue val = CValue::CreateNull(); JSSmart jsVal = CJSContext::createNull(); EXPECT_TRUE(compare(val, jsVal)); val = CValue(); EXPECT_FALSE(compare(val, jsVal, false)); } TEST_F(CJSONTest, bool_) { CValue val = true; JSSmart jsVal = CJSContext::createBool(true); EXPECT_TRUE(compare(val, jsVal)); val = false; EXPECT_FALSE(compare(val, jsVal, false)); } TEST_F(CJSONTest, int_) { CValue val = 42; EXPECT_TRUE(val.IsDouble()); EXPECT_TRUE(val.IsInt()); JSSmart jsVal = CJSContext::createInt(42); EXPECT_TRUE(compare(val, jsVal)); val = 100; EXPECT_FALSE(compare(val, jsVal, false)); val = 3; jsVal = CJSContext::createDouble(3.0); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, double_) { CValue val = 3.141592; EXPECT_FALSE(val.IsInt()); JSSmart jsVal = CJSContext::createDouble(3.141592); EXPECT_TRUE(compare(val, jsVal)); val = 2.81828; jsVal = CJSContext::createDouble(3.0); EXPECT_FALSE(compare(val, jsVal, false)); val = 3.0; jsVal = CJSContext::createInt(3); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, string_char_constructor) { CValue val = "test"; JSSmart jsVal = CJSContext::createString("test"); EXPECT_TRUE(compare(val, jsVal)); jsVal = CJSContext::createString(L"test"); EXPECT_TRUE(compare(val, jsVal)); val = ""; EXPECT_FALSE(compare(val, jsVal, false)); } TEST_F(CJSONTest, string_string_constructor) { CValue val = std::string("test"); JSSmart jsVal = CJSContext::createString("test"); EXPECT_TRUE(compare(val, jsVal)); jsVal = CJSContext::createString(L"test"); EXPECT_TRUE(compare(val, jsVal)); val = ""; EXPECT_FALSE(compare(val, jsVal, false)); } TEST_F(CJSONTest, wstring_wchar_constructor) { CValue val = L"тест"; JSSmart jsVal = CJSContext::createString(L"тест"); EXPECT_TRUE(compare(val, jsVal)); val = "test"; EXPECT_FALSE(compare(val, jsVal, false)); jsVal = CJSContext::createString(L"test"); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, wstring_wstring_constructor) { CValue val = std::wstring(L"тест"); JSSmart jsVal = CJSContext::createString(L"тест"); EXPECT_TRUE(compare(val, jsVal)); val = "test"; EXPECT_FALSE(compare(val, jsVal, false)); jsVal = CJSContext::createString(L"test"); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, array_from_static_method) { CValue arr = CValue::CreateArray(4); arr[0] = CValue::CreateNull(); arr[1] = 42; arr[2] = CValue::CreateArray(2); arr[2][0] = true; arr[2][1] = 2.5; EXPECT_TRUE(arr[3].IsUndefined()); #ifdef JSON_DEBUG EXPECT_THROW(arr[100].IsUndefined(), std::out_of_range); EXPECT_THROW(arr[-1].IsUndefined(), std::out_of_range); EXPECT_THROW(arr[5] = 1, std::out_of_range); #else EXPECT_TRUE(arr[100].IsUndefined()); EXPECT_TRUE(arr[-1].IsUndefined()); arr[5] = 1; #endif EXPECT_EQ(arr.GetCount(), 4); JSSmart jsArr = CJSContext::createArray(4); jsArr->set(0, CJSContext::createNull()); jsArr->set(1, CJSContext::createInt(42)); jsArr->set(2, CJSContext::createArray(2)); jsArr->get(2)->toArray()->set(0, CJSContext::createBool(true)); jsArr->get(2)->toArray()->set(1, CJSContext::createDouble(2.5)); jsArr->set(3, CJSContext::createUndefined()); EXPECT_TRUE(compare(arr, jsArr->toValue())); arr[1] = 41; EXPECT_FALSE(compare(arr, jsArr->toValue(), false)); } TEST_F(CJSONTest, array_from_initializer_list) { CValue arr = {CValue::CreateNull(), 42, {true, 2.5}, CValue::CreateUndefined()}; EXPECT_TRUE(arr[3].IsUndefined()); #ifdef JSON_DEBUG EXPECT_THROW(arr[100].IsUndefined(), std::out_of_range); EXPECT_THROW(arr[-1].IsUndefined(), std::out_of_range); EXPECT_THROW(arr[5] = 1, std::out_of_range); #else EXPECT_TRUE(arr[100].IsUndefined()); EXPECT_TRUE(arr[-1].IsUndefined()); arr[5] = 1; #endif EXPECT_EQ(arr.GetCount(), 4); JSSmart jsArr = CJSContext::createArray(4); jsArr->set(0, CJSContext::createNull()); jsArr->set(1, CJSContext::createInt(42)); jsArr->set(2, CJSContext::createArray(2)); jsArr->get(2)->toArray()->set(0, CJSContext::createBool(true)); jsArr->get(2)->toArray()->set(1, CJSContext::createDouble(2.5)); jsArr->set(3, CJSContext::createUndefined()); EXPECT_TRUE(compare(arr, jsArr->toValue())); arr[1] = 41; EXPECT_FALSE(compare(arr, jsArr->toValue(), false)); } TEST_F(CJSONTest, array_empty) { CValue arr = CValue::CreateArray(0); EXPECT_TRUE(arr.IsArray()); EXPECT_EQ(arr.GetCount(), 0); #ifdef JSON_DEBUG EXPECT_THROW(arr[0].IsUndefined(), std::out_of_range); #else EXPECT_TRUE(arr[0].IsUndefined()); #endif } TEST_F(CJSONTest, array_negative_size) { CValue arr = CValue::CreateArray(-1); EXPECT_TRUE(arr.IsUndefined()); #ifdef JSON_DEBUG EXPECT_THROW(arr[0].IsUndefined(), std::bad_cast); #else EXPECT_TRUE(arr[0].IsUndefined()); #endif } TEST_F(CJSONTest, typed_array_externalize) { BYTE* data = CValue::AllocTypedArray(10); CValue typedArr = CValue::CreateTypedArray(data, 10); JSSmart jsTypedArr = CJSContext::createUint8Array(data, 10); EXPECT_TRUE(compare(typedArr, jsTypedArr->toValue())); typedArr = CValue::CreateTypedArray(data, 11); EXPECT_FALSE(compare(typedArr, jsTypedArr->toValue(), false)); BYTE* data2 = CValue::AllocTypedArray(10); typedArr = CValue::CreateTypedArray(data2, 10); EXPECT_FALSE(compare(typedArr, jsTypedArr->toValue(), false)); CValue::FreeTypedArray(data, 10); CValue::FreeTypedArray(data2, 10); } TEST_F(CJSONTest, typed_array_not_externalize) { BYTE* data = CValue::AllocTypedArray(10); CValue typedArr = CValue::CreateTypedArray(data, 10, false); JSSmart jsTypedArr = CJSContext::createUint8Array(data, 10); EXPECT_TRUE(compare(typedArr, jsTypedArr->toValue())); } TEST_F(CJSONTest, typed_array_empty) { BYTE* data = CValue::AllocTypedArray(0); CValue typedArr = CValue::CreateTypedArray(data, 0, false); EXPECT_TRUE(typedArr.IsUndefined()); } TEST_F(CJSONTest, typed_array_negative_size) { BYTE* data = CValue::AllocTypedArray(10); CValue typedArr = CValue::CreateTypedArray(nullptr, -10); EXPECT_TRUE(typedArr.IsUndefined()); CValue::FreeTypedArray(data, 10); } TEST_F(CJSONTest, typed_array_copy) { BYTE* data = CValue::AllocTypedArray(10); CValue typedArr = CValue::CreateTypedArray(data, 10, false); { CValue typedArr2 = typedArr; typedArr2.GetData()[4] = 0x42; } EXPECT_EQ(typedArr.GetData()[4], 0x42); } TEST_F(CJSONTest, object) { CValue obj = CValue::CreateObject(); JSSmart jsObj = CJSContext::createObject(); EXPECT_TRUE(compare(obj, jsObj->toValue())); obj["name"] = "Foo"; jsObj->set("name", CJSContext::createString("Foo")); EXPECT_TRUE(compare(obj, jsObj->toValue())); obj["name"] = "Bar"; EXPECT_FALSE(compare(obj, jsObj->toValue(), false)); obj["number"] = 42; jsObj->set("name", CJSContext::createString("Bar")); jsObj->set("number", CJSContext::createInt(42)); EXPECT_TRUE(compare(obj, jsObj->toValue())); obj["extra"] = CValue::CreateUndefined(); EXPECT_TRUE(compare(obj, jsObj->toValue())); obj["extra"] = CValue::CreateNull(); EXPECT_FALSE(compare(obj, jsObj->toValue(), false)); } TEST_F(CJSONTest, image_externalize) { int width = 1080; int height = 480; BYTE* bits = CValue::AllocImageBits(width, height); CValue img = CValue::CreateImage(bits, width, height, ifBGRA); EXPECT_TRUE(img.IsImage()); EXPECT_EQ(img.GetImageBits(), bits); EXPECT_EQ(img.GetImageWidth(), width); EXPECT_EQ(img.GetImageHeight(), height); EXPECT_EQ(img.GetImageFormat(), ifBGRA); CValue::FreeImageBits(bits); width = 50; height = 100; bits = CValue::AllocImageBits(width, height); img = CValue::CreateImage(bits, width, height, ifRGBA); EXPECT_TRUE(img.IsImage()); EXPECT_EQ(img.GetImageBits(), bits); EXPECT_EQ(img.GetImageWidth(), width); EXPECT_EQ(img.GetImageHeight(), height); EXPECT_EQ(img.GetImageFormat(), ifRGBA); CValue::FreeImageBits(bits); } TEST_F(CJSONTest, image_not_externalize) { int width = 320; int height = 480; BYTE* bits = CValue::AllocImageBits(width, height); CValue img = CValue::CreateImage(bits, width, height, ifARGB, false); EXPECT_TRUE(img.IsImage()); EXPECT_EQ(img.GetImageBits(), bits); EXPECT_EQ(img.GetImageWidth(), width); EXPECT_EQ(img.GetImageHeight(), height); EXPECT_EQ(img.GetImageFormat(), ifARGB); } TEST_F(CJSONTest, image_wrong_size) { BYTE* bits = CValue::AllocImageBits(100, 100); CValue img = CValue::CreateImage(bits, 0, 100, ifARGB, false); EXPECT_TRUE(img.IsUndefined()); img = CValue::CreateImage(bits, -1, 100, ifARGB, false); EXPECT_TRUE(img.IsUndefined()); img = CValue::CreateImage(bits, 0, 0, ifARGB, false); EXPECT_TRUE(img.IsUndefined()); img = CValue::CreateImage(bits, -100, -100, ifARGB, false); EXPECT_TRUE(img.IsUndefined()); } TEST_F(CJSONTest, references) { CValue val = 42; CValueRef ref = val; EXPECT_EQ((int)val, 42); EXPECT_EQ((int)ref, 42); ref = 10; EXPECT_EQ((int)val, 10); EXPECT_EQ((int)ref, 10); // CValue from CValueRef CValue val2 = ref; val2 = 12; EXPECT_EQ((int)val2, 12); EXPECT_EQ((int)ref, 10); EXPECT_EQ((int)val, 10); // chaining CValueRef-s CValueRef ref2 = ref; val = "foo"; EXPECT_EQ((std::string)ref, "foo"); EXPECT_EQ((std::string)ref2, "foo"); val = {1, 2, 3}; EXPECT_EQ((int)ref[1], 2); // CValue assignment to CValueRef val2 = ref2; EXPECT_EQ((int)val2[0], 1); val2 = true; EXPECT_EQ((bool)val2, true); EXPECT_TRUE(ref.IsArray()); EXPECT_TRUE(ref2.IsArray()); // CValueRef from operator[] CValueRef ref3 = val[2]; ref3 = ref2[0]; EXPECT_EQ((int)val[2], 1); } TEST_F(CJSONTest, constants) { const CValue val = 42; // NOTE: you can't change constant value explicitly, but you can do it using references: CValueRef ref = val; ref = 10; EXPECT_EQ((int)val, 10); EXPECT_EQ((int)ref, 10); // NOTE: const references saves its const properties const CValueRef ref2 = ref; // you can't do that: // ref2 = 100; EXPECT_EQ((int)ref2, 10); } TEST_F(CJSONTest, wrong_usage) { CValue val = 42; #ifdef JSON_DEBUG EXPECT_THROW(val["name"].IsUndefined(), std::bad_cast); EXPECT_THROW(val[0].IsUndefined(), std::bad_cast); EXPECT_THROW(val.GetPropertyNames(), std::bad_cast); EXPECT_THROW(val.GetCount(), std::bad_cast); EXPECT_THROW(val.GetData(), std::bad_cast); EXPECT_THROW(val.GetImageBits(), std::bad_cast); EXPECT_THROW(val.GetImageFormat(), std::bad_cast); EXPECT_THROW(val.GetImageHeight(), std::bad_cast); EXPECT_THROW(val.GetImageWidth(), std::bad_cast); #else EXPECT_TRUE(val["name"].IsUndefined()); EXPECT_TRUE(val[0].IsUndefined()); EXPECT_TRUE(val.GetPropertyNames().empty()); EXPECT_EQ(val.GetCount(), 0); EXPECT_EQ(val.GetData(), nullptr); EXPECT_EQ(val.GetImageBits(), nullptr); EXPECT_EQ(val.GetImageFormat(), ifInvalid); EXPECT_EQ(val.GetImageHeight(), 0); EXPECT_EQ(val.GetImageWidth(), 0); #endif EXPECT_DOUBLE_EQ(val.ToDouble(), 42.0); #ifdef JSON_DEBUG EXPECT_THROW((bool)val, std::bad_cast); EXPECT_THROW((std::string)val, std::bad_cast); EXPECT_THROW((std::wstring)val, std::bad_cast); #else EXPECT_EQ((bool)val, false); EXPECT_EQ((std::string)val, ""); EXPECT_EQ((std::wstring)val, L""); #endif val = 3.1415926535; EXPECT_EQ((int)val, 3); val = "test"; #ifdef JSON_DEBUG EXPECT_THROW((bool)val, std::bad_cast); EXPECT_THROW((int)val, std::bad_cast); EXPECT_THROW((double)val, std::bad_cast); #else EXPECT_EQ((bool)val, false); EXPECT_EQ((int)val, 0); EXPECT_EQ((double)val, 0.0); #endif EXPECT_EQ((std::wstring)val, L"test"); val = L"test"; #ifdef JSON_DEBUG EXPECT_THROW((bool)val, std::bad_cast); EXPECT_THROW((int)val, std::bad_cast); EXPECT_THROW((double)val, std::bad_cast); #else EXPECT_EQ((bool)val, false); EXPECT_EQ((int)val, 0); EXPECT_EQ((double)val, 0.0); #endif EXPECT_EQ((std::string)val, "test"); val = CValue::CreateObject(); #ifdef JSON_DEBUG EXPECT_THROW(val[0].IsUndefined(), std::bad_cast); EXPECT_THROW((bool)val, std::bad_cast); EXPECT_THROW((int)val, std::bad_cast); EXPECT_THROW((double)val, std::bad_cast); EXPECT_THROW((std::string)val, std::bad_cast); EXPECT_THROW((std::wstring)val, std::bad_cast); #else EXPECT_TRUE(val[0].IsUndefined()); EXPECT_EQ((bool)val, false); EXPECT_EQ((int)val, 0); EXPECT_EQ((double)val, 0.0); EXPECT_EQ((std::string)val, ""); EXPECT_EQ((std::wstring)val, L""); #endif } TEST_F(CJSONTest, is_init) { CValue val; EXPECT_FALSE(val.IsInit()); val = CValue::CreateNull(); EXPECT_FALSE(val.IsInit()); val = 42; EXPECT_TRUE(val.IsInit()); CValue val2 = val; val2 = CValue::CreateUndefined(); EXPECT_TRUE(val.IsInit()); EXPECT_FALSE(val2.IsInit()); CValueRef ref = val; ref = CValue::CreateUndefined(); EXPECT_FALSE(val.IsInit()); EXPECT_FALSE(ref.IsInit()); ref = 3; EXPECT_TRUE(val.IsInit()); EXPECT_TRUE(ref.IsInit()); } // ----------- toJS() tests ----------- TEST_F(CJSONTest, toJS_undefined) { CValue val; JSSmart jsVal = toJS(val); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, toJS_null) { CValue val = CValue::CreateNull(); JSSmart jsVal = toJS(val); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, toJS_typed_arrays) { BYTE* data = CValue::AllocTypedArray(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; CValue typedArr = CValue::CreateTypedArray(data, 4, false); JSSmart jsTypedArr = toJS(typedArr); // NOTE: BE CAREFUL WHEN CALLLING toJS() MULTIPLE TIMES WITH THE SAME TYPED ARRAY! // second typed array will NOT be initialized properly, since there is another `CJSTypedArray` containing this memory! // JSSmart jsTypedArr2 = toJS(typedArr); EXPECT_TRUE(compare(typedArr, jsTypedArr)); } TEST_F(CJSONTest, toJS_arrays) { BYTE* data = CValue::AllocTypedArray(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; CValue typedArr = CValue::CreateTypedArray(data, 4, false); // can't add typed array to this inner array (see test above), only to external array CValue arrInner = {true, 42, L"тест функции toJS()", 2.71828, CValue(), "abc de f", L"test"}; CValue arr = {0, arrInner, arrInner, CValue::CreateNull(), arrInner, CValue::CreateArray(4), typedArr}; JSSmart jsArr = toJS(arr); EXPECT_TRUE(compare(arr, jsArr)); } TEST_F(CJSONTest, toJS_arrays_circular) { // NOTE: BE CAREFULL WHEN CREATING CIRCULAR REFERENCE DEPENDENCY IN YOUR ARRAY OR OBJECT! CValue arr = CValue::CreateArray(2); CValueRef ref = arr; arr[0] = 3; arr[1] = ref; // or simply: // arr[1] = arr; EXPECT_EQ((int)arr[0], 3); EXPECT_EQ((int)arr[1][0], 3); EXPECT_EQ((int)arr[1][1][1][1][1][1][1][1][1][1][0], 3); EXPECT_TRUE(arr[1][1][1][1].IsArray()); // here you will get stack overflow, because each inner array reference will be transformed to a new copy of `CJSArray` // JSSmart jsArr = toJS(arr); // keep only 2 inner recursions CValue arrRec = arr[1][1]; arrRec[1] = 42; EXPECT_TRUE(arr[1].IsInt()); JSSmart jsArr = toJS(arr); EXPECT_TRUE(compare(arr, jsArr)); } TEST_F(CJSONTest, toJS_objects) { CValue obj = CValue::CreateObject(); obj["name"] = L"Foo"; obj["parameters"] = CValue::CreateObject(); CValueRef parameters = obj["parameters"]; parameters["size"] = 42; parameters["arr"] = {CValue::CreateNull(), CValue::CreateArray(0), {42, L"тест функции toJS()", 2.71828}, CValue::CreateObject(), CValue(), "abc de f", L"test"}; parameters["0"] = 0; BYTE* data = CValue::AllocTypedArray(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; CValue typedArr = CValue::CreateTypedArray(data, 4, false); obj["typed"] = typedArr; // NOTE: you can create property even without a name (like in JS) obj[""] = "Bar"; JSSmart jsObj = toJS(obj); EXPECT_TRUE(compare(obj, jsObj)); } // ----------- fromJS() tests ----------- TEST_F(CJSONTest, fromJS_undefined) { JSSmart jsVal = CJSContext::createUndefined(); CValue val = fromJS(jsVal); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, fromJS_null) { JSSmart jsVal = CJSContext::createNull(); CValue val = fromJS(jsVal); EXPECT_TRUE(compare(val, jsVal)); } TEST_F(CJSONTest, fromJS_and_toJS_edge_numbers) { JSSmart jsVal = CJSContext::createArray(16); jsVal->set(0, INT_MIN); jsVal->set(1, INT_MAX); jsVal->set(2, 0); jsVal->set(3, 42); jsVal->set(4, 0.0); jsVal->set(5, 3.1415926535); jsVal->set(6, 42.0); jsVal->set(7, (double)INT_MIN); jsVal->set(8, (double)INT_MAX); jsVal->set(9, std::pow(2.0, 31) - 1); jsVal->set(10, std::pow(2.0, 31)); jsVal->set(11, -std::pow(2.0, 31)); jsVal->set(12, -std::pow(2.0, 31) - 1); jsVal->set(13, INFINITY); jsVal->set(14, NAN); jsVal->set(15, -INFINITY); CValue val = fromJS(jsVal->toValue()); EXPECT_TRUE(compare(val, jsVal->toValue())); EXPECT_TRUE(val[0].IsInt()); // INT_MIN EXPECT_TRUE(val[1].IsInt()); // INT_MAX EXPECT_TRUE(val[2].IsInt()); // 0 EXPECT_TRUE(val[3].IsInt()); // 42 EXPECT_TRUE(val[4].IsInt()); // 0.0 EXPECT_TRUE(val[5].IsDouble()); // 3.1415926535 EXPECT_TRUE(val[6].IsInt()); // 42.0 EXPECT_TRUE(val[7].IsInt()); // (double)INT_MIN EXPECT_TRUE(val[8].IsInt()); // (double)INT_MAX EXPECT_TRUE(val[9].IsInt()); // 2^31 - 1 == INT_MAX EXPECT_TRUE(val[10].IsDouble()); // 2^31 EXPECT_TRUE(val[11].IsInt()); // -2^31 == INT_MIN EXPECT_TRUE(val[12].IsDouble()); // -2^31 - 1 EXPECT_TRUE(val[13].IsDouble()); // inf EXPECT_TRUE(val[14].IsDouble()); // NaN EXPECT_TRUE(val[15].IsDouble()); // -inf JSSmart jsVal2 = toJS(val); EXPECT_TRUE(compare(val, jsVal2)); } TEST_F(CJSONTest, fromJS_typed_arrays) { BYTE* data = NSAllocator::Alloc(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; JSSmart jsTypedArr = CJSContext::createUint8Array(data, 4, false); CValue typedArr = fromJS(jsTypedArr); EXPECT_TRUE(compare(typedArr, jsTypedArr)); } TEST_F(CJSONTest, fromJS_arrays) { BYTE* data = NSAllocator::Alloc(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; JSSmart jsTypedArr = CJSContext::createUint8Array(data, 4, false); JSSmart jsArrInner = CJSContext::createArray(0); jsArrInner->add_bool(true); jsArrInner->add_int(42); jsArrInner->add_string(L"тест функции fromJS()"); jsArrInner->add_double(2.71828); jsArrInner->add_undefined(); jsArrInner->add_stringa("abc de f"); jsArrInner->add_string(L"test"); JSSmart jsArr = CJSContext::createArray(0); jsArr->add_int(0); jsArr->add(jsArrInner->toValue()); jsArr->add(jsArrInner->toValue()); jsArr->add_null(); jsArr->add(jsArrInner->toValue()); jsArr->add(CJSContext::createArray(4)); jsArr->add(jsTypedArr); CValue arr = fromJS(jsArr->toValue()); EXPECT_TRUE(compare(arr, jsArr->toValue())); } TEST_F(CJSONTest, fromJS_objects) { JSSmart jsObj = CJSContext::createObject(); jsObj->set("name", CJSContext::createString(L"Foo")); JSSmart jsParam = CJSContext::createObject(); jsParam->set("size", 42); jsParam->set("arr", CJSContext::createArray(0)); JSSmart jsArr = jsParam->get("arr")->toArray(); jsArr->add_null(); jsArr->add(CJSContext::createArray(0)); jsArr->add(CJSContext::createArray(3)); jsArr->get(2)->toArray()->set(0, 42); jsArr->get(2)->toArray()->set(1, CJSContext::createString(L"тест функции fromJS()")); jsArr->get(2)->toArray()->set(2, 2.71828); jsArr->add(CJSContext::createObject()); jsArr->add_undefined(); jsArr->add_stringa("abc de f"); jsArr->add_bool(true); jsParam->set("0", 0); jsObj->set("parameters", jsParam); BYTE* data = NSAllocator::Alloc(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; JSSmart jsTypedArr = CJSContext::createUint8Array(data, 4, false); jsObj->set("typed", jsTypedArr); jsObj->set("", CJSContext::createString("Bar")); CValue obj = fromJS(jsObj->toValue()); EXPECT_TRUE(compare(obj, jsObj->toValue())); } TEST_F(CJSONTest, serialization_with_script) { JSSmart jsObj = m_pContext->runScript( "(() => {" " let obj = {};" " obj['name'] = 'Foo';" " obj['parameters'] = { size: 42, arr: [null, [], [42, 'test', 2.71828], {}, undefined, 'abc de f', ''], 0: 0, typedArr: { data: null, count: 0 } };" " obj[''] = 'Bar';" " return obj;" "})();" ); CValue obj = fromJS(jsObj); EXPECT_EQ(obj["name"].ToStringW(), L"Foo"); EXPECT_EQ(obj[""].ToStringW(), L"Bar"); EXPECT_EQ((double)obj["parameters"]["arr"][2][0], 42); EXPECT_TRUE(obj["parameters"]["arr"][4].IsUndefined()); EXPECT_EQ((int)obj["parameters"]["0"], 0); EXPECT_TRUE(obj["parameters"]["typedArr"]["data"].IsNull()); EXPECT_TRUE(compare(obj, jsObj)); // function test() returns 0 if all checks have passed and number of failed check otherwise m_pContext->runScript( "function test(obj) {" " if (obj['name'] !== 'Foo')" " return 1;" " if (obj[''] !== 'Bar')" " return 2;" " if (obj['parameters']['arr'][2][0] !== 42)" " return 3;" " if (obj['parameters']['arr'][4] !== undefined)" " return 4;" " if (obj['parameters']['0'] !== 0)" " return 5;" " if (obj['parameters']['typedArr']['data'] !== null)" " return 6;" " return 0;" "}" ); JSSmart global = m_pContext->GetGlobal(); JSSmart args[1]; args[0] = toJS(obj); JSSmart jsCheckResult = global->call_func("test", 1, args); EXPECT_TRUE(jsCheckResult->isNumber()); EXPECT_EQ(jsCheckResult->toInt32(), 0); EXPECT_TRUE(compare(obj, args[0])); } // ----------- CValue::ToJSON() tests ----------- namespace { // helper function std::string normalizeJSONObject(const std::string& str, int& pos) { // '{' was checked pos++; std::vector keyValuePairs; std::string keyValue; int nSquareBrackets = 0; for (; str[pos] != '}'; pos++) { if (str[pos] == '{') { keyValue += normalizeJSONObject(str, pos); } else if (str[pos] == ',' && nSquareBrackets == 0) { keyValuePairs.emplace_back(std::move(keyValue)); keyValue = ""; } else { if (str[pos] == '[') { nSquareBrackets++; } else if (str[pos] == ']') { nSquareBrackets--; } keyValue += str[pos]; } } if (!keyValue.empty()) keyValuePairs.emplace_back(std::move(keyValue)); std::sort(keyValuePairs.begin(), keyValuePairs.end()); std::string result = "{"; for (const std::string& keyValue : keyValuePairs) { result += keyValue; result += ','; } // pop last ',' if (result.back() == ',') result.pop_back(); result += '}'; return result; } // test function that sorts key-value pairs for objects in JSON-strings // works only with valid JSONs std::string normalizeJSON(const std::string& str) { const int n = (int)str.size(); if (str[0] != '{' && str[0] != '[') return str; std::string result; for (int pos = 0; pos < n; pos++) { if (str[pos] == '{') { result += normalizeJSONObject(str, pos); // decrement because it will be incremented again } else { result += str[pos]; } } return result; } } TEST_F(CJSONTest, ToJSON_undefined) { CValue val = CValue::CreateUndefined(); EXPECT_EQ(val.ToJSON(), ""); } TEST_F(CJSONTest, ToJSON_null) { CValue val = CValue::CreateNull(); EXPECT_EQ(val.ToJSON(), "null"); } TEST_F(CJSONTest, ToJSON_bool) { CValue val(false); EXPECT_EQ(val.ToJSON(), "false"); val = true; EXPECT_EQ(val.ToJSON(), "true"); } TEST_F(CJSONTest, ToJSON_int) { CValue val = 42; EXPECT_EQ(val.ToJSON(), "42"); val = 0; EXPECT_EQ(val.ToJSON(), "0"); val = -73; EXPECT_EQ(val.ToJSON(), "-73"); val = INT_MIN; EXPECT_EQ(val.ToJSON(), "-2147483648"); val = INT_MAX; EXPECT_EQ(val.ToJSON(), "2147483647"); } TEST_F(CJSONTest, ToJSON_double) { CValue val = 4.2; EXPECT_EQ(val.ToJSON(), "4.2"); val = 42.0; EXPECT_EQ(val.ToJSON(), "42"); val = 0.0; EXPECT_EQ(val.ToJSON(), "0"); val = 3.1415926535; EXPECT_EQ(val.ToJSON(), "3.1415926535"); val = (double)INT_MIN; EXPECT_EQ(val.ToJSON(), "-2147483648"); val = (double)INT_MAX; EXPECT_EQ(val.ToJSON(), "2147483647"); } TEST_F(CJSONTest, ToJSON_double_critical_values) { CValue val = 8e30; EXPECT_EQ(val.ToJSON(), "8e+30"); val = 8e-30; EXPECT_EQ(val.ToJSON(), "8e-30"); val = INFINITY; EXPECT_EQ(val.ToJSON(), "null"); val = NAN; EXPECT_EQ(val.ToJSON(), "null"); val = -INFINITY; EXPECT_EQ(val.ToJSON(), "null"); val = std::numeric_limits::max(); std::string strResult = val.ToJSON(); // we cannot expect predefined specified result from this test case so just check if it is not empty EXPECT_TRUE(!strResult.empty()); EXPECT_TRUE(strResult != "null"); val = std::numeric_limits::min(); strResult = val.ToJSON(); // we cannot expect predefined specified result from this test case so just check if it is not empty EXPECT_TRUE(!strResult.empty()); EXPECT_TRUE(strResult != "null"); } TEST_F(CJSONTest, ToJSON_string) { CValue val = " test "; EXPECT_EQ(val.ToJSON(), "\" test \""); val = ""; EXPECT_EQ(val.ToJSON(), "\"\""); val = L" test "; EXPECT_EQ(val.ToJSON(), "\" test \""); // test parsing of special symbols inside strings val = "foo: {bar: 42}[} ,))"; EXPECT_EQ(val.ToJSON(), "\"foo: {bar: 42}[} ,))\""); } TEST_F(CJSONTest, ToJSON_string_escaped_characters) { CValue val = "HEL\"LO"; std::string strRes = val.ToJSON(); EXPECT_EQ(strRes, "\"HEL\\\"LO\""); val = "HEL\\/LO"; strRes = val.ToJSON(); EXPECT_EQ(strRes, "\"HEL\\\\/LO\""); val = "\tH\bE\fL\nL\nO\r"; strRes = val.ToJSON(); EXPECT_EQ(strRes, "\"\\tH\\bE\\fL\\nL\\nO\\r\""); // other symbols std::string spec = "/"; for (char ch = 0; ch <= 0x21; ch++) { spec += ch; } spec += '/'; val = spec; strRes = val.ToJSON(); EXPECT_EQ(strRes, "\"/\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b" "\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f !/\""); } TEST_F(CJSONTest, ToJSON_typed_arrays) { BYTE* data = CValue::AllocTypedArray(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; CValue typedArr = CValue::CreateTypedArray(data, 4, false); EXPECT_EQ(typedArr.ToJSON(), "{\"0\":26,\"1\":84,\"2\":254,\"3\":255}"); } TEST_F(CJSONTest, ToJSON_arrays) { CValue arr = CValue::CreateArray(0); EXPECT_EQ(arr.ToJSON(), "[]"); arr = CValue::CreateArray(4); arr[0] = 42; arr[1] = "foo"; arr[2] = 2.71828; EXPECT_EQ(arr.ToJSON(), "[42,\"foo\",2.71828,null]"); CValue arrInner = {CValue::CreateNull(), 73.0}; arr[3] = arrInner; EXPECT_EQ(arr.ToJSON(), "[42,\"foo\",2.71828,[null,73]]"); } TEST_F(CJSONTest, ToJSON_arrays_complex) { BYTE* data = CValue::AllocTypedArray(5); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; data[4] = 0x00; CValue typedArr = CValue::CreateTypedArray(data, 5, false); CValue arrInner = {true, 42, L"test function ToJSON()", 2.71828, CValue(), "abc de f", L"test"}; CValue arr = {0, arrInner, arrInner, CValue::CreateNull(), arrInner, CValue::CreateArray(4), typedArr}; std::string strRes = arr.ToJSON(); std::string strExpected = "[0," "[true,42,\"test function ToJSON()\",2.71828,null,\"abc de f\",\"test\"]," "[true,42,\"test function ToJSON()\",2.71828,null,\"abc de f\",\"test\"]," "null," "[true,42,\"test function ToJSON()\",2.71828,null,\"abc de f\",\"test\"]," "[null,null,null,null]," "{\"0\":26,\"1\":84,\"2\":254,\"3\":255,\"4\":0}]"; EXPECT_EQ(strRes, strExpected); } TEST_F(CJSONTest, ToJSON_arrays_circular) { // NOTE: BE CAREFULL WHEN CREATING CIRCULAR REFERENCE DEPENDENCY IN YOUR ARRAY OR OBJECT! CValue arr = CValue::CreateArray(2); CValue ref = arr; arr[0] = 3; arr[1] = ref; // here you will get stack overflow, because each inner array reference will be recursively converted to JSON-string // arr.ToJSON(); // remove recursions CValue arrRec = arr[1][1]; arrRec[1] = 42; EXPECT_EQ(arr.ToJSON(), "[3,42]"); } TEST_F(CJSONTest, ToJSON_objects) { CValue obj = CValue::CreateObject(); EXPECT_EQ(obj.ToJSON(), "{}"); obj["name"] = "Foo"; obj["number"] = 42; std::string strRes = normalizeJSON(obj.ToJSON()); std::string strExpected = normalizeJSON("{\"name\":\"Foo\",\"number\":42}"); EXPECT_EQ(strRes, strExpected); obj["undef"] = CValue::CreateUndefined(); obj["null"] = CValue::CreateNull(); strRes = normalizeJSON(obj.ToJSON()); strExpected = normalizeJSON("{\"name\":\"Foo\",\"number\":42,\"null\":null}"); EXPECT_EQ(strRes, strExpected); } TEST_F(CJSONTest, ToJSON_objects_complex) { CValue obj = CValue::CreateObject(); obj["name"] = L"Foo"; obj["parameters"] = CValue::CreateObject(); CValueRef parameters = obj["parameters"]; parameters["0"] = 0; parameters["size"] = 42; parameters["arr"] = {CValue::CreateNull(), CValue::CreateArray(1), {42, L"test function ToJSON()", 2.71828}, CValue::CreateObject(), CValue(), "abc de f", L"test"}; BYTE* data = CValue::AllocTypedArray(4); data[0] = 0x1A; data[1] = 0x54; data[2] = 0xFE; data[3] = 0xFF; CValue typedArr = CValue::CreateTypedArray(data, 4, false); obj["typed"] = typedArr; // property without a name obj[""] = "Bar"; std::string strRes = normalizeJSON(obj.ToJSON()); std::string strExpected = normalizeJSON( \ "{" "\"name\":\"Foo\"," "\"parameters\":{\"arr\":[null,[null],[42,\"test function ToJSON()\",2.71828],{},null,\"abc de f\",\"test\"],\"size\":42,\"0\":0}," "\"typed\":{\"0\":26,\"1\":84,\"2\":254,\"3\":255}," "\"\":\"Bar\"" "}"); EXPECT_EQ(strRes, strExpected); } // ----------- CValue::FromJSON() tests ----------- TEST_F(CJSONTest, FromJSON_null) { std::string strJson = "null"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsNull()); } TEST_F(CJSONTest, FromJSON_null_invalid) { // empty json string is invalid std::string strJson = ""; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // there is no undefined in JSON standard strJson = "undefined"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "nil"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "n"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "NULL"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "nullptr"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); } TEST_F(CJSONTest, FromJSON_bool) { std::string strJson = "false"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsBool()); EXPECT_EQ((bool)val, false); strJson = "true "; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsBool()); EXPECT_EQ((bool)val, true); } TEST_F(CJSONTest, FromJSON_bool_invalid) { std::string strJson = "fals"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "truE"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "f"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "t"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "True"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); } TEST_F(CJSONTest, FromJSON_int) { std::string strJson = "42"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, 42); strJson = "0"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, 0); strJson = "-73"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, -73); strJson = "-2147483648"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, std::numeric_limits::min()); strJson = "2147483647"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, std::numeric_limits::max()); strJson = "2147483648"; val = CValue::FromJSON(strJson); // this value does not fit into 32-bit integer limits EXPECT_FALSE(val.IsInt()); EXPECT_TRUE(val.IsDouble()); } TEST_F(CJSONTest, FromJSON_double) { std::string strJson = "4.2"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_DOUBLE_EQ((double)val, 4.2); strJson = "42.0"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, 42); strJson = "0.0"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, 0); strJson = "3.1415926535"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_DOUBLE_EQ((double)val, 3.1415926535); strJson = "2147483648"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_DOUBLE_EQ((double)val, 2147483648.0); } TEST_F(CJSONTest, FromJSON_double_critical_values) { std::string strJson = "-8e30"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_DOUBLE_EQ((double)val, -8e30); strJson = "8e+30"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_DOUBLE_EQ((double)val, 8e30); strJson = "8e-30"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_DOUBLE_EQ((double)val, 8e-30); // divide by 10 to prevent getting 'inf' when converting to string CValue maxDouble = std::numeric_limits::max() / 10; strJson = maxDouble.ToJSON(); val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_NEAR((double)val, (double)maxDouble, 1e-15 * std::numeric_limits::max()); // multiply by 10 to prevent getting '0' when converting to string CValue minDouble = std::numeric_limits::min() * 10; strJson = minDouble.ToJSON(); val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_NEAR((double)val, (double)minDouble, 1e-15); // if value is outside of double range, it is expected to be inf strJson = "10e+1000"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_EQ((double)val, INFINITY); strJson = "-10e1000"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_EQ((double)val, -INFINITY); // expected zero for too small values strJson = "10e-1000"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_EQ((double)val, 0.0); } TEST_F(CJSONTest, FromJSON_numbers_invalid) { // leading zeroes are invalid in JSON standard, but here it is considered valid std::string strJson = "0123"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, 123); // multiple decimal points strJson = "12.34.56"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // trailing decimal point also considered invalid in JSON, but allowed here strJson = "42."; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsDouble()); EXPECT_EQ((int)val, 42); // invalid exponent format strJson = "1e"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "1e+"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "1e+a"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "1e+-"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // invalid characters strJson = "123abc"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "123\"abc\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "123-42"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // sign without digits strJson = "-"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // sign without digits strJson = "-"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // whitespace in numbers strJson = "1 234"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "1 a"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); } TEST_F(CJSONTest, FromJSON_string) { std::string strJson = "\" test \""; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), " test "); strJson = "\"\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), ""); // test for parsing of special symbols inside strings strJson = "\"foo: {bar: 42}[} ,))\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), "foo: {bar: 42}[} ,))"); // test for using unicode codes instead of ASCII characters strJson = "\"\\u0061\\u006E\\u0073\\u0077\\u0065\\u0072\\u0020\\u0069\\u0073\\u0020\\u0034\\u0032\\u0021\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), "answer is 42!"); } TEST_F(CJSONTest, FromJSON_string_escaped_characters) { std::string strJson = "\"\\\\\""; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), "\\"); strJson = "\"HEL\\\"LO\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), "HEL\"LO"); strJson = "\"HEL\\\\/LO\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), "HEL\\/LO"); strJson = "\"\\tH\\bE\\fL\\nL\\nO\\r\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), "\tH\bE\fL\nL\nO\r"); // characters from 0x00 to 0x21 (inclusive) strJson = "\"/\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b" "\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f !\\/\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); std::string spec = "/"; for (char ch = 0; ch <= 0x21; ch++) { spec += ch; } spec += '/'; EXPECT_EQ(val.ToStringA(), spec); } TEST_F(CJSONTest, FromJSON_string_invalid) { // using unescaped control characters std::string strJson = "\"this\nis a test"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "\"\t\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // no closing quote strJson = "\" test "; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // no symbol after backslash strJson = "\"\\\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // wrong symbol after backslash strJson = "\"\\x\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // invalid unicode sequence strJson = "\"\\u\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "\"\\u00\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "\"\\u0100\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "\"\\u001\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "\"\\u00GH\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "\"\\u00fH\""; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); } TEST_F(CJSONTest, FromJSON_whitespace_symbols) { // allowed space symbols are: 0x09 (\t), 0x0A (\n), 0x0D (\r) and 0x20 (space) std::string strJson = " null "; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsNull()); strJson = "\t \n\n42\r"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsInt()); EXPECT_EQ((int)val, 42); strJson = "\n\t \" foo\\n \"\n \t\n"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsString()); EXPECT_EQ(val.ToStringA(), " foo\n "); strJson = " \t\v 42 \t"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = " a 42 \t"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); } TEST_F(CJSONTest, FromJSON_arrays) { std::string strJson = "[]"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 0); #ifndef JSON_DEBUG EXPECT_TRUE(val[0].IsUndefined()); #else EXPECT_THROW(val[0], std::out_of_range); #endif strJson = "[1, 2, 3]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 3); EXPECT_EQ((int)val[0], 1); EXPECT_EQ((int)val[1], 2); EXPECT_EQ((int)val[2], 3); strJson = " [ \"][\" ] "; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 1); EXPECT_EQ(val[0].ToStringA(), "]["); strJson = " [ 73 , null\n\t,4.2]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 3); EXPECT_EQ((int)val[0], 73); EXPECT_TRUE(val[1].IsNull()); EXPECT_DOUBLE_EQ((double)val[2], 4.2); strJson = "[1, \"two\", 3.14, true, null]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 5); EXPECT_EQ(val[0].ToInt(), 1); EXPECT_EQ(val[1].ToStringA(), "two"); EXPECT_DOUBLE_EQ(val[2].ToDouble(), 3.14); EXPECT_TRUE(val[3].ToBool()); EXPECT_TRUE(val[4].IsNull()); } TEST_F(CJSONTest, FromJSON_arrays_inner) { std::string strJson = "[[1, 2, 3]]"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 1); EXPECT_TRUE(val[0].IsArray()); EXPECT_EQ(val[0].GetCount(), 3); EXPECT_EQ((int)val[0][0], 1); EXPECT_EQ((int)val[0][1], 2); EXPECT_EQ((int)val[0][2], 3); strJson = "[1, [2, 3], [4, [5, 6]], 7]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 4); EXPECT_EQ((int)val[0], 1); EXPECT_TRUE(val[1].IsArray()); EXPECT_EQ(val[1].GetCount(), 2); EXPECT_EQ((int)val[1][0], 2); EXPECT_EQ((int)val[1][1], 3); EXPECT_TRUE(val[2].IsArray()); EXPECT_EQ(val[2].GetCount(), 2); EXPECT_EQ((int)val[2][0], 4); EXPECT_TRUE(val[2][1].IsArray()); EXPECT_EQ(val[2][1].GetCount(), 2); EXPECT_EQ((int)val[2][1][0], 5); EXPECT_EQ((int)val[2][1][1], 6); EXPECT_EQ((int)val[3], 7); strJson = "[[],[[]],[[],[]]]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsArray()); EXPECT_EQ(val.GetCount(), 3); EXPECT_TRUE(val[0].IsArray()); EXPECT_EQ(val[0].GetCount(), 0); EXPECT_TRUE(val[1].IsArray()); EXPECT_EQ(val[1].GetCount(), 1); EXPECT_TRUE(val[1][0].IsArray()); EXPECT_EQ(val[1][0].GetCount(), 0); EXPECT_TRUE(val[2].IsArray()); EXPECT_EQ(val[2].GetCount(), 2); EXPECT_TRUE(val[2][0].IsArray()); EXPECT_EQ(val[2][0].GetCount(), 0); EXPECT_TRUE(val[2][1].IsArray()); EXPECT_EQ(val[2][1].GetCount(), 0); } TEST_F(CJSONTest, FromJSON_arrays_invalid) { // no closing bracket std::string strJson = "["; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "[1,2,3"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // trailing comma strJson = "[1,2,]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // leading comma strJson = "[,1]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // missing comma strJson = "[1 2, 3]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // extra commas strJson = "[1,,2,3]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "[,]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // invalid elements strJson = "[1, 2, undefined, 4]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // non-JSON values as elements strJson = "[1, 2, f(), 4]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // invalid syntax within elements strJson = "[\"foo\", 123, [1, 2, }, 4]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // whitespace issues strJson = "[1, 2 3, 4]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // empty array without brackets strJson = "]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // extra brackets strJson = "[[1, 2, 3]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "[1, 2, 3]]"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // elements outside of the array strJson = "[1, 2, 3], 5"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "[1, 2, 3] 4"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); } TEST_F(CJSONTest, FromJSON_objects) { std::string strJson = "{}"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsObject()); EXPECT_EQ(val.GetPropertyNames().size(), 0); strJson = "{\"name\" : \"Foo\"}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsObject()); EXPECT_EQ(val.GetPropertyNames().size(), 1); EXPECT_EQ(val["name"].ToStringA(), "Foo"); strJson = "{\"name\":\"Bar\",\"value\":42}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsObject()); EXPECT_EQ(val.GetPropertyNames().size(), 2); EXPECT_EQ(val["name"].ToStringA(), "Bar"); EXPECT_EQ((int)val["value"], 42); strJson = "{\"\\u0070\\u0069\":\n3.1415\t, \"\":null ,\"flag\":true,\"arr \":[4,2],\"str\":\"test\"}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsObject()); EXPECT_EQ(val.GetPropertyNames().size(), 5); EXPECT_DOUBLE_EQ((double)val["pi"], 3.1415); EXPECT_TRUE(val[""].IsNull()); EXPECT_EQ((bool)val["flag"], true); EXPECT_TRUE(val["arr "].IsArray()); EXPECT_EQ(val["arr "].GetCount(), 2); EXPECT_EQ((int)val["arr "][0], 4); EXPECT_EQ((int)val["arr "][1], 2); EXPECT_EQ(val["str"].ToStringA(), "test"); } TEST_F(CJSONTest, FromJSON_objects_as_typed_arrays) { // typed arrays in JSON are represented as objects with properties "0", "1", ... and corresponding integer values // if you still want to make typed array from such object, you need to do it manually std::string strJson = "{\"0\":26,\"1\":179,\"2\":254,\"3\":255,\"4\":0}"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsObject()); EXPECT_EQ((int)val["0"], 26); EXPECT_EQ((int)val["1"], 179); EXPECT_EQ((int)val["2"], 254); EXPECT_EQ((int)val["3"], 255); EXPECT_EQ((int)val["4"], 0); } TEST_F(CJSONTest, FromJSON_objects_inner) { std::string strJson = "{\"inner\":{}}"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsObject()); EXPECT_EQ(val.GetPropertyNames().size(), 1); EXPECT_TRUE(val["inner"].IsObject()); EXPECT_TRUE(val["inner"].GetPropertyNames().empty()); } TEST_F(CJSONTest, FromJSON_objects_and_arrays) { // more complex example std::string strJson = \ "{\n" " \"name\": \"Foo\",\n" " \"parameters\": {\n" " \"size\": 42,\n" " \"arr\": [\n" " null,\n" " [],\n" " [42, \"test\", 2.71828],\n" " {\n" " \"name\": \"Bar\",\n" " \"good\": false\n" " },\n" " null,\n" " \"abc de f\",\n" " \"\"\n" " ],\n" " \"0\": 0,\n" " \"data\": {\n" " \"data\": null,\n" " \"count\": 0\n" " }\n" " },\n" " \"\": \"empty\"\n" "}\n"; CValue val = CValue::FromJSON(strJson); // Check root-level object EXPECT_TRUE(val.IsObject()); EXPECT_EQ(val.GetPropertyNames().size(), 3); // Check "name" property EXPECT_EQ(val["name"].ToStringA(), "Foo"); // Check "parameters" object EXPECT_TRUE(val["parameters"].IsObject()); EXPECT_EQ(val["parameters"].GetPropertyNames().size(), 4); // Check "size" property EXPECT_EQ((int)val["parameters"]["size"], 42); // Check "arr" property (array) EXPECT_TRUE(val["parameters"]["arr"].IsArray()); EXPECT_EQ(val["parameters"]["arr"].GetCount(), 7); // First element of "arr" (null) EXPECT_TRUE(val["parameters"]["arr"][0].IsNull()); // Second element of "arr" (empty array) EXPECT_TRUE(val["parameters"]["arr"][1].IsArray()); EXPECT_EQ(val["parameters"]["arr"][1].GetCount(), 0); // Third element of "arr" (array with mixed types) EXPECT_TRUE(val["parameters"]["arr"][2].IsArray()); EXPECT_EQ(val["parameters"]["arr"][2].GetCount(), 3); EXPECT_EQ((int)val["parameters"]["arr"][2][0], 42); EXPECT_EQ(val["parameters"]["arr"][2][1].ToStringA(), "test"); EXPECT_DOUBLE_EQ((double)val["parameters"]["arr"][2][2], 2.71828); // Fourth element of "arr" (object) EXPECT_TRUE(val["parameters"]["arr"][3].IsObject()); EXPECT_EQ(val["parameters"]["arr"][3].GetPropertyNames().size(), 2); EXPECT_EQ(val["parameters"]["arr"][3]["name"].ToStringA(), "Bar"); EXPECT_TRUE(val["parameters"]["arr"][3]["good"].IsBool()); EXPECT_FALSE(val["parameters"]["arr"][3]["good"]); // Fifth element of "arr" (null) EXPECT_TRUE(val["parameters"]["arr"][4].IsNull()); // Sixth element of "arr" (string) EXPECT_EQ(val["parameters"]["arr"][5].ToStringA(), "abc de f"); // Seventh element of "arr" (empty string) EXPECT_EQ(val["parameters"]["arr"][6].ToStringA(), ""); // Check "0" property (numeric value) EXPECT_EQ((int)val["parameters"]["0"], 0); // Check "data" object inside "parameters" EXPECT_TRUE(val["parameters"]["data"].IsObject()); EXPECT_EQ(val["parameters"]["data"].GetPropertyNames().size(), 2); // Check "data" property inside "data" (null) EXPECT_TRUE(val["parameters"]["data"]["data"].IsNull()); // Check "count" property inside "data" EXPECT_EQ((int)val["parameters"]["data"]["count"], 0); } TEST_F(CJSONTest, FromJSON_objects_invalid) { // no closing bracket std::string strJson = "{"; CValue val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{ \"key1\": \"value1\", \"key2\": 42"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // trailing comma strJson = "{ \"key1\": \"value1\", \"key2\": 42, }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // duplicating properties are technically allowed. Only the last value will apply. strJson = "{ \"key\": \"value1\", \"key\": \"value2\" }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsObject()); EXPECT_EQ(val.GetPropertyNames().size(), 1); EXPECT_EQ(val["key"].ToStringA(), "value2"); // keys are not enslosed in double quotes strJson = "{ key1: \"value1\", \"key2\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // missing colon between the key and the value strJson = "{ \"key1\" \"value1\", \"key2\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{\"key\"}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{key}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // wrong value formats strJson = "{ \"key1\": undefined, \"key2\": 42 }"; ; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{ \"key1\": 12.34.56, \"key2\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{ \"key1\": \"value1, \"key2\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // non-string key strJson = "{ 123: \"value\", \"key2\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // missing coma strJson = "{ \"key1\": \"value1\" \"key2\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // some invalid inner object strJson = "{ \"outerKey\": { \"innerKey\": \"value\", \"anotherInnerKey\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{ \"outerKey\": { innerKey: \"value\" } }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{ \"outerKey\": { \"innerKey\" \"value\" } }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{ \"outerKey\": { \"innerKey\": \"value\", }, \"key2\": 42 }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{ \"outerKey\": { \"innerKey\": \"value\", \"anotherInnerKey\": 42, \"thirdKey\": { \"deepKey\": \"value\" } }"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // no opening bracket strJson = "}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "\"key1\": \"value1\", \"key2\": 42}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // extra brackets strJson = "{{\"key1\": \"value1\", \"key2\": 42}}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{{\"key1\": \"value1\", \"key2\": 42}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{\"key1\": \"value1\", \"key2\": 42}}"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); // elements outside of the object strJson = "{\"key1\": \"value1\", \"key2\": 42}, \"key3\": 3.14"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); strJson = "{\"key1\": \"value1\", \"key2\": 42} \"key3\": 3.14"; val = CValue::FromJSON(strJson); EXPECT_TRUE(val.IsUndefined()); } #else int main() { JSSmart pContext = new CJSContext(); CJSContextScope scope(pContext); // top object with some text parameters CValue textPr = CValue::CreateObject(); CValue colorRGB = CValue::CreateObject(); colorRGB["r"] = 12; colorRGB["g"] = 34; colorRGB["b"] = 56; CValue colorRGBA = CValue::CreateObject(); colorRGBA["rgb"] = colorRGB; colorRGBA["alpha"] = 80; // CValueRef behaves as reference CValueRef name = textPr["name"]; // from now on, name - is a reference to the property "name" of textPr. Changing it will affect this object property: name = "Foo"; // Also you can chain references CValueRef name3 = name; name3 = "FooBar"; // this change applies to textPr["name"] // The same but with CValue wouldnt' work: CValue name2 = textPr["name"]; name2 = "Bar"; // this change doesn't affect the property "name" of textPr textPr["size"] = 4.2; textPr["color"] = colorRGBA; textPr["font"] = CValue::CreateObject(); textPr["font"]["fontName"] = L"Times New Roman"; textPr["font"]["bold"] = true; // undefined member: textPr["font"]["italic"] = CValue::CreateUndefined(); // or just // textPr["font"]["italic"]; // null member: textPr["extras"] = CValue::CreateNull(); // array CValue numbers = {10000, 12, 42, 0, 147}; // inner array CValue innerArray = {true, "abc", 3.1415926535, L"ABC", 4}; numbers[3] = innerArray; textPr["numbers"] = numbers; // create typed array BYTE* pData = CValue::AllocTypedArray(4); pData[0] = 11; pData[1] = 23; pData[2] = 58; pData[3] = 13; // add typed array CValue typedArr = CValue::CreateTypedArray(pData, 4, false); textPr["typedArr"] = typedArr; // convert to JS JSSmart jsObj = toJS(textPr)->toObject(); JSSmart global = pContext->GetGlobal(); global->set("textPr", jsObj); JSSmart ret = pContext->runScript("(function () { return JSON.stringify(textPr, null, 4); })();"); if (ret.IsInit()) { std::cout << ret->toStringA() << std::endl; } // convert fromJS() the same object: CValue textPr2 = fromJS(jsObj->toValue()); // and then back toJS() to see if result is the same JSSmart jsObj2 = toJS(textPr2)->toObject(); global->set("textPr2", jsObj2); ret = pContext->runScript("(function () { return JSON.stringify(textPr2, null, 4); })();"); if (ret.IsInit()) { std::cout << ret->toStringA() << std::endl; } return 0; } #endif // JSON_GOOGLE_TEST