From 2212b5f900089e3dcb17a602f0484769eb75a487 Mon Sep 17 00:00:00 2001 From: ProjectSky Date: Mon, 20 Oct 2025 20:39:17 +0800 Subject: [PATCH] docs: fix documentation errors, typos and improve API clarity --- extensions/yyjson/IYYJSONManager.h | 33 +- extensions/yyjson/YYJSONManager.cpp | 7 +- extensions/yyjson/YYJSONManager.h | 2 +- extensions/yyjson/extension.cpp | 5 - extensions/yyjson/extension.h | 1 - extensions/yyjson/json_natives.cpp | 2 +- plugins/include/yyjson.inc | 82 +- plugins/testsuite/test_yyjson.sp | 2041 +++++++++++++++++++++++++++ plugins/testsuite/yyjson_test.sp | 566 -------- 9 files changed, 2121 insertions(+), 618 deletions(-) create mode 100755 plugins/testsuite/test_yyjson.sp delete mode 100755 plugins/testsuite/yyjson_test.sp diff --git a/extensions/yyjson/IYYJSONManager.h b/extensions/yyjson/IYYJSONManager.h index 7ac04a26b..2f2928cf1 100755 --- a/extensions/yyjson/IYYJSONManager.h +++ b/extensions/yyjson/IYYJSONManager.h @@ -5,6 +5,7 @@ using SourceMod::Handle_t; using SourceMod::HandleType_t; +using SourceMod::SMInterface; using SourcePawn::IPluginContext; // Forward declaration @@ -27,7 +28,7 @@ enum YYJSON_SORT_ORDER /** * @brief Parameter provider interface for Pack operation - * + * * Allows Pack to retrieve parameters in a platform-independent way. */ class IPackParamProvider @@ -56,7 +57,7 @@ public: * SM_GET_LATE_IFACE(YYJSONMANAGER, g_pYYJSONManager); * } */ -class IYYJSONManager : public SourceMod::SMInterface +class IYYJSONManager : public SMInterface { public: virtual const char *GetInterfaceName() override { @@ -87,8 +88,11 @@ public: * @param buffer Output buffer * @param buffer_size Buffer size * @param write_flg Write flags (YYJSON_WRITE_FLAG values, default: 0) - * @param out_size Pointer to receive actual size (optional, default: nullptr) - * @return true on success + * @param out_size Pointer to receive actual size written (including null terminator), optional + * @return true on success, false if buffer is too small or on error + * + * @note The out_size parameter returns the size including null terminator + * @note Use GetSerializedSize() with the same write_flg to determine buffer size */ virtual bool WriteToString(YYJSONValue* handle, char* buffer, size_t buffer_size, uint32_t write_flg = 0, size_t* out_size = nullptr) = 0; @@ -126,15 +130,28 @@ public: /** * Get human-readable type description string * @param handle JSON value - * @return Type description string (e.g., "object", "array", "string", "number", "true", "false", "null") + * @return Type description string (e.g., "object", "array", "string", "number", "true", "false", "unknown") */ virtual const char* GetTypeDesc(YYJSONValue* handle) = 0; /** * Get the size needed to serialize this JSON value + * * @param handle JSON value * @param write_flg Write flags (YYJSON_WRITE_FLAG values, default: 0) * @return Size in bytes (including null terminator) + * + * @note The returned size depends on the write_flg parameter. + * You MUST use the same flags when calling both GetSerializedSize() + * and WriteToString(). Using different flags will return + * different sizes and may cause buffer overflow. + * + * @example + * // Correct usage: + * auto flags = YYJSON_WRITE_PRETTY; + * size_t size = g_pYYJSONManager->GetSerializedSize(handle, flags); + * char* buffer = new char[size]; + * g_pYYJSONManager->WriteToString(handle, buffer, size, flags); // Use same flags */ virtual size_t GetSerializedSize(YYJSONValue* handle, uint32_t write_flg = 0) = 0; @@ -278,7 +295,11 @@ public: /** * Get the number of bytes read when parsing this document * @param handle JSON value - * @return Number of bytes read during parsing, 0 if not from parsing + * @return Number of bytes read during parsing (excluding null terminator), 0 if not from parsing + * + * @note This value only applies to documents created from parsing + * @note Manually created documents (ObjectInit, CreateBool, etc.) will return 0 + * @note The returned size does not include the null terminator */ virtual size_t GetReadSize(YYJSONValue* handle) = 0; diff --git a/extensions/yyjson/YYJSONManager.cpp b/extensions/yyjson/YYJSONManager.cpp index 85925ba00..f3b1fc97f 100755 --- a/extensions/yyjson/YYJSONManager.cpp +++ b/extensions/yyjson/YYJSONManager.cpp @@ -527,7 +527,12 @@ size_t YYJSONManager::GetReadSize(YYJSONValue* handle) return 0; } - return handle->m_readSize; + // this not happen in normal case, but it's possible if the document is not from parsing. + if (handle->m_readSize == 0) { + return 0; + } + + return handle->m_readSize + 1; } YYJSONValue* YYJSONManager::ObjectInit() diff --git a/extensions/yyjson/YYJSONManager.h b/extensions/yyjson/YYJSONManager.h index d6d76d5ae..802e5c5eb 100755 --- a/extensions/yyjson/YYJSONManager.h +++ b/extensions/yyjson/YYJSONManager.h @@ -61,7 +61,7 @@ public: yyjson_obj_iter m_iterObjImm; yyjson_arr_iter m_iterArrImm; - SourceMod::Handle_t m_handle{ BAD_HANDLE }; + Handle_t m_handle{ BAD_HANDLE }; size_t m_arrayIndex{ 0 }; size_t m_readSize{ 0 }; bool m_iterInitialized{ false }; diff --git a/extensions/yyjson/extension.cpp b/extensions/yyjson/extension.cpp index 4c2888f2f..f927f2316 100755 --- a/extensions/yyjson/extension.cpp +++ b/extensions/yyjson/extension.cpp @@ -54,9 +54,4 @@ void JsonExtension::SDK_OnUnload() void JSONHandler::OnHandleDestroy(HandleType_t type, void* object) { delete (YYJSONValue*)object; -} - -IYYJSONManager* JsonExtension::GetYYJSONManager() -{ - return g_pYYJSONManager; } \ No newline at end of file diff --git a/extensions/yyjson/extension.h b/extensions/yyjson/extension.h index 3d205584d..0d1d503cf 100755 --- a/extensions/yyjson/extension.h +++ b/extensions/yyjson/extension.h @@ -11,7 +11,6 @@ class JsonExtension : public SDKExtension public: virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); virtual void SDK_OnUnload(); - IYYJSONManager* GetYYJSONManager(); }; class JSONHandler : public IHandleTypeDispatch diff --git a/extensions/yyjson/json_natives.cpp b/extensions/yyjson/json_natives.cpp index ea3e902e7..b4774e670 100755 --- a/extensions/yyjson/json_natives.cpp +++ b/extensions/yyjson/json_natives.cpp @@ -669,7 +669,7 @@ static cell_t json_val_get_read_size(IPluginContext* pContext, const cell_t* par size_t size = g_pYYJSONManager->GetReadSize(handle); if (size == 0) return 0; - return static_cast(size + 1); + return static_cast(size); } static cell_t json_val_create_null(IPluginContext* pContext, const cell_t* params) diff --git a/plugins/include/yyjson.inc b/plugins/include/yyjson.inc index e19aafa21..1813e8297 100755 --- a/plugins/include/yyjson.inc +++ b/plugins/include/yyjson.inc @@ -198,7 +198,7 @@ methodmap YYJSON < Handle * @param maxlength Maximum length of the string buffer * @param flag The JSON write options * - * @return Number of characters written to the buffer, including the null terminator. 0 on failure + * @return Number of characters written to the buffer (including null terminator) or 0 on failure */ public native int ToString(char[] buffer, int maxlength, YYJSON_WRITE_FLAG flag = YYJSON_WRITE_NOFLAG); @@ -278,7 +278,7 @@ methodmap YYJSON < Handle public static native YYJSON CreateFloat(float value); /** - * Creates and returns a int value + * Creates and returns an int value * * @note Needs to be freed using delete or CloseHandle() * @@ -289,11 +289,11 @@ methodmap YYJSON < Handle public static native YYJSON CreateInt(int value); /** - * Creates and returns a intger64 value + * Creates and returns an integer64 value * * @note Needs to be freed using delete or CloseHandle() * - * @param value The intger64 value to be set + * @param value The integer64 value to be set * * @return JSON handle, NULL on error */ @@ -347,7 +347,7 @@ methodmap YYJSON < Handle public static native int GetInt(const YYJSON value); /** - * Get intger64 value by a JSON Handle + * Get integer64 value by a JSON Handle * * @param value JSON handle * @param buffer Buffer to copy to @@ -369,12 +369,17 @@ methodmap YYJSON < Handle public static native bool GetString(const YYJSON value, char[] buffer, int maxlength); /** - * Get JSON Handle serialized size in bytes (including null-terminator) - * - * @param flag The JSON write options - * - * @return serialized size - */ + * Get JSON Handle serialized size in bytes (including null-terminator) + * + * @param flag The JSON write options + * + * @return Size in bytes (including null terminator) + * + * @note The returned size depends on the flag parameter. + * You MUST use the same flags when calling both GetSerializedSize() + * and ToString(). Using different flags will return different sizes + * and may cause buffer overflow. + */ public native int GetSerializedSize(YYJSON_WRITE_FLAG flag = YYJSON_WRITE_NOFLAG); /** @@ -435,7 +440,7 @@ methodmap YYJSON < Handle * * @return True on success, false on failure */ - public native bool PtrGetString(const char[] path, char[] buffer, int maxlength) + public native bool PtrGetString(const char[] path, char[] buffer, int maxlength); /** * Get value is null by a JSON Pointer @@ -444,18 +449,20 @@ methodmap YYJSON < Handle * * @return True if the value is null, false otherwise */ - public native bool PtrGetIsNull(const char[] path) + public native bool PtrGetIsNull(const char[] path); /** * Get JSON content length (string length, array size, object size) - * Returns 0 if val is NULL or type is not string/array/object - * if str including null-terminator + * + * @note For strings: returns string length including null-terminator + * @note For arrays/objects: returns number of elements + * @note Returns 0 if value is NULL or type is not string/array/object * * @param path The JSON pointer string * * @return JSON content length */ - public native int PtrGetLength(const char[] path) + public native int PtrGetLength(const char[] path); /** * Set value by a JSON Pointer @@ -463,7 +470,7 @@ methodmap YYJSON < Handle * @note The parent nodes will be created if they do not exist. If the target value already exists, it will be replaced by the new value * * @param path The JSON pointer string - * @param value The value to be set, pass NULL to remove + * @param value The value to be set * * @return true if JSON pointer is valid and new value is set, false otherwise */ @@ -475,7 +482,7 @@ methodmap YYJSON < Handle * @note The parent nodes will be created if they do not exist. If the target value already exists, it will be replaced by the new value * * @param path The JSON pointer string - * @param value The boolean value to be set, pass NULL to remove + * @param value The boolean value to be set * * @return true if JSON pointer is valid and new value is set, false otherwise */ @@ -487,7 +494,7 @@ methodmap YYJSON < Handle * @note The parent nodes will be created if they do not exist. If the target value already exists, it will be replaced by the new value * * @param path The JSON pointer string - * @param value The float value to be set, pass NULL to remove + * @param value The float value to be set * * @return true if JSON pointer is valid and new value is set, false otherwise */ @@ -499,19 +506,19 @@ methodmap YYJSON < Handle * @note The parent nodes will be created if they do not exist. If the target value already exists, it will be replaced by the new value * * @param path The JSON pointer string - * @param value The integer value to be set, pass NULL to remove + * @param value The integer value to be set * * @return true if JSON pointer is valid and new value is set, false otherwise */ public native bool PtrSetInt(const char[] path, int value); /** - * Set intger64 value by a JSON Pointer + * Set integer64 value by a JSON Pointer * * @note The parent nodes will be created if they do not exist. If the target value already exists, it will be replaced by the new value * * @param path The JSON pointer string - * @param value The intger64 value to be set, pass NULL to remove + * @param value The integer64 value to be set * * @return true if JSON pointer is valid and new value is set, false otherwise */ @@ -523,7 +530,7 @@ methodmap YYJSON < Handle * @note The parent nodes will be created if they do not exist. If the target value already exists, it will be replaced by the new value * * @param path The JSON pointer string - * @param value The string value to be set, pass NULL to remove + * @param value The string value to be set * * @return true if JSON pointer is valid and new value is set, false otherwise */ @@ -535,7 +542,6 @@ methodmap YYJSON < Handle * @note The parent nodes will be created if they do not exist. If the target value already exists, it will be replaced by the new value * * @param path The JSON pointer string - * @param value The null value to be set, pass NULL to remove * * @return true if JSON pointer is valid and new value is set, false otherwise */ @@ -585,7 +591,7 @@ methodmap YYJSON < Handle * Add (insert) integer64 value by a JSON pointer * * @param path The JSON pointer string - * @param value The intger64 value to be added + * @param value The integer64 value to be added * * @return true if JSON pointer is valid and new value is set, false otherwise */ @@ -617,7 +623,7 @@ methodmap YYJSON < Handle * * @return true if removed value, false otherwise */ - public native bool PtrRemove(const char[] path) + public native bool PtrRemove(const char[] path); /** * Try to get value by a JSON Pointer @@ -801,14 +807,16 @@ methodmap YYJSON < Handle } /** - * Retrieves read size of the JSON data + * Retrieves the size of the JSON data as it was originally read from parsing * - * @note This value reflects the size of the JSON data as read from the document - * - It does not auto update if the document is modified - * - For modified document, use GetSerializedSize to obtain the current size + * @return Size in bytes (including null terminator) or 0 if not from parsing * + * @note This value only applies to documents created from parsing (Parse, FromString, FromFile) + * @note Manually created documents (new YYJSONObject(), YYJSON.CreateBool(), etc.) will return 0 + * @note This value does not auto-update if the document is modified + * @note For modified documents, use GetSerializedSize() to obtain the current size */ - property bool ReadSize { + property int ReadSize { public native get(); } }; @@ -1219,7 +1227,7 @@ methodmap YYJSONArray < YYJSON public native bool SetFloat(int index, float value); /** - * Replaces a integer value at index + * Replaces an integer value at index * * @param index The index to which to replace the value * @param value The new int value to replace @@ -1229,10 +1237,10 @@ methodmap YYJSONArray < YYJSON public native bool SetInt(int index, int value); /** - * Replaces a intger64 value at index + * Replaces an integer64 value at index * * @param index The index to which to replace the value - * @param value The new intger64 value to replace + * @param value The new integer64 value to replace * * @return True if succeed, false otherwise */ @@ -1285,7 +1293,7 @@ methodmap YYJSONArray < YYJSON public native bool PushFloat(float value); /** - * Inserts a integer value at the end of the array + * Inserts an integer value at the end of the array * * @param value integer to set * @@ -1294,9 +1302,9 @@ methodmap YYJSONArray < YYJSON public native bool PushInt(int value); /** - * Inserts a intger64 value at the end of the array + * Inserts an integer64 value at the end of the array * - * @param value intger64 value + * @param value integer64 value * * @return The value to be inserted. Returns false if it is NULL */ diff --git a/plugins/testsuite/test_yyjson.sp b/plugins/testsuite/test_yyjson.sp new file mode 100755 index 000000000..8d3f7fb54 --- /dev/null +++ b/plugins/testsuite/test_yyjson.sp @@ -0,0 +1,2041 @@ +#include +#include + +#pragma newdecls required +#pragma semicolon 1 + +public Plugin myinfo = +{ + name = "YYJSON Test Suite", + author = "ProjectSky", + description = "test suite for YYJSON functions", + version = "1.0.0", + url = "https://github.com/ProjectSky/sm-ext-yyjson" +}; + +// Test statistics +int g_iTotalTests = 0; +int g_iPassedTests = 0; +int g_iFailedTests = 0; +char g_sCurrentTest[128]; +bool g_bCurrentTestFailed = false; + +public void OnPluginStart() +{ + RegServerCmd("test_yyjson", Command_RunTests, "Run YYJSON test suite"); +} + +// ============================================================================ +// Test Framework - Assertion Functions +// ============================================================================ + +/** + * Start a new test case + */ +void TestStart(const char[] name) +{ + g_iTotalTests++; + strcopy(g_sCurrentTest, sizeof(g_sCurrentTest), name); + g_bCurrentTestFailed = false; +} + +/** + * End current test case + */ +void TestEnd() +{ + if (!g_bCurrentTestFailed) + { + g_iPassedTests++; + PrintToServer("[PASS] %s", g_sCurrentTest); + } + else + { + g_iFailedTests++; + } +} + +/** + * Assert a condition is true + */ +void Assert(bool condition, const char[] message = "") +{ + if (!condition) + { + g_bCurrentTestFailed = true; + PrintToServer("[FAIL] %s - %s", g_sCurrentTest, message); + } +} + +/** + * Assert two integers are equal + */ +void AssertEq(int a, int b, const char[] message = "") +{ + if (a != b) + { + g_bCurrentTestFailed = true; + char buffer[256]; + if (message[0] != '\0') + { + Format(buffer, sizeof(buffer), "%s (expected %d, got %d)", message, b, a); + } + else + { + Format(buffer, sizeof(buffer), "Expected %d, got %d", b, a); + } + PrintToServer("[FAIL] %s - %s", g_sCurrentTest, buffer); + } +} + +/** + * Assert two integers are not equal + */ +stock void AssertNeq(int a, int b, const char[] message = "") +{ + if (a == b) + { + g_bCurrentTestFailed = true; + char buffer[256]; + if (message[0] != '\0') + { + Format(buffer, sizeof(buffer), "%s (values should not be equal: %d)", message, a); + } + else + { + Format(buffer, sizeof(buffer), "Values should not be equal: %d", a); + } + PrintToServer("[FAIL] %s - %s", g_sCurrentTest, buffer); + } +} + +/** + * Assert condition is true + */ +void AssertTrue(bool condition, const char[] message = "") +{ + Assert(condition, message[0] != '\0' ? message : "Expected true, got false"); +} + +/** + * Assert condition is false + */ +void AssertFalse(bool condition, const char[] message = "") +{ + Assert(!condition, message[0] != '\0' ? message : "Expected false, got true"); +} + +/** + * Assert two strings are equal + */ +void AssertStrEq(const char[] a, const char[] b, const char[] message = "") +{ + if (!StrEqual(a, b)) + { + g_bCurrentTestFailed = true; + char buffer[512]; + if (message[0] != '\0') + { + Format(buffer, sizeof(buffer), "%s (expected \"%s\", got \"%s\")", message, b, a); + } + else + { + Format(buffer, sizeof(buffer), "Expected \"%s\", got \"%s\"", b, a); + } + PrintToServer("[FAIL] %s - %s", g_sCurrentTest, buffer); + } +} + +/** + * Assert two floats are equal within epsilon + */ +void AssertFloatEq(float a, float b, const char[] message = "", float epsilon = 0.0001) +{ + if (FloatAbs(a - b) > epsilon) + { + g_bCurrentTestFailed = true; + char buffer[256]; + if (message[0] != '\0') + { + Format(buffer, sizeof(buffer), "%s (expected %.4f, got %.4f)", message, b, a); + } + else + { + Format(buffer, sizeof(buffer), "Expected %.4f, got %.4f", b, a); + } + PrintToServer("[FAIL] %s - %s", g_sCurrentTest, buffer); + } +} + +/** + * Assert handle is valid + */ +void AssertValidHandle(Handle handle, const char[] message = "") +{ + Assert(handle != null, message[0] != '\0' ? message : "Handle should not be null"); +} + +/** + * Assert handle is null + */ +void AssertNullHandle(Handle handle, const char[] message = "") +{ + Assert(handle == null, message[0] != '\0' ? message : "Handle should be null"); +} + +// ============================================================================ +// Test Command +// ============================================================================ + +public Action Command_RunTests(int args) +{ + PrintToServer("========================================"); + PrintToServer("YYJSON Test Suite"); + PrintToServer("========================================"); + + // Reset statistics + g_iTotalTests = 0; + g_iPassedTests = 0; + g_iFailedTests = 0; + + // Run all test categories + Test_BasicValues(); + Test_ObjectOperations(); + Test_ArrayOperations(); + Test_ParseAndSerialize(); + Test_Iterators(); + Test_JSONPointer(); + Test_AdvancedFeatures(); + Test_EdgeCases(); + + // Print results + PrintToServer("========================================"); + PrintToServer("YYJSON Test Suite Results"); + PrintToServer("========================================"); + PrintToServer("Total Tests: %d", g_iTotalTests); + PrintToServer("Passed: %d", g_iPassedTests); + PrintToServer("Failed: %d", g_iFailedTests); + + if (g_iTotalTests > 0) + { + float successRate = (float(g_iPassedTests) / float(g_iTotalTests)) * 100.0; + PrintToServer("Success Rate: %.2f%%", successRate); + } + + PrintToServer("========================================"); + + return Plugin_Handled; +} + +// ============================================================================ +// 2.1 Basic Value Tests +// ============================================================================ + +void Test_BasicValues() +{ + PrintToServer("\n[Category] Basic Value Tests"); + + // Test CreateBool & GetBool + TestStart("BasicValues_CreateBool_True"); + { + YYJSON val = YYJSON.CreateBool(true); + AssertValidHandle(val); + AssertTrue(YYJSON.GetBool(val)); + AssertEq(val.Type, YYJSON_TYPE_BOOL); + AssertTrue(val.IsBool); + AssertTrue(val.IsTrue); + AssertFalse(val.IsFalse); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateBool_False"); + { + YYJSON val = YYJSON.CreateBool(false); + AssertValidHandle(val); + AssertFalse(YYJSON.GetBool(val)); + AssertTrue(val.IsBool); + AssertFalse(val.IsTrue); + AssertTrue(val.IsFalse); + delete val; + } + TestEnd(); + + // Test CreateInt & GetInt + TestStart("BasicValues_CreateInt_Positive"); + { + YYJSON val = YYJSON.CreateInt(42); + AssertValidHandle(val); + AssertEq(YYJSON.GetInt(val), 42); + AssertEq(val.Type, YYJSON_TYPE_NUM); + AssertTrue(val.IsInt); + AssertTrue(val.IsNum); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateInt_Negative"); + { + YYJSON val = YYJSON.CreateInt(-123); + AssertValidHandle(val); + AssertEq(YYJSON.GetInt(val), -123); + AssertTrue(val.IsSint); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateInt_Zero"); + { + YYJSON val = YYJSON.CreateInt(0); + AssertValidHandle(val); + AssertEq(YYJSON.GetInt(val), 0); + delete val; + } + TestEnd(); + + // Test CreateFloat & GetFloat + TestStart("BasicValues_CreateFloat_Positive"); + { + YYJSON val = YYJSON.CreateFloat(3.14159); + AssertValidHandle(val); + AssertFloatEq(YYJSON.GetFloat(val), 3.14159); + AssertTrue(val.IsFloat); + AssertTrue(val.IsNum); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateFloat_Negative"); + { + YYJSON val = YYJSON.CreateFloat(-2.71828); + AssertValidHandle(val); + AssertFloatEq(YYJSON.GetFloat(val), -2.71828); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateFloat_Zero"); + { + YYJSON val = YYJSON.CreateFloat(0.0); + AssertValidHandle(val); + AssertFloatEq(YYJSON.GetFloat(val), 0.0); + delete val; + } + TestEnd(); + + // Test CreateString & GetString + TestStart("BasicValues_CreateString_Regular"); + { + YYJSON val = YYJSON.CreateString("Hello, World!"); + AssertValidHandle(val); + char buffer[64]; + AssertTrue(YYJSON.GetString(val, buffer, sizeof(buffer))); + AssertStrEq(buffer, "Hello, World!"); + AssertEq(val.Type, YYJSON_TYPE_STR); + AssertTrue(val.IsStr); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateString_Empty"); + { + YYJSON val = YYJSON.CreateString(""); + AssertValidHandle(val); + char buffer[64]; + AssertTrue(YYJSON.GetString(val, buffer, sizeof(buffer))); + AssertStrEq(buffer, ""); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateString_Unicode"); + { + YYJSON val = YYJSON.CreateString("测试字符串"); + AssertValidHandle(val); + char buffer[64]; + AssertTrue(YYJSON.GetString(val, buffer, sizeof(buffer))); + AssertStrEq(buffer, "测试字符串"); + delete val; + } + TestEnd(); + + // Test CreateInt64 & GetInt64 + TestStart("BasicValues_CreateInt64_Large"); + { + YYJSON val = YYJSON.CreateInt64("9223372036854775807"); + AssertValidHandle(val); + char buffer[32]; + AssertTrue(YYJSON.GetInt64(val, buffer, sizeof(buffer))); + AssertStrEq(buffer, "9223372036854775807"); + delete val; + } + TestEnd(); + + TestStart("BasicValues_CreateInt64_Negative"); + { + YYJSON val = YYJSON.CreateInt64("-9223372036854775808"); + AssertValidHandle(val); + char buffer[32]; + AssertTrue(YYJSON.GetInt64(val, buffer, sizeof(buffer))); + AssertStrEq(buffer, "-9223372036854775808"); + delete val; + } + TestEnd(); + + // Test CreateNull + TestStart("BasicValues_CreateNull"); + { + YYJSON val = YYJSON.CreateNull(); + AssertValidHandle(val); + AssertEq(val.Type, YYJSON_TYPE_NULL); + AssertTrue(val.IsNull); + delete val; + } + TestEnd(); + + // Test GetTypeDesc + TestStart("BasicValues_GetTypeDesc"); + { + YYJSON boolVal = YYJSON.CreateBool(true); + YYJSON intVal = YYJSON.CreateInt(42); + YYJSON floatVal = YYJSON.CreateFloat(3.14); + YYJSON strVal = YYJSON.CreateString("test"); + YYJSON nullVal = YYJSON.CreateNull(); + + char buffer[32]; + + YYJSON.GetTypeDesc(boolVal, buffer, sizeof(buffer)); + AssertStrEq(buffer, "true"); + + YYJSON.GetTypeDesc(intVal, buffer, sizeof(buffer)); + AssertTrue(StrEqual(buffer, "uint") || StrEqual(buffer, "sint")); + + YYJSON.GetTypeDesc(floatVal, buffer, sizeof(buffer)); + AssertStrEq(buffer, "real"); + + YYJSON.GetTypeDesc(strVal, buffer, sizeof(buffer)); + AssertStrEq(buffer, "string"); + + YYJSON.GetTypeDesc(nullVal, buffer, sizeof(buffer)); + AssertStrEq(buffer, "null"); + + delete boolVal; + delete intVal; + delete floatVal; + delete strVal; + delete nullVal; + } + TestEnd(); +} + +// ============================================================================ +// 2.2 Object Operations Tests +// ============================================================================ + +void Test_ObjectOperations() +{ + PrintToServer("\n[Category] Object Operations Tests"); + + // Test object creation + TestStart("Object_Constructor"); + { + YYJSONObject obj = new YYJSONObject(); + AssertValidHandle(obj); + AssertTrue(obj.IsObject); + AssertEq(obj.Size, 0); + delete obj; + } + TestEnd(); + + // Test Set/Get methods + TestStart("Object_SetGetInt"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.SetInt("number", 42)); + AssertEq(obj.GetInt("number"), 42); + AssertEq(obj.Size, 1); + delete obj; + } + TestEnd(); + + TestStart("Object_SetGetFloat"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.SetFloat("pi", 3.14159)); + AssertFloatEq(obj.GetFloat("pi"), 3.14159); + delete obj; + } + TestEnd(); + + TestStart("Object_SetGetBool"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.SetBool("flag", true)); + AssertTrue(obj.GetBool("flag")); + delete obj; + } + TestEnd(); + + TestStart("Object_SetGetString"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.SetString("name", "test")); + char buffer[64]; + AssertTrue(obj.GetString("name", buffer, sizeof(buffer))); + AssertStrEq(buffer, "test"); + delete obj; + } + TestEnd(); + + TestStart("Object_SetGetInt64"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.SetInt64("bignum", "9223372036854775807")); + char buffer[32]; + AssertTrue(obj.GetInt64("bignum", buffer, sizeof(buffer))); + AssertStrEq(buffer, "9223372036854775807"); + delete obj; + } + TestEnd(); + + TestStart("Object_SetGetNull"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.SetNull("nullable")); + AssertTrue(obj.IsNull("nullable")); + delete obj; + } + TestEnd(); + + // Test HasKey + TestStart("Object_HasKey"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("exists", 1); + AssertTrue(obj.HasKey("exists")); + AssertFalse(obj.HasKey("notexists")); + delete obj; + } + TestEnd(); + + // Test GetKey and GetValueAt + TestStart("Object_GetKeyAndValueAt"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("first", 1); + obj.SetInt("second", 2); + + char key[32]; + AssertTrue(obj.GetKey(0, key, sizeof(key))); + AssertStrEq(key, "first"); + + YYJSON val = obj.GetValueAt(0); + AssertValidHandle(val); + AssertEq(YYJSON.GetInt(val), 1); + + delete val; + delete obj; + } + TestEnd(); + + // Test Remove + TestStart("Object_Remove"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("remove_me", 1); + obj.SetInt("keep_me", 2); + AssertEq(obj.Size, 2); + + AssertTrue(obj.Remove("remove_me")); + AssertEq(obj.Size, 1); + AssertFalse(obj.HasKey("remove_me")); + AssertTrue(obj.HasKey("keep_me")); + + delete obj; + } + TestEnd(); + + // Test Clear + TestStart("Object_Clear"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("a", 1); + obj.SetInt("b", 2); + obj.SetInt("c", 3); + AssertEq(obj.Size, 3); + + AssertTrue(obj.Clear()); + AssertEq(obj.Size, 0); + + delete obj; + } + TestEnd(); + + // Test RenameKey + TestStart("Object_RenameKey"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("oldname", 42); + + AssertTrue(obj.RenameKey("oldname", "newname")); + AssertFalse(obj.HasKey("oldname")); + AssertTrue(obj.HasKey("newname")); + AssertEq(obj.GetInt("newname"), 42); + + delete obj; + } + TestEnd(); + + // Test FromString + TestStart("Object_FromString"); + { + YYJSONObject obj = YYJSONObject.FromString("{\"key\":\"value\",\"num\":123}"); + AssertValidHandle(obj); + + char buffer[64]; + AssertTrue(obj.GetString("key", buffer, sizeof(buffer))); + AssertStrEq(buffer, "value"); + AssertEq(obj.GetInt("num"), 123); + + delete obj; + } + TestEnd(); + + // Test FromStrings + TestStart("Object_FromStrings"); + { + char pairs[][] = {"name", "test", "type", "demo", "version", "1.0"}; + YYJSONObject obj = YYJSONObject.FromStrings(pairs, sizeof(pairs)); + AssertValidHandle(obj); + AssertEq(obj.Size, 3); + + char buffer[64]; + AssertTrue(obj.GetString("name", buffer, sizeof(buffer))); + AssertStrEq(buffer, "test"); + + delete obj; + } + TestEnd(); + + // Test Sort + TestStart("Object_Sort_Ascending"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("zebra", 1); + obj.SetInt("alpha", 2); + obj.SetInt("beta", 3); + + AssertTrue(obj.Sort(YYJSON_SORT_ASC)); + + char key[32]; + obj.GetKey(0, key, sizeof(key)); + AssertStrEq(key, "alpha"); + + delete obj; + } + TestEnd(); + + TestStart("Object_Sort_Descending"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("alpha", 1); + obj.SetInt("beta", 2); + obj.SetInt("gamma", 3); + + AssertTrue(obj.Sort(YYJSON_SORT_DESC)); + + char key[32]; + obj.GetKey(0, key, sizeof(key)); + AssertStrEq(key, "gamma"); + + delete obj; + } + TestEnd(); + + // Test Set with handle + TestStart("Object_SetWithHandle"); + { + YYJSONObject obj = new YYJSONObject(); + YYJSON val = YYJSON.CreateInt(999); + + AssertTrue(obj.Set("nested", val)); + AssertEq(obj.GetInt("nested"), 999); + + delete val; + delete obj; + } + TestEnd(); + + // Test Get with handle + TestStart("Object_GetHandle"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("value", 42); + + YYJSON val = obj.Get("value"); + AssertValidHandle(val); + AssertEq(YYJSON.GetInt(val), 42); + + delete val; + delete obj; + } + TestEnd(); +} + +// ============================================================================ +// 2.3 Array Operations Tests +// ============================================================================ + +void Test_ArrayOperations() +{ + PrintToServer("\n[Category] Array Operations Tests"); + + // Test array creation + TestStart("Array_Constructor"); + { + YYJSONArray arr = new YYJSONArray(); + AssertValidHandle(arr); + AssertTrue(arr.IsArray); + AssertEq(arr.Length, 0); + delete arr; + } + TestEnd(); + + // Test Push methods + TestStart("Array_PushInt"); + { + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.PushInt(42)); + AssertEq(arr.Length, 1); + AssertEq(arr.GetInt(0), 42); + delete arr; + } + TestEnd(); + + TestStart("Array_PushFloat"); + { + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.PushFloat(3.14)); + AssertFloatEq(arr.GetFloat(0), 3.14); + delete arr; + } + TestEnd(); + + TestStart("Array_PushBool"); + { + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.PushBool(true)); + AssertTrue(arr.GetBool(0)); + delete arr; + } + TestEnd(); + + TestStart("Array_PushString"); + { + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.PushString("test")); + char buffer[64]; + AssertTrue(arr.GetString(0, buffer, sizeof(buffer))); + AssertStrEq(buffer, "test"); + delete arr; + } + TestEnd(); + + TestStart("Array_PushInt64"); + { + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.PushInt64("9223372036854775807")); + char buffer[32]; + arr.GetInt64(0, buffer, sizeof(buffer)); + AssertStrEq(buffer, "9223372036854775807"); + delete arr; + } + TestEnd(); + + TestStart("Array_PushNull"); + { + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.PushNull()); + AssertTrue(arr.IsNull(0)); + delete arr; + } + TestEnd(); + + // Test Set/Get methods + TestStart("Array_SetGetInt"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(0); + AssertTrue(arr.SetInt(0, 100)); + AssertEq(arr.GetInt(0), 100); + delete arr; + } + TestEnd(); + + TestStart("Array_SetGetFloat"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushFloat(0.0); + AssertTrue(arr.SetFloat(0, 2.718)); + AssertFloatEq(arr.GetFloat(0), 2.718); + delete arr; + } + TestEnd(); + + // Test Remove methods + TestStart("Array_Remove"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(1); + arr.PushInt(2); + arr.PushInt(3); + AssertEq(arr.Length, 3); + + AssertTrue(arr.Remove(1)); + AssertEq(arr.Length, 2); + AssertEq(arr.GetInt(0), 1); + AssertEq(arr.GetInt(1), 3); + + delete arr; + } + TestEnd(); + + TestStart("Array_RemoveFirst"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(1); + arr.PushInt(2); + arr.PushInt(3); + + AssertTrue(arr.RemoveFirst()); + AssertEq(arr.Length, 2); + AssertEq(arr.GetInt(0), 2); + + delete arr; + } + TestEnd(); + + TestStart("Array_RemoveLast"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(1); + arr.PushInt(2); + arr.PushInt(3); + + AssertTrue(arr.RemoveLast()); + AssertEq(arr.Length, 2); + AssertEq(arr.GetInt(1), 2); + + delete arr; + } + TestEnd(); + + TestStart("Array_RemoveRange"); + { + YYJSONArray arr = new YYJSONArray(); + for (int i = 0; i < 10; i++) + { + arr.PushInt(i); + } + + AssertTrue(arr.RemoveRange(2, 5)); + AssertEq(arr.Length, 5); + + delete arr; + } + TestEnd(); + + TestStart("Array_Clear"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(1); + arr.PushInt(2); + arr.PushInt(3); + + AssertTrue(arr.Clear()); + AssertEq(arr.Length, 0); + + delete arr; + } + TestEnd(); + + // Test First/Last properties + TestStart("Array_FirstLast"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(10); + arr.PushInt(20); + arr.PushInt(30); + + YYJSON first = arr.First; + YYJSON last = arr.Last; + + AssertValidHandle(first); + AssertValidHandle(last); + AssertEq(YYJSON.GetInt(first), 10); + AssertEq(YYJSON.GetInt(last), 30); + + delete first; + delete last; + delete arr; + } + TestEnd(); + + // Test IndexOf methods + TestStart("Array_IndexOfInt"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(10); + arr.PushInt(20); + arr.PushInt(30); + + AssertEq(arr.IndexOfInt(20), 1); + AssertEq(arr.IndexOfInt(999), -1); + + delete arr; + } + TestEnd(); + + TestStart("Array_IndexOfBool"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushBool(false); + arr.PushBool(true); + arr.PushBool(false); + + AssertEq(arr.IndexOfBool(true), 1); + + delete arr; + } + TestEnd(); + + TestStart("Array_IndexOfString"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushString("apple"); + arr.PushString("banana"); + arr.PushString("cherry"); + + AssertEq(arr.IndexOfString("banana"), 1); + AssertEq(arr.IndexOfString("orange"), -1); + + delete arr; + } + TestEnd(); + + TestStart("Array_IndexOfFloat"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushFloat(1.1); + arr.PushFloat(2.2); + arr.PushFloat(3.3); + + AssertEq(arr.IndexOfFloat(2.2), 1); + + delete arr; + } + TestEnd(); + + TestStart("Array_IndexOfInt64"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt64("1000000000000"); + arr.PushInt64("2000000000000"); + + AssertEq(arr.IndexOfInt64("2000000000000"), 1); + AssertEq(arr.IndexOfInt64("3000000000000"), -1); + + delete arr; + } + TestEnd(); + + // Test FromString + TestStart("Array_FromString"); + { + YYJSONArray arr = YYJSONArray.FromString("[1,2,3,4,5]"); + AssertValidHandle(arr); + AssertEq(arr.Length, 5); + AssertEq(arr.GetInt(0), 1); + AssertEq(arr.GetInt(4), 5); + delete arr; + } + TestEnd(); + + // Test FromStrings + TestStart("Array_FromStrings"); + { + char strings[][] = {"apple", "banana", "cherry"}; + YYJSONArray arr = YYJSONArray.FromStrings(strings, sizeof(strings)); + AssertValidHandle(arr); + AssertEq(arr.Length, 3); + + char buffer[64]; + arr.GetString(1, buffer, sizeof(buffer)); + AssertStrEq(buffer, "banana"); + + delete arr; + } + TestEnd(); + + // Test Sort + TestStart("Array_Sort_Ascending"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(3); + arr.PushInt(1); + arr.PushInt(4); + arr.PushInt(1); + arr.PushInt(5); + + AssertTrue(arr.Sort(YYJSON_SORT_ASC)); + AssertEq(arr.GetInt(0), 1); + AssertEq(arr.GetInt(4), 5); + + delete arr; + } + TestEnd(); + + TestStart("Array_Sort_Descending"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(1); + arr.PushInt(2); + arr.PushInt(3); + arr.PushInt(4); + arr.PushInt(5); + + AssertTrue(arr.Sort(YYJSON_SORT_DESC)); + AssertEq(arr.GetInt(0), 5); + AssertEq(arr.GetInt(4), 1); + + delete arr; + } + TestEnd(); + + // Test Push with handle + TestStart("Array_PushHandle"); + { + YYJSONArray arr = new YYJSONArray(); + YYJSON val = YYJSON.CreateInt(999); + + AssertTrue(arr.Push(val)); + AssertEq(arr.GetInt(0), 999); + + delete val; + delete arr; + } + TestEnd(); + + // Test Get with handle + TestStart("Array_GetHandle"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(42); + + YYJSON val = arr.Get(0); + AssertValidHandle(val); + AssertEq(YYJSON.GetInt(val), 42); + + delete val; + delete arr; + } + TestEnd(); +} + +// ============================================================================ +// 2.4 Parse & Serialize Tests +// ============================================================================ + +void Test_ParseAndSerialize() +{ + PrintToServer("\n[Category] Parse & Serialize Tests"); + + // Test Parse string + TestStart("Parse_SimpleObject"); + { + YYJSON json = YYJSON.Parse("{\"key\":\"value\",\"num\":42}"); + AssertValidHandle(json); + AssertTrue(json.IsObject); + delete json; + } + TestEnd(); + + TestStart("Parse_SimpleArray"); + { + YYJSON json = YYJSON.Parse("[1,2,3,4,5]"); + AssertValidHandle(json); + AssertTrue(json.IsArray); + delete json; + } + TestEnd(); + + TestStart("Parse_NestedStructure"); + { + YYJSON json = YYJSON.Parse("{\"user\":{\"name\":\"test\",\"age\":25},\"items\":[1,2,3]}"); + AssertValidHandle(json); + delete json; + } + TestEnd(); + + // Test mutable/immutable + TestStart("Parse_ImmutableDocument"); + { + YYJSON json = YYJSON.Parse("{\"key\":\"value\"}"); + AssertValidHandle(json); + AssertTrue(json.IsImmutable); + AssertFalse(json.IsMutable); + delete json; + } + TestEnd(); + + TestStart("Parse_MutableDocument"); + { + YYJSON json = YYJSON.Parse("{\"key\":\"value\"}", false, true); + AssertValidHandle(json); + AssertTrue(json.IsMutable); + AssertFalse(json.IsImmutable); + delete json; + } + TestEnd(); + + // Test ToString + TestStart("Serialize_ToString"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("num", 42); + obj.SetString("str", "test"); + + char buffer[256]; + int len = obj.ToString(buffer, sizeof(buffer)); + AssertTrue(len > 0); + Assert(StrContains(buffer, "num") != -1); + Assert(StrContains(buffer, "test") != -1); + + delete obj; + } + TestEnd(); + + TestStart("Serialize_ToString_Pretty"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("a", 1); + obj.SetInt("b", 2); + + char buffer[256]; + int len = obj.ToString(buffer, sizeof(buffer), YYJSON_WRITE_PRETTY); + AssertTrue(len > 0); + Assert(StrContains(buffer, "\n") != -1, "Pretty output should contain newlines"); + + delete obj; + } + TestEnd(); + + // Test GetSerializedSize + TestStart("Serialize_GetSerializedSize"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("test", 123); + + int size = obj.GetSerializedSize(); + AssertTrue(size > 0); + + char[] buffer = new char[size]; + int written = obj.ToString(buffer, size); + AssertEq(written, size); + + delete obj; + } + TestEnd(); + + // Test read flags + TestStart("Parse_WithTrailingCommas"); + { + YYJSON json = YYJSON.Parse("[1,2,3,]", false, false, YYJSON_READ_ALLOW_TRAILING_COMMAS); + AssertValidHandle(json); + delete json; + } + TestEnd(); + + TestStart("Parse_WithComments"); + { + YYJSON json = YYJSON.Parse("/* comment */ {\"key\":\"value\"}", false, false, YYJSON_READ_ALLOW_COMMENTS); + AssertValidHandle(json); + delete json; + } + TestEnd(); + + // Test file operations (create temporary test file) + TestStart("Parse_ToFile_FromFile"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("filetest", 999); + obj.SetString("name", "testfile"); + + // Write to file + AssertTrue(obj.ToFile("yyjson_test_temp.json")); + + // Read from file + YYJSONObject loaded = YYJSON.Parse("yyjson_test_temp.json", true); + AssertValidHandle(loaded); + + // Verify content + YYJSONObject loadedObj = loaded; + AssertEq(loadedObj.GetInt("filetest"), 999); + + char buffer[64]; + loadedObj.GetString("name", buffer, sizeof(buffer)); + AssertStrEq(buffer, "testfile"); + + delete obj; + delete loaded; + + // Cleanup + DeleteFile("yyjson_test_temp.json"); + } + TestEnd(); + + // Test round-trip serialization + TestStart("Parse_RoundTrip"); + { + char original[] = "{\"int\":42,\"float\":3.14,\"bool\":true,\"str\":\"test\",\"null\":null}"; + YYJSON json1 = YYJSON.Parse(original); + + char buffer[256]; + json1.ToString(buffer, sizeof(buffer)); + + YYJSON json2 = YYJSON.Parse(buffer); + AssertTrue(YYJSON.Equals(json1, json2)); + + delete json1; + delete json2; + } + TestEnd(); +} + +// ============================================================================ +// 2.5 Iterator Tests +// ============================================================================ + +void Test_Iterators() +{ + PrintToServer("\n[Category] Iterator Tests"); + + // Test ForeachObject + TestStart("Iterator_ForeachObject"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("a", 1); + obj.SetInt("b", 2); + obj.SetInt("c", 3); + + int count = 0; + char key[32]; + YYJSON value; + + while (obj.ForeachObject(key, sizeof(key), value)) + { + count++; + AssertValidHandle(value); + delete value; + } + + AssertEq(count, 3); + delete obj; + } + TestEnd(); + + // Test ForeachArray + TestStart("Iterator_ForeachArray"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(10); + arr.PushInt(20); + arr.PushInt(30); + + int count = 0; + int index; + YYJSON value; + + while (arr.ForeachArray(index, value)) + { + AssertEq(index, count); + AssertValidHandle(value); + delete value; + count++; + } + + AssertEq(count, 3); + delete arr; + } + TestEnd(); + + // Test ForeachKey + TestStart("Iterator_ForeachKey"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("key1", 1); + obj.SetInt("key2", 2); + obj.SetInt("key3", 3); + + int count = 0; + char key[32]; + + while (obj.ForeachKey(key, sizeof(key))) + { + AssertTrue(strlen(key) > 0); + count++; + } + + AssertEq(count, 3); + delete obj; + } + TestEnd(); + + // Test ForeachIndex + TestStart("Iterator_ForeachIndex"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(1); + arr.PushInt(2); + arr.PushInt(3); + + int count = 0; + int index; + + while (arr.ForeachIndex(index)) + { + AssertEq(index, count); + count++; + } + + AssertEq(count, 3); + delete arr; + } + TestEnd(); + + // Test empty iterations + TestStart("Iterator_EmptyObject"); + { + YYJSONObject obj = new YYJSONObject(); + + char key[32]; + YYJSON value; + AssertFalse(obj.ForeachObject(key, sizeof(key), value)); + + delete obj; + } + TestEnd(); + + TestStart("Iterator_EmptyArray"); + { + YYJSONArray arr = new YYJSONArray(); + + int index; + YYJSON value; + AssertFalse(arr.ForeachArray(index, value)); + + delete arr; + } + TestEnd(); +} + +// ============================================================================ +// 2.6 JSON Pointer Tests +// ============================================================================ + +void Test_JSONPointer() +{ + PrintToServer("\n[Category] JSON Pointer Tests"); + + // Test PtrSet methods + TestStart("Pointer_PtrSetInt"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.PtrSetInt("/number", 42)); + AssertEq(obj.PtrGetInt("/number"), 42); + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrSetFloat"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.PtrSetFloat("/pi", 3.14159)); + AssertFloatEq(obj.PtrGetFloat("/pi"), 3.14159); + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrSetBool"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.PtrSetBool("/flag", true)); + AssertTrue(obj.PtrGetBool("/flag")); + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrSetString"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.PtrSetString("/name", "test")); + + char buffer[64]; + AssertTrue(obj.PtrGetString("/name", buffer, sizeof(buffer))); + AssertStrEq(buffer, "test"); + + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrSetInt64"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.PtrSetInt64("/bignum", "9223372036854775807")); + + char buffer[32]; + AssertTrue(obj.PtrGetInt64("/bignum", buffer, sizeof(buffer))); + AssertStrEq(buffer, "9223372036854775807"); + + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrSetNull"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.PtrSetNull("/nullable")); + AssertTrue(obj.PtrGetIsNull("/nullable")); + delete obj; + } + TestEnd(); + + // Test nested path creation + TestStart("Pointer_NestedPathCreation"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.PtrSetInt("/a/b/c/d", 123)); + AssertEq(obj.PtrGetInt("/a/b/c/d"), 123); + delete obj; + } + TestEnd(); + + // Test PtrGet with handle + TestStart("Pointer_PtrGet"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetInt("/test", 999); + + YYJSON val = obj.PtrGet("/test"); + AssertValidHandle(val); + AssertEq(YYJSON.GetInt(val), 999); + + delete val; + delete obj; + } + TestEnd(); + + // Test PtrAdd methods + TestStart("Pointer_PtrAddInt"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetInt("/arr/0", 1); + AssertTrue(obj.PtrAddInt("/arr/1", 2)); + AssertEq(obj.PtrGetInt("/arr/1"), 2); + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrAddString"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetString("/items/0", "first"); + AssertTrue(obj.PtrAddString("/items/1", "second")); + + char buffer[64]; + obj.PtrGetString("/items/1", buffer, sizeof(buffer)); + AssertStrEq(buffer, "second"); + + delete obj; + } + TestEnd(); + + // Test PtrRemove + TestStart("Pointer_PtrRemove"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetInt("/remove_me", 1); + obj.PtrSetInt("/keep_me", 2); + + AssertTrue(obj.PtrRemove("/remove_me")); + + YYJSON val; + obj.PtrTryGetVal("/remove_me", val); + AssertNullHandle(val); + + delete obj; + } + TestEnd(); + + // Test PtrGetLength + TestStart("Pointer_PtrGetLength"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetString("/text", "hello"); + + int len = obj.PtrGetLength("/text"); + AssertEq(len, 6); // Including null terminator + + delete obj; + } + TestEnd(); + + // Test PtrTryGet methods + TestStart("Pointer_PtrTryGetInt"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetInt("/value", 42); + + int value; + AssertTrue(obj.PtrTryGetInt("/value", value)); + AssertEq(value, 42); + + AssertFalse(obj.PtrTryGetInt("/nonexistent", value)); + + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrTryGetBool"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetBool("/flag", true); + + bool value; + AssertTrue(obj.PtrTryGetBool("/flag", value)); + AssertTrue(value); + + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrTryGetFloat"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetFloat("/pi", 3.14); + + float value; + AssertTrue(obj.PtrTryGetFloat("/pi", value)); + AssertFloatEq(value, 3.14); + + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrTryGetString"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetString("/name", "test"); + + char buffer[64]; + AssertTrue(obj.PtrTryGetString("/name", buffer, sizeof(buffer))); + AssertStrEq(buffer, "test"); + + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrTryGetInt64"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetInt64("/bignum", "123456789012345"); + + char buffer[32]; + AssertTrue(obj.PtrTryGetInt64("/bignum", buffer, sizeof(buffer))); + AssertStrEq(buffer, "123456789012345"); + + delete obj; + } + TestEnd(); + + TestStart("Pointer_PtrTryGetVal"); + { + YYJSONObject obj = new YYJSONObject(); + obj.PtrSetInt("/test", 42); + + YYJSON value; + AssertTrue(obj.PtrTryGetVal("/test", value)); + AssertValidHandle(value); + AssertEq(YYJSON.GetInt(value), 42); + + delete value; + delete obj; + } + TestEnd(); +} + +// ============================================================================ +// 2.7 Advanced Features Tests +// ============================================================================ + +void Test_AdvancedFeatures() +{ + PrintToServer("\n[Category] Advanced Features Tests"); + + // Test DeepCopy + TestStart("Advanced_DeepCopy_Object"); + { + YYJSONObject original = new YYJSONObject(); + original.SetInt("num", 42); + original.SetString("str", "test"); + + YYJSONObject target = new YYJSONObject(); + YYJSONObject copy = YYJSON.DeepCopy(target, original); + + AssertValidHandle(copy); + AssertEq(copy.GetInt("num"), 42); + + char buffer[64]; + copy.GetString("str", buffer, sizeof(buffer)); + AssertStrEq(buffer, "test"); + + delete original; + delete target; + delete copy; + } + TestEnd(); + + TestStart("Advanced_DeepCopy_Array"); + { + YYJSONArray original = new YYJSONArray(); + original.PushInt(1); + original.PushInt(2); + original.PushInt(3); + + YYJSONArray target = new YYJSONArray(); + YYJSONArray copy = YYJSON.DeepCopy(target, original); + + AssertValidHandle(copy); + AssertEq(copy.Length, 3); + AssertEq(copy.GetInt(0), 1); + AssertEq(copy.GetInt(2), 3); + + delete original; + delete target; + delete copy; + } + TestEnd(); + + // Test Equals + TestStart("Advanced_Equals_True"); + { + YYJSONObject obj1 = new YYJSONObject(); + obj1.SetInt("a", 1); + obj1.SetString("b", "test"); + + YYJSONObject obj2 = new YYJSONObject(); + obj2.SetInt("a", 1); + obj2.SetString("b", "test"); + + AssertTrue(YYJSON.Equals(obj1, obj2)); + + delete obj1; + delete obj2; + } + TestEnd(); + + TestStart("Advanced_Equals_False"); + { + YYJSONObject obj1 = new YYJSONObject(); + obj1.SetInt("a", 1); + + YYJSONObject obj2 = new YYJSONObject(); + obj2.SetInt("a", 2); + + AssertFalse(YYJSON.Equals(obj1, obj2)); + + delete obj1; + delete obj2; + } + TestEnd(); + + // Test ToMutable/ToImmutable + TestStart("Advanced_ToMutable"); + { + YYJSON immutable = YYJSON.Parse("{\"key\":\"value\"}"); + AssertTrue(immutable.IsImmutable); + + YYJSON mutable = immutable.ToMutable(); + AssertValidHandle(mutable); + AssertTrue(mutable.IsMutable); + + delete immutable; + delete mutable; + } + TestEnd(); + + TestStart("Advanced_ToImmutable"); + { + YYJSONObject mutable = new YYJSONObject(); + mutable.SetInt("key", 42); + AssertTrue(mutable.IsMutable); + + YYJSON immutable = mutable.ToImmutable(); + AssertValidHandle(immutable); + AssertTrue(immutable.IsImmutable); + + delete mutable; + delete immutable; + } + TestEnd(); + + // Test Pack + TestStart("Advanced_Pack_SimpleObject"); + { + YYJSONObject json = YYJSON.Pack("{s:i,s:s,s:b}", + "num", 42, + "str", "test", + "bool", true + ); + + AssertValidHandle(json); + AssertTrue(json.IsObject); + + YYJSONObject obj = json; + AssertEq(obj.GetInt("num"), 42); + + char buffer[64]; + obj.GetString("str", buffer, sizeof(buffer)); + AssertStrEq(buffer, "test"); + + AssertTrue(obj.GetBool("bool")); + + delete json; + } + TestEnd(); + + TestStart("Advanced_Pack_Array"); + { + YYJSONArray json = YYJSON.Pack("[i,i,i]", 1, 2, 3); + + AssertValidHandle(json); + AssertTrue(json.IsArray); + + YYJSONArray arr = json; + AssertEq(arr.Length, 3); + AssertEq(arr.GetInt(0), 1); + AssertEq(arr.GetInt(2), 3); + + delete json; + } + TestEnd(); + + TestStart("Advanced_Pack_Nested"); + { + YYJSONObject json = YYJSON.Pack("{s:{s:s,s:i}}", + "user", + "name", "test", + "age", 25 + ); + + AssertValidHandle(json); + + char buffer[64]; + YYJSONObject obj = json; + obj.PtrGetString("/user/name", buffer, sizeof(buffer)); + AssertStrEq(buffer, "test"); + AssertEq(obj.PtrGetInt("/user/age"), 25); + + delete json; + } + TestEnd(); + + // Test mixed type array sorting + TestStart("Advanced_MixedTypeSort"); + { + YYJSONArray json = YYJSON.Parse("[true, 42, \"hello\", 1.5, false]", false, true); + YYJSONArray arr = json; + + AssertTrue(arr.Sort(YYJSON_SORT_ASC)); + AssertEq(arr.Length, 5); + + delete json; + } + TestEnd(); +} + +// ============================================================================ +// 2.8 Edge Cases & Error Handling Tests +// ============================================================================ + +void Test_EdgeCases() +{ + PrintToServer("\n[Category] Edge Cases & Error Handling Tests"); + + // Test empty containers + TestStart("EdgeCase_EmptyObject"); + { + YYJSONObject obj = new YYJSONObject(); + AssertEq(obj.Size, 0); + AssertFalse(obj.HasKey("anything")); + + char buffer[64]; + int len = obj.ToString(buffer, sizeof(buffer)); + AssertTrue(len > 0); + + delete obj; + } + TestEnd(); + + TestStart("EdgeCase_EmptyArray"); + { + YYJSONArray arr = new YYJSONArray(); + AssertEq(arr.Length, 0); + delete arr; + } + TestEnd(); + + // Test nonexistent keys/indices + TestStart("EdgeCase_NonexistentKey"); + { + YYJSONObject obj = new YYJSONObject(); + obj.SetInt("exists", 1); + + AssertTrue(obj.HasKey("exists")); + AssertFalse(obj.HasKey("notexists")); + + delete obj; + } + TestEnd(); + + TestStart("EdgeCase_ArrayOutOfBounds"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(1); + + // Verify array bounds + AssertEq(arr.Length, 1); + AssertEq(arr.GetInt(0), 1); + + delete arr; + } + TestEnd(); + + // Test very long strings + TestStart("EdgeCase_LongString"); + { + char longStr[1024]; + for (int i = 0; i < sizeof(longStr) - 1; i++) + { + longStr[i] = 'A' + (i % 26); + } + longStr[sizeof(longStr) - 1] = '\0'; + + YYJSON val = YYJSON.CreateString(longStr); + AssertValidHandle(val); + + char retrieved[1024]; + AssertTrue(YYJSON.GetString(val, retrieved, sizeof(retrieved))); + AssertStrEq(retrieved, longStr); + + delete val; + } + TestEnd(); + + // Test deep nesting + TestStart("EdgeCase_DeepNesting"); + { + YYJSONObject obj = new YYJSONObject(); + + // Create deeply nested structure + obj.PtrSetInt("/a/b/c/d/e/f/g/h/i/j", 42); + AssertEq(obj.PtrGetInt("/a/b/c/d/e/f/g/h/i/j"), 42); + + delete obj; + } + TestEnd(); + + // Test large arrays + TestStart("EdgeCase_LargeArray"); + { + YYJSONArray arr = new YYJSONArray(); + + for (int i = 0; i < 1000; i++) + { + arr.PushInt(i); + } + + AssertEq(arr.Length, 1000); + AssertEq(arr.GetInt(0), 0); + AssertEq(arr.GetInt(999), 999); + + delete arr; + } + TestEnd(); + + // Test large objects + TestStart("EdgeCase_LargeObject"); + { + YYJSONObject obj = new YYJSONObject(); + + char key[32]; + for (int i = 0; i < 100; i++) + { + Format(key, sizeof(key), "key_%d", i); + obj.SetInt(key, i); + } + + AssertEq(obj.Size, 100); + AssertEq(obj.GetInt("key_0"), 0); + AssertEq(obj.GetInt("key_99"), 99); + + delete obj; + } + TestEnd(); + + // Test int64 boundaries + TestStart("EdgeCase_Int64_MaxValue"); + { + YYJSON val = YYJSON.CreateInt64("9223372036854775807"); + AssertValidHandle(val); + + char buffer[32]; + YYJSON.GetInt64(val, buffer, sizeof(buffer)); + AssertStrEq(buffer, "9223372036854775807"); + + delete val; + } + TestEnd(); + + TestStart("EdgeCase_Int64_MinValue"); + { + YYJSON val = YYJSON.CreateInt64("-9223372036854775808"); + AssertValidHandle(val); + + char buffer[32]; + YYJSON.GetInt64(val, buffer, sizeof(buffer)); + AssertStrEq(buffer, "-9223372036854775808"); + + delete val; + } + TestEnd(); + + // Test special float values + TestStart("EdgeCase_Float_VerySmall"); + { + YYJSON val = YYJSON.CreateFloat(0.000001); + AssertValidHandle(val); + AssertFloatEq(YYJSON.GetFloat(val), 0.000001); + delete val; + } + TestEnd(); + + TestStart("EdgeCase_Float_VeryLarge"); + { + YYJSON val = YYJSON.CreateFloat(999999.999999); + AssertValidHandle(val); + AssertFloatEq(YYJSON.GetFloat(val), 999999.999999, "", 0.001); + delete val; + } + TestEnd(); + + // Test special characters in strings + TestStart("EdgeCase_SpecialCharacters"); + { + YYJSON val = YYJSON.CreateString("Line1\nLine2\tTabbed\"Quoted\""); + AssertValidHandle(val); + + char buffer[128]; + YYJSON.GetString(val, buffer, sizeof(buffer)); + Assert(StrContains(buffer, "Line1") != -1); + + delete val; + } + TestEnd(); + + // Test removing from empty array + TestStart("EdgeCase_RemoveFromEmptyArray"); + { + YYJSONArray arr = new YYJSONArray(); + AssertFalse(arr.RemoveFirst()); + AssertFalse(arr.RemoveLast()); + delete arr; + } + TestEnd(); + + // Test clearing already empty containers + TestStart("EdgeCase_ClearEmpty"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.Clear()); + AssertEq(obj.Size, 0); + + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.Clear()); + AssertEq(arr.Length, 0); + + delete obj; + delete arr; + } + TestEnd(); + + // Test IndexOf on empty array + TestStart("EdgeCase_IndexOfEmpty"); + { + YYJSONArray arr = new YYJSONArray(); + AssertEq(arr.IndexOfInt(42), -1); + AssertEq(arr.IndexOfString("test"), -1); + AssertEq(arr.IndexOfBool(true), -1); + delete arr; + } + TestEnd(); + + // Test pointer to nonexistent path + TestStart("EdgeCase_PointerNonexistentPath"); + { + YYJSONObject obj = new YYJSONObject(); + + // Note: PtrGet throws exception for nonexistent paths (expected behavior) + // Use PtrTryGet methods for safe access + int intVal; + AssertFalse(obj.PtrTryGetInt("/does/not/exist", intVal)); + + delete obj; + } + TestEnd(); + + // Test sorting empty containers + TestStart("EdgeCase_SortEmpty"); + { + YYJSONObject obj = new YYJSONObject(); + AssertTrue(obj.Sort()); + + YYJSONArray arr = new YYJSONArray(); + AssertTrue(arr.Sort()); + + delete obj; + delete arr; + } + TestEnd(); + + // Test single element operations + TestStart("EdgeCase_SingleElement"); + { + YYJSONArray arr = new YYJSONArray(); + arr.PushInt(42); + + AssertTrue(arr.Sort()); + AssertEq(arr.GetInt(0), 42); + + AssertEq(arr.IndexOfInt(42), 0); + + YYJSON first = arr.First; + YYJSON last = arr.Last; + AssertEq(YYJSON.GetInt(first), YYJSON.GetInt(last)); + + delete first; + delete last; + delete arr; + } + TestEnd(); +} \ No newline at end of file diff --git a/plugins/testsuite/yyjson_test.sp b/plugins/testsuite/yyjson_test.sp deleted file mode 100755 index 4b439d34d..000000000 --- a/plugins/testsuite/yyjson_test.sp +++ /dev/null @@ -1,566 +0,0 @@ -#include -#include - -public Plugin myinfo = -{ - name = "YYJSON Test Suite", - author = "ProjectSky", - description = "Test suite for YYJSON extension", - version = "1.0.2", - url = "https://github.com/ProjectSky/sm-ext-yyjson" -}; - -public void OnPluginStart() -{ - RegServerCmd("sm_yyjson_test", Command_RunTests, "Run YYJSON test suite"); -} - -Action Command_RunTests(int args) -{ - // Run all test cases - TestBasicOperations(); - TestArrayOperations(); - TestObjectOperations(); - TestSortOperations(); - TestSearchOperations(); - TestPointerOperations(); - TestIterationOperations(); - TestTypeOperations(); - TestFileOperations(); - TestImmutabilityOperations(); - TestPackOperations(); - TestFromStringsOperations(); - - PrintToServer("[YYJSON] All tests completed!"); - return Plugin_Handled; -} - -void TestBasicOperations() -{ - PrintToServer("[YYJSON] Testing basic operations..."); - - // Test creation and parsing - YYJSONObject obj = new YYJSONObject(); - obj.SetInt("int", 42); - obj.SetFloat("float", 3.14); - obj.SetBool("bool", true); - obj.SetString("string", "hello"); - obj.SetNull("null"); - - // Test serialization - char buffer[1024]; - obj.ToString(buffer, sizeof(buffer)); - PrintToServer("Serialized: %s", buffer); - - // Test type checking - PrintToServer("Type of 'int': %d", obj.Type); - PrintToServer("SubType of 'int': %d", obj.SubType); - PrintToServer("Is object: %d", obj.IsObject); - - // Test value existence - PrintToServer("Has 'int': %d", obj.HasKey("int")); - PrintToServer("Has 'nonexistent': %d", obj.HasKey("nonexistent")); - - // Test value retrieval - PrintToServer("Int value: %d", obj.GetInt("int")); - PrintToServer("Float value: %f", obj.GetFloat("float")); - PrintToServer("Bool value: %d", obj.GetBool("bool")); - - char strBuffer[64]; - obj.GetString("string", strBuffer, sizeof(strBuffer)); - PrintToServer("String value: %s", strBuffer); - - PrintToServer("Is 'null' null: %d", obj.IsNull("null")); - - // Test parsing - YYJSONObject parsed = YYJSON.Parse(buffer); - - // Test equality - PrintToServer("Objects are equal: %d", YYJSON.Equals(obj, parsed)); - - // Test deep copy - YYJSONObject copy = new YYJSONObject(); - YYJSONObject copyResult = YYJSON.DeepCopy(copy, obj); - PrintToServer("Copy equals original: %d", YYJSON.Equals(copyResult, obj)); - - // Test size and read size - PrintToServer("Object size: %d", obj.Size); - PrintToServer("Read size: %d", obj.ReadSize); - - // Test type description - char typeDesc[64]; - YYJSON.GetTypeDesc(obj, typeDesc, sizeof(typeDesc)); - PrintToServer("Type description: %s", typeDesc); - - delete obj; - delete parsed; - delete copy; - delete copyResult; -} - -void TestArrayOperations() -{ - PrintToServer("[YYJSON] Testing array operations..."); - - YYJSONArray arr = new YYJSONArray(); - - // Test push operations - arr.PushInt(1); - arr.PushFloat(2.5); - arr.PushBool(true); - arr.PushString("test"); - arr.PushNull(); - - PrintToServer("Array after push operations:"); - PrintJson(arr); - - // Test get operations - PrintToServer("First element: %d", arr.GetInt(0)); - PrintToServer("Second element: %f", arr.GetFloat(1)); - PrintToServer("Third element: %d", arr.GetBool(2)); - - char strBuffer[64]; - arr.GetString(3, strBuffer, sizeof(strBuffer)); - PrintToServer("Fourth element: %s", strBuffer); - - PrintToServer("Fifth element is null: %d", arr.IsNull(4)); - - YYJSON first = arr.First; - YYJSON last = arr.Last; - - // Test array properties - PrintToServer("Array length: %d", arr.Length); - PrintToServer("First value: %x", first); - PrintToServer("Last value: %x", last); - - // Test set operations - arr.SetInt(0, 100); - arr.SetFloat(1, 3.14); - arr.SetBool(2, false); - arr.SetString(3, "modified"); - arr.SetNull(4); - - PrintToServer("Array after set operations:"); - PrintJson(arr); - - // Test remove operations - arr.RemoveFirst(); - PrintToServer("After RemoveFirst:"); - PrintJson(arr); - - arr.RemoveLast(); - PrintToServer("After RemoveLast:"); - PrintJson(arr); - - arr.Remove(1); - PrintToServer("After Remove(1):"); - PrintJson(arr); - - arr.RemoveRange(0, 1); - PrintToServer("After RemoveRange(0, 1):"); - PrintJson(arr); - - arr.Clear(); - PrintToServer("Array length after Clear: %d", arr.Length); - - delete arr; - delete first; - delete last; -} - -void TestObjectOperations() -{ - PrintToServer("[YYJSON] Testing object operations..."); - - YYJSONObject obj = new YYJSONObject(); - - // Test set operations - obj.SetInt("int", 123); - obj.SetFloat("float", 3.14); - obj.SetBool("bool", true); - obj.SetString("string", "test"); - obj.SetNull("null"); - - PrintToServer("Object after set operations:"); - PrintJson(obj); - - // Test get operations - PrintToServer("Int value: %d", obj.GetInt("int")); - PrintToServer("Float value: %f", obj.GetFloat("float")); - PrintToServer("Bool value: %d", obj.GetBool("bool")); - - char strBuffer[64]; - obj.GetString("string", strBuffer, sizeof(strBuffer)); - PrintToServer("String value: %s", strBuffer); - - PrintToServer("Is null value null: %d", obj.IsNull("null")); - - // Test key operations - char key[64]; - for (int i = 0; i < obj.Size; i++) - { - obj.GetKey(i, key, sizeof(key)); - PrintToServer("Key at %d: %s", i, key); - - YYJSON value = obj.GetValueAt(i); - PrintToServer("Value type at %d: %d", i, value.Type); - delete value; - } - - // Test rename key - obj.RenameKey("int", "number"); - PrintToServer("After renaming 'int' to 'number':"); - PrintJson(obj); - - // Test remove operations - obj.Remove("number"); - PrintToServer("After removing 'number':"); - PrintJson(obj); - - obj.Clear(); - PrintToServer("Object size after Clear: %d", obj.Size); - - delete obj; -} - -void TestSortOperations() -{ - PrintToServer("[YYJSON] Testing sort operations..."); - - // Test array sorting - YYJSONArray arr = YYJSON.Parse("[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]", .is_mutable_doc = true); - - PrintToServer("Original array:"); - PrintJson(arr); - - arr.Sort(); - PrintToServer("After ascending sort:"); - PrintJson(arr); - - arr.Sort(YYJSON_SORT_DESC); - PrintToServer("After descending sort:"); - PrintJson(arr); - - arr.Sort(YYJSON_SORT_RANDOM); - PrintToServer("After random sort:"); - PrintJson(arr); - - // Test mixed type array sorting - YYJSONArray mixed = YYJSON.Parse("[true, 42, \"hello\", 1.23, false, \"world\"]", .is_mutable_doc = true); - PrintToServer("Original mixed array:"); - PrintJson(mixed); - - mixed.Sort(); - PrintToServer("After sorting mixed array:"); - PrintJson(mixed); - - // Test object sorting - YYJSONObject obj = YYJSON.Parse("{\"zebra\": 1, \"alpha\": 2, \"beta\": 3}", .is_mutable_doc = true); - PrintToServer("Original object:"); - PrintJson(obj); - - obj.Sort(); - PrintToServer("After ascending sort:"); - PrintJson(obj); - - obj.Sort(YYJSON_SORT_DESC); - PrintToServer("After descending sort:"); - PrintJson(obj); - - obj.Sort(YYJSON_SORT_RANDOM); - PrintToServer("After random sort:"); - PrintJson(obj); - - delete arr; - delete mixed; - delete obj; -} - -void TestSearchOperations() -{ - PrintToServer("[YYJSON] Testing search operations..."); - - YYJSONArray arr = YYJSON.Parse("[42, true, \"hello\", 3.14, \"world\", false, 42]"); - PrintToServer("Test array:"); - PrintJson(arr); - - // Test all search methods - PrintToServer("Search results:"); - PrintToServer("IndexOfInt(42): %d", arr.IndexOfInt(42)); - PrintToServer("IndexOfInt(999): %d", arr.IndexOfInt(999)); - - PrintToServer("IndexOfBool(true): %d", arr.IndexOfBool(true)); - PrintToServer("IndexOfBool(false): %d", arr.IndexOfBool(false)); - - PrintToServer("IndexOfString(\"hello\"): %d", arr.IndexOfString("hello")); - PrintToServer("IndexOfString(\"missing\"): %d", arr.IndexOfString("missing")); - - PrintToServer("IndexOfFloat(3.14): %d", arr.IndexOfFloat(3.14)); - PrintToServer("IndexOfFloat(2.718): %d", arr.IndexOfFloat(2.718)); - - delete arr; -} - -void TestPointerOperations() -{ - PrintToServer("[YYJSON] Testing JSON pointer operations..."); - - YYJSONObject obj = new YYJSONObject(); - - // Test setting nested values - obj.PtrSetInt("/a/b/c", 1); - obj.PtrSetString("/a/b/name", "test"); - obj.PtrSetBool("/a/flag", true); - obj.PtrSetFloat("/a/b/pi", 3.14); - obj.PtrSetNull("/a/b/empty"); - - PrintToServer("After setting values:"); - PrintJson(obj); - - // Test getting values - PrintToServer("Pointer get operations:"); - PrintToServer("/a/b/c: %d", obj.PtrGetInt("/a/b/c")); - - char strBuffer[64]; - obj.PtrGetString("/a/b/name", strBuffer, sizeof(strBuffer)); - PrintToServer("/a/b/name: %s", strBuffer); - - PrintToServer("/a/flag: %d", obj.PtrGetBool("/a/flag")); - PrintToServer("/a/b/pi: %f", obj.PtrGetFloat("/a/b/pi")); - PrintToServer("/a/b/empty is null: %d", obj.PtrGetIsNull("/a/b/empty")); - - // Test adding values - obj.PtrAddInt("/a/b/numbers/0", 1); - obj.PtrAddInt("/a/b/numbers/1", 2); - obj.PtrAddString("/a/b/strings", "append"); - - PrintToServer("After adding values:"); - PrintJson(obj); - - // Test length - PrintToServer("Length of /a/b/numbers: %d", obj.PtrGetLength("/a/b/numbers")); - - // Test removing values - obj.PtrRemove("/a/b/c"); - PrintToServer("After removing /a/b/c:"); - PrintJson(obj); - - delete obj; -} - -void TestIterationOperations() -{ - PrintToServer("[YYJSON] Testing iteration operations..."); - - // Test object iteration - YYJSONObject obj = YYJSON.Parse("{\"a\": 1, \"b\": 2, \"c\": 3}"); - char key[64]; - YYJSON value; - - while (obj.ForeachObject(key, sizeof(key), value)) - { - PrintToServer("Key: %s", key); - delete value; - } - - // Test array iteration - YYJSONArray arr = YYJSON.Parse("[1, 2, 3, 4, 5]"); - int index; - - while (arr.ForeachArray(index, value)) - { - PrintToServer("Index: %d", index); - delete value; - } - - delete obj; - delete arr; -} - -void TestTypeOperations() -{ - PrintToServer("[YYJSON] Testing type operations..."); - - // Test value creation - YYJSON boolVal = YYJSON.CreateBool(true); - YYJSON intVal = YYJSON.CreateInt(42); - YYJSON floatVal = YYJSON.CreateFloat(3.14); - YYJSON strVal = YYJSON.CreateString("test"); - YYJSON nullVal = YYJSON.CreateNull(); - - // Test value types - PrintToServer("Value types:"); - PrintToServer("Bool type: %d", boolVal.Type); - PrintToServer("Int type: %d", intVal.Type); - PrintToServer("Float type: %d", floatVal.Type); - PrintToServer("String type: %d", strVal.Type); - PrintToServer("Null type: %d", nullVal.Type); - - // Test value retrieval - PrintToServer("Value contents:"); - PrintToServer("Bool value: %d", YYJSON.GetBool(boolVal)); - PrintToServer("Int value: %d", YYJSON.GetInt(intVal)); - PrintToServer("Float value: %f", YYJSON.GetFloat(floatVal)); - - char strBuffer[64]; - YYJSON.GetString(strVal, strBuffer, sizeof(strBuffer)); - PrintToServer("String value: %s", strBuffer); - - delete boolVal; - delete intVal; - delete floatVal; - delete strVal; - delete nullVal; -} - -void TestFileOperations() -{ - PrintToServer("[YYJSON] Testing file operations..."); - - // Create test data - YYJSONObject obj = new YYJSONObject(); - obj.SetInt("id", 1); - obj.SetString("name", "test"); - obj.SetBool("active", true); - - // Test file writing - PrintToServer("Writing to file..."); - obj.ToFile("test.json", YYJSON_WRITE_PRETTY_TWO_SPACES); - - // Test file reading - PrintToServer("Reading from file..."); - YYJSONObject loaded = YYJSON.Parse("test.json", true); - - PrintToServer("File content:"); - PrintJson(loaded); - delete loaded; - - delete obj; -} - -void TestImmutabilityOperations() -{ - PrintToServer("[YYJSON] Testing immutability operations..."); - - // Test immutable document creation - YYJSONObject immutable = YYJSON.Parse("{\"key\": 123, \"str\": \"test\"}"); - PrintToServer("Created immutable document:"); - PrintJson(immutable); - - // Test property checks - PrintToServer("Is mutable: %d", immutable.IsMutable); - PrintToServer("Is immutable: %d", immutable.IsImmutable); - - // Test read operations (should succeed) - PrintToServer("Read operations on immutable document:"); - PrintToServer("Int value: %d", immutable.GetInt("key")); - char buffer[64]; - immutable.GetString("str", buffer, sizeof(buffer)); - PrintToServer("String value: %s", buffer); - - // Test conversion to mutable - YYJSONObject mutable = immutable.ToMutable(); - PrintToServer("\nConverted to mutable document:"); - PrintToServer("Is mutable: %d", mutable.IsMutable); - PrintToServer("Is immutable: %d", mutable.IsImmutable); - - // Now modifications should work - mutable.SetInt("key", 456) - PrintToServer("Successfully modified mutable document:"); - PrintJson(mutable); - - // Test conversion back to immutable - YYJSONObject backToImmutable = mutable.ToImmutable(); - PrintToServer("\nConverted back to immutable:"); - PrintToServer("Is mutable: %d", backToImmutable.IsMutable); - PrintToServer("Is immutable: %d", backToImmutable.IsImmutable); - delete backToImmutable; - delete mutable; - delete immutable; - - // Test file operations with immutability - PrintToServer("\nTesting file operations with immutability..."); - - // Create and write a mutable document - YYJSONObject writeObj = new YYJSONObject(); - writeObj.SetInt("test", 123); - writeObj.ToFile("test_immutable.json"); - delete writeObj; - - // Read as immutable - YYJSONObject readImmutable = YYJSON.Parse("test_immutable.json", true); - PrintToServer("Read as immutable document:"); - PrintJson(readImmutable); - PrintToServer("Is mutable: %d", readImmutable.IsMutable); - PrintToServer("Is immutable: %d", readImmutable.IsImmutable); - delete readImmutable; -} - -void TestPackOperations() -{ - PrintToServer("[YYJSON] Testing pack operations..."); - - // Test basic pack operation with different types - YYJSON packed = YYJSON.Pack("{s:s,s:i,s:f,s:b,s:n}", - "name", "test", - "age", 25, - "height", 1.75, - "active", true, - "extra" - ); - - PrintToServer("Packed JSON:"); - PrintJson(packed); - - // Test nested object packing - YYJSON nested = YYJSON.Pack("{s:{s:s,s:[i,i,i]}}", - "user", - "name", "test", - "scores", 85, 90, 95 - ); - - PrintToServer("Nested packed JSON:"); - PrintJson(nested); - - // Test array packing with mixed types - YYJSON array = YYJSON.Pack("[s,i,f,b,n]", - "test", 42, 3.14, true - ); - - PrintToServer("Array packed JSON:"); - PrintJson(array); - - delete packed; - delete nested; - delete array; -} - -void TestFromStringsOperations() -{ - PrintToServer("[YYJSON] Testing FromStrings operations..."); - - // Test object creation from key-value string arrays - char pairs[][] = {"name", "test", "type", "demo", "version", "1.0.0"}; - - YYJSONObject obj = YYJSONObject.FromStrings(pairs, sizeof(pairs)); - PrintToServer("Object from strings:"); - PrintJson(obj); - - // Test array creation from string array - char items[][] = {"apple", "banana", "orange", "grape"}; - YYJSONArray arr = YYJSONArray.FromStrings(items, sizeof(items)); - PrintToServer("Array from strings:"); - PrintJson(arr); - - delete obj; - delete arr; -} - -// Helper function to print json contents -void PrintJson(YYJSON data) -{ - int len = data.GetSerializedSize(YYJSON_WRITE_PRETTY_TWO_SPACES); - char[] buffer = new char[len]; - data.ToString(buffer, len, YYJSON_WRITE_PRETTY_TWO_SPACES); - PrintToServer("%s", buffer); -} \ No newline at end of file