Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_memdb_memdbOpen.c275 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/* Prototype of the test wrapper provided in the module */8int test_memdbOpen(sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFd, int flags, int *pOutFlags);9 10/* A large-enough sqlite3_file object so memdbOpen can treat it as MemFile */11typedef struct BigFile {12  sqlite3_file base;13  unsigned char extra[2048];14} BigFile;15 16static void BigFile_Init(BigFile *p){17  memset(p, 0, sizeof(*p));18}19 20void setUp(void) {21  /* Ensure SQLite is initialized for mutex/malloc subsystems used by memdb */22  sqlite3_initialize();23}24 25void tearDown(void) {26  /* Must ensure all files are closed prior to shutdown in tests */27  sqlite3_shutdown();28}29 30/* Helper to open a memdb file using the wrapper */31static int open_memdb(const char *zName, BigFile *pFile, int flags, int *pOutFlags){32  BigFile_Init(pFile);33  return test_memdbOpen(NULL, zName, &pFile->base, flags, pOutFlags);34}35 36/* Helper to close a memdb file if opened */37static void close_memdb(BigFile *pFile){38  if( pFile && pFile->base.pMethods ){39    pFile->base.pMethods->xClose(&pFile->base);40    pFile->base.pMethods = NULL;41  }42}43 44/* Basic separate store open and read/write/size behavior */45void test_memdbOpen_separate_basic_rw(void){46  BigFile f;47  int rc;48  sqlite3_int64 sz = -1;49  const char *zName = "separate_db"; /* No leading '/' => separate store */50 51  rc = open_memdb(zName, &f, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);52  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);53  TEST_ASSERT_NOT_NULL(f.base.pMethods);54 55  /* Initial size must be 0 */56  rc = f.base.pMethods->xFileSize(&f.base, &sz);57  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);58  TEST_ASSERT_EQUAL_INT64(0, sz);59 60  /* Write some data at offset 0 */61  const char payload1[] = "Hello, memdb";62  rc = f.base.pMethods->xWrite(&f.base, payload1, (int)sizeof(payload1), 0);63  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);64 65  /* File size should now be sizeof(payload1) */66  rc = f.base.pMethods->xFileSize(&f.base, &sz);67  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);68  TEST_ASSERT_EQUAL_INT64((sqlite3_int64)sizeof(payload1), sz);69 70  /* Read it back fully */71  char buf[64];72  memset(buf, 0, sizeof(buf));73  rc = f.base.pMethods->xRead(&f.base, buf, (int)sizeof(payload1), 0);74  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);75  TEST_ASSERT_EQUAL_INT(0, memcmp(buf, payload1, sizeof(payload1)));76 77  /* Read beyond EOF should zero-fill tail and return SHORT_READ */78  memset(buf, 0xAA, sizeof(buf));79  rc = f.base.pMethods->xRead(&f.base, buf, (int)sizeof(payload1)+5, 0);80  TEST_ASSERT_EQUAL_INT(SQLITE_IOERR_SHORT_READ, rc);81  TEST_ASSERT_EQUAL_INT(0, memcmp(buf, payload1, sizeof(payload1)));82  for(int i=(int)sizeof(payload1); i<(int)sizeof(payload1)+5; i++){83    TEST_ASSERT_EQUAL_UINT8(0, (unsigned char)buf[i]);84  }85 86  /* Sparse write: write at offset > current size, gap should be zero-filled */87  const char payload2[] = "XYZ";88  rc = f.base.pMethods->xWrite(&f.base, payload2, (int)sizeof(payload2), 5 + (sqlite3_int64)sizeof(payload1));89  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);90 91  /* New size */92  rc = f.base.pMethods->xFileSize(&f.base, &sz);93  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);94  TEST_ASSERT_EQUAL_INT64(5 + (sqlite3_int64)sizeof(payload1) + (sqlite3_int64)sizeof(payload2), sz);95 96  /* Verify the zero-filled gap: read from end of payload1 to start of payload2 */97  char verify[64];98  memset(verify, 0xCC, sizeof(verify));99  rc = f.base.pMethods->xRead(&f.base, verify, 5 + (int)sizeof(payload2), (sqlite3_int64)sizeof(payload1));100  /* Some portion is beyond EOF if earlier size was smaller; however after write it should all exist */101  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);102 103  /* First 5 bytes after payload1 must be zeros */104  for(int i=0; i<5; i++){105    TEST_ASSERT_EQUAL_UINT8(0, (unsigned char)verify[i]);106  }107  TEST_ASSERT_EQUAL_INT(0, memcmp(&verify[5], payload2, sizeof(payload2)));108 109  /* Truncate smaller: OK */110  rc = f.base.pMethods->xTruncate(&f.base, 7);111  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);112  rc = f.base.pMethods->xFileSize(&f.base, &sz);113  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);114  TEST_ASSERT_EQUAL_INT64(7, sz);115 116  /* Truncate larger than current size: should be SQLITE_CORRUPT */117  rc = f.base.pMethods->xTruncate(&f.base, 1000);118  TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, rc);119 120  /* Sync should be OK (no-op) */121  rc = f.base.pMethods->xSync(&f.base, 0);122  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);123 124  close_memdb(&f);125}126 127/* Verify initial size limit via SQLITE_FCNTL_SIZE_LIMIT and enforcement of SQLITE_FULL */128void test_memdbOpen_separate_size_limit(void){129  BigFile f;130  int rc;131  sqlite3_int64 sizeLimit;132 133  rc = open_memdb("limit_db", &f, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);134  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);135 136  /* With empty file, passing -1 should return current szMax (sqlite3GlobalConfig.mxMemdbSize) */137  sizeLimit = -1;138  rc = f.base.pMethods->xFileControl(&f.base, SQLITE_FCNTL_SIZE_LIMIT, &sizeLimit);139  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);140  /* We cannot assert an exact value easily, but it must be >= 0 */141  TEST_ASSERT_TRUE(sizeLimit >= 0);142 143  /* Set a small limit and verify writes beyond it fail with SQLITE_FULL */144  sizeLimit = 1024;145  rc = f.base.pMethods->xFileControl(&f.base, SQLITE_FCNTL_SIZE_LIMIT, &sizeLimit);146  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);147  TEST_ASSERT_EQUAL_INT64(1024, sizeLimit);148 149  /* Attempt to write past the limit: offset 900, 200 bytes => end at 1100 > 1024 */150  unsigned char data[200];151  memset(data, 0x5A, sizeof(data));152  rc = f.base.pMethods->xWrite(&f.base, data, (int)sizeof(data), 900);153  TEST_ASSERT_EQUAL_INT(SQLITE_FULL, rc);154 155  /* Write under the limit should succeed (e.g., 512 bytes at 0) */156  rc = f.base.pMethods->xWrite(&f.base, data, 512, 0);157  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);158 159  close_memdb(&f);160}161 162/* Shared store behavior: two handles to the same name share content and locks */163void test_memdbOpen_shared_two_handles_share_and_locking(void){164  BigFile f1, f2;165  int rc;166  const char *zName = "/shared_memdb_A"; /* Leading '/' => shared store */167 168  rc = open_memdb(zName, &f1, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);169  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);170  rc = open_memdb(zName, &f2, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);171  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);172 173  /* Write through f1, read through f2 */174  const char msg[] = "Shared hello";175  rc = f1.base.pMethods->xWrite(&f1.base, msg, (int)sizeof(msg), 0);176  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);177  char buf[32];178  memset(buf, 0, sizeof(buf));179  rc = f2.base.pMethods->xRead(&f2.base, buf, (int)sizeof(msg), 0);180  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);181  TEST_ASSERT_EQUAL_INT(0, memcmp(buf, msg, sizeof(msg)));182 183  /* Locking: both can take SHARED */184  rc = f1.base.pMethods->xLock(&f1.base, SQLITE_LOCK_SHARED);185  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);186  rc = f2.base.pMethods->xLock(&f2.base, SQLITE_LOCK_SHARED);187  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);188 189  /* With two shared readers, EXCLUSIVE should be BUSY */190  rc = f2.base.pMethods->xLock(&f2.base, SQLITE_LOCK_EXCLUSIVE);191  TEST_ASSERT_EQUAL_INT(SQLITE_BUSY, rc);192 193  /* Release f1, then f2 should be able to escalate to EXCLUSIVE */194  rc = f1.base.pMethods->xUnlock(&f1.base, SQLITE_LOCK_NONE);195  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);196 197  /* Go to RESERVED then EXCLUSIVE */198  rc = f2.base.pMethods->xLock(&f2.base, SQLITE_LOCK_RESERVED);199  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);200  rc = f2.base.pMethods->xLock(&f2.base, SQLITE_LOCK_EXCLUSIVE);201  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);202 203  /* Drop back to NONE */204  rc = f2.base.pMethods->xUnlock(&f2.base, SQLITE_LOCK_NONE);205  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);206 207  close_memdb(&f1);208  close_memdb(&f2);209}210 211/* pOutFlags behavior and null handling */212void test_memdbOpen_pOutFlags_and_null(void){213  BigFile f1, f2;214  int rc;215  int outFlags = 0;216 217  rc = open_memdb("flags_db", &f1, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, &outFlags);218  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);219  /* Must contain SQLITE_OPEN_MEMORY plus passed flags */220  TEST_ASSERT_TRUE((outFlags & SQLITE_OPEN_MEMORY) != 0);221  TEST_ASSERT_TRUE((outFlags & SQLITE_OPEN_READWRITE) != 0);222  TEST_ASSERT_TRUE((outFlags & SQLITE_OPEN_CREATE) != 0);223  close_memdb(&f1);224 225  /* Null pOutFlags should be accepted */226  rc = open_memdb("flags_db_null", &f2, 0, NULL);227  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);228  close_memdb(&f2);229}230 231/* VFSNAME file-control returns a formatted string */232void test_memdbOpen_filecontrol_vfsname(void){233  BigFile f;234  int rc;235  rc = open_memdb("vfsname_db", &f, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);236  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);237 238  char *zName = NULL;239  rc = f.base.pMethods->xFileControl(&f.base, SQLITE_FCNTL_VFSNAME, &zName);240  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);241  TEST_ASSERT_NOT_NULL(zName);242  /* Should begin with "memdb(" */243  TEST_ASSERT_TRUE(strncmp(zName, "memdb(", 6) == 0);244  sqlite3_free(zName);245 246  close_memdb(&f);247}248 249/* Fetch should return NULL when RESIZEABLE flag is set (default) */250void test_memdbOpen_fetch_behavior_resizeable(void){251  BigFile f;252  int rc;253  void *pPage = (void*)0x1; /* will be overwritten */254  rc = open_memdb("fetch_db", &f, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);255  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);256 257  rc = f.base.pMethods->xFetch(&f.base, 0, 100, &pPage);258  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);259  TEST_ASSERT_NULL(pPage);260 261  close_memdb(&f);262}263 264int main(void) {265  UNITY_BEGIN();266 267  RUN_TEST(test_memdbOpen_separate_basic_rw);268  RUN_TEST(test_memdbOpen_separate_size_limit);269  RUN_TEST(test_memdbOpen_shared_two_handles_share_and_locking);270  RUN_TEST(test_memdbOpen_pOutFlags_and_null);271  RUN_TEST(test_memdbOpen_filecontrol_vfsname);272  RUN_TEST(test_memdbOpen_fetch_behavior_resizeable);273 274  return UNITY_END();275}