| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- #include "testBase.h"
- static RyanJson_t createObjectWithIntMember(const char *key, int32_t value)
- {
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddIntToObject(obj, key, value));
- return obj;
- }
- static RyanJsonPrintStyle makeStyle(const char *indent, const char *newline, uint8_t spaceAfterColon, RyanJsonBool_e format)
- {
- RyanJsonPrintStyle style = {
- .indent = indent,
- .indentLen = (uint32_t)strlen(indent),
- .newline = newline,
- .newlineLen = (uint32_t)strlen(newline),
- .spaceAfterColon = spaceAfterColon,
- .format = format,
- };
- return style;
- }
- void testPrintStyleEdgeCases(void)
- {
- // NULL 输入
- TEST_ASSERT_NULL(RyanJsonPrint(NULL, 10, RyanJsonTrue, NULL));
- RyanJson_t json = RyanJsonCreateObject();
- TEST_ASSERT_NULL(RyanJsonPrintWithStyle(json, 10, NULL, NULL));
- // format=false 但提供打印风格
- RyanJsonPrintStyle style = makeStyle(" ", "\n", 1, RyanJsonFalse);
- char *minified = RyanJsonPrintWithStyle(json, 10, &style, NULL);
- TEST_ASSERT_NOT_NULL(minified);
- TEST_ASSERT_EQUAL_STRING("{}", minified); // 应为压缩输出,不包含空格与换行
- RyanJsonFree(minified);
- // 极端小缓冲区(验证内部自动扩容)
- // RyanJsonPrint 内部会强制最小缓冲区尺寸,这里只需验证行为正确且不崩溃
- RyanJsonAddIntToObject(json, "a", 1);
- char *smallBufPrint = RyanJsonPrint(json, 1, RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL(smallBufPrint);
- TEST_ASSERT_EQUAL_STRING("{\"a\":1}", smallBufPrint);
- RyanJsonFree(smallBufPrint);
- RyanJsonDelete(json);
- }
- static void testPrintPreallocatedTooSmall(void)
- {
- RyanJson_t obj = RyanJsonCreateObject();
- RyanJsonAddStringToObject(obj, "k", "v");
- char smallBuf[4] = {0};
- char *out = RyanJsonPrintPreallocated(obj, smallBuf, sizeof(smallBuf), RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(out, "Preallocated buffer 过小应返回 NULL");
- RyanJsonDelete(obj);
- }
- static void testPrintPreallocatedExactFit(void)
- {
- RyanJson_t nullJson = RyanJsonCreateNull(NULL);
- char buf[5];
- memset(buf, 'X', sizeof(buf));
- char *out = RyanJsonPrintPreallocated(nullJson, buf, sizeof(buf), RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(out, "Preallocated buffer 刚好够用应成功");
- TEST_ASSERT_EQUAL_STRING("null", out);
- RyanJsonDelete(nullJson);
- }
- static void testPrintPreallocatedExactFitObjectString(void)
- {
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddStringToObject(obj, "k", "v"));
- uint32_t expectedLen = 0;
- char *expected = RyanJsonPrint(obj, 0, RyanJsonFalse, &expectedLen);
- TEST_ASSERT_NOT_NULL(expected);
- char exactBuf[16] = {0};
- TEST_ASSERT_TRUE_MESSAGE(expectedLen + 1U <= sizeof(exactBuf), "测试缓冲区长度不足");
- char *out = RyanJsonPrintPreallocated(obj, exactBuf, expectedLen + 1U, RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(out, "对象(string)预分配刚好够用应成功");
- TEST_ASSERT_EQUAL_STRING(expected, out);
- RyanJsonFree(expected);
- RyanJsonDelete(obj);
- }
- // 该用例覆盖预分配缓冲行为,后续可按场景继续拆分
- static void testPrintPreallocatedObjectIntHeadroom(void)
- {
- RyanJson_t obj = createObjectWithIntMember("a", 1);
- // 先拿到基准输出长度
- uint32_t expectLen = 0;
- char *expect = RyanJsonPrint(obj, 0, RyanJsonFalse, &expectLen);
- if (NULL == expect)
- {
- RyanJsonDelete(obj);
- TEST_FAIL_MESSAGE("基准打印失败,无法构造刚好够用缓冲区");
- return;
- }
- // 对象中包含 int 时,PrintNumber 内部会做 INT32_MIN 级别的预留校验,
- // 因此“仅够最终输出长度”的缓冲区会失败,这是当前实现的既定行为。
- char *exactBuf = (char *)malloc((size_t)expectLen + 1U);
- if (NULL == exactBuf)
- {
- RyanJsonFree(expect);
- RyanJsonDelete(obj);
- TEST_FAIL_MESSAGE("malloc 失败");
- return;
- }
- char *out = RyanJsonPrintPreallocated(obj, exactBuf, expectLen + 1U, RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(out, "对象(int)预分配仅按最终长度应失败(需要额外预留空间)");
- // 给足预留空间后应成功
- char headroomBuf[32] = {0};
- out = RyanJsonPrintPreallocated(obj, headroomBuf, sizeof(headroomBuf), RyanJsonFalse, NULL);
- if (NULL == out)
- {
- RyanJsonFree(expect);
- free(exactBuf);
- RyanJsonDelete(obj);
- TEST_FAIL_MESSAGE("对象(int)预分配带预留空间应成功");
- return;
- }
- TEST_ASSERT_EQUAL_STRING(expect, out);
- TEST_ASSERT_EQUAL_UINT32(expectLen, (uint32_t)strlen(out));
- RyanJsonFree(expect);
- free(exactBuf);
- RyanJsonDelete(obj);
- }
- static void testPrintPreallocatedWithStyleNullArgs(void)
- {
- RyanJson_t obj = createObjectWithIntMember("a", 1);
- RyanJsonPrintStyle style = makeStyle(" ", "\n", 1, RyanJsonTrue);
- char buf[64] = {0};
- TEST_ASSERT_NULL(RyanJsonPrintPreallocatedWithStyle(NULL, buf, sizeof(buf), &style, NULL));
- TEST_ASSERT_NULL(RyanJsonPrintPreallocatedWithStyle(obj, NULL, sizeof(buf), &style, NULL));
- TEST_ASSERT_NULL(RyanJsonPrintPreallocatedWithStyle(obj, buf, sizeof(buf), NULL, NULL));
- TEST_ASSERT_NULL(RyanJsonPrintPreallocatedWithStyle(obj, buf, 0, &style, NULL));
- RyanJsonDelete(obj);
- }
- static void testPrintPreallocatedWithStyleTooSmall(void)
- {
- RyanJson_t obj = createObjectWithIntMember("a", 1);
- RyanJsonPrintStyle style = makeStyle(" ", "\n", 1, RyanJsonTrue);
- char tinyBuf[2] = {0};
- char *out = RyanJsonPrintPreallocatedWithStyle(obj, tinyBuf, sizeof(tinyBuf), &style, NULL);
- TEST_ASSERT_NULL_MESSAGE(out, "WithStyle 预分配缓冲区过小应返回 NULL");
- RyanJsonDelete(obj);
- }
- static void testPrintPreallocatedWithStyleSuccessAndLen(void)
- {
- RyanJson_t obj = createObjectWithIntMember("a", 1);
- RyanJsonPrintStyle style = makeStyle(" ", "\n", 2, RyanJsonTrue);
- char buf[64] = {0};
- uint32_t len = 0;
- char *out = RyanJsonPrintPreallocatedWithStyle(obj, buf, sizeof(buf), &style, &len);
- TEST_ASSERT_NOT_NULL(out);
- TEST_ASSERT_EQUAL_STRING("{\n \"a\": 1\n}", out);
- TEST_ASSERT_EQUAL_UINT32((uint32_t)strlen(out), len);
- RyanJsonDelete(obj);
- }
- static void testPrintPreallocatedWithStyleFormatFalseMinified(void)
- {
- RyanJson_t obj = createObjectWithIntMember("a", 1);
- RyanJsonPrintStyle style = makeStyle("--------", "\r\n", 4, RyanJsonFalse);
- char buf[64] = {0};
- char *out = RyanJsonPrintPreallocatedWithStyle(obj, buf, sizeof(buf), &style, NULL);
- TEST_ASSERT_NOT_NULL(out);
- TEST_ASSERT_EQUAL_STRING("{\"a\":1}", out);
- RyanJsonDelete(obj);
- }
- static void testPrintIntBoundaryPreallocated(void)
- {
- RyanJson_t intJson = RyanJsonCreateInt(NULL, INT32_MIN);
- TEST_ASSERT_NOT_NULL(intJson);
- char tooSmall[11] = {0};
- char *out = RyanJsonPrintPreallocated(intJson, tooSmall, sizeof(tooSmall), RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(out, "INT32_MIN 预分配 11 字节应失败");
- char exactFit[12] = {0};
- out = RyanJsonPrintPreallocated(intJson, exactFit, sizeof(exactFit), RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(out, "INT32_MIN 预分配 12 字节应成功");
- TEST_ASSERT_EQUAL_STRING("-2147483648", out);
- RyanJsonDelete(intJson);
- }
- static void testPrintDoubleBoundaryPreallocated(void)
- {
- RyanJson_t doubleJson = RyanJsonCreateDouble(NULL, 1.5);
- TEST_ASSERT_NOT_NULL(doubleJson);
- uint32_t expectLen = 0;
- char *expect = RyanJsonPrint(doubleJson, 0, RyanJsonFalse, &expectLen);
- if (NULL == expect)
- {
- RyanJsonDelete(doubleJson);
- TEST_FAIL_MESSAGE("double 基准打印失败");
- return;
- }
- // 与 int 类似,double 路径会先申请固定工作区(RyanJsonDoubleBufferSize),
- // 所以仅按最终输出长度预分配会失败。
- char *exactBuf = (char *)malloc((size_t)expectLen + 1U);
- if (NULL == exactBuf)
- {
- RyanJsonFree(expect);
- RyanJsonDelete(doubleJson);
- TEST_FAIL_MESSAGE("malloc 失败");
- return;
- }
- char *out = RyanJsonPrintPreallocated(doubleJson, exactBuf, expectLen + 1U, RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(out, "double 预分配仅按最终长度应失败(需要额外预留空间)");
- char headroomBuf[128] = {0};
- out = RyanJsonPrintPreallocated(doubleJson, headroomBuf, sizeof(headroomBuf), RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(out, "double 预分配带预留空间应成功");
- TEST_ASSERT_EQUAL_STRING(expect, out);
- RyanJsonFree(expect);
- free(exactBuf);
- RyanJsonDelete(doubleJson);
- // 特殊值:NaN / Infinity 应打印为 null
- RyanJson_t nanJson = RyanJsonCreateDouble(NULL, NAN);
- RyanJson_t infJson = RyanJsonCreateDouble(NULL, INFINITY);
- TEST_ASSERT_NOT_NULL(nanJson);
- TEST_ASSERT_NOT_NULL(infJson);
- char specialBuf[128] = {0};
- out = RyanJsonPrintPreallocated(nanJson, specialBuf, sizeof(specialBuf), RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL(out);
- TEST_ASSERT_EQUAL_STRING("null", out);
- memset(specialBuf, 0, sizeof(specialBuf));
- out = RyanJsonPrintPreallocated(infJson, specialBuf, sizeof(specialBuf), RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL(out);
- TEST_ASSERT_EQUAL_STRING("null", out);
- RyanJsonDelete(nanJson);
- RyanJsonDelete(infJson);
- }
- static void testPrintDoubleScientificAndRoundtrip(void)
- {
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "large", 1.0e20));
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "tiny", 1.0e-5));
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "frac", 0.1));
- char *printed = RyanJsonPrint(obj, 128, RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL(printed);
- // large 在当前测试配置下应走科学计数法
- char *largePos = strstr(printed, "\"large\":");
- char *tinyPos = strstr(printed, "\"tiny\":");
- TEST_ASSERT_NOT_NULL(largePos);
- TEST_ASSERT_NOT_NULL(tinyPos);
- char *largeValueStart = strchr(largePos, ':');
- TEST_ASSERT_NOT_NULL(largeValueStart);
- largeValueStart++;
- char *largeComma = strpbrk(largePos, ",}");
- char *tinyComma = strpbrk(tinyPos, ",}");
- TEST_ASSERT_NOT_NULL(largeComma);
- TEST_ASSERT_NOT_NULL(tinyComma);
- int32_t largeHasScientific = (NULL != memchr((const void *)largeValueStart, 'e', (size_t)(largeComma - largeValueStart))) ||
- (NULL != memchr((const void *)largeValueStart, 'E', (size_t)(largeComma - largeValueStart)));
- #ifdef RyanJsonLinuxTestEnv
- // Linux 测试分支会在实现内覆盖格式选择,large 路径固定走科学计数法
- TEST_ASSERT_TRUE_MESSAGE(largeHasScientific, "RyanJsonLinuxTestEnv 下 large 应包含科学计数法输出");
- #elif true == RyanJsonSnprintfSupportScientific
- TEST_ASSERT_TRUE_MESSAGE(largeHasScientific, "开启科学计数法时 large 应包含科学计数法输出");
- #else
- TEST_ASSERT_FALSE_MESSAGE(largeHasScientific, "关闭科学计数法时 large 不应包含科学计数法输出");
- #endif
- RyanJson_t roundtrip = RyanJsonParse(printed);
- RyanJsonFree(printed);
- TEST_ASSERT_NOT_NULL(roundtrip);
- TEST_ASSERT_TRUE(RyanJsonCompareDouble(1.0e20, RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(roundtrip, "large"))));
- TEST_ASSERT_TRUE(RyanJsonCompareDouble(1.0e-5, RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(roundtrip, "tiny"))));
- TEST_ASSERT_TRUE(RyanJsonCompareDouble(0.1, RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(roundtrip, "frac"))));
- RyanJsonDelete(roundtrip);
- RyanJsonDelete(obj);
- }
- static int32_t gPrintFailAfter = -1;
- static int32_t gPrintAllocCount = 0;
- static void *printFailMalloc(size_t size)
- {
- if (gPrintFailAfter >= 0 && gPrintAllocCount++ >= gPrintFailAfter) { return NULL; }
- return unityTestMalloc(size);
- }
- static void *printFailRealloc(void *block, size_t size)
- {
- if (gPrintFailAfter >= 0 && gPrintAllocCount++ >= gPrintFailAfter) { return NULL; }
- return unityTestRealloc(block, size);
- }
- static void setPrintFailAfter(int32_t failAfter)
- {
- gPrintFailAfter = failAfter;
- gPrintAllocCount = 0;
- RyanJsonInitHooks(printFailMalloc, unityTestFree, printFailRealloc);
- }
- static void restorePrintHooks(void)
- {
- RyanJsonInitHooks(unityTestMalloc, unityTestFree, unityTestRealloc);
- gPrintFailAfter = -1;
- gPrintAllocCount = 0;
- }
- static void testPrintOom(void)
- {
- RyanJson_t obj = createObjectWithIntMember("a", 1);
- setPrintFailAfter(0);
- char *printed = RyanJsonPrint(obj, 32, RyanJsonFalse, NULL);
- // 恢复 hooks
- restorePrintHooks();
- if (printed) { RyanJsonFree(printed); }
- TEST_ASSERT_NULL_MESSAGE(printed, "Print OOM 应返回 NULL");
- RyanJsonDelete(obj);
- }
- static void testPrintFinalAppendOom(void)
- {
- char longStr[58];
- memset(longStr, 'a', sizeof(longStr) - 1);
- longStr[sizeof(longStr) - 1] = '\0';
- RyanJson_t obj = RyanJsonCreateObject();
- RyanJsonAddStringToObject(obj, "k", longStr);
- setPrintFailAfter(1);
- char *printed = RyanJsonPrint(obj, RyanJsonPrintfPreAlloSize, RyanJsonFalse, NULL);
- // 恢复 hooks
- restorePrintHooks();
- if (printed) { RyanJsonFree(printed); }
- TEST_ASSERT_NULL_MESSAGE(printed, "Print 末尾扩容失败应返回 NULL");
- RyanJsonDelete(obj);
- }
- /**
- * @brief 定制化打印风格测试
- * 验证 RyanJsonPrintWithStyle 接口以及各种格式化选项
- */
- static void testPrintCrazy(void)
- {
- // 打印空对象/空数组
- RyanJson_t emptyObj = RyanJsonCreateObject();
- char *s = RyanJsonPrint(emptyObj, 0, RyanJsonFalse, NULL);
- TEST_ASSERT_EQUAL_STRING("{}", s);
- RyanJsonFree(s);
- RyanJsonDelete(emptyObj);
- RyanJson_t emptyArr = RyanJsonCreateArray();
- s = RyanJsonPrint(emptyArr, 0, RyanJsonFalse, NULL);
- TEST_ASSERT_EQUAL_STRING("[]", s);
- RyanJsonFree(s);
- RyanJsonDelete(emptyArr);
- // 极限缓冲区测试
- RyanJson_t json = RyanJsonCreateObject();
- RyanJsonAddStringToObject(json, "k", "v");
- // size=0:自动选择初始缓冲
- s = RyanJsonPrint(json, 0, RyanJsonFalse, NULL);
- TEST_ASSERT_EQUAL_STRING("{\"k\":\"v\"}", s);
- RyanJsonFree(s);
- // size=1:初始空间过小,预期自动扩容
- s = RyanJsonPrint(json, 1, RyanJsonFalse, NULL);
- TEST_ASSERT_EQUAL_STRING("{\"k\":\"v\"}", s);
- RyanJsonFree(s);
- RyanJsonDelete(json);
- // 极端打印风格
- RyanJson_t obj = createObjectWithIntMember("a", 1);
- RyanJsonPrintStyle crazyStyle = makeStyle("--------", "\n\n", 4, RyanJsonTrue);
- uint32_t len;
- s = RyanJsonPrintWithStyle(obj, 10, &crazyStyle, &len);
- // 预期片段:{\n\n--------"a": 1\n\n}
- // 校验长度与关键片段
- TEST_ASSERT_EQUAL_INT(strlen(s), len);
- TEST_ASSERT_NOT_NULL(strstr(s, "--------\"a\": 1"));
- RyanJsonFree(s);
- RyanJsonDelete(obj);
- }
- static void testPrintDefault(void)
- {
- const char *jsonstr = "{\"a\":1,\"b\":true}";
- RyanJson_t json = RyanJsonParse(jsonstr);
- TEST_ASSERT_NOT_NULL(json);
- // 测试默认格式化打印(使用 RyanJsonPrint 简化校验)
- char *defaultFormat = RyanJsonPrint(json, 256, RyanJsonTrue, NULL);
- // 期望形如:
- // {
- // \t"a": 1,
- // \t"b": true
- // }
- TEST_ASSERT_NOT_NULL(defaultFormat);
- TEST_ASSERT_TRUE_MESSAGE(strstr(defaultFormat, "\t\"a\": 1") != NULL, "默认格式化:缩进或冒号后空格错误");
- RyanJsonFree(defaultFormat);
- RyanJsonDelete(json);
- }
- static void testPrintCustomStyle(void)
- {
- const char *jsonstr = "{\"a\":1,\"b\":true}";
- RyanJson_t json = RyanJsonParse(jsonstr);
- TEST_ASSERT_NOT_NULL(json);
- // 测试自定义风格:2 个空格缩进、Windows 换行、冒号后 2 个空格
- RyanJsonPrintStyle style = makeStyle(" ", "\r\n", 2, RyanJsonTrue);
- uint32_t len = 0;
- char *customPrint = RyanJsonPrintWithStyle(json, 256, &style, &len);
- TEST_ASSERT_NOT_NULL(customPrint);
- // 校验特征点
- TEST_ASSERT_TRUE_MESSAGE(strstr(customPrint, "\r\n \"a\": 1") != NULL, "自定义格式化:缩进、换行或冒号后空格匹配失败");
- TEST_ASSERT_EQUAL_INT_MESSAGE(strlen(customPrint), len, "返回长度不一致");
- RyanJsonFree(customPrint);
- RyanJsonDelete(json);
- }
- void testPrintRunner(void)
- {
- UnitySetTestFile(__FILE__);
- RUN_TEST(testPrintStyleEdgeCases);
- RUN_TEST(testPrintPreallocatedTooSmall);
- RUN_TEST(testPrintPreallocatedExactFit);
- RUN_TEST(testPrintPreallocatedExactFitObjectString);
- RUN_TEST(testPrintPreallocatedObjectIntHeadroom);
- RUN_TEST(testPrintPreallocatedWithStyleNullArgs);
- RUN_TEST(testPrintPreallocatedWithStyleTooSmall);
- RUN_TEST(testPrintPreallocatedWithStyleSuccessAndLen);
- RUN_TEST(testPrintPreallocatedWithStyleFormatFalseMinified);
- RUN_TEST(testPrintIntBoundaryPreallocated);
- RUN_TEST(testPrintDoubleBoundaryPreallocated);
- RUN_TEST(testPrintDoubleScientificAndRoundtrip);
- RUN_TEST(testPrintOom);
- RUN_TEST(testPrintFinalAppendOom);
- RUN_TEST(testPrintCrazy);
- RUN_TEST(testPrintDefault);
- RUN_TEST(testPrintCustomStyle);
- }
|