Ver Fonte

test: 优化测试代码

RyanCW há 4 dias atrás
pai
commit
c02b973431

+ 2 - 1
.clang-format-ignore

@@ -1,2 +1,3 @@
 # 忽略外部包
-externalModule/*
+/test/externalModule/cJSON/**
+/test/externalModule/yyjson/**

+ 33 - 16
Makefile

@@ -1,28 +1,45 @@
 
-CFLAGS_INC = -I RyanJson
-CFLAGS_INC +=  -I cJSON
-CFLAGS_INC +=  -I yyjson
-CFLAGS_INC +=  -I RyanJsonExample/valloc
-CFLAGS_INC +=  -I RyanJsonExample
+# 编译器设置
+CC = gcc
+C_FLAGS = -std=gnu99 -O2 -Wall -Wextra -Wno-unused-parameter
 
+# 头文件包含目录
+CFLAGS_INC = -I ./RyanJson
+CFLAGS_INC += -I ./example
+CFLAGS_INC += -I ./test
+CFLAGS_INC += -I ./test/baseTest
+CFLAGS_INC += -I ./test/externalModule/valloc
+CFLAGS_INC += -I ./test/externalModule/tlsf
+CFLAGS_INC += -I ./test/externalModule/cJSON
+CFLAGS_INC += -I ./test/externalModule/yyjson
 
+# 源文件扫描 (排除 fuzzer)
 src = $(wildcard ./RyanJson/*.c)
-src += $(wildcard ./cJSON/*.c)
-src += $(wildcard ./yyjson/*.c)
-src += $(wildcard ./RyanJsonExample/valloc/*.c)
-src += $(wildcard ./RyanJsonExample/*.c)
+src += $(wildcard ./example/*.c)
+src += $(wildcard ./test/*.c)
+src += $(wildcard ./test/baseTest/*.c)
+src += $(wildcard ./test/externalModule/valloc/*.c)
+src += $(wildcard ./test/externalModule/tlsf/*.c)
+src += $(wildcard ./test/externalModule/cJSON/*.c)
+src += $(wildcard ./test/externalModule/yyjson/*.c)
 
-obj = $(patsubst %.c, %.o, $(src))
-target = app.o
-CC = gcc
-C_FLAGS = -Wall -Wextra -Wno-unused-parameter -Wformat=2
+# 中间对象
+obj = $(src:.c=.o)
+
+# 目标程序 - 修改名字避免与源码文件夹 RyanJson 重名
+target = app
+
+# 默认规则
+all: $(target)
 
 $(target): $(obj)
-	$(CC) $(CFLAGS_INC) $(obj) $(C_FLAGS) -o $(target) -lm
+	$(CC) $(obj) $(C_FLAGS) -o $(target) -lm
 
+# 编译模式规则
 %.o: %.c
-	$(CC) $(CFLAGS_INC)  $(C_FLAGS) -c $< -o $@ -lm
+	$(CC) $(CFLAGS_INC) $(C_FLAGS) -c $< -o $@
 
+# 清理规则
 .PHONY: clean
 clean:
-	rm -rf $(obj) $(target)
+	rm -f $(obj) $(target)

+ 12 - 28
example/RyanJsonExample.c

@@ -1,20 +1,5 @@
 #include "RyanJson.h"
 #include "RyanJsonUtils.h"
-#include "valloc.h"
-
-static inline RyanJsonBool_e RyanJsonCompareDouble(double a, double b)
-{
-	double diff = fabs(a - b);
-	double absA = fabs(a);
-	double absB = fabs(b);
-	double maxVal = (absA > absB ? absA : absB);
-
-	// 允许的容差:相对误差 + 绝对误差
-	double epsilon = DBL_EPSILON * maxVal;
-	double minTolerance = 1e-12; // 可调的绝对容差
-
-	return diff <= (epsilon > minTolerance ? epsilon : minTolerance);
-}
 
 /**
  * @brief 生成json示例
@@ -97,7 +82,6 @@ static RyanJsonBool_e createJsonExample(void)
 	RyanJsonFree(str);
 
 	RyanJsonDelete(jsonRoot);
-
 	return RyanJsonTrue;
 }
 
@@ -109,19 +93,19 @@ static RyanJsonBool_e createJsonExample(void)
 static RyanJsonBool_e loadJsonExample(void)
 {
 	char *str = NULL;
-	RyanJson_t jsonRoot;
-	const char jsonstr[] = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
-			       "\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
-			       "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,"
-			       "16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\","
-			       "\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{"
-			       "\"inter\":16,\"double\":16.89,\"string\":\"hello\","
-			       "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
-			       "\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]}";
+	RyanJson_t jsonRoot = NULL;
+	const char *jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,"
+			      "\"item\":{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
+			      "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,"
+			      "16.89,16.89,16.89,16.89],\"arrayString\":[\"hello\","
+			      "\"hello\",\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{"
+			      "\"inter\":16,\"double\":16.89,\"string\":\"hello\","
+			      "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\","
+			      "\"boolTrue\":true,\"boolFalse\":false,\"null\":null}]}";
 
 	// 解析json数据
 	jsonRoot = RyanJsonParse(jsonstr);
-	if (jsonRoot == NULL)
+	if (NULL == jsonRoot)
 	{
 		printf("%s:%d 序列化失败\r\n", __FILE__, __LINE__);
 		return RyanJsonFalse;
@@ -275,7 +259,7 @@ static RyanJsonBool_e changeJsonExample(void)
 
 RyanJsonBool_e RyanJsonExample(void)
 {
-	RyanJsonInitHooks(v_malloc, v_free, v_realloc);
+	RyanJsonInitHooks(malloc, free, NULL);
 
 	printf("\r\n--------------------------- RyanJson 生成示例 --------------------------\r\n");
 	RyanJsonCheckReturnFalse(RyanJsonTrue == createJsonExample());
@@ -286,7 +270,7 @@ RyanJsonBool_e RyanJsonExample(void)
 	printf("\r\n--------------------------- RyanJson 修改json示例 --------------------------\r\n");
 	RyanJsonCheckReturnFalse(RyanJsonTrue == changeJsonExample());
 
-    // 更多功能请查看 RyanJson.h 文件,不了解的可以查看 test/baseTest 下的文件
+	// 更多功能请查看 RyanJson.h 文件,不了解的可以查看 test/baseTest 下的文件
 
 	return RyanJsonTrue;
 }

+ 37 - 0
run_base_coverage.sh

@@ -0,0 +1,37 @@
+#!/bin/bash
+set -e  # 遇到错误立即退出
+
+xmake
+echo "xmake build success"
+
+# ================================
+# 1. 运行
+# ================================
+./build/linux/x86/release/RyanJson
+
+
+# ================================
+# 2. 合并 profile 数据
+# ================================
+llvm-profdata merge -sparse default.profraw -o default.profdata
+
+# ================================
+# 3. 生成覆盖率报告(文本汇总)
+# ================================
+# 注意:llvm-cov report 只支持汇总统计,不支持行级参数
+# --show-functions       显示函数级覆盖率
+# --show-region-summary  显示区域覆盖率
+llvm-cov report ./build/linux/x86/release/RyanJson \
+  -instr-profile=default.profdata \
+  -show-mcdc-summary \
+  -sources ./RyanJson
+
+# ================================
+# 4. 生成覆盖率报告(HTML详细)
+# ================================
+llvm-cov show ./build/linux/x86/release/RyanJson \
+  -instr-profile=default.profdata \
+  -format=html \
+  -output-dir=coverage/docs \
+  -sources ./RyanJson
+#   -sources ./test/fuzzer ./RyanJson

+ 5 - 17
test/RyanJsonRFC8259JsonTest.c

@@ -74,17 +74,6 @@ static int testFile(const char *path, jsonParseData jsonParseDataHandle)
 			break;
 		}
 
-		// if (use != (len + 10))
-		// {
-		// 	int area = 0, use = 0;
-		// 	v_mcheck(&area, &use);
-		// 	free(data);
-		// 	printf("内存泄漏 %s len: %ld\r\n", data, len);
-		// 	// printf("内存泄漏 %x len: %ld\r\n", (unsigned int)data, len);
-		// 	// printf("内存泄漏 %c len: %ld\r\n", (int)data, len);
-		// 	printf("|||----------->>> area = %d, size = %d\r\n", area, use);
-		// 	break;
-		// }
 		free(data);
 	}
 
@@ -564,13 +553,13 @@ RyanJsonBool_e RFC8259JsonTest(void)
 {
 	int result = 0;
 
-	cJSON_Hooks hooks = {.malloc_fn = v_malloc, .free_fn = v_free};
+	cJSON_Hooks hooks = {.malloc_fn = v_malloc_tlsf, .free_fn = v_free_tlsf};
 	cJSON_InitHooks(&hooks);
 
-	printf("开始 RFC 8259 JSON 测试");
+#define TEST_FILE_PATH "./test/RFC8259JsonData"
 
 	printf("\r\n--------------------------- RFC8259  RyanJson --------------------------\r\n");
-	result = testFile("../../../../test//RFC8259JsonData", RyanJsonParseData);
+	result = testFile(TEST_FILE_PATH, RyanJsonParseData);
 	if (0 != result)
 	{
 		printf("%s:%d RyanJson RFC8259JsonTest fail\r\n", __FILE__, __LINE__);
@@ -578,7 +567,7 @@ RyanJsonBool_e RFC8259JsonTest(void)
 	}
 
 	printf("\r\n--------------------------- RFC8259  cJSON --------------------------\r\n");
-	result = testFile("../../../../test//RFC8259JsonData", cJSONParseData);
+	result = testFile(TEST_FILE_PATH, cJSONParseData);
 	if (0 != result)
 	{
 		printf("%s:%d cJSON RFC8259JsonTest fail\r\n", __FILE__, __LINE__);
@@ -586,14 +575,13 @@ RyanJsonBool_e RFC8259JsonTest(void)
 	}
 
 	printf("\r\n--------------------------- RFC8259  yyjson --------------------------\r\n");
-	result = testFile("../../../../test//RFC8259JsonData", yyjsonParseData);
+	result = testFile(TEST_FILE_PATH, yyjsonParseData);
 	if (0 != result)
 	{
 		printf("%s:%d yyjson RFC8259JsonTest fail\r\n", __FILE__, __LINE__);
 		goto err;
 	}
 
-	displayMem();
 	return RyanJsonTrue;
 
 err:

+ 42 - 17
test/RyanJsonTest.c

@@ -17,50 +17,60 @@ static void printfTitle(char *title)
 
 static tlsf_t tlsfHandler;
 
-static int32_t total2 = LV_MEM_SIZE, used2 = 0, available = 0;
+static size_t total2 = LV_MEM_SIZE, used2 = 0, available = 0;
 bool tlsf_walker_callback(void *ptr, size_t size, int used, void *user)
 {
+	(void)ptr;
+	(void)user; // suppress unused warnings
 	if (1 == used) { used2 += size + tlsf_alloc_overhead(); }
 	return true;
 }
 
 void showMemoryInfo(void)
 {
-	int32_t total = 0, used = 0, max_used = 0;
+	size_t total = 0, used = 0, max_used = 0;
 	rt_memory_info22(tlsfHandler, &total, &used, &max_used);
-	jsonLogByTest("total: %d, used: %d, max_used: %d, available: %d\r\n", total, used, max_used, total - used);
+	jsonLogByTest("total: %zu, used: %zu, max_used: %zu, available: %zu\r\n", total, used, max_used, total - used);
 
-	used2 = 0, available = 0;
+	used2 = 0;
+	available = 0;
 	total2 = total;
 	tlsf_walk_pool(tlsf_get_pool(tlsfHandler), tlsf_walker_callback, NULL);
-	jsonLogByTest("total2: %d, used2: %d, max_used2: %d, available2: %d\r\n", total2, used2, 0, total2 - used2);
+	jsonLogByTest("total2: %zu, used2: %zu, max_used2: %d, available2: %zu\r\n", total2, used2, 0, total2 - used2);
 }
 
 int32_t vallocGetUseByTlsf(void)
 {
-	int32_t total = 0, used = 0, max_used = 0;
+	size_t total = 0, used = 0, max_used = 0;
 	rt_memory_info22(tlsfHandler, &total, &used, &max_used);
-	return used;
+	return (int32_t)used;
 }
 
+#define RyanJsonAlign(size, align) (((size) + (align) - 1) & ~((align) - 1))
 void *v_malloc_tlsf(size_t size)
 {
 	if (size == 0) { return NULL; }
 
-	return tlsf_malloc(tlsfHandler, size);
+	return tlsf_malloc(tlsfHandler, RyanJsonAlign(size + RyanJsonMallocHeaderSize - 4, RyanJsonMallocAlign));
 }
 
 void v_free_tlsf(void *block)
 {
-	if (!block) { return; }
-
-	tlsf_free(tlsfHandler, block);
+	if (block) { tlsf_free(tlsfHandler, block); }
 }
 
-void *v_realloc_tlsf(void *block, size_t size) { return tlsf_realloc(tlsfHandler, block, size); }
+void *v_realloc_tlsf(void *block, size_t size)
+{
+	void *newBlock = v_malloc_tlsf(size);
+	if (newBlock) { memmove(newBlock, block, size); }
 
-#ifndef isEnableFuzzer
-int main(void)
+	v_free_tlsf(block);
+	return newBlock;
+
+	// return tlsf_realloc(tlsfHandler, block, size);
+}
+
+RyanJsonBool_e RyanJsonTestFun(void)
 {
 	char *tlsfMemBuf = v_malloc(LV_MEM_SIZE);
 	tlsfHandler = tlsf_create_with_pool((void *)tlsfMemBuf, LV_MEM_SIZE, LV_MEM_SIZE);
@@ -101,22 +111,29 @@ int main(void)
 
 	showMemoryInfo();
 
+	// example内部会替换hooks,所以需要重新设置
 	result = RyanJsonExample();
+	RyanJsonInitHooks(v_malloc_tlsf, v_free_tlsf, v_realloc_tlsf);
 	if (RyanJsonTrue != result)
 	{
 		printf("%s:%d RyanJsonExample fail\r\n", __FILE__, __LINE__);
-		return -1;
+		return RyanJsonFalse;
 	}
 
 	result = RyanJsonBaseTest();
 	if (RyanJsonTrue != result)
 	{
 		printf("%s:%d RyanJsonBaseTest fail\r\n", __FILE__, __LINE__);
-		return -1;
+		return RyanJsonFalse;
 	}
 
 	printfTitle("RyanJson / cJSON / yyjson RFC8259标准测试");
-	RFC8259JsonTest();
+	result = RFC8259JsonTest();
+	if (RyanJsonTrue != result)
+	{
+		printf("%s:%d RFC8259JsonTest fail\r\n", __FILE__, __LINE__);
+		return RyanJsonFalse;
+	}
 
 	printfTitle("RyanJson / cJSON / yyjson 内存对比程序");
 	RyanJsonMemoryFootprintTest();
@@ -125,6 +142,14 @@ int main(void)
 	showMemoryInfo();
 	v_free(tlsfMemBuf);
 	displayMem();
+
+	return RyanJsonTrue;
+}
+
+#ifndef isEnableFuzzer
+int main(void)
+{
+	RyanJsonTestFun();
 	return 0;
 }
 

+ 1 - 0
test/RyanJsonTest.h

@@ -50,6 +50,7 @@ extern RyanJsonBool_e RyanJsonExample(void);
 extern RyanJsonBool_e RyanJsonBaseTest(void);
 extern RyanJsonBool_e RFC8259JsonTest(void);
 extern RyanJsonBool_e RyanJsonMemoryFootprintTest(void);
+extern RyanJsonBool_e RyanJsonTestFun(void);
 #ifdef __cplusplus
 }
 #endif

+ 2 - 0
test/baseTest/RyanJsonBaseTest.h

@@ -16,6 +16,8 @@ extern "C" {
 #include "cJSON.h"
 #include "valloc.h"
 #include "RyanJsonTest.h"
+
+#undef jsonLog
 #define jsonLog(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
 
 // 定义枚举类型

+ 25 - 0
test/baseTest/RyanJsonBaseTestCompareJson.c

@@ -20,65 +20,79 @@ RyanJsonBool_e RyanJsonBaseTestCompareJson(void)
 
 	// 比较函数
 	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompare(json, json), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddStringToObject(json2, "test", "hello");
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddIntToObject(json2, "test", 1);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddDoubleToObject(json2, "test", 2.0);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddBoolToObject(json2, "test", RyanJsonTrue);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddNullToObject(json2, "test");
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddIntToArray(RyanJsonGetObjectToKey(json2, "arrayInt"), 2);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddDoubleToArray(RyanJsonGetObjectToKey(json2, "arrayDouble"), 2.0);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddStringToArray(RyanJsonGetObjectToKey(json2, "arrayString"), "hello");
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonAddItemToArray(RyanJsonGetObjectToKey(json2, "arrayItem"), RyanJsonCreateString("test", "hello"));
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeKey(RyanJsonGetObjectToKey(json2, "inter"), "int2");
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeIntValue(RyanJsonGetObjectToKey(json2, "inter"), 17);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeDoubleValue(RyanJsonGetObjectToKey(json2, "double"), 20.89);
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 	if (RyanJsonFalse != RyanJsonCompare(json, json2))
 	{
 		printf("%s:%d 解析失败\r\n", __FILE__, __LINE__);
@@ -90,26 +104,31 @@ RyanJsonBool_e RyanJsonBaseTestCompareJson(void)
 	RyanJsonDelete(RyanJsonDetachByKey(json2, "double"));
 	RyanJsonAddIntToObject(json2, "double", 20); // 改为int
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeStringValue(RyanJsonGetObjectToKey(json2, "string"), "49");
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json2, "boolTrue"), RyanJsonFalse);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeBoolValue(RyanJsonGetObjectToKey(json2, "item", "boolTrue"), RyanJsonFalse);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayInt"), 0), 17);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
@@ -120,32 +139,38 @@ RyanJsonBool_e RyanJsonBaseTestCompareJson(void)
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeStringValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayString"), 0), "20.89");
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeIntValue(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "array"), 0), 17);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonChangeIntValue(RyanJsonGetObjectToKey(RyanJsonGetObjectToIndex(RyanJsonGetObjectToKey(json2, "arrayItem"), 0), "inter"),
 			       17);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonTrue == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonDeleteByKey(json2, "arrayItem");
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonDeleteByIndex(RyanJsonGetObjectToKey(json2, "arrayInt"), 2);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json2);
 	json2 = RyanJsonParse(jsonstr);
 	RyanJsonDeleteByIndex(RyanJsonGetObjectToKey(json2, "arrayItem"), 0);
 	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompare(json, json2), { goto err; });
+	RyanJsonCheckCode(RyanJsonFalse == RyanJsonCompareOnlyKey(json, json2), { goto err; });
 
 	RyanJsonDelete(json);
 	RyanJsonDelete(json2);

+ 78 - 11
test/baseTest/RyanJsonBaseTestLoadJson.c

@@ -7,17 +7,17 @@ RyanJsonBool_e RyanJsonBaseTestLoadJson(void)
 {
 	char *str = NULL;
 	RyanJson_t json;
-	char jsonstr[] = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
-			 "{\"inter\":16,\"double\":16."
-			 "89,\"string\":\"hello\","
-			 "\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,"
-			 "16.89,16.89,16.89],"
-			 "\"arrayString\":[\"hello\",\"hello\","
-			 "\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
-			 "\"double\":16.89,\"string\":"
-			 "\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
-			 "\"boolFalse\":false,\"null\":null}]}";
+	char *jsonstr = "{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"item\":"
+			"{\"inter\":16,\"double\":16."
+			"89,\"string\":\"hello\","
+			"\"boolTrue\":true,\"boolFalse\":false,\"null\":null},\"arrayInt\":[16,16,16,16,16],\"arrayDouble\":[16.89,16.89,"
+			"16.89,16.89,16.89],"
+			"\"arrayString\":[\"hello\",\"hello\","
+			"\"hello\",\"hello\",\"hello\"],\"array\":[16,16.89,\"hello\",true,false,null],\"arrayItem\":[{\"inter\":16,"
+			"\"double\":16.89,\"string\":"
+			"\"hello\",\"boolTrue\":true,"
+			"\"boolFalse\":false,\"null\":null},{\"inter\":16,\"double\":16.89,\"string\":\"hello\",\"boolTrue\":true,"
+			"\"boolFalse\":false,\"null\":null}],\"unicode\":\"😀\"}";
 
 	json = RyanJsonParse(jsonstr);
 	RyanJsonCheckReturnFalse(NULL != json);
@@ -38,6 +38,73 @@ RyanJsonBool_e RyanJsonBaseTestLoadJson(void)
 
 	RyanJsonDelete(json);
 
+	/**
+	 * @brief 测试 Unicode
+	 *
+	 */
+	char printfBuf[1024] = {0};
+	json = RyanJsonParse("{\"emoji\":\"\\uD83D\\uDE00\"}");
+	RyanJsonCheckReturnFalse(NULL != json);
+	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
+	RyanJsonDelete(json);
+
+	// 测试数字 0-9 分支: \u0030 = '0', \u0039 = '9'
+	json = RyanJsonParse("{\"num\":\"\\u0030\\u0039\"}");
+	RyanJsonCheckReturnFalse(NULL != json);
+	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
+	RyanJsonCheckCode(0 == strcmp(str, "{\"num\":\"09\"}"), {
+		RyanJsonDelete(json);
+		return RyanJsonFalse;
+	});
+	RyanJsonDelete(json);
+
+	// 测试小写 a-f 分支: \u0061 = 'a', \u0066 = 'f'
+	json = RyanJsonParse("{\"lower\":\"\\u0061\\u0062\\u0063\\u0064\\u0065\\u0066\"}");
+	RyanJsonCheckReturnFalse(NULL != json);
+	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
+	RyanJsonCheckCode(0 == strcmp(str, "{\"lower\":\"abcdef\"}"), {
+		RyanJsonDelete(json);
+		return RyanJsonFalse;
+	});
+	RyanJsonDelete(json);
+
+	// 测试大写 A-F 分支: \u0041 = 'A', \u0046 = 'F'
+	json = RyanJsonParse("{\"upper\":\"\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\"}");
+	RyanJsonCheckReturnFalse(NULL != json);
+	str = RyanJsonPrintPreallocated(json, printfBuf, sizeof(printfBuf), RyanJsonFalse, NULL);
+	RyanJsonCheckCode(0 == strcmp(str, "{\"upper\":\"ABCDEF\"}"), {
+		RyanJsonDelete(json);
+		return RyanJsonFalse;
+	});
+	RyanJsonDelete(json);
+
+	// 测试混合大小写: \uAbCd (混合大小写十六进制)
+	json = RyanJsonParse("{\"mixed\":\"\\uAbCd\"}");
+	RyanJsonCheckReturnFalse(NULL != json);
+	RyanJsonFree(RyanJsonPrint(json, 50, RyanJsonFalse, NULL));
+	RyanJsonDelete(json);
+
+	// 测试 default 分支 (非法十六进制字符 'G')
+	json = RyanJsonParse("{\"invalid\":\"\\uGGGG\"}");
+	RyanJsonCheckCode(NULL == json, {
+		RyanJsonDelete(json);
+		return RyanJsonFalse;
+	});
+
+	// 测试 default 分支 (非法十六进制字符 'Z')
+	json = RyanJsonParse("{\"invalid\":\"\\u00ZZ\"}");
+	RyanJsonCheckCode(NULL == json, {
+		RyanJsonDelete(json);
+		return RyanJsonFalse;
+	});
+
+	// 测试 default 分支 (非法十六进制字符 '!')
+	json = RyanJsonParse("{\"invalid\":\"\\u00!!\"}");
+	RyanJsonCheckCode(NULL == json, {
+		RyanJsonDelete(json);
+		return RyanJsonFalse;
+	});
+
 	/**
 	 * @brief 测试序列化错误json结构
 	 *

+ 1 - 1
test/fuzzer/RyanJsonFuzzer.dict

@@ -212,6 +212,7 @@
 # 循环/深度嵌套场景
 "{\"a\":{\"b\":{\"c\":{\"d\":{\"e\":{\"f\":{\"g\":true}}}}}}}"
 "[[[[[[[[[[1]]]]]]]]]]"
+"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"
 "[{\"a\":[{\"b\":[{\"c\":[{\"d\":true}]}]}]}]"
 "{\"a\":[{\"b\":[{\"c\":[{\"d\":[{\"e\":false}]}]}]}]}"
 "{\"node\":{\"id\":1,\"child\":{\"id\":2,\"child\":{\"id\":3,\"child\":{\"id\":4}}}}}"
@@ -231,7 +232,6 @@
 ",{}"
 ":[]"
 "[[]]"
-"{{}}"
 ",[]"
 ":{}"
 "''"

+ 12 - 10
xmake.lua

@@ -15,8 +15,10 @@ target("RyanJson", function()
     -- 定义宏:启用 Fuzzer 功能
     -- Fuzzer 与覆盖率相关编译/链接选项
     -- add_defines("isEnableFuzzer")
-    -- add_cxflags("-fsanitize=fuzzer", "-fprofile-instr-generate", "-fcoverage-mapping", {force = true})
-    -- add_ldflags("-fsanitize=fuzzer", "-fprofile-instr-generate", "-fcoverage-mapping", {force = true})
+    -- add_cxflags("-fsanitize=fuzzer", {force = true})
+    -- add_ldflags("-fsanitize=fuzzer", {force = true})
+    add_cxflags("-fprofile-instr-generate", "-fcoverage-mapping", {force = true})
+    add_ldflags("-fprofile-instr-generate", "-fcoverage-mapping", {force = true})
 
     -- 编译优化策略
     set_policy("build.ccache", false) -- 禁用 ccache 缓存
@@ -123,20 +125,20 @@ target("RyanJson", function()
     add_includedirs('./example', {public = true})
     add_includedirs('./test/fuzzer', {public = true})
     add_includedirs('./test', {public = true})
-    add_includedirs('./test/valloc', {public = true})
-    add_includedirs('./test/tlsf', {public = true})
     add_includedirs('./test/baseTest', {public = true})
-    add_includedirs('./externalModule/cJSON', {public = true})
-    add_includedirs('./externalModule/yyjson', {public = true})
+    add_includedirs('./test/externalModule/valloc', {public = true})
+    add_includedirs('./test/externalModule/tlsf', {public = true})
+    add_includedirs('./test/externalModule/cJSON', {public = true})
+    add_includedirs('./test/externalModule/yyjson', {public = true})
 
     -- 源文件分开列出,保持清晰结构
     add_files('./RyanJson/*.c', {public = true})
     add_files('./example/*.c', {public = true})
     add_files('./test/fuzzer/*.c', {public = true})
     add_files('./test/*.c', {public = true}, {cxflags = "-w"})          -- 测试代码,关闭警告
-    add_files('./test/valloc/*.c', {public = true}, {cxflags = "-w"})   -- valloc 测试,关闭警告
-    add_files('./test/tlsf/*.c', {public = true}, {cxflags = "-w"})   -- valloc 测试,关闭警告
     add_files('./test/baseTest/*.c', {public = true}, {cxflags = "-w"}) -- 基础测试,关闭警告
-    add_files('./externalModule/cJSON/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 cJSON,关闭警告
-    add_files('./externalModule/yyjson/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 yyjson,关闭警告
+    add_files('./test/externalModule/valloc/*.c', {public = true}, {cxflags = "-w"})   -- valloc,关闭警告
+    add_files('./test/externalModule/tlsf/*.c', {public = true}, {cxflags = "-w"})     -- tlsf,关闭警告
+    add_files('./test/externalModule/cJSON/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 cJSON,关闭警告
+    add_files('./test/externalModule/yyjson/*.c', {public = true}, {cxflags = "-w"}) -- 第三方库 yyjson,关闭警告
 end)