testRfc8259.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. #include "testCommon.h"
  2. #include "valloc.h"
  3. #if defined(RyanJsonTestPlatformQemu)
  4. void testRfc8259Runner(void)
  5. {
  6. UnitySetTestFile(__FILE__);
  7. }
  8. #else
  9. #include <dirent.h>
  10. #include <errno.h>
  11. #define PrintfStrCmpEnable
  12. #ifndef RyanJsonProjectRootPath
  13. #define RyanJsonProjectRootPath "."
  14. #endif
  15. #define RFC8259TestFilePath RyanJsonProjectRootPath "/test/data/rfc8259"
  16. #define RFC8259TotalCaseCount 322U
  17. #define RFC8259RyanJsonPassCountStrict 320U
  18. #define RFC8259RyanJsonPassCountNonStrict 322U
  19. typedef RyanJsonBool_e (*jsonParseData)(char *fileName, char *data, uint32_t len);
  20. typedef struct
  21. {
  22. char **fileNames;
  23. uint32_t fileCount;
  24. } rfc8259FileList_t;
  25. static void freeRfc8259FileList(rfc8259FileList_t *fileList)
  26. {
  27. if (NULL == fileList) { return; }
  28. if (NULL != fileList->fileNames)
  29. {
  30. for (uint32_t i = 0U; i < fileList->fileCount; ++i)
  31. {
  32. free(fileList->fileNames[i]);
  33. }
  34. free(fileList->fileNames);
  35. }
  36. fileList->fileNames = NULL;
  37. fileList->fileCount = 0U;
  38. }
  39. static RyanJsonBool_e hasJsonSuffix(const char *fileName)
  40. {
  41. size_t fileNameLen = 0U;
  42. static const size_t jsonSuffixLen = sizeof(".json") - 1U;
  43. if (NULL == fileName) { return RyanJsonFalse; }
  44. fileNameLen = strlen(fileName);
  45. if (fileNameLen < jsonSuffixLen) { return RyanJsonFalse; }
  46. return (0 == strcmp(fileName + fileNameLen - jsonSuffixLen, ".json")) ? RyanJsonTrue : RyanJsonFalse;
  47. }
  48. static int compareFileNameAsc(const void *left, const void *right)
  49. {
  50. const char *leftName = *(const char *const *)left;
  51. const char *rightName = *(const char *const *)right;
  52. if (NULL == leftName && NULL == rightName) { return 0; }
  53. if (NULL == leftName) { return -1; }
  54. if (NULL == rightName) { return 1; }
  55. return strcmp(leftName, rightName);
  56. }
  57. static RyanJsonBool_e collectRfc8259FileList(const char *path, rfc8259FileList_t *fileListOut)
  58. {
  59. DIR *dir = NULL;
  60. struct dirent *entry = NULL;
  61. int32_t scanErrno = 0;
  62. uint32_t fileCapacity = 0U;
  63. rfc8259FileList_t fileList = {NULL, 0U};
  64. if (NULL == path || NULL == fileListOut) { return RyanJsonFalse; }
  65. errno = 0;
  66. dir = opendir(path);
  67. if (NULL == dir)
  68. {
  69. (void)testLog("打开 RFC8259 目录失败: %s\n", path);
  70. return RyanJsonFalse;
  71. }
  72. while (NULL != (entry = readdir(dir)))
  73. {
  74. const char *fileName = entry->d_name;
  75. char *nameCopy = NULL;
  76. if (NULL == fileName) { continue; }
  77. if (0 == strcmp(fileName, ".") || 0 == strcmp(fileName, "..")) { continue; }
  78. if (RyanJsonTrue != hasJsonSuffix(fileName)) { continue; }
  79. if (fileList.fileCount == fileCapacity)
  80. {
  81. uint32_t newCapacity = (0U == fileCapacity) ? 64U : (fileCapacity * 2U);
  82. char **newFileNames = (char **)realloc(fileList.fileNames, (size_t)newCapacity * sizeof(char *));
  83. if (NULL == newFileNames)
  84. {
  85. (void)closedir(dir);
  86. freeRfc8259FileList(&fileList);
  87. return RyanJsonFalse;
  88. }
  89. fileList.fileNames = newFileNames;
  90. fileCapacity = newCapacity;
  91. }
  92. nameCopy = (char *)malloc(strlen(fileName) + 1U);
  93. if (NULL == nameCopy)
  94. {
  95. (void)closedir(dir);
  96. freeRfc8259FileList(&fileList);
  97. return RyanJsonFalse;
  98. }
  99. (void)strcpy(nameCopy, fileName);
  100. fileList.fileNames[fileList.fileCount++] = nameCopy;
  101. }
  102. scanErrno = errno;
  103. (void)closedir(dir);
  104. if (0 != scanErrno)
  105. {
  106. freeRfc8259FileList(&fileList);
  107. return RyanJsonFalse;
  108. }
  109. if (fileList.fileCount > 1U) { qsort(fileList.fileNames, fileList.fileCount, sizeof(char *), compareFileNameAsc); }
  110. *fileListOut = fileList;
  111. return RyanJsonTrue;
  112. }
  113. /* Read a file, parse, render back, etc. */
  114. static void testFile(const char *path, jsonParseData jsonParseDataHandle, const char *libName, uint32_t *passCountOut,
  115. uint32_t *totalCountOut)
  116. {
  117. uint32_t passCount = 0U;
  118. uint32_t usedCount = 0U;
  119. rfc8259FileList_t fileList = {NULL, 0U};
  120. if (RyanJsonTrue != collectRfc8259FileList(path, &fileList))
  121. {
  122. TEST_FAIL_MESSAGE("RFC8259 扫描目录失败");
  123. return;
  124. }
  125. // 初始缓冲区
  126. uint32_t bufferCap = 4096U;
  127. char *data = (char *)malloc(bufferCap);
  128. if (NULL == data)
  129. {
  130. freeRfc8259FileList(&fileList);
  131. TEST_FAIL_MESSAGE("内存分配失败 (RFC8259 数据缓冲区)");
  132. return;
  133. }
  134. for (uint32_t i = 0U; i < fileList.fileCount; ++i)
  135. {
  136. char *name = fileList.fileNames[i];
  137. if (NULL == name || 0 == strlen(name)) { continue; }
  138. char fullPath[512] = {0};
  139. int32_t ret = snprintf(fullPath, sizeof(fullPath), "%s/%s", path, name);
  140. if (ret < 0 || ret >= (int32_t)sizeof(fullPath)) { continue; }
  141. FILE *f = fopen(fullPath, "rb");
  142. if (NULL == f)
  143. {
  144. (void)testLog("打开文件失败: %s\n", fullPath);
  145. continue;
  146. }
  147. if (0 != fseek(f, 0, SEEK_END))
  148. {
  149. (void)fclose(f);
  150. continue;
  151. }
  152. long fileSize = ftell(f);
  153. if (fileSize < 0)
  154. {
  155. (void)fclose(f);
  156. continue;
  157. }
  158. uint32_t len = (uint32_t)fileSize;
  159. if (0 != fseek(f, 0, SEEK_SET))
  160. {
  161. (void)fclose(f);
  162. continue;
  163. }
  164. // 必要时自动扩容
  165. if (len + 1 > bufferCap)
  166. {
  167. bufferCap = len + 128; // 预留一点空间
  168. char *newData = (char *)realloc(data, bufferCap);
  169. if (NULL == newData)
  170. {
  171. (void)fclose(f);
  172. break;
  173. }
  174. data = newData;
  175. }
  176. if (len != fread(data, 1, len, f))
  177. {
  178. (void)fclose(f);
  179. continue;
  180. }
  181. data[len] = '\0';
  182. (void)fclose(f);
  183. int32_t startUse = unityTestGetUse();
  184. RyanJsonBool_e status = jsonParseDataHandle(name, data, len);
  185. usedCount++;
  186. // 判定逻辑优化
  187. if (0 == strncmp("y_", name, 2))
  188. {
  189. if (RyanJsonTrue == status) { passCount++; }
  190. else
  191. {
  192. (void)testLog("RFC8259 期望成功但失败: %s, 内容: %s\n", name, data);
  193. }
  194. }
  195. else if (0 == strncmp("n_", name, 2))
  196. {
  197. if (RyanJsonFalse == status) { passCount++; }
  198. else
  199. {
  200. (void)testLog("RFC8259 期望失败但成功: %s, 内容: %s\n", name, data);
  201. }
  202. }
  203. else if (0 == strncmp("i_", name, 2)) { passCount++; }
  204. // 内存泄漏检查
  205. if (startUse != unityTestGetUse())
  206. {
  207. (void)testLog("RFC8259 内存泄漏于文件: %s\n", name);
  208. TEST_ASSERT_EQUAL_INT_MESSAGE(startUse, unityTestGetUse(), "RFC8259 内存泄漏");
  209. }
  210. }
  211. free(data);
  212. freeRfc8259FileList(&fileList);
  213. if (NULL != passCountOut) { *passCountOut = passCount; }
  214. if (NULL != totalCountOut) { *totalCountOut = usedCount; }
  215. (void)testLog("\033[1;34m[\033[1;36m%s\033[1;34m] RFC 8259 Json 统计: \033[1;32m(%u/%u)\033[0m\r\n\r\n", libName, passCount,
  216. usedCount);
  217. }
  218. #include "testRfc8259Util.h"
  219. static void checkJsonSemanticEquality(char *data, uint32_t len, char *str, uint32_t strLen, uint32_t *errorCount)
  220. {
  221. if (0 != strcmp(data, str))
  222. {
  223. if (!RyanJsonValueSemanticEqual(data, len, str, strLen))
  224. {
  225. (*errorCount)++;
  226. (void)testLog("%d 数据不完全一致 -- 原始: %s -- 序列化: %s\n", *errorCount, data, str);
  227. }
  228. }
  229. }
  230. static RyanJsonBool_e RyanJsonParseData(char *fileName, char *data, uint32_t len)
  231. {
  232. if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json"))
  233. {
  234. return RyanJsonFalse;
  235. }
  236. RyanJson_t json = RyanJsonParseOptions(data, len, RyanJsonTrue, NULL);
  237. if (NULL == json) { return RyanJsonFalse; }
  238. #ifdef PrintfStrCmpEnable
  239. int32_t strLen = 0;
  240. char *str = RyanJsonPrint(json, 60, RyanJsonFalse, &strLen);
  241. if (NULL == str)
  242. {
  243. (void)RyanJsonDelete(json);
  244. return RyanJsonFalse;
  245. }
  246. RyanJsonMinify(data, (int32_t)len);
  247. static uint32_t semanticErrorCount = 0;
  248. checkJsonSemanticEquality(data, len, str, strLen, &semanticErrorCount);
  249. RyanJsonFree(str);
  250. #endif
  251. (void)RyanJsonDelete(json);
  252. return RyanJsonTrue;
  253. }
  254. static RyanJsonBool_e cJSONParseData(char *fileName, char *data, uint32_t len)
  255. {
  256. if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json"))
  257. {
  258. return RyanJsonFalse;
  259. }
  260. cJSON *json = cJSON_ParseWithLengthOpts(data, len + sizeof(""), NULL, RyanJsonTrue);
  261. if (NULL == json) { return RyanJsonFalse; }
  262. #ifdef PrintfStrCmpEnable
  263. char *str = cJSON_PrintBuffered(json, 60, RyanJsonFalse);
  264. if (NULL == str)
  265. {
  266. (void)cJSON_Delete(json);
  267. return RyanJsonFalse;
  268. }
  269. cJSON_Minify(data);
  270. static uint32_t semanticErrorCount = 0;
  271. checkJsonSemanticEquality(data, len, str, strlen(str), &semanticErrorCount);
  272. cJSON_free(str);
  273. #endif
  274. (void)cJSON_Delete(json);
  275. return RyanJsonTrue;
  276. }
  277. static RyanJsonBool_e yyjsonParseData(char *fileName, char *data, uint32_t len)
  278. {
  279. if (0 == strcmp(fileName, "n_structure_100000_opening_arrays.json") || 0 == strcmp(fileName, "n_structure_open_array_object.json"))
  280. {
  281. return RyanJsonFalse;
  282. }
  283. yyjson_doc *doc = yyjson_read(data, len, 0);
  284. if (NULL == doc) { return RyanJsonFalse; }
  285. #ifdef PrintfStrCmpEnable
  286. char *str = yyjson_write(doc, 0, NULL);
  287. if (NULL == str)
  288. {
  289. (void)yyjson_doc_free(doc);
  290. return RyanJsonFalse;
  291. }
  292. cJSON_Minify(data);
  293. static uint32_t semanticErrorCount = 0;
  294. checkJsonSemanticEquality(data, len, str, strlen(str), &semanticErrorCount);
  295. free(str);
  296. #endif
  297. (void)yyjson_doc_free(doc);
  298. return RyanJsonTrue;
  299. }
  300. static void testRfc8259RyanJson(void)
  301. {
  302. cJSON_Hooks hooks = {.malloc_fn = unityTestMalloc, .free_fn = unityTestFree};
  303. (void)cJSON_InitHooks(&hooks);
  304. uint32_t passCount = 0;
  305. uint32_t totalCount = 0;
  306. testFile(RFC8259TestFilePath, RyanJsonParseData, "RyanJson", &passCount, &totalCount);
  307. TEST_ASSERT_EQUAL_UINT32_MESSAGE(RFC8259TotalCaseCount, totalCount, "RFC8259 总用例数应为 322");
  308. uint32_t expectedPassCount =
  309. (true == RyanJsonStrictObjectKeyCheck) ? RFC8259RyanJsonPassCountStrict : RFC8259RyanJsonPassCountNonStrict;
  310. TEST_ASSERT_EQUAL_UINT32_MESSAGE(expectedPassCount, passCount, "RyanJson RFC8259 通过数不符合预期(非严格=322,严格=320)");
  311. }
  312. static void testRfc8259Yyjson(void)
  313. {
  314. testFile(RFC8259TestFilePath, yyjsonParseData, "yyjson", NULL, NULL);
  315. }
  316. static void testRfc8259Cjson(void)
  317. {
  318. testFile(RFC8259TestFilePath, cJSONParseData, "cJSON", NULL, NULL);
  319. }
  320. void testRfc8259Runner(void)
  321. {
  322. UnitySetTestFile(__FILE__);
  323. RUN_TEST(testRfc8259RyanJson);
  324. RUN_TEST(testRfc8259Yyjson);
  325. RUN_TEST(testRfc8259Cjson);
  326. }
  327. #endif