Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_json_jsonCacheInsert.c198 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* The target function is static in the module. We must call the provided7** global wrapper instead of the original static function. */8int test_jsonCacheInsert(sqlite3_context *ctx, JsonParse *pParse);9 10static sqlite3 *gDb = NULL;11 12/* Helper: Create a minimally valid JsonParse object that satisfies asserts13** in jsonCacheInsert. We set nJPRef=1 to avoid frees in jsonParseFree. */14static JsonParse *makeDummyParse(sqlite3 *db, const char *label, int initialRef){15  JsonParse *p = (JsonParse*)sqlite3DbMallocZero(db, sizeof(JsonParse));16  if( p==0 ) return 0;17  p->db = db;18  /* Use a non-NULL string pointer. We mark it as RCStr for assertions,19  ** but ensure nJPRef>1 at free-time so it is not freed. */20  p->zJson = (char*)label;21  p->bJsonIsRCStr = 1;22  p->delta = 0;23  p->nBlobAlloc = 1;24  p->nJPRef = initialRef;      /* 1 so insert -> 2; free path will only decrement */25  p->eEdit = 123;              /* Non-zero to confirm it is reset to 0 by insert */26  p->bReadOnly = 0;27  return p;28}29 30/* A SQL function wrapper that exercises test_jsonCacheInsert within a valid31** sqlite3_context and returns an integer indicating pass(1)/fail(0).32**33** Usage: SELECT json_cache_test_op(op, n);34**   op==1: Basic behavior test:35**          - Insert one object, verify flags updated and refcount incremented.36**          - Insert a second object in the same context to exercise existing-cache path.37**   op==2: Eviction behavior test:38**          - Insert many objects (64) to force at least one eviction.39**          - Verify oldest's refcount decremented, newest shows insertion changes.40** The second argument 'n' is currently ignored (reserved for future use).41*/42static void json_cache_test_op(sqlite3_context *ctx, int argc, sqlite3_value **argv){43  int op = sqlite3_value_int(argv[0]);44  sqlite3 *db = sqlite3_context_db_handle(ctx);45 46  if( op==1 ){47    /* Basic behavior + existing cache path */48    int rc;49    int ok = 1;50 51    JsonParse *p1 = makeDummyParse(db, "A", 1);52    if( p1==0 ){ sqlite3_result_int(ctx, 0); return; }53 54    rc = test_jsonCacheInsert(ctx, p1);55    if( rc!=SQLITE_OK ) ok = 0;56    if( p1->eEdit!=0 ) ok = 0;57    if( p1->bReadOnly!=1 ) ok = 0;58    if( p1->nJPRef!=2 ) ok = 0;59 60    /* Insert a second object to exercise path where cache already exists */61    JsonParse *p2 = makeDummyParse(db, "B", 1);62    if( p2==0 ){ sqlite3_result_int(ctx, 0); return; }63 64    rc = test_jsonCacheInsert(ctx, p2);65    if( rc!=SQLITE_OK ) ok = 0;66    if( p2->eEdit!=0 ) ok = 0;67    if( p2->bReadOnly!=1 ) ok = 0;68    if( p2->nJPRef!=2 ) ok = 0;69 70    sqlite3_result_int(ctx, ok ? 1 : 0);71    return;72  }else if( op==2 ){73    /* Eviction behavior: push enough entries to cause at least one eviction.74       We avoid directly inspecting the internal cache since its private75       constants are not exported. Instead, we infer eviction by the refcount76       change of the oldest element: it should be decremented by jsonParseFree()77       when the cache is full and the oldest entry is expelled. */78    int ok = 1;79    const int N = 64; /* Big enough to exceed reasonable cache sizes */80    JsonParse **ap = (JsonParse**)sqlite3_malloc(sizeof(JsonParse*)*N);81    int i;82    if( ap==0 ){ sqlite3_result_int(ctx, 0); return; }83 84    for(i=0; i<N; i++){85      char labelBuf[16];86      /* Create small stable labels; pointers are stable string literals here.87         We construct separate literals via a small static table if desired,88         but simple small labels as stack strings are fine since we only89         store the pointer in our own JsonParse and never free it. */90      /* Use a set of static labels to ensure non-NULL zJson with distinct C strings. */91      static const char *labels[] = {92        "L00","L01","L02","L03","L04","L05","L06","L07",93        "L08","L09","L10","L11","L12","L13","L14","L15",94        "L16","L17","L18","L19","L20","L21","L22","L23",95        "L24","L25","L26","L27","L28","L29","L30","L31",96        "L32","L33","L34","L35","L36","L37","L38","L39",97        "L40","L41","L42","L43","L44","L45","L46","L47",98        "L48","L49","L50","L51","L52","L53","L54","L55",99        "L56","L57","L58","L59","L60","L61","L62","L63"100      };101      const char *lbl = labels[i % (sizeof(labels)/sizeof(labels[0]))];102      ap[i] = makeDummyParse(db, lbl, 1);103      if( ap[i]==0 ){ ok = 0; break; }104      if( test_jsonCacheInsert(ctx, ap[i])!=SQLITE_OK ){ ok = 0; break; }105      /* Newest element should always reflect insertion-side changes */106      if( ap[i]->bReadOnly!=1 || ap[i]->eEdit!=0 || ap[i]->nJPRef!=2 ){107        ok = 0; break;108      }109    }110 111    /* If eviction happened at least once, the very first element ap[0] will112       have been expelled and its refcount decremented by jsonParseFree().113       Since we started with 1 and insert incremented to 2, after eviction114       we expect nJPRef==1. If the cache is unusually large and did not evict,115       this check may remain at 2. In typical builds the cache is much smaller116       than 64, so we expect eviction and nJPRef==1. */117    if( ok ){118      if( ap[0]->nJPRef!=1 ){119        /* Did not observe expected eviction; treat as failure */120        ok = 0;121      }122      /* Also ensure the latest element still shows post-insert state */123      if( ap[N-1]->nJPRef!=2 || ap[N-1]->bReadOnly!=1 || ap[N-1]->eEdit!=0 ){124        ok = 0;125      }126    }127 128    sqlite3_free(ap);129    sqlite3_result_int(ctx, ok ? 1 : 0);130    return;131  }132 133  /* Unknown op */134  sqlite3_result_int(ctx, 0);135}136 137void setUp(void) {138  int rc = sqlite3_open(":memory:", &gDb);139  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);140  rc = sqlite3_create_function(gDb, "json_cache_test_op", 2, SQLITE_UTF8, NULL,141                               json_cache_test_op, NULL, NULL);142  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);143}144 145void tearDown(void) {146  if( gDb ){147    sqlite3_close(gDb);148    gDb = NULL;149  }150}151 152/* Helper: Evaluate SELECT json_cache_test_op(op, n) and return integer result */153static int eval_json_cache_test(int op, int n){154  sqlite3_stmt *pStmt = NULL;155  int rc, rv = -1;156 157  rc = sqlite3_prepare_v2(gDb, "SELECT json_cache_test_op(?1,?2);", -1, &pStmt, 0);158  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);159 160  rc = sqlite3_bind_int(pStmt, 1, op);161  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);162  rc = sqlite3_bind_int(pStmt, 2, n);163  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);164 165  rc = sqlite3_step(pStmt);166  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);167 168  rv = sqlite3_column_int(pStmt, 0);169 170  rc = sqlite3_finalize(pStmt);171  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);172 173  return rv;174}175 176/* Test 1: Basic behavior of jsonCacheInsert.177   - First call must create the cache, set flags, and increment refcount.178   - Second call within same sqlite3_context must use existing cache. */179void test_jsonCacheInsert_basic_behavior(void) {180  int ok = eval_json_cache_test(1, 0);181  TEST_ASSERT_EQUAL_INT_MESSAGE(1, ok, "Basic insert behavior failed");182}183 184/* Test 2: Eviction behavior when the cache is overfilled.185   - Insert many objects to force eviction of the oldest.186   - Verify the oldest object's refcount is decremented (evicted),187     and a newest object's fields are set as expected. */188void test_jsonCacheInsert_eviction_behavior(void) {189  int ok = eval_json_cache_test(2, 0);190  TEST_ASSERT_EQUAL_INT_MESSAGE(1, ok, "Eviction behavior failed");191}192 193int main(void) {194  UNITY_BEGIN();195  RUN_TEST(test_jsonCacheInsert_basic_behavior);196  RUN_TEST(test_jsonCacheInsert_eviction_behavior);197  return UNITY_END();198}