AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Simple comparator implementations for registration */7static int cmp_by_mem(void* pCtx, int n1, const void* s1, int n2, const void* s2){8 int c = memcmp(s1, s2, (n1<n2)?n1:n2);9 if( c ) return c;10 return (n1 - n2);11}12static int cmp_by_mem_rev(void* pCtx, int n1, const void* s1, int n2, const void* s2){13 /* Reverse of cmp_by_mem for distinction */14 int c = memcmp(s2, s1, (n1<n2)?n1:n2);15 if( c ) return c;16 return (n2 - n1);17}18 19/* Destructor helpers */20static void del_inc(void* p){21 if( p ) (*(int*)p)++;22}23static void del_inc2(void* p){24 if( p ) (*(int*)p)++;25}26 27/* Unity setUp/tearDown */28void setUp(void) {29 /* No global setup required */30}31void tearDown(void) {32 /* No global teardown required */33}34 35/* Utility to open an in-memory database and assert success */36static sqlite3* open_db_or_fail(void){37 sqlite3 *db = 0;38 int rc = sqlite3_open(":memory:", &db);39 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");40 TEST_ASSERT_NOT_NULL(db);41 return db;42}43 44static CollSeq* find_coll(sqlite3* db, int enc, const char* zName){45 return sqlite3FindCollSeq(db, (u8)enc, zName, 0);46}47 48/* Test: basic UTF-8 registration succeeds and sets fields */49void test_sqlite3_create_collation_v2_basic_utf8_ok(void){50 sqlite3 *db = open_db_or_fail();51 const char *name = "coll_basic_utf8";52 int rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, 0, cmp_by_mem, 0);53 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);54 55 CollSeq *p = find_coll(db, SQLITE_UTF8, name);56 TEST_ASSERT_NOT_NULL(p);57 TEST_ASSERT_EQUAL_PTR((void*)cmp_by_mem, (void*)p->xCmp);58 TEST_ASSERT_EQUAL_PTR(0, p->pUser);59 TEST_ASSERT_EQUAL_PTR(0, (void*)p->xDel);60 61 sqlite3_close(db);62}63 64/* Test: SQLITE_UTF16 alias maps to native encoding and comparator is set */65void test_sqlite3_create_collation_v2_utf16_alias_mapping(void){66 sqlite3 *db = open_db_or_fail();67 const char *name = "coll_utf16_alias";68 int rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF16, 0, cmp_by_mem, 0);69 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);70 71 /* Should be stored under native UTF16 encoding */72 CollSeq *p = find_coll(db, SQLITE_UTF16NATIVE, name);73 TEST_ASSERT_NOT_NULL(p);74 TEST_ASSERT_EQUAL_PTR((void*)cmp_by_mem, (void*)p->xCmp);75 76 sqlite3_close(db);77}78 79/* Test: SQLITE_UTF16_ALIGNED preserves aligned bit in stored CollSeq->enc */80void test_sqlite3_create_collation_v2_utf16_aligned_bit_preserved(void){81 sqlite3 *db = open_db_or_fail();82 const char *name = "coll_utf16_aligned";83 int rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF16_ALIGNED, 0, cmp_by_mem, 0);84 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);85 86 CollSeq *p = find_coll(db, SQLITE_UTF16NATIVE, name);87 TEST_ASSERT_NOT_NULL(p);88 TEST_ASSERT_EQUAL_PTR((void*)cmp_by_mem, (void*)p->xCmp);89 /* Verify aligned bit is set in p->enc */90 TEST_ASSERT_TRUE( (p->enc & SQLITE_UTF16_ALIGNED) != 0 );91 92 sqlite3_close(db);93}94 95/* Test: invalid encoding returns SQLITE_MISUSE */96void test_sqlite3_create_collation_v2_invalid_encoding_returns_misuse(void){97 sqlite3 *db = open_db_or_fail();98 const char *name = "coll_invalid_enc";99 int rc = sqlite3_create_collation_v2(db, name, 0 /* invalid */, 0, cmp_by_mem, 0);100 TEST_ASSERT_EQUAL_INT(SQLITE_MISUSE, rc);101 sqlite3_close(db);102}103 104/* Test: replacing an existing collation calls the old xDel exactly once */105void test_sqlite3_create_collation_v2_replace_calls_destructor_once(void){106 sqlite3 *db = open_db_or_fail();107 const char *name = "coll_replace_del";108 int delCount = 0;109 int rc;110 111 rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, &delCount, cmp_by_mem, del_inc);112 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);113 114 /* Replace with a different comparator; use xDel=NULL so close() won't affect delCount */115 rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, 0, cmp_by_mem_rev, 0);116 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);117 118 /* Old destructor should have been called exactly once */119 TEST_ASSERT_EQUAL_INT(1, delCount);120 121 /* Verify the collation now points to the new comparator */122 CollSeq *p = find_coll(db, SQLITE_UTF8, name);123 TEST_ASSERT_NOT_NULL(p);124 TEST_ASSERT_EQUAL_PTR((void*)cmp_by_mem_rev, (void*)p->xCmp);125 126 sqlite3_close(db);127}128 129/* Test: attempting to replace while there is an active VM returns SQLITE_BUSY and does not call xDel */130void test_sqlite3_create_collation_v2_busy_when_active_vm(void){131 sqlite3 *db = open_db_or_fail();132 const char *name = "coll_busy_case";133 int delCount = 0;134 int rc;135 136 rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, &delCount, cmp_by_mem, del_inc);137 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);138 139 /* Create an active VM by stepping a simple SELECT to SQLITE_ROW and not finalizing yet */140 sqlite3_stmt *pStmt = 0;141 rc = sqlite3_prepare_v2(db, "SELECT 1", -1, &pStmt, 0);142 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);143 rc = sqlite3_step(pStmt);144 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);145 146 /* Now attempt to replace the collation: should return SQLITE_BUSY */147 rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, 0, cmp_by_mem_rev, 0);148 TEST_ASSERT_EQUAL_INT(SQLITE_BUSY, rc);149 /* Destructor must not have been called */150 TEST_ASSERT_EQUAL_INT(0, delCount);151 152 /* Finalize the statement to clear the active VM */153 rc = sqlite3_finalize(pStmt);154 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);155 156 /* Now replacement should succeed; use xDel=NULL so we don't affect delCount on close */157 rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, 0, cmp_by_mem_rev, 0);158 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);159 /* Old destructor should now have been called exactly once */160 TEST_ASSERT_EQUAL_INT(1, delCount);161 162 sqlite3_close(db);163}164 165/* Test: deleting a collation (xCompare==NULL) calls destructor and clears xCmp */166void test_sqlite3_create_collation_v2_delete_collation_clears_and_calls_destructor(void){167 sqlite3 *db = open_db_or_fail();168 const char *name = "coll_delete_case";169 int delCount = 0;170 int rc;171 172 rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, &delCount, cmp_by_mem, del_inc);173 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);174 175 /* Ensure it exists */176 CollSeq *p = find_coll(db, SQLITE_UTF8, name);177 TEST_ASSERT_NOT_NULL(p);178 TEST_ASSERT_NOT_NULL(p->xCmp);179 180 /* Delete by setting xCompare==NULL */181 rc = sqlite3_create_collation_v2(db, name, SQLITE_UTF8, 0, 0, 0);182 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);183 184 /* Destructor called once */185 TEST_ASSERT_EQUAL_INT(1, delCount);186 187 /* Verify xCmp is cleared */188 p = find_coll(db, SQLITE_UTF8, name);189 TEST_ASSERT_NOT_NULL(p);190 TEST_ASSERT_EQUAL_PTR(0, (void*)p->xCmp);191 192 sqlite3_close(db);193}194 195int main(void) {196 UNITY_BEGIN();197 RUN_TEST(test_sqlite3_create_collation_v2_basic_utf8_ok);198 RUN_TEST(test_sqlite3_create_collation_v2_utf16_alias_mapping);199 RUN_TEST(test_sqlite3_create_collation_v2_utf16_aligned_bit_preserved);200 RUN_TEST(test_sqlite3_create_collation_v2_invalid_encoding_returns_misuse);201 RUN_TEST(test_sqlite3_create_collation_v2_replace_calls_destructor_once);202 RUN_TEST(test_sqlite3_create_collation_v2_busy_when_active_vm);203 RUN_TEST(test_sqlite3_create_collation_v2_delete_collation_clears_and_calls_destructor);204 return UNITY_END();205}