| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- #include "testBase.h"
- static void testLoadFailureNullInput(void)
- {
- // 仅验证 API 级别的空指针输入保护(不与 RFC8259 语法样本重复)。
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(NULL), "Parse(NULL) 应返回 NULL");
- const char *end = (const char *)0x1;
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParseOptions(NULL, 0, RyanJsonFalse, &end), "ParseOptions(NULL) 应返回 NULL");
- TEST_ASSERT_EQUAL_PTR_MESSAGE((const char *)0x1, end, "ParseOptions(NULL) 不应改写 end 指针");
- }
- static void testLoadFailureWhitespaceOnlySlice(void)
- {
- const char *text = " \t\r\n ";
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParseOptions(text, (uint32_t)strlen(text), RyanJsonFalse, NULL),
- "ParseOptions(仅空白, 非 strict) 应失败");
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParseOptions(text, (uint32_t)strlen(text), RyanJsonTrue, NULL),
- "ParseOptions(仅空白, strict) 应失败");
- }
- static void testLoadFailureHugeNumberOverflow(void)
- {
- // 超长 Int:应在数值累乘过程中触发 isfinite 防御并失败
- const uint32_t intLen = 1024;
- char *hugeInt = (char *)malloc((size_t)intLen + 1U);
- TEST_ASSERT_NOT_NULL(hugeInt);
- hugeInt[0] = '1';
- memset(hugeInt + 1, '9', intLen - 1U);
- hugeInt[intLen] = '\0';
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(hugeInt), "Parse(超长 Int 溢出) 应返回 NULL");
- free(hugeInt);
- // 超长小数:同样应触发 isfinite 防御并失败
- const uint32_t fracLen = 1024;
- char *hugeFrac = (char *)malloc((size_t)fracLen + 3U);
- TEST_ASSERT_NOT_NULL(hugeFrac);
- hugeFrac[0] = '0';
- hugeFrac[1] = '.';
- memset(hugeFrac + 2, '9', fracLen);
- hugeFrac[fracLen + 2U] = '\0';
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(hugeFrac), "Parse(超长小数溢出) 应返回 NULL");
- free(hugeFrac);
- }
- static void testLoadFailureExponentAccumulatorOverflow(void)
- {
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParse("1e2147483647"), "指数过大应触发 isfinite 防御");
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParse("1e2147483648"), "指数累积溢出应解析失败");
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParse("1e-2147483648"), "负指数累积溢出应解析失败");
- }
- static void testLoadParseOptionsFailure(void)
- {
- // 禁止尾部垃圾:requireNullTerminator = true
- const char *text = " {\"a\":1} trailing";
- RyanJson_t json = RyanJsonParseOptions(text, (uint32_t)strlen(text), RyanJsonTrue, NULL);
- TEST_ASSERT_NULL_MESSAGE(json, "ParseOptions(require null terminator) 应失败");
- }
- static void testLoadParseOptionsSequentialValidThenInvalidIsolation(void)
- {
- // 复杂链路:
- // ParseOptions(文档1成功, 非强制) -> ParseOptions(文档2失败) -> 校验文档1语义未被污染。
- const char *stream = "{\"ok\":1}{\"bad\":}";
- const uint32_t len = (uint32_t)strlen(stream);
- const char *end = NULL;
- RyanJson_t first = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end);
- TEST_ASSERT_NOT_NULL_MESSAGE(first, "顺序流文档1 解析应成功");
- TEST_ASSERT_NOT_NULL(end);
- TEST_ASSERT_EQUAL_CHAR('{', *end);
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(first, "ok")));
- uint32_t remain = (uint32_t)(len - (uint32_t)(end - stream));
- RyanJson_t second = RyanJsonParseOptions(end, remain, RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(second, "顺序流文档2(非法)解析应失败");
- RyanJson_t baseline = RyanJsonParse("{\"ok\":1}");
- TEST_ASSERT_NOT_NULL(baseline);
- TEST_ASSERT_TRUE_MESSAGE(RyanJsonCompare(first, baseline), "文档2 失败后,文档1 语义不应改变");
- RyanJson_t strictWhole = RyanJsonParseOptions(stream, len, RyanJsonTrue, NULL);
- TEST_ASSERT_NULL_MESSAGE(strictWhole, "整串包含非法尾文档时 strict 解析应失败");
- RyanJsonDelete(baseline);
- RyanJsonDelete(first);
- }
- static void testLoadParseOptionsBinaryTailFailureIsolation(void)
- {
- // 复杂链路:
- // ParseOptions(二进制切片, 首文档成功) -> ParseOptions(尾片段失败) -> 校验首文档结果仍稳定。
- const uint8_t buffer[] = {'{', '"', 'a', '"', ':', '1', '}', '#', '!', '\0'};
- const uint32_t bufLen = 9U; // 不包含末尾 '\0'
- const char *end = NULL;
- RyanJson_t head = RyanJsonParseOptions((const char *)buffer, bufLen, RyanJsonFalse, &end);
- TEST_ASSERT_NOT_NULL_MESSAGE(head, "二进制切片首文档解析应成功");
- TEST_ASSERT_NOT_NULL(end);
- TEST_ASSERT_EQUAL_CHAR('#', *end);
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "a")));
- uint32_t remain = (uint32_t)(bufLen - (uint32_t)(end - (const char *)buffer));
- RyanJson_t tail = RyanJsonParseOptions(end, remain, RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(tail, "二进制切片尾片段解析应失败");
- // 校验首文档保持稳定。
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "a")));
- RyanJson_t strictWhole = RyanJsonParseOptions((const char *)buffer, bufLen, RyanJsonTrue, NULL);
- TEST_ASSERT_NULL_MESSAGE(strictWhole, "strict 模式应拒绝二进制切片中的尾部垃圾");
- RyanJsonDelete(head);
- }
- static void testLoadParseOptionsSequentialInvalidThenTailResync(void)
- {
- // 复杂链路:
- // ParseOptions(文档1成功) -> ParseOptions(文档2失败) -> 手动重同步到文档3并解析成功。
- const char *stream = "{\"head\":1}{\"bad\":}{\"tail\":3}";
- const uint32_t len = (uint32_t)strlen(stream);
- const char *end = NULL;
- RyanJson_t head = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end);
- TEST_ASSERT_NOT_NULL_MESSAGE(head, "文档1 解析应成功");
- TEST_ASSERT_NOT_NULL(end);
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
- uint32_t remain = (uint32_t)(len - (uint32_t)(end - stream));
- RyanJson_t bad = RyanJsonParseOptions(end, remain, RyanJsonFalse, NULL);
- TEST_ASSERT_NULL_MESSAGE(bad, "文档2(非法) 解析应失败");
- const char *tailStart = strstr(end, "{\"tail\":3}");
- TEST_ASSERT_NOT_NULL_MESSAGE(tailStart, "应可在原始缓冲中定位到文档3 起点");
- RyanJson_t tail = RyanJsonParseOptions(tailStart, (uint32_t)strlen(tailStart), RyanJsonTrue, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(tail, "文档3 解析应成功");
- TEST_ASSERT_EQUAL_INT(3, RyanJsonGetIntValue(RyanJsonGetObjectToKey(tail, "tail")));
- // 再次确认文档1 未受失败路径影响。
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
- RyanJsonDelete(tail);
- RyanJsonDelete(head);
- }
- static void testLoadParseOptionsFailureKeepsEndPtrStable(void)
- {
- // 复杂链路:
- // ParseOptions(文档1成功) -> ParseOptions(文档2失败, 传入 parseEndPtr) -> 复用旧偏移重同步文档3。
- const char *stream = "{\"head\":1}{\"bad\":}{\"tail\":2}";
- const uint32_t len = (uint32_t)strlen(stream);
- const char *end1 = NULL;
- RyanJson_t head = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end1);
- TEST_ASSERT_NOT_NULL_MESSAGE(head, "文档1 解析应成功");
- TEST_ASSERT_NOT_NULL(end1);
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
- // 将 parseEndPtr 预置为“文档1结束位置”,用于验证失败调用不会修改它。
- const char *failEnd = end1;
- uint32_t remain = (uint32_t)(len - (uint32_t)(end1 - stream));
- RyanJson_t bad = RyanJsonParseOptions(end1, remain, RyanJsonFalse, &failEnd);
- TEST_ASSERT_NULL_MESSAGE(bad, "文档2(非法) 解析应失败");
- TEST_ASSERT_EQUAL_PTR_MESSAGE(end1, failEnd, "失败解析不应改写调用方的 parseEndPtr");
- // 从保留下来的偏移位置继续重同步到文档3。
- const char *tailStart = strstr(failEnd, "{\"tail\":2}");
- TEST_ASSERT_NOT_NULL_MESSAGE(tailStart, "应可基于 failEnd 重同步定位文档3");
- RyanJson_t tail = RyanJsonParseOptions(tailStart, (uint32_t)strlen(tailStart), RyanJsonTrue, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(tail, "文档3 解析应成功");
- TEST_ASSERT_EQUAL_INT(2, RyanJsonGetIntValue(RyanJsonGetObjectToKey(tail, "tail")));
- // 再次确认文档1 未被失败路径污染。
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
- RyanJsonDelete(tail);
- RyanJsonDelete(head);
- }
- static void testLoadFailureOomParse(void)
- {
- const char *longKeyJson =
- "{\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\":1}";
- UNITY_TEST_OOM_BEGIN(0);
- RyanJson_t json = RyanJsonParse("{\"a\":1}");
- UNITY_TEST_OOM_END();
- if (json) { RyanJsonDelete(json); }
- TEST_ASSERT_NULL_MESSAGE(json, "Parse OOM(根节点分配失败) 应返回 NULL");
- UNITY_TEST_OOM_BEGIN(1); // root 成功,key buffer 失败
- json = RyanJsonParse(longKeyJson);
- UNITY_TEST_OOM_END();
- if (json) { RyanJsonDelete(json); }
- TEST_ASSERT_NULL_MESSAGE(json, "Parse OOM 应返回 NULL");
- }
- static void testLoadFailureAllocatedKeyCleanupOnValueError(void)
- {
- const char *bad = "{\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\":}";
- TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(bad), "长 key 后 value 非法时应解析失败");
- }
- static void testLoadParseOptionsSequentialOomRecovery(void)
- {
- // 复杂链路:
- // ParseOptions(文档1成功) -> 人工 OOM 让文档2 失败 -> 恢复 hooks 后文档2 再解析成功。
- const char *stream = "{\"a\":1}{\"b\":2}";
- const uint32_t len = (uint32_t)strlen(stream);
- const char *end = NULL;
- RyanJson_t first = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end);
- TEST_ASSERT_NOT_NULL_MESSAGE(first, "文档1 解析应成功");
- TEST_ASSERT_NOT_NULL(end);
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(first, "a")));
- uint32_t remain = (uint32_t)(len - (uint32_t)(end - stream));
- UNITY_TEST_OOM_BEGIN(0);
- RyanJson_t secondFail = RyanJsonParseOptions(end, remain, RyanJsonTrue, NULL);
- UNITY_TEST_OOM_END();
- TEST_ASSERT_NULL_MESSAGE(secondFail, "OOM 注入下文档2 解析应失败");
- RyanJson_t second = RyanJsonParseOptions(end, remain, RyanJsonTrue, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(second, "恢复 hooks 后文档2 应可解析成功");
- TEST_ASSERT_EQUAL_INT(2, RyanJsonGetIntValue(RyanJsonGetObjectToKey(second, "b")));
- // 再确认文档1 不受 OOM 链路污染。
- TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(first, "a")));
- RyanJsonDelete(second);
- RyanJsonDelete(first);
- }
- static void testLoadFailureInvalidUtf8AndEmbeddedNull(void)
- {
- // 复杂输入场景:
- // - 非法 UTF-8 字节序列(截断、错误续字节、过长编码);
- // - String 内容中内嵌 NUL(\0)字节。
- // 目标:
- // - 记录当前实现对“非法 UTF-8 原始字节”的行为:按普通字节透传,不在 Parse 阶段拦截;
- // - 验证内嵌 NUL(控制字符)仍会严格失败,防止混入不可见截断字节。
- const uint8_t invalidUtf8Truncated2[] = {'{', '"', 's', '"', ':', '"', 0xC2, '"', '}', '\0'};
- const uint8_t invalidUtf8BadContinuation[] = {'{', '"', 's', '"', ':', '"', 0xE2, 0x28, 0xA1, '"', '}', '\0'};
- const uint8_t invalidUtf8Overlong[] = {'{', '"', 's', '"', ':', '"', 0xC0, 0x80, '"', '}', '\0'};
- const uint8_t invalidUtf8Truncated4[] = {'{', '"', 's', '"', ':', '"', 0xF0, 0x9F, 0x92, '"', '}', '\0'};
- {
- RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8Truncated2, (uint32_t)(sizeof(invalidUtf8Truncated2) - 1U),
- RyanJsonTrue, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,截断 2 字节 UTF-8 原始字节应被透传解析");
- char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
- TEST_ASSERT_NOT_NULL(s);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0xC2, (uint8_t)s[0]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[1]);
- RyanJsonDelete(doc);
- }
- {
- RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8BadContinuation,
- (uint32_t)(sizeof(invalidUtf8BadContinuation) - 1U), RyanJsonTrue, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,错误续字节 UTF-8 原始字节应被透传解析");
- char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
- TEST_ASSERT_NOT_NULL(s);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0xE2, (uint8_t)s[0]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x28, (uint8_t)s[1]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0xA1, (uint8_t)s[2]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[3]);
- RyanJsonDelete(doc);
- }
- {
- RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8Overlong, (uint32_t)(sizeof(invalidUtf8Overlong) - 1U),
- RyanJsonTrue, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,过长编码 UTF-8 原始字节应被透传解析");
- char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
- TEST_ASSERT_NOT_NULL(s);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0xC0, (uint8_t)s[0]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x80, (uint8_t)s[1]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[2]);
- RyanJsonDelete(doc);
- }
- {
- RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8Truncated4, (uint32_t)(sizeof(invalidUtf8Truncated4) - 1U),
- RyanJsonTrue, NULL);
- TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,截断 4 字节 UTF-8 原始字节应被透传解析");
- char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
- TEST_ASSERT_NOT_NULL(s);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0xF0, (uint8_t)s[0]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x9F, (uint8_t)s[1]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x92, (uint8_t)s[2]);
- TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[3]);
- RyanJsonDelete(doc);
- }
- const uint8_t embeddedNulInString[] = {'{', '"', 's', '"', ':', '"', 'a', 'b', '\0', 'c', 'd', '"', '}', '\0'};
- TEST_ASSERT_NULL_MESSAGE(
- RyanJsonParseOptions((const char *)embeddedNulInString, (uint32_t)(sizeof(embeddedNulInString) - 1U), RyanJsonTrue, NULL),
- "String 内嵌 NUL 且 strict 终止时应解析失败");
- TEST_ASSERT_NULL_MESSAGE(
- RyanJsonParseOptions((const char *)embeddedNulInString, (uint32_t)(sizeof(embeddedNulInString) - 1U), RyanJsonFalse, NULL),
- "String 内嵌 NUL 且流式解析时也应解析失败");
- }
- void testLoadFailureRunner(void)
- {
- UnitySetTestFile(__FILE__);
- RUN_TEST(testLoadFailureNullInput);
- RUN_TEST(testLoadFailureWhitespaceOnlySlice);
- RUN_TEST(testLoadFailureHugeNumberOverflow);
- RUN_TEST(testLoadFailureExponentAccumulatorOverflow);
- RUN_TEST(testLoadParseOptionsFailure);
- RUN_TEST(testLoadParseOptionsSequentialValidThenInvalidIsolation);
- RUN_TEST(testLoadParseOptionsBinaryTailFailureIsolation);
- RUN_TEST(testLoadParseOptionsSequentialInvalidThenTailResync);
- RUN_TEST(testLoadParseOptionsFailureKeepsEndPtrStable);
- RUN_TEST(testLoadFailureOomParse);
- RUN_TEST(testLoadFailureAllocatedKeyCleanupOnValueError);
- RUN_TEST(testLoadParseOptionsSequentialOomRecovery);
- RUN_TEST(testLoadFailureInvalidUtf8AndEmbeddedNull);
- }
|