AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4 5/* Prototype for the wrapper of the static function under test */6int test_btreeCursorWithLock(7 Btree *p, Pgno iTable, int wrFlag, struct KeyInfo *pKeyInfo, BtCursor *pCur8);9 10static sqlite3 *gDb = NULL;11static Btree *gBtree = NULL;12 13static BtCursor* alloc_zeroed_cursor(void){14 int n = sqlite3BtreeCursorSize();15 BtCursor *pCur = (BtCursor*)sqlite3_malloc(n);16 TEST_ASSERT_NOT_NULL_MESSAGE(pCur, "Failed to allocate BtCursor");17 memset(pCur, 0, n);18 sqlite3BtreeCursorZero(pCur);19 return pCur;20}21 22static void free_and_close_cursor(BtCursor *pCur){23 if( pCur ){24 if( pCur->pBtree ){25 /* Only close if it was successfully opened */26 sqlite3BtreeCloseCursor(pCur);27 }28 sqlite3_free(pCur);29 }30}31 32static void begin_read_txn(Btree *p){33 sqlite3_mutex_enter(gDb->mutex);34 int rc = sqlite3BtreeBeginTrans(p, 0, NULL);35 sqlite3_mutex_leave(gDb->mutex);36 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);37}38 39static void begin_write_txn(Btree *p){40 sqlite3_mutex_enter(gDb->mutex);41 int rc = sqlite3BtreeBeginTrans(p, 1, NULL);42 sqlite3_mutex_leave(gDb->mutex);43 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);44}45 46static Pgno create_table(Btree *p){47 Pgno iTable = 0;48 sqlite3_mutex_enter(gDb->mutex);49 begin_write_txn(p);50 int rc = sqlite3BtreeCreateTable(p, &iTable, BTREE_INTKEY|BTREE_LEAFDATA);51 sqlite3_mutex_leave(gDb->mutex);52 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);53 TEST_ASSERT_TRUE_MESSAGE(iTable>=2, "Expected a valid root page number >=2");54 return iTable;55}56 57void setUp(void) {58 int rc = sqlite3_open(":memory:", &gDb);59 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);60 /* Get the main database btree */61 gBtree = gDb->aDb[0].pBt;62 TEST_ASSERT_NOT_NULL_MESSAGE(gBtree, "Main Btree should be available");63}64 65void tearDown(void) {66 if( gDb ){67 sqlite3_close(gDb);68 gDb = NULL;69 gBtree = NULL;70 }71}72 73/* Test creating a read-only cursor on a new table */74void test_btreeCursorWithLock_read_cursor_ok(void){75 Pgno root = create_table(gBtree);76 BtCursor *c1 = alloc_zeroed_cursor();77 78 begin_write_txn(gBtree); /* Having a write txn still satisfies read txn requirement */79 sqlite3_mutex_enter(gDb->mutex);80 int rc = test_btreeCursorWithLock(gBtree, root, 0, NULL, c1);81 sqlite3_mutex_leave(gDb->mutex);82 83 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);84 TEST_ASSERT_EQUAL_UINT32(root, c1->pgnoRoot);85 TEST_ASSERT_EQUAL_INT(0, (c1->curFlags & BTCF_WriteFlag)!=0);86 TEST_ASSERT_EQUAL_INT(PAGER_GET_READONLY, c1->curPagerFlags);87 88 free_and_close_cursor(c1);89}90 91/* Test creating a write cursor on a new table */92void test_btreeCursorWithLock_write_cursor_ok_and_tmpSpace(void){93 Pgno root = create_table(gBtree);94 BtCursor *c2 = alloc_zeroed_cursor();95 96 begin_write_txn(gBtree);97 sqlite3_mutex_enter(gDb->mutex);98 int rc = test_btreeCursorWithLock(gBtree, root, BTREE_WRCSR, NULL, c2);99 sqlite3_mutex_leave(gDb->mutex);100 101 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);102 TEST_ASSERT_TRUE_MESSAGE((c2->curFlags & BTCF_WriteFlag)!=0, "Write flag not set on cursor");103 TEST_ASSERT_NOT_NULL_MESSAGE(gBtree->pBt->pTmpSpace, "pTmpSpace should be allocated for write cursor");104 105 free_and_close_cursor(c2);106}107 108/* Test multiple cursors on the same table set BTCF_Multiple flag on both */109void test_btreeCursorWithLock_multiple_cursors_flag_set(void){110 Pgno root = create_table(gBtree);111 112 BtCursor *c1 = alloc_zeroed_cursor();113 BtCursor *c2 = alloc_zeroed_cursor();114 115 begin_write_txn(gBtree);116 117 sqlite3_mutex_enter(gDb->mutex);118 int rc1 = test_btreeCursorWithLock(gBtree, root, 0, NULL, c1);119 sqlite3_mutex_leave(gDb->mutex);120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc1);121 /* First cursor alone might not have BTCF_Multiple yet */122 123 sqlite3_mutex_enter(gDb->mutex);124 int rc2 = test_btreeCursorWithLock(gBtree, root, 0, NULL, c2);125 sqlite3_mutex_leave(gDb->mutex);126 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc2);127 128 /* Now both should have BTCF_Multiple */129 TEST_ASSERT_TRUE_MESSAGE((c1->curFlags & BTCF_Multiple)!=0, "First cursor missing BTCF_Multiple");130 TEST_ASSERT_TRUE_MESSAGE((c2->curFlags & BTCF_Multiple)!=0, "Second cursor missing BTCF_Multiple");131 132 free_and_close_cursor(c2);133 free_and_close_cursor(c1);134}135 136/* Test invalid iTable less than 1 returns error and does not modify cursor */137void test_btreeCursorWithLock_invalid_root_page_error(void){138 BtCursor *c = alloc_zeroed_cursor();139 140 begin_read_txn(gBtree);141 sqlite3_mutex_enter(gDb->mutex);142 int rc = test_btreeCursorWithLock(gBtree, 0, 0, NULL, c);143 sqlite3_mutex_leave(gDb->mutex);144 145 TEST_ASSERT_NOT_EQUAL_INT(SQLITE_OK, rc);146 TEST_ASSERT_NULL_MESSAGE(c->pBtree, "Cursor should not be initialized on failure");147 TEST_ASSERT_NULL_MESSAGE(c->pBt, "Cursor should not be initialized on failure");148 free_and_close_cursor(c);149}150 151/* Test empty database with iTable=1 and read-only cursor */152void test_btreeCursorWithLock_empty_db_root1_read_ok_pgnoRoot_zero(void){153 /* Open a fresh connection to ensure empty database state */154 sqlite3 *db2 = NULL;155 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db2));156 Btree *bt2 = db2->aDb[0].pBt;157 TEST_ASSERT_NOT_NULL(bt2);158 159 BtCursor *c = alloc_zeroed_cursor();160 161 /* Begin read transaction to initialize pPage1 and keep nPage==0 */162 sqlite3_mutex_enter(db2->mutex);163 int rcBegin = sqlite3BtreeBeginTrans(bt2, 0, NULL);164 sqlite3_mutex_leave(db2->mutex);165 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rcBegin);166 167 sqlite3_mutex_enter(db2->mutex);168 int rc = test_btreeCursorWithLock(bt2, 1, 0, NULL, c);169 sqlite3_mutex_leave(db2->mutex);170 171 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);172 TEST_ASSERT_EQUAL_UINT32(0u, c->pgnoRoot); /* Expect 0 due to empty db special-case */173 174 free_and_close_cursor(c);175 sqlite3_close(db2);176}177 178int main(void) {179 UNITY_BEGIN();180 RUN_TEST(test_btreeCursorWithLock_read_cursor_ok);181 RUN_TEST(test_btreeCursorWithLock_write_cursor_ok_and_tmpSpace);182 RUN_TEST(test_btreeCursorWithLock_multiple_cursors_flag_set);183 RUN_TEST(test_btreeCursorWithLock_invalid_root_page_error);184 RUN_TEST(test_btreeCursorWithLock_empty_db_root1_read_ok_pgnoRoot_zero);185 return UNITY_END();186}