mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
889 lines
24 KiB
C++
889 lines
24 KiB
C++
#include "json/serialization.h"
|
|
|
|
#ifdef JSON_GOOGLE_TEST
|
|
#include "gtest/gtest.h"
|
|
#include <algorithm>
|
|
#else
|
|
#include <iostream>
|
|
#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<CJSValue> 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<CJSArray> 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<CJSTypedArray> 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<CJSObject> jsObj = jsValue->toObject();
|
|
std::vector<std::string> properties = value.GetPropertyNames();
|
|
std::vector<std::string> 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 (value.IsStringA())
|
|
{
|
|
if (makeExpects)
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
if (makeExpects)
|
|
{
|
|
EXPECT_TRUE(value.IsStringW());
|
|
EXPECT_TRUE(jsValue->isString());
|
|
}
|
|
if (!jsValue->isString())
|
|
return false;
|
|
|
|
std::wstring val = value.ToStringW();
|
|
std::wstring jsVal = jsValue->toStringW();
|
|
if (makeExpects)
|
|
EXPECT_EQ(val, jsVal);
|
|
if (val != jsVal)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
JSSmart<CJSObject> getObject(const std::string& objLiteral)
|
|
{
|
|
JSSmart<CJSObject> jsObj = m_pContext->runScript("(() => { return " + objLiteral + ";})();")->toObject();
|
|
return jsObj;
|
|
}
|
|
|
|
public:
|
|
JSSmart<CJSContext> m_pContext;
|
|
};
|
|
|
|
// --------- CValue basic functinality tests ----------
|
|
|
|
TEST_F(CJSONTest, undefined_from_default_constructor)
|
|
{
|
|
CValue val;
|
|
JSSmart<CJSValue> 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<CJSValue> 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<CJSValue> jsVal = CJSContext::createNull();
|
|
EXPECT_TRUE(compare(val, jsVal));
|
|
val = CValue();
|
|
EXPECT_FALSE(compare(val, jsVal, false));
|
|
}
|
|
|
|
TEST_F(CJSONTest, bool_)
|
|
{
|
|
CValue val = true;
|
|
JSSmart<CJSValue> 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_FALSE(val.IsDouble());
|
|
JSSmart<CJSValue> 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<CJSValue> 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<CJSValue> 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<CJSValue> 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<CJSValue> 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<CJSValue> 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());
|
|
EXPECT_TRUE(arr[100].IsUndefined());
|
|
EXPECT_TRUE(arr[-1].IsUndefined());
|
|
arr[5] = 1;
|
|
JSSmart<CJSArray> 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));
|
|
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());
|
|
EXPECT_TRUE(arr[100].IsUndefined());
|
|
EXPECT_TRUE(arr[-1].IsUndefined());
|
|
arr[5] = 1;
|
|
JSSmart<CJSArray> 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));
|
|
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);
|
|
EXPECT_TRUE(arr[0].IsUndefined());
|
|
}
|
|
|
|
TEST_F(CJSONTest, array_negative_size)
|
|
{
|
|
CValue arr = CValue::CreateArray(-1);
|
|
EXPECT_TRUE(arr.IsUndefined());
|
|
EXPECT_TRUE(arr[100].IsUndefined());
|
|
}
|
|
|
|
TEST_F(CJSONTest, typed_array_externalize)
|
|
{
|
|
BYTE* data = CValue::AllocTypedArray(10);
|
|
CValue typedArr = CValue::CreateTypedArray(data, 10);
|
|
JSSmart<CJSTypedArray> 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<CJSTypedArray> 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<CJSObject> 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, 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;
|
|
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_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);
|
|
EXPECT_THROW((std::wstring)val, std::bad_cast);
|
|
#else
|
|
EXPECT_EQ((bool)val, false);
|
|
EXPECT_EQ((int)val, 0);
|
|
EXPECT_EQ((double)val, 0.0);
|
|
EXPECT_EQ((std::wstring)val, L"");
|
|
#endif
|
|
|
|
val = L"тест";
|
|
#ifdef JSON_DEBUG
|
|
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);
|
|
#else
|
|
EXPECT_EQ((bool)val, false);
|
|
EXPECT_EQ((int)val, 0);
|
|
EXPECT_EQ((double)val, 0.0);
|
|
EXPECT_EQ((std::string)val, "");
|
|
#endif
|
|
|
|
val = CValue::CreateObject();
|
|
#ifdef JSON_DEBUG
|
|
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_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
|
|
}
|
|
|
|
// ----------- toJS() tests -----------
|
|
|
|
TEST_F(CJSONTest, toJS_undefined)
|
|
{
|
|
CValue val;
|
|
JSSmart<CJSValue> jsVal = toJS(val);
|
|
EXPECT_TRUE(compare(val, jsVal));
|
|
}
|
|
|
|
TEST_F(CJSONTest, toJS_null)
|
|
{
|
|
CValue val = CValue::CreateNull();
|
|
JSSmart<CJSValue> 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<CJSValue> 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<CJSValue> 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<CJSValue> 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<CJSValue> jsArr = toJS(arr);
|
|
|
|
// keep only 2 inner recursions
|
|
CValue arrRec = arr[1][1];
|
|
arrRec[1] = 42;
|
|
EXPECT_TRUE(arr[1].IsInt());
|
|
|
|
JSSmart<CJSValue> 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<CJSValue> jsObj = toJS(obj);
|
|
EXPECT_TRUE(compare(obj, jsObj));
|
|
}
|
|
|
|
// ----------- fromJS() tests -----------
|
|
|
|
TEST_F(CJSONTest, fromJS_undefined)
|
|
{
|
|
JSSmart<CJSValue> jsVal = CJSContext::createUndefined();
|
|
CValue val = fromJS(jsVal);
|
|
EXPECT_TRUE(compare(val, jsVal));
|
|
}
|
|
|
|
TEST_F(CJSONTest, fromJS_null)
|
|
{
|
|
JSSmart<CJSValue> jsVal = CJSContext::createNull();
|
|
CValue val = fromJS(jsVal);
|
|
EXPECT_TRUE(compare(val, jsVal));
|
|
}
|
|
|
|
TEST_F(CJSONTest, fromJS_and_toJS_edge_numbers)
|
|
{
|
|
JSSmart<CJSArray> 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<CJSValue> 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<CJSValue> 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<CJSValue> jsTypedArr = CJSContext::createUint8Array(data, 4, false);
|
|
JSSmart<CJSArray> 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<CJSArray> 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<CJSObject> jsObj = CJSContext::createObject();
|
|
jsObj->set("name", CJSContext::createString(L"Foo"));
|
|
|
|
JSSmart<CJSObject> jsParam = CJSContext::createObject();
|
|
jsParam->set("size", 42);
|
|
jsParam->set("arr", CJSContext::createArray(0));
|
|
|
|
JSSmart<CJSArray> 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);
|
|
|
|
BYTE* data = NSAllocator::Alloc(4);
|
|
data[0] = 0x1A;
|
|
data[1] = 0x54;
|
|
data[2] = 0xFE;
|
|
data[3] = 0xFF;
|
|
JSSmart<CJSValue> jsTypedArr = CJSContext::createUint8Array(data, 4, false);
|
|
|
|
CValue obj = fromJS(jsObj->toValue());
|
|
EXPECT_TRUE(compare(obj, jsObj->toValue()));
|
|
}
|
|
|
|
#else
|
|
int main()
|
|
{
|
|
JSSmart<CJSContext> 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<CJSObject> jsObj = toJS(textPr)->toObject();
|
|
JSSmart<CJSObject> global = pContext->GetGlobal();
|
|
global->set("textPr", jsObj);
|
|
JSSmart<CJSValue> 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<CJSObject> 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
|