From 3e5eb3d2510f4615824b6f8f38b304bc0c623227 Mon Sep 17 00:00:00 2001 From: Mikhail Lobotskiy Date: Mon, 27 Nov 2023 21:29:56 +0400 Subject: [PATCH] Added tests to basic CValue functionality --- DesktopEditor/doctrenderer/json/json.cpp | 6 + .../doctrenderer/json/json_values.cpp | 2 +- DesktopEditor/doctrenderer/test/json/main.cpp | 467 +++++++++++++++++- DesktopEditor/doctrenderer/test/json/test.pro | 10 +- 4 files changed, 475 insertions(+), 10 deletions(-) diff --git a/DesktopEditor/doctrenderer/json/json.cpp b/DesktopEditor/doctrenderer/json/json.cpp index 32d5c07071..f2ded6054e 100644 --- a/DesktopEditor/doctrenderer/json/json.cpp +++ b/DesktopEditor/doctrenderer/json/json.cpp @@ -318,6 +318,9 @@ namespace NSJSON CValue CValue::CreateArray(int count) { CValue ret; + if (count < 0) + return ret; + ret.m_internal->m_value = std::make_shared(count); ret.m_internal->m_type = CTypedValue::vtArray; return ret; @@ -326,6 +329,9 @@ namespace NSJSON CValue CValue::CreateTypedArray(BYTE* data, int count, bool isExternalize) { CValue ret; + if (count <= 0) + return ret; + ret.m_internal->m_value = std::make_shared(data, count, isExternalize); ret.m_internal->m_type = CTypedValue::vtTypedArray; return ret; diff --git a/DesktopEditor/doctrenderer/json/json_values.cpp b/DesktopEditor/doctrenderer/json/json_values.cpp index eaab4b5963..d37ea38804 100644 --- a/DesktopEditor/doctrenderer/json/json_values.cpp +++ b/DesktopEditor/doctrenderer/json/json_values.cpp @@ -125,7 +125,7 @@ namespace NSJSON if (m_type == ptDouble) return m_double; if (m_type == ptInteger) - return (double)m_double; + return (double)m_int; #ifdef JSON_DEBUG throw std::bad_cast(); #endif diff --git a/DesktopEditor/doctrenderer/test/json/main.cpp b/DesktopEditor/doctrenderer/test/json/main.cpp index f4adb40fec..c0df5bbc45 100644 --- a/DesktopEditor/doctrenderer/test/json/main.cpp +++ b/DesktopEditor/doctrenderer/test/json/main.cpp @@ -1,14 +1,17 @@ -//#include "gtest/gtest.h" -#include "js_internal/js_base.h" #include "json/serialization.h" +#ifdef JSON_GOOGLE_TEST +#include "gtest/gtest.h" +#include +#else #include -#include +#endif using namespace NSJSBase; using namespace NSJSON; -/* +#ifdef JSON_GOOGLE_TEST + class CJSONTest : public testing::Test { public: @@ -24,16 +27,462 @@ public: 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(); + // strict check without tolerance + if (makeExpects) + EXPECT_EQ(val, jsVal); + if (val != 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(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 getObject(const std::string& objLiteral) + { + JSSmart jsObj = m_pContext->runScript("(() => { return " + objLiteral + ";})();")->toObject(); + return jsObj; + } + public: JSSmart m_pContext; }; -TEST_F(..., ...) +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_FALSE(val.IsDouble()); + 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()); + EXPECT_TRUE(arr[100].IsUndefined()); + EXPECT_TRUE(arr[-1].IsUndefined()); + arr[5] = 1; + 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)); + 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 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 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, 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, 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); + 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); + CValueRef ref3 = val[2]; + ref3 = ref2[0]; + EXPECT_EQ((int)val[2], 1); +} + +TEST_F(CJSONTest, constants) +{ + const CValue val = 42; + // 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); + // const references saves its const properties + const CValueRef ref2 = ref; + 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_EQ(val.ToStringA(), ""); + EXPECT_EQ(val.ToStringW(), L""); + EXPECT_EQ(val.ToBool(), false); + EXPECT_DOUBLE_EQ(val.ToDouble(), 42.0); +} + +#else int main() { JSSmart pContext = new CJSContext(); @@ -99,8 +548,9 @@ int main() std::cout << ret->toStringA() << std::endl; } - // Test fromJS() on the same object: + // 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); })();"); @@ -111,3 +561,4 @@ int main() return 0; } +#endif // JSON_GOOGLE_TEST diff --git a/DesktopEditor/doctrenderer/test/json/test.pro b/DesktopEditor/doctrenderer/test/json/test.pro index 2669f84897..e6323902bf 100644 --- a/DesktopEditor/doctrenderer/test/json/test.pro +++ b/DesktopEditor/doctrenderer/test/json/test.pro @@ -14,7 +14,15 @@ CORE_3DPARTY_DIR = $$CORE_ROOT_DIR/Common/3dParty PWD_ROOT_DIR = $$PWD include($$CORE_ROOT_DIR/Common/base.pri) -#include($$CORE_3DPARTY_DIR/googletest/googletest.pri) + +# Comment to inspect simple usage example +# Uncomment to run google tests +CONFIG += json_google_test + +json_google_test { + include($$CORE_3DPARTY_DIR/googletest/googletest.pri) + DEFINES += JSON_GOOGLE_TEST +} DESTDIR = $$PWD/build