AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5 6static sqlite3 *gDb = NULL;7 8/* Helper: count number of savepoints currently linked on db->pSavepoint */9static int count_savepoints(sqlite3 *db){10 int cnt = 0;11 Savepoint *p = db->pSavepoint;12 while( p ){13 cnt++;14 p = p->pNext;15 }16 return cnt;17}18 19/* Helper: push a savepoint with the given name onto db->pSavepoint.20 Allocation layout matches typical SQLite pattern: zName stored inline21 after the Savepoint struct so a single sqlite3DbFree() frees everything. */22static Savepoint* push_savepoint(sqlite3 *db, const char *zName){23 size_t n = strlen(zName);24 Savepoint *p = (Savepoint*)sqlite3DbMallocZero(db, sizeof(Savepoint) + n + 1);25 TEST_ASSERT_NOT_NULL_MESSAGE(p, "sqlite3DbMallocZero returned NULL for Savepoint");26 p->zName = (char*)&p[1];27 memcpy(p->zName, zName, n + 1);28 p->pNext = db->pSavepoint;29 db->pSavepoint = p;30 db->nSavepoint++; /* Keep db->nSavepoint consistent (where appropriate) */31 return p;32}33 34void setUp(void) {35 int rc = sqlite3_open(":memory:", &gDb);36 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");37 /* Ensure a known clean starting state for savepoints and counters */38 gDb->pSavepoint = NULL;39 gDb->nSavepoint = 0;40 gDb->nStatement = 0;41 gDb->isTransactionSavepoint = 0;42}43 44void tearDown(void) {45 if( gDb ){46 /* Ensure any lingering savepoints are closed before closing the db */47 sqlite3CloseSavepoints(gDb);48 sqlite3_close(gDb);49 gDb = NULL;50 }51}52 53/* Test: With no savepoints, the function should zero counters and leave pSavepoint NULL */54void test_sqlite3CloseSavepoints_no_savepoints_resets_counters(void){55 /* Set counters to non-zero values to ensure they are reset */56 gDb->nSavepoint = 5;57 gDb->nStatement = 3;58 gDb->isTransactionSavepoint = 1;59 60 TEST_ASSERT_NULL(gDb->pSavepoint);61 sqlite3CloseSavepoints(gDb);62 63 TEST_ASSERT_NULL(gDb->pSavepoint);64 TEST_ASSERT_EQUAL_INT(0, gDb->nSavepoint);65 TEST_ASSERT_EQUAL_INT(0, gDb->nStatement);66 TEST_ASSERT_EQUAL_INT(0, gDb->isTransactionSavepoint);67}68 69/* Test: Single savepoint is freed and counters reset */70void test_sqlite3CloseSavepoints_single_savepoint_freed_and_counters_reset(void){71 push_savepoint(gDb, "sp1");72 gDb->nStatement = 2;73 gDb->isTransactionSavepoint = 1;74 75 TEST_ASSERT_EQUAL_INT(1, count_savepoints(gDb));76 sqlite3CloseSavepoints(gDb);77 78 TEST_ASSERT_NULL(gDb->pSavepoint);79 TEST_ASSERT_EQUAL_INT(0, gDb->nSavepoint);80 TEST_ASSERT_EQUAL_INT(0, gDb->nStatement);81 TEST_ASSERT_EQUAL_INT(0, gDb->isTransactionSavepoint);82}83 84/* Test: Multiple savepoints are all freed and counters reset */85void test_sqlite3CloseSavepoints_multiple_savepoints_chain_cleared(void){86 push_savepoint(gDb, "sp1");87 push_savepoint(gDb, "sp2");88 push_savepoint(gDb, "sp3");89 gDb->nStatement = 7;90 gDb->isTransactionSavepoint = 1;91 92 TEST_ASSERT_EQUAL_INT(3, count_savepoints(gDb));93 sqlite3CloseSavepoints(gDb);94 95 TEST_ASSERT_NULL(gDb->pSavepoint);96 TEST_ASSERT_EQUAL_INT(0, gDb->nSavepoint);97 TEST_ASSERT_EQUAL_INT(0, gDb->nStatement);98 TEST_ASSERT_EQUAL_INT(0, gDb->isTransactionSavepoint);99}100 101/* Test: Calling sqlite3CloseSavepoints twice is safe (idempotent) */102void test_sqlite3CloseSavepoints_double_call_safe(void){103 push_savepoint(gDb, "a");104 push_savepoint(gDb, "b");105 106 TEST_ASSERT_TRUE(count_savepoints(gDb) >= 1);107 sqlite3CloseSavepoints(gDb);108 109 /* First call cleared everything */110 TEST_ASSERT_NULL(gDb->pSavepoint);111 TEST_ASSERT_EQUAL_INT(0, gDb->nSavepoint);112 TEST_ASSERT_EQUAL_INT(0, gDb->nStatement);113 TEST_ASSERT_EQUAL_INT(0, gDb->isTransactionSavepoint);114 115 /* Second call should be a no-op and remain consistent */116 sqlite3CloseSavepoints(gDb);117 TEST_ASSERT_NULL(gDb->pSavepoint);118 TEST_ASSERT_EQUAL_INT(0, gDb->nSavepoint);119 TEST_ASSERT_EQUAL_INT(0, gDb->nStatement);120 TEST_ASSERT_EQUAL_INT(0, gDb->isTransactionSavepoint);121}122 123/* Test: Counters reset even if nSavepoint does not match actual chain length */124void test_sqlite3CloseSavepoints_mismatched_count_still_resets(void){125 push_savepoint(gDb, "s1");126 push_savepoint(gDb, "s2");127 push_savepoint(gDb, "s3");128 129 /* Deliberately set an incorrect nSavepoint to simulate inconsistency */130 gDb->nSavepoint = 1234;131 gDb->nStatement = 9;132 gDb->isTransactionSavepoint = 1;133 134 TEST_ASSERT_EQUAL_INT(3, count_savepoints(gDb));135 sqlite3CloseSavepoints(gDb);136 137 TEST_ASSERT_NULL(gDb->pSavepoint);138 TEST_ASSERT_EQUAL_INT(0, gDb->nSavepoint);139 TEST_ASSERT_EQUAL_INT(0, gDb->nStatement);140 TEST_ASSERT_EQUAL_INT(0, gDb->isTransactionSavepoint);141}142 143/* Test: Large chain of savepoints is fully cleared */144void test_sqlite3CloseSavepoints_large_chain(void){145 const int N = 100;146 char name[32];147 148 for(int i = 0; i < N; i++){149 snprintf(name, sizeof(name), "sp_%d", i);150 push_savepoint(gDb, name);151 }152 gDb->nStatement = 5;153 gDb->isTransactionSavepoint = 1;154 155 TEST_ASSERT_EQUAL_INT(N, count_savepoints(gDb));156 sqlite3CloseSavepoints(gDb);157 158 TEST_ASSERT_NULL(gDb->pSavepoint);159 TEST_ASSERT_EQUAL_INT(0, gDb->nSavepoint);160 TEST_ASSERT_EQUAL_INT(0, gDb->nStatement);161 TEST_ASSERT_EQUAL_INT(0, gDb->isTransactionSavepoint);162}163 164int main(void) {165 UNITY_BEGIN();166 RUN_TEST(test_sqlite3CloseSavepoints_no_savepoints_resets_counters);167 RUN_TEST(test_sqlite3CloseSavepoints_single_savepoint_freed_and_counters_reset);168 RUN_TEST(test_sqlite3CloseSavepoints_multiple_savepoints_chain_cleared);169 RUN_TEST(test_sqlite3CloseSavepoints_double_call_safe);170 RUN_TEST(test_sqlite3CloseSavepoints_mismatched_count_still_resets);171 RUN_TEST(test_sqlite3CloseSavepoints_large_chain);172 return UNITY_END();173}