Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_json_jsonCreateEditSubstructure.c327 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdio.h>6 7/* External wrapper for the static function under test */8extern u32 test_jsonCreateEditSubstructure(JsonParse *pParse, JsonParse *pIns, const char *zTail);9 10/* Some edit-mode constants should be available via sqliteInt.h.11   We rely on JEDIT_INS to allow insertion of substructure. */12 13/* Global DB handle for allocator context */14static sqlite3 *gDb = NULL;15 16/* Minimal JSONB utilities for the tests, based on the documented encoding:17   - Type is in low 4 bits of first byte.18   - Upper 4 bits select payload size or length-encoding scheme. */19 20/* Return header size and set *pPayloadSize to payload size, for element starting at a[i] */21static u32 jsonb_hdrsize_and_psz(const unsigned char *a, u32 i, u32 *pPayloadSize){22  unsigned char x = a[i];23  unsigned char hi = (unsigned char)(x >> 4);24  if( hi <= 11 ){25    *pPayloadSize = hi;26    return 1;27  }else if( hi == 12 ){28    *pPayloadSize = a[i+1];29    return 2;30  }else if( hi == 13 ){31    *pPayloadSize = ((u32)a[i+1] << 8) | (u32)a[i+2];32    return 3;33  }else if( hi == 14 ){34    *pPayloadSize = ((u32)a[i+1] << 24) | ((u32)a[i+2] << 16)35                  | ((u32)a[i+3] << 8) | (u32)a[i+4];36    return 5;37  }else{38    /* 8-byte big-endian length */39    u64 v = 0;40    for(int k=1; k<=8; k++){41      v = (v<<8) | a[i+k];42    }43    if( v > 0xffffffffu ){ /* clamp to 32-bit for test use */44      *pPayloadSize = 0xffffffffu;45    }else{46      *pPayloadSize = (u32)v;47    }48    return 9;49  }50}51 52/* Build a minimal JSONB TEXT element for a small ASCII string s (len <= 11).53   Writes header+payload to buf. Returns total size. */54static u32 build_jsonb_text_small(const char *s, unsigned char *buf, u32 cap){55  u32 n = (u32)strlen(s);56  TEST_ASSERT_MESSAGE(n <= 11, "Test helper only supports text length <= 11");57  TEST_ASSERT_MESSAGE(cap >= 1+n, "Insufficient buffer capacity for JSONB text");58  buf[0] = (unsigned char)((n << 4) | (JSONB_TEXT & 0x0f));59  memcpy(&buf[1], s, n);60  return 1 + n;61}62 63/* Helpers to parse first label/value pair of an object:64   On entry, a points to the object header at index 0.65   Outputs:66     - label text pointer/length67     - value index (index into a[] at start of value element header)68     - returns 0 on success, non-zero on error.69*/70static int parse_single_kv_from_object(const unsigned char *a, u32 nBlob,71                                       const char **pzLabel, u32 *pnLabel,72                                       u32 *pIdxValue){73  u32 szPayload = 0;74  u32 hsz = jsonb_hdrsize_and_psz(a, 0, &szPayload);75  if( (a[0] & 0x0f) != JSONB_OBJECT ) return 1;76  u32 j = hsz;77  u32 iEnd = j + szPayload;78  if( j >= nBlob || j >= iEnd ) return 2;79  /* Label element */80  unsigned char ltype = a[j] & 0x0f;81  if( ltype < JSONB_TEXT || ltype > JSONB_TEXTRAW ) return 3;82  u32 lpsz = 0;83  u32 lhsz = jsonb_hdrsize_and_psz(a, j, &lpsz);84  u32 k = j + lhsz;85  if( k + lpsz > nBlob || k + lpsz > iEnd ) return 4;86  *pzLabel = (const char*)&a[k];87  *pnLabel = lpsz;88  /* Value element immediately after label payload */89  u32 v = k + lpsz;90  if( v >= nBlob || v >= iEnd ) return 5;91  *pIdxValue = v;92  return 0;93}94 95/* Parse first element of array at a[0], output index of element header into *pIdxEl0 */96static int parse_first_elem_from_array(const unsigned char *a, u32 nBlob, u32 *pIdxEl0){97  u32 szPayload = 0;98  u32 hsz = jsonb_hdrsize_and_psz(a, 0, &szPayload);99  if( (a[0] & 0x0f) != JSONB_ARRAY ) return 1;100  u32 j = hsz;101  u32 iEnd = j + szPayload;102  if( j >= nBlob ) return 2;103  if( j == iEnd ){104    /* empty array */105    return 3;106  }107  *pIdxEl0 = j;108  return 0;109}110 111void setUp(void) {112  if( gDb==NULL ){113    int rc = sqlite3_open(":memory:", &gDb);114    TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");115  }116}117 118void tearDown(void) {119  if( gDb ){120    sqlite3_close(gDb);121    gDb = NULL;122  }123}124 125/* Test: empty zTail returns the provided insertion blob directly */126void test_jsonCreateEditSubstructure_empty_tail_returns_given_blob(void) {127  JsonParse orig, ins;128  memset(&orig, 0, sizeof(orig));129  memset(&ins, 0, sizeof(ins));130  orig.db = gDb;131 132  unsigned char val[8];133  u32 vlen = build_jsonb_text_small("Z", val, sizeof(val));134  orig.aIns = val;135  orig.nIns = vlen;136  orig.eEdit = JEDIT_INS; /* value doesn't matter for empty tail */137 138  u32 rc = test_jsonCreateEditSubstructure(&orig, &ins, "");139  TEST_ASSERT_EQUAL_UINT32(0u, rc);140  TEST_ASSERT_EQUAL_PTR(orig.aIns, ins.aBlob);141  TEST_ASSERT_EQUAL_UINT32(orig.nIns, ins.nBlob);142  TEST_ASSERT_EQUAL_PTR(gDb, ins.db);143 144  /* Do not call jsonParseReset(&ins) here since ins.aBlob aliases orig.aIns */145}146 147/* Test: simple object path ".k" builds {"k": <val>} */148void test_jsonCreateEditSubstructure_creates_object_with_key_and_value(void) {149  JsonParse orig, sub;150  memset(&orig, 0, sizeof(orig));151  memset(&sub, 0, sizeof(sub));152  orig.db = gDb;153 154  unsigned char val[8];155  u32 vlen = build_jsonb_text_small("V", val, sizeof(val));156  orig.aIns = val;157  orig.nIns = vlen;158  orig.eEdit = JEDIT_INS;159 160  u32 rc = test_jsonCreateEditSubstructure(&orig, &sub, ".k");161  TEST_ASSERT_MESSAGE(rc < JSON_LOOKUP_PATHERROR, "Expected success (no JSON_LOOKUP_* error)");162 163  /* Validate structure: object with one label "k" and value "V" */164  TEST_ASSERT_EQUAL_UINT8(JSONB_OBJECT, sub.aBlob[0] & 0x0f);165 166  const char *zLabel = NULL;167  u32 nLabel = 0;168  u32 idxValue = 0;169  int prc = parse_single_kv_from_object(sub.aBlob, sub.nBlob, &zLabel, &nLabel, &idxValue);170  TEST_ASSERT_EQUAL_INT_MESSAGE(0, prc, "Failed to parse object key/value");171 172  TEST_ASSERT_EQUAL_UINT32(1u, nLabel);173  TEST_ASSERT_EQUAL_CHAR('k', zLabel[0]);174 175  /* Check value is TEXT "V" */176  TEST_ASSERT_EQUAL_UINT8(JSONB_TEXT, sub.aBlob[idxValue] & 0x0f);177  u32 psz = 0;178  u32 hsz = jsonb_hdrsize_and_psz(sub.aBlob, idxValue, &psz);179  TEST_ASSERT_EQUAL_UINT32(1u, psz);180  TEST_ASSERT_EQUAL_CHAR('V', (char)sub.aBlob[idxValue + hsz]);181 182  jsonParseReset(&sub);183}184 185/* Test: simple array path "[0]" builds [ <val> ] */186void test_jsonCreateEditSubstructure_creates_array_and_inserts_element(void) {187  JsonParse orig, sub;188  memset(&orig, 0, sizeof(orig));189  memset(&sub, 0, sizeof(sub));190  orig.db = gDb;191 192  unsigned char val[8];193  u32 vlen = build_jsonb_text_small("V", val, sizeof(val));194  orig.aIns = val;195  orig.nIns = vlen;196  orig.eEdit = JEDIT_INS;197 198  u32 rc = test_jsonCreateEditSubstructure(&orig, &sub, "[0]");199  TEST_ASSERT_MESSAGE(rc < JSON_LOOKUP_PATHERROR, "Expected success (no JSON_LOOKUP_* error)");200 201  TEST_ASSERT_EQUAL_UINT8(JSONB_ARRAY, sub.aBlob[0] & 0x0f);202 203  u32 idxEl0 = 0;204  int prc = parse_first_elem_from_array(sub.aBlob, sub.nBlob, &idxEl0);205  TEST_ASSERT_EQUAL_INT_MESSAGE(0, prc, "Failed to parse first array element");206 207  TEST_ASSERT_EQUAL_UINT8(JSONB_TEXT, sub.aBlob[idxEl0] & 0x0f);208  u32 psz = 0;209  u32 hsz = jsonb_hdrsize_and_psz(sub.aBlob, idxEl0, &psz);210  TEST_ASSERT_EQUAL_UINT32(1u, psz);211  TEST_ASSERT_EQUAL_CHAR('V', (char)sub.aBlob[idxEl0 + hsz]);212 213  jsonParseReset(&sub);214}215 216/* Test: deep mixed path ".a.b[0]" builds {"a":{"b":[<val>]}} */217void test_jsonCreateEditSubstructure_creates_nested_structure_mixed(void) {218  JsonParse orig, sub;219  memset(&orig, 0, sizeof(orig));220  memset(&sub, 0, sizeof(sub));221  orig.db = gDb;222 223  unsigned char val[8];224  u32 vlen = build_jsonb_text_small("V", val, sizeof(val));225  orig.aIns = val;226  orig.nIns = vlen;227  orig.eEdit = JEDIT_INS;228 229  u32 rc = test_jsonCreateEditSubstructure(&orig, &sub, ".a.b[0]");230  TEST_ASSERT_MESSAGE(rc < JSON_LOOKUP_PATHERROR, "Expected success (no JSON_LOOKUP_* error)");231 232  /* Root: object -> key "a" -> object */233  TEST_ASSERT_EQUAL_UINT8(JSONB_OBJECT, sub.aBlob[0] & 0x0f);234  const char *zA = NULL; u32 nA = 0; u32 idxAVal = 0;235  int prc = parse_single_kv_from_object(sub.aBlob, sub.nBlob, &zA, &nA, &idxAVal);236  TEST_ASSERT_EQUAL_INT(0, prc);237  TEST_ASSERT_EQUAL_UINT32(1u, nA);238  TEST_ASSERT_EQUAL_CHAR('a', zA[0]);239 240  TEST_ASSERT_EQUAL_UINT8(JSONB_OBJECT, sub.aBlob[idxAVal] & 0x0f);241  /* Parse inner object at idxAVal: key "b" -> array */242  u32 inner_psz = 0, inner_hsz = jsonb_hdrsize_and_psz(sub.aBlob, idxAVal, &inner_psz);243  const unsigned char *inner = &sub.aBlob[idxAVal];244  const char *zB = NULL; u32 nB = 0; u32 idxBVal = 0;245  int prc2 = parse_single_kv_from_object(inner, sub.nBlob - idxAVal, &zB, &nB, &idxBVal);246  TEST_ASSERT_EQUAL_INT(0, prc2);247  TEST_ASSERT_EQUAL_UINT32(1u, nB);248  TEST_ASSERT_EQUAL_CHAR('b', zB[0]);249 250  /* idxBVal is relative to inner; convert to absolute index */251  u32 idxBValAbs = idxAVal + idxBVal;252  TEST_ASSERT_EQUAL_UINT8(JSONB_ARRAY, sub.aBlob[idxBValAbs] & 0x0f);253 254  /* Parse first element of array at idxBValAbs */255  u32 arr_psz = 0, arr_hsz = jsonb_hdrsize_and_psz(sub.aBlob, idxBValAbs, &arr_psz);256  const unsigned char *arr = &sub.aBlob[idxBValAbs];257  u32 idxEl0Rel = 0;258  int prc3 = parse_first_elem_from_array(arr, sub.nBlob - idxBValAbs, &idxEl0Rel);259  TEST_ASSERT_EQUAL_INT(0, prc3);260  u32 idxEl0Abs = idxBValAbs + idxEl0Rel;261 262  TEST_ASSERT_EQUAL_UINT8(JSONB_TEXT, sub.aBlob[idxEl0Abs] & 0x0f);263  u32 psz = 0, hsz = jsonb_hdrsize_and_psz(sub.aBlob, idxEl0Abs, &psz);264  TEST_ASSERT_EQUAL_UINT32(1u, psz);265  TEST_ASSERT_EQUAL_CHAR('V', (char)sub.aBlob[idxEl0Abs + hsz]);266 267  jsonParseReset(&sub);268}269 270/* Test: invalid tail returns PATHERROR */271void test_jsonCreateEditSubstructure_patherror_for_invalid_tail(void) {272  JsonParse orig, sub;273  memset(&orig, 0, sizeof(orig));274  memset(&sub, 0, sizeof(sub));275  orig.db = gDb;276 277  unsigned char val[8];278  u32 vlen = build_jsonb_text_small("V", val, sizeof(val));279  orig.aIns = val;280  orig.nIns = vlen;281  orig.eEdit = JEDIT_INS;282 283  u32 rc1 = test_jsonCreateEditSubstructure(&orig, &sub, "invalid");284  TEST_ASSERT_EQUAL_UINT32(JSON_LOOKUP_PATHERROR, rc1);285  /* No allocation expected to validate, but reset if anything happened */286  if( sub.aBlob && sub.aBlob != orig.aIns ){287    jsonParseReset(&sub);288  }289 290  memset(&sub, 0, sizeof(sub));291  u32 rc2 = test_jsonCreateEditSubstructure(&orig, &sub, "[x]");292  TEST_ASSERT_EQUAL_UINT32(JSON_LOOKUP_PATHERROR, rc2);293  if( sub.aBlob && sub.aBlob != orig.aIns ){294    jsonParseReset(&sub);295  }296}297 298/* Test: array index out of range returns NOTFOUND (no insertion at [1] in empty array) */299void test_jsonCreateEditSubstructure_notfound_for_array_index_out_of_range(void) {300  JsonParse orig, sub;301  memset(&orig, 0, sizeof(orig));302  memset(&sub, 0, sizeof(sub));303  orig.db = gDb;304 305  unsigned char val[8];306  u32 vlen = build_jsonb_text_small("V", val, sizeof(val));307  orig.aIns = val;308  orig.nIns = vlen;309  orig.eEdit = JEDIT_INS;310 311  u32 rc = test_jsonCreateEditSubstructure(&orig, &sub, "[1]");312  TEST_ASSERT_EQUAL_UINT32(JSON_LOOKUP_NOTFOUND, rc);313  if( sub.aBlob && sub.aBlob != orig.aIns ){314    jsonParseReset(&sub);315  }316}317 318int main(void) {319  UNITY_BEGIN();320  RUN_TEST(test_jsonCreateEditSubstructure_empty_tail_returns_given_blob);321  RUN_TEST(test_jsonCreateEditSubstructure_creates_object_with_key_and_value);322  RUN_TEST(test_jsonCreateEditSubstructure_creates_array_and_inserts_element);323  RUN_TEST(test_jsonCreateEditSubstructure_creates_nested_structure_mixed);324  RUN_TEST(test_jsonCreateEditSubstructure_patherror_for_invalid_tail);325  RUN_TEST(test_jsonCreateEditSubstructure_notfound_for_array_index_out_of_range);326  return UNITY_END();327}