AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdint.h>6 7/* Prototype of the global test wrapper provided in the module */8u32 test_jsonTranslateBlobToText(const JsonParse *pParse, u32 i, JsonString *pOut);9 10/* Helpers to build headers */11#define HDR(sz,type) (unsigned char)(((unsigned int)(sz)<<4) | ((type)&0x0f))12 13/* Initialize a JsonString equivalent to jsonStringZero/init without calling static funcs */14static void js_init(JsonString *p){15 memset(p, 0, sizeof(*p));16 p->zBuf = p->zSpace;17 p->nAlloc = (u32)sizeof(p->zSpace);18 p->nUsed = 0;19 p->bStatic = 1;20 p->eErr = 0;21 p->pCtx = NULL;22}23 24/* Initialize a JsonParse with a given blob */25static void jp_init(JsonParse *p, const unsigned char *aBlob, u32 nBlob){26 memset(p, 0, sizeof(*p));27 p->aBlob = aBlob;28 p->nBlob = nBlob;29 p->delta = 0;30}31 32/* Add C-string terminator for comparisons */33static void js_terminate(JsonString *p){34 /* Ensure there's room; the buffer implementations always keep capacity >= nUsed.35 Our test outputs are small and fit in the default zSpace. */36 if( p->nUsed < p->nAlloc ){37 p->zBuf[p->nUsed] = 0;38 }else if( p->nAlloc > 0 ){39 /* Fallback: overwrite last byte (should not happen with small outputs) */40 p->zBuf[p->nAlloc-1] = 0;41 }42}43 44void setUp(void) {45 /* No global setup needed */46}47void tearDown(void) {48 /* No global teardown needed */49}50 51/* Test NULL, TRUE, FALSE primitives */52void test_jsonTranslateBlobToText_primitives(void){53 unsigned char blob_null[] = { HDR(0, JSONB_NULL) };54 unsigned char blob_true[] = { HDR(0, JSONB_TRUE) };55 unsigned char blob_false[] = { HDR(0, JSONB_FALSE) };56 57 JsonParse p; JsonString out; u32 r;58 59 /* NULL */60 jp_init(&p, blob_null, sizeof(blob_null)); js_init(&out);61 r = test_jsonTranslateBlobToText(&p, 0, &out);62 js_terminate(&out);63 TEST_ASSERT_EQUAL_UINT32(1, r);64 TEST_ASSERT_EQUAL_STRING("null", out.zBuf);65 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);66 67 /* TRUE */68 jp_init(&p, blob_true, sizeof(blob_true)); js_init(&out);69 r = test_jsonTranslateBlobToText(&p, 0, &out);70 js_terminate(&out);71 TEST_ASSERT_EQUAL_UINT32(1, r);72 TEST_ASSERT_EQUAL_STRING("true", out.zBuf);73 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);74 75 /* FALSE */76 jp_init(&p, blob_false, sizeof(blob_false)); js_init(&out);77 r = test_jsonTranslateBlobToText(&p, 0, &out);78 js_terminate(&out);79 TEST_ASSERT_EQUAL_UINT32(1, r);80 TEST_ASSERT_EQUAL_STRING("false", out.zBuf);81 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);82}83 84/* Test INT and FLOAT payloads */85void test_jsonTranslateBlobToText_int_and_float(void){86 /* INT "123" => header size 3, type INT(3) => 0x33, payload '123' */87 unsigned char blob_int[] = { HDR(3, JSONB_INT), '1','2','3' };88 /* FLOAT "3.14" => header size 4, type FLOAT(5) => 0x45, payload '3.14' */89 unsigned char blob_float[] = { HDR(4, JSONB_FLOAT), '3','.','1','4' };90 91 JsonParse p; JsonString out; u32 r;92 93 jp_init(&p, blob_int, sizeof(blob_int)); js_init(&out);94 r = test_jsonTranslateBlobToText(&p, 0, &out);95 js_terminate(&out);96 TEST_ASSERT_EQUAL_UINT32(sizeof(blob_int), r);97 TEST_ASSERT_EQUAL_STRING("123", out.zBuf);98 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);99 100 jp_init(&p, blob_float, sizeof(blob_float)); js_init(&out);101 r = test_jsonTranslateBlobToText(&p, 0, &out);102 js_terminate(&out);103 TEST_ASSERT_EQUAL_UINT32(sizeof(blob_float), r);104 TEST_ASSERT_EQUAL_STRING("3.14", out.zBuf);105 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);106}107 108/* Test INT5 hex with sign and overflow behavior */109void test_jsonTranslateBlobToText_int5_and_overflow(void){110 /* INT5 "-0x1f" => -31 */111 unsigned char blob_hex_neg[] = { HDR(5, JSONB_INT5), '-', '0','x','1','f' };112 113 /* INT5 "0x100000000000000000" (18 hex digits beyond 64-bit) => overflow -> 9.0e999114 We'll use 0x10000000000000000 (17 hex digits after 0x) */115 const char *hex_over = "0x10000000000000000";116 unsigned sz = (unsigned)strlen(hex_over);117 unsigned char blob_hex_over[1 + 32] = {0};118 blob_hex_over[0] = HDR(sz, JSONB_INT5);119 memcpy(&blob_hex_over[1], hex_over, sz);120 121 JsonParse p; JsonString out; u32 r;122 123 /* Negative hex */124 jp_init(&p, blob_hex_neg, sizeof(blob_hex_neg)); js_init(&out);125 r = test_jsonTranslateBlobToText(&p, 0, &out);126 js_terminate(&out);127 TEST_ASSERT_EQUAL_UINT32(sizeof(blob_hex_neg), r);128 TEST_ASSERT_EQUAL_STRING("-31", out.zBuf);129 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);130 131 /* Overflow hex */132 jp_init(&p, blob_hex_over, 1+sz); js_init(&out);133 r = test_jsonTranslateBlobToText(&p, 0, &out);134 js_terminate(&out);135 TEST_ASSERT_EQUAL_UINT32(1+sz, r);136 TEST_ASSERT_EQUAL_STRING("9.0e999", out.zBuf);137 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);138}139 140/* Test FLOAT5 normalization */141void test_jsonTranslateBlobToText_float5(void){142 /* FLOAT5 "-.5" => "-0.5" */143 unsigned char blob_f5a[] = { HDR(3, JSONB_FLOAT5), '-', '.','5' };144 /* FLOAT5 "." => "0.0" */145 unsigned char blob_f5b[] = { HDR(1, JSONB_FLOAT5), '.' };146 147 JsonParse p; JsonString out; u32 r;148 149 jp_init(&p, blob_f5a, sizeof(blob_f5a)); js_init(&out);150 r = test_jsonTranslateBlobToText(&p, 0, &out);151 js_terminate(&out);152 TEST_ASSERT_EQUAL_STRING("-0.5", out.zBuf);153 TEST_ASSERT_EQUAL_UINT32(sizeof(blob_f5a), r);154 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);155 156 jp_init(&p, blob_f5b, sizeof(blob_f5b)); js_init(&out);157 r = test_jsonTranslateBlobToText(&p, 0, &out);158 js_terminate(&out);159 TEST_ASSERT_EQUAL_STRING("0.0", out.zBuf);160 TEST_ASSERT_EQUAL_UINT32(sizeof(blob_f5b), r);161 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);162}163 164/* Test TEXT and TEXTJ simple quoting */165void test_jsonTranslateBlobToText_text_and_textj(void){166 /* TEXT "abc" => output "abc" (with quotes) */167 unsigned char blob_text[] = { HDR(3, JSONB_TEXT), 'a','b','c' };168 /* TEXTJ with backslash n should remain as two chars in output (JSON escaping) */169 unsigned char blob_textj[] = { HDR(3, JSONB_TEXTJ), 'a','\\','n' };170 171 JsonParse p; JsonString out; u32 r;172 173 jp_init(&p, blob_text, sizeof(blob_text)); js_init(&out);174 r = test_jsonTranslateBlobToText(&p, 0, &out);175 js_terminate(&out);176 TEST_ASSERT_EQUAL_STRING("\"abc\"", out.zBuf);177 TEST_ASSERT_EQUAL_UINT32(sizeof(blob_text), r);178 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);179 180 jp_init(&p, blob_textj, sizeof(blob_textj)); js_init(&out);181 r = test_jsonTranslateBlobToText(&p, 0, &out);182 js_terminate(&out);183 TEST_ASSERT_EQUAL_STRING("\"a\\n\"", out.zBuf);184 TEST_ASSERT_EQUAL_UINT32(sizeof(blob_textj), r);185 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);186}187 188/* Test TEXT5 with various escapes */189void test_jsonTranslateBlobToText_text5_escapes(void){190 /* Payload: " then \x41 then \' then \v then \0191 Sequence: [ '"' , '\\','x','4','1' , '\\','\'' , '\\','v' , '\\','0' ]192 Expect inside quotes: \"\u0041'\u000b\u0000193 */194 unsigned char payload[] = { '"', '\\','x','4','1', '\\','\'', '\\','v', '\\','0' };195 unsigned char blob[1 + sizeof(payload)];196 blob[0] = HDR((u32)sizeof(payload), JSONB_TEXT5);197 memcpy(&blob[1], payload, sizeof(payload));198 199 JsonParse p; JsonString out; u32 r;200 jp_init(&p, blob, sizeof(blob)); js_init(&out);201 r = test_jsonTranslateBlobToText(&p, 0, &out);202 js_terminate(&out);203 204 TEST_ASSERT_EQUAL_UINT32(sizeof(blob), r);205 TEST_ASSERT_EQUAL_STRING("\"\\\"\\u0041'\\u000b\\u0000\"", out.zBuf);206 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);207}208 209/* TEXTRAW should JSON-escape/quote via jsonAppendString */210void test_jsonTranslateBlobToText_textraw(void){211 /* Payload "a\nb" (actual newline byte 0x0a) should become "a\nb" with \n escaped in JSON text */212 unsigned char payload[] = { 'a', '\n', 'b' };213 unsigned char blob[1 + sizeof(payload)];214 blob[0] = HDR((u32)sizeof(payload), JSONB_TEXTRAW);215 memcpy(&blob[1], payload, sizeof(payload));216 217 JsonParse p; JsonString out; u32 r;218 jp_init(&p, blob, sizeof(blob)); js_init(&out);219 r = test_jsonTranslateBlobToText(&p, 0, &out);220 js_terminate(&out);221 222 TEST_ASSERT_EQUAL_UINT32(sizeof(blob), r);223 TEST_ASSERT_EQUAL_STRING("\"a\\nb\"", out.zBuf);224 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);225}226 227/* Extended header size for TEXT (size nibble 12) with 12-byte payload */228void test_jsonTranslateBlobToText_extended_header_text(void){229 const char *twelve = "abcdefghijkl"; /* 12 chars */230 unsigned char blob[2 + 12];231 blob[0] = HDR(12, JSONB_TEXT); /* signals 1 extra size byte */232 blob[1] = 12; /* payload length */233 memcpy(&blob[2], twelve, 12);234 235 JsonParse p; JsonString out; u32 r;236 jp_init(&p, blob, sizeof(blob)); js_init(&out);237 r = test_jsonTranslateBlobToText(&p, 0, &out);238 js_terminate(&out);239 240 TEST_ASSERT_EQUAL_UINT32(sizeof(blob), r);241 TEST_ASSERT_EQUAL_STRING("\"abcdefghijkl\"", out.zBuf);242 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);243}244 245/* Array and Object basic cases */246void test_jsonTranslateBlobToText_array_and_object(void){247 /* Array [true] => element TRUE (1 byte), array header: size 1, type ARRAY (11) => 0x1B */248 unsigned char arr1[] = { HDR(1, JSONB_ARRAY), HDR(0, JSONB_TRUE) };249 250 /* Array [null,false,true] */251 unsigned char arrN[] = {252 /* header: payload 3 bytes */253 HDR(3, JSONB_ARRAY),254 HDR(0, JSONB_NULL),255 HDR(0, JSONB_FALSE),256 HDR(0, JSONB_TRUE)257 };258 259 /* Object {"x":false}260 key: TEXT "x" => header 0x17, payload 'x'261 value: FALSE => 0x02262 object header: payload size 2+1=3, type OBJECT(12) => 0x3C263 */264 unsigned char obj1[] = { HDR(3, JSONB_OBJECT), HDR(1, JSONB_TEXT), 'x', HDR(0, JSONB_FALSE) };265 266 JsonParse p; JsonString out; u32 r;267 268 /* [true] */269 jp_init(&p, arr1, sizeof(arr1)); js_init(&out);270 r = test_jsonTranslateBlobToText(&p, 0, &out);271 js_terminate(&out);272 TEST_ASSERT_EQUAL_UINT32(sizeof(arr1), r);273 TEST_ASSERT_EQUAL_STRING("[true]", out.zBuf);274 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);275 276 /* [null,false,true] */277 jp_init(&p, arrN, sizeof(arrN)); js_init(&out);278 r = test_jsonTranslateBlobToText(&p, 0, &out);279 js_terminate(&out);280 TEST_ASSERT_EQUAL_UINT32(sizeof(arrN), r);281 TEST_ASSERT_EQUAL_STRING("[null,false,true]", out.zBuf);282 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);283 284 /* {"x":false} */285 jp_init(&p, obj1, sizeof(obj1)); js_init(&out);286 r = test_jsonTranslateBlobToText(&p, 0, &out);287 js_terminate(&out);288 TEST_ASSERT_EQUAL_UINT32(sizeof(obj1), r);289 TEST_ASSERT_EQUAL_STRING("{\"x\":false}", out.zBuf);290 TEST_ASSERT_EQUAL_UINT32(0, out.eErr);291}292 293/* Object with odd element count should set malformed error */294void test_jsonTranslateBlobToText_object_odd_count_error(void){295 /* Object with only one element (true), payload size 1 */296 unsigned char obj_bad[] = { HDR(1, JSONB_OBJECT), HDR(0, JSONB_TRUE) };297 298 JsonParse p; JsonString out; u32 r;299 jp_init(&p, obj_bad, sizeof(obj_bad)); js_init(&out);300 r = test_jsonTranslateBlobToText(&p, 0, &out);301 /* Output may be "{true}" (since trailing ':' trimmed). Validate error flag. */302 TEST_ASSERT_NOT_EQUAL(0u, out.eErr & JSTRING_MALFORMED);303}304 305/* Malformed header: claims extended size but truncated */306void test_jsonTranslateBlobToText_malformed_header_truncated(void){307 /* First byte indicates extended 1-byte size for INT (x==12), but missing size byte */308 unsigned char blob_bad[] = { HDR(12, JSONB_INT) /* no following size byte */ };309 310 JsonParse p; JsonString out; u32 r;311 jp_init(&p, blob_bad, sizeof(blob_bad)); js_init(&out);312 r = test_jsonTranslateBlobToText(&p, 0, &out);313 314 TEST_ASSERT_EQUAL_UINT32(p.nBlob + 1, r);315 TEST_ASSERT_NOT_EQUAL(0u, out.eErr & JSTRING_MALFORMED);316}317 318/* Malformed numeric: INT with zero-length payload should be flagged */319void test_jsonTranslateBlobToText_malformed_numeric_empty_payload(void){320 unsigned char blob_bad[] = { HDR(0, JSONB_INT) };321 322 JsonParse p; JsonString out; u32 r;323 jp_init(&p, blob_bad, sizeof(blob_bad)); js_init(&out);324 r = test_jsonTranslateBlobToText(&p, 0, &out);325 326 TEST_ASSERT_EQUAL_UINT32(1, r);327 TEST_ASSERT_NOT_EQUAL(0u, out.eErr & JSTRING_MALFORMED);328}329 330int main(void){331 UNITY_BEGIN();332 RUN_TEST(test_jsonTranslateBlobToText_primitives);333 RUN_TEST(test_jsonTranslateBlobToText_int_and_float);334 RUN_TEST(test_jsonTranslateBlobToText_int5_and_overflow);335 RUN_TEST(test_jsonTranslateBlobToText_float5);336 RUN_TEST(test_jsonTranslateBlobToText_text_and_textj);337 RUN_TEST(test_jsonTranslateBlobToText_text5_escapes);338 RUN_TEST(test_jsonTranslateBlobToText_textraw);339 RUN_TEST(test_jsonTranslateBlobToText_extended_header_text);340 RUN_TEST(test_jsonTranslateBlobToText_array_and_object);341 RUN_TEST(test_jsonTranslateBlobToText_object_odd_count_error);342 RUN_TEST(test_jsonTranslateBlobToText_malformed_header_truncated);343 RUN_TEST(test_jsonTranslateBlobToText_malformed_numeric_empty_payload);344 return UNITY_END();345}