| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- #include "testBase.h"
- static void testPrintNullRootGuard(void)
- {
- TEST_ASSERT_NULL(RyanJsonPrint(NULL, 10, RyanJsonTrue, NULL));
- }
- static void testPrintPreallocatedExactFitNull(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 刚好容纳 null 应成功");
- TEST_ASSERT_EQUAL_STRING("null", out);
- RyanJsonDelete(nullJson);
- }
- static void testPrintPreallocatedExactFitUtf8String(void)
- {
- // 覆盖 UTF-8 key/value 的预分配精确长度与输出一致性。
- const char *key = "\xE4\xB8\xAD";
- const char *val = "\xF0\x9F\x90\x82";
- char expectLiteral[] = "{\"\xE4\xB8\xAD\":\"\xF0\x9F\x90\x82\"}";
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddStringToObject(obj, key, val));
- uint32_t expectedLen = 0;
- char *expected = RyanJsonPrint(obj, 0, RyanJsonFalse, &expectedLen);
- TEST_ASSERT_NOT_NULL(expected);
- TEST_ASSERT_EQUAL_UINT32((uint32_t)strlen(expected), expectedLen);
- TEST_ASSERT_EQUAL_STRING(expectLiteral, expected);
- char *buf = (char *)malloc((size_t)expectedLen + 1U);
- TEST_ASSERT_NOT_NULL(buf);
- char *out = RyanJsonPrintPreallocated(obj, buf, expectedLen + 1U, RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(out, "UTF-8 预分配刚好够用应成功");
- TEST_ASSERT_EQUAL_STRING(expectLiteral, out);
- RyanJson_t roundtrip = RyanJsonParse(out);
- TEST_ASSERT_NOT_NULL(roundtrip);
- TEST_ASSERT_TRUE(RyanJsonCompare(obj, roundtrip));
- RyanJsonDelete(roundtrip);
- free(buf);
- RyanJsonFree(expected);
- RyanJsonDelete(obj);
- }
- static void testPrintPreallocatedObjectIntHeadroom(void)
- {
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddIntToObject(obj, "a", 1));
- uint32_t expectLen = 0;
- char *expect = RyanJsonPrint(obj, 0, RyanJsonFalse, &expectLen);
- TEST_ASSERT_NOT_NULL(expect);
- // Object 中包含 Int 时,内部 Number 路径会预留固定工作区,
- // 因而“仅够最终输出长度”的缓冲区不一定足够。
- char *exactBuf = (char *)malloc((size_t)expectLen + 1U);
- TEST_ASSERT_NOT_NULL(exactBuf);
- char *out = RyanJsonPrintPreallocated(obj, exactBuf, expectLen + 1U, RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(out, "Object(int) 预分配仅按最终长度应失败");
- char headroomBuf[32] = {0};
- out = RyanJsonPrintPreallocated(obj, headroomBuf, sizeof(headroomBuf), RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(out, "Object(int) 预分配带预留空间应成功");
- TEST_ASSERT_EQUAL_STRING(expect, out);
- TEST_ASSERT_EQUAL_UINT32(expectLen, (uint32_t)strlen(out));
- RyanJsonFree(expect);
- free(exactBuf);
- 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);
- TEST_ASSERT_NOT_NULL(expect);
- // Double 路径同样会先申请内部工作区,仅按最终输出长度预分配不一定足够。
- char *exactBuf = (char *)malloc((size_t)expectLen + 1U);
- TEST_ASSERT_NOT_NULL(exactBuf);
- 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);
- 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);
- 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
- 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 void testPrintDoubleFixedPointBoundary(void)
- {
- // 固定点边界分支:
- // - 真实 0 必须打印成 0.0;
- // - 小于 1e15 的 Int 样式 Double 仍应保留一位小数;
- // - 到达 1e15 后应切到“大数”分支,避免继续沿用固定点规则。
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "zero", 0.0));
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "belowLimit", 999999999999999.0));
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "atLimit", 1.0e15));
- char *printed = RyanJsonPrint(obj, 160, RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL(printed);
- TEST_ASSERT_NOT_NULL_MESSAGE(strstr(printed, "\"zero\":0.0"), "0.0 应固定打印为 0.0");
- TEST_ASSERT_NOT_NULL_MESSAGE(strstr(printed, "\"belowLimit\":999999999999999.0"), "<1e15 的 Int 样式 double 应保留一位小数");
- char *atLimitPos = strstr(printed, "\"atLimit\":");
- TEST_ASSERT_NOT_NULL(atLimitPos);
- char *atLimitValue = strchr(atLimitPos, ':');
- TEST_ASSERT_NOT_NULL(atLimitValue);
- atLimitValue++;
- char *atLimitEnd = strpbrk(atLimitValue, ",}");
- TEST_ASSERT_NOT_NULL(atLimitEnd);
- int32_t atLimitHasScientific = (NULL != memchr((const void *)atLimitValue, 'e', (size_t)(atLimitEnd - atLimitValue))) ||
- (NULL != memchr((const void *)atLimitValue, 'E', (size_t)(atLimitEnd - atLimitValue)));
- #ifdef RyanJsonLinuxTestEnv
- TEST_ASSERT_TRUE_MESSAGE(atLimitHasScientific, "RyanJsonLinuxTestEnv 下 1e15 应走科学计数法路径");
- #elif true == RyanJsonSnprintfSupportScientific
- TEST_ASSERT_TRUE_MESSAGE(atLimitHasScientific, "开启科学计数法时 1e15 应走科学计数法路径");
- #else
- TEST_ASSERT_FALSE_MESSAGE(atLimitHasScientific, "关闭科学计数法时 1e15 不应走科学计数法路径");
- TEST_ASSERT_EQUAL_UINT32_MESSAGE((uint32_t)strlen("1000000000000000.0"), (uint32_t)(atLimitEnd - atLimitValue),
- "关闭科学计数法时 1e15 应保留固定一位小数");
- TEST_ASSERT_EQUAL_MEMORY("1000000000000000.0", atLimitValue, strlen("1000000000000000.0"));
- #endif
- RyanJson_t roundtrip = RyanJsonParse(printed);
- TEST_ASSERT_NOT_NULL(roundtrip);
- TEST_ASSERT_TRUE(RyanJsonCompare(obj, roundtrip));
- RyanJsonDelete(roundtrip);
- RyanJsonFree(printed);
- RyanJsonDelete(obj);
- }
- static void testPrintTinyDoubleNotZeroed(void)
- {
- // 极小非零 Double 不能只靠 roundtrip 证明;
- // 这里直接检查原始输出 token,防止打印阶段先被抹成 0.0 / -0.0。
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "tinyPos", 1.0e-20));
- TEST_ASSERT_TRUE(RyanJsonAddDoubleToObject(obj, "tinyNeg", -1.0e-20));
- char *printed = RyanJsonPrint(obj, 128, RyanJsonFalse, NULL);
- TEST_ASSERT_NOT_NULL(printed);
- char *tinyPosField = strstr(printed, "\"tinyPos\":");
- char *tinyNegField = strstr(printed, "\"tinyNeg\":");
- TEST_ASSERT_NOT_NULL(tinyPosField);
- TEST_ASSERT_NOT_NULL(tinyNegField);
- char *tinyPosValue = strchr(tinyPosField, ':');
- char *tinyNegValue = strchr(tinyNegField, ':');
- TEST_ASSERT_NOT_NULL(tinyPosValue);
- TEST_ASSERT_NOT_NULL(tinyNegValue);
- tinyPosValue++;
- tinyNegValue++;
- char *tinyPosEnd = strpbrk(tinyPosValue, ",}");
- char *tinyNegEnd = strpbrk(tinyNegValue, ",}");
- TEST_ASSERT_NOT_NULL(tinyPosEnd);
- TEST_ASSERT_NOT_NULL(tinyNegEnd);
- TEST_ASSERT_FALSE_MESSAGE(((uint32_t)(tinyPosEnd - tinyPosValue) == (uint32_t)strlen("0.0")) &&
- (0 == strncmp(tinyPosValue, "0.0", strlen("0.0"))),
- "tiny 正数原始输出不应退化成 0.0");
- TEST_ASSERT_FALSE_MESSAGE(((uint32_t)(tinyNegEnd - tinyNegValue) == (uint32_t)strlen("-0.0")) &&
- (0 == strncmp(tinyNegValue, "-0.0", strlen("-0.0"))),
- "tiny 负数原始输出不应退化成 -0.0");
- RyanJson_t roundtrip = RyanJsonParse(printed);
- RyanJsonFree(printed);
- TEST_ASSERT_NOT_NULL(roundtrip);
- double tinyPos = RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(roundtrip, "tinyPos"));
- double tinyNeg = RyanJsonGetDoubleValue(RyanJsonGetObjectToKey(roundtrip, "tinyNeg"));
- TEST_ASSERT_TRUE_MESSAGE(tinyPos > 0.0, "tiny 正数不应在打印后被抹为 0");
- TEST_ASSERT_TRUE_MESSAGE(tinyNeg < 0.0, "tiny 负数不应在打印后被抹为 0");
- RyanJsonDelete(roundtrip);
- RyanJsonDelete(obj);
- }
- static void testPrintPreallocatedArgGuards(void)
- {
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddStringToObject(obj, "k", "v"));
- char buf[16] = {0};
- TEST_ASSERT_NULL(RyanJsonPrintPreallocated(NULL, buf, sizeof(buf), RyanJsonFalse, NULL));
- TEST_ASSERT_NULL(RyanJsonPrintPreallocated(obj, NULL, sizeof(buf), RyanJsonFalse, NULL));
- TEST_ASSERT_NULL(RyanJsonPrintPreallocated(obj, buf, 0, RyanJsonFalse, NULL));
- uint32_t len = 0;
- char *out = RyanJsonPrintPreallocated(obj, buf, sizeof(buf), RyanJsonFalse, &len);
- TEST_ASSERT_NOT_NULL(out);
- TEST_ASSERT_EQUAL_STRING("{\"k\":\"v\"}", out);
- TEST_ASSERT_EQUAL_UINT32((uint32_t)strlen(out), len);
- RyanJsonDelete(obj);
- }
- static void testPrintOom(void)
- {
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddIntToObject(obj, "a", 1));
- UNITY_TEST_OOM_BEGIN(0);
- char *printed = RyanJsonPrint(obj, 32, RyanJsonFalse, NULL);
- UNITY_TEST_OOM_END();
- 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();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddStringToObject(obj, "k", longStr));
- UNITY_TEST_OOM_BEGIN(1);
- char *printed = RyanJsonPrint(obj, RyanJsonPrintfPreAlloSize, RyanJsonFalse, NULL);
- UNITY_TEST_OOM_END();
- if (printed) { RyanJsonFree(printed); }
- TEST_ASSERT_NULL_MESSAGE(printed, "Print 末尾扩容失败应返回 NULL");
- RyanJsonDelete(obj);
- }
- static void testPrintExpandFallbackWithoutRealloc(void)
- {
- char longValue[600] = {0};
- memset(longValue, 'x', sizeof(longValue) - 1U);
- RyanJson_t obj = RyanJsonCreateObject();
- TEST_ASSERT_NOT_NULL(obj);
- TEST_ASSERT_TRUE(RyanJsonAddStringToObject(obj, "k", longValue));
- // 先让初始缓冲成功,再让扩容阶段走“无 realloc 时改用 malloc”分支并失败。
- UNITY_TEST_OOM_BEGIN_NO_REALLOC(1);
- char *printed = RyanJsonPrint(obj, RyanJsonPrintfPreAlloSize, RyanJsonFalse, NULL);
- UNITY_TEST_OOM_END();
- if (printed) { RyanJsonFree(printed); }
- TEST_ASSERT_NULL_MESSAGE(printed, "Print 扩容 fallback(malloc) 失败应返回 NULL");
- RyanJsonDelete(obj);
- }
- void testPrintGeneralRunner(void)
- {
- UnitySetTestFile(__FILE__);
- RUN_TEST(testPrintNullRootGuard);
- RUN_TEST(testPrintPreallocatedExactFitNull);
- RUN_TEST(testPrintPreallocatedExactFitUtf8String);
- RUN_TEST(testPrintPreallocatedObjectIntHeadroom);
- RUN_TEST(testPrintIntBoundaryPreallocated);
- RUN_TEST(testPrintDoubleBoundaryPreallocated);
- RUN_TEST(testPrintDoubleScientificAndRoundtrip);
- RUN_TEST(testPrintDoubleFixedPointBoundary);
- RUN_TEST(testPrintTinyDoubleNotZeroed);
- RUN_TEST(testPrintPreallocatedArgGuards);
- RUN_TEST(testPrintOom);
- RUN_TEST(testPrintFinalAppendOom);
- RUN_TEST(testPrintExpandFallbackWithoutRealloc);
- }
|