AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdlib.h>5#include <string.h>6 7/* ----------------- Custom allocator used for testing ----------------- */8 9static volatile unsigned gMallocCount = 0;10static volatile unsigned gFreeCount = 0;11static volatile unsigned gReallocCount = 0;12static volatile unsigned gInitCount = 0;13static volatile unsigned gShutdownCount = 0;14 15/* Store size in a header just before the returned pointer. */16typedef struct CustomHdr {17 int n;18} CustomHdr;19 20static void *custom_xMalloc(int n){21 if( n<=0 ) n = 1;22 gMallocCount++;23 CustomHdr *h = (CustomHdr*)malloc(sizeof(CustomHdr) + (size_t)n);24 if( !h ) return NULL;25 h->n = n;26 return (void*)((unsigned char*)h + sizeof(CustomHdr));27}28 29static void custom_xFree(void *p){30 if( !p ) return;31 gFreeCount++;32 unsigned char *base = (unsigned char*)p - sizeof(CustomHdr);33 free(base);34}35 36static void *custom_xRealloc(void *p, int n){37 if( !p ){38 return custom_xMalloc(n);39 }40 if( n<=0 ){41 custom_xFree(p);42 return NULL;43 }44 gReallocCount++;45 unsigned char *base = (unsigned char*)p - sizeof(CustomHdr);46 CustomHdr *oldh = (CustomHdr*)base;47 size_t newSize = sizeof(CustomHdr) + (size_t)n;48 CustomHdr *newh = (CustomHdr*)realloc(oldh, newSize);49 if( !newh ) return NULL;50 newh->n = n;51 return (void*)((unsigned char*)newh + sizeof(CustomHdr));52}53 54static int custom_xSize(void *p){55 if( !p ) return 0;56 CustomHdr *h = (CustomHdr*)((unsigned char*)p - sizeof(CustomHdr));57 return h->n;58}59 60static int custom_xRoundup(int n){61 if( n<=0 ) return 0;62 return (n + 7) & ~7;63}64 65static int custom_xInit(void *NotUsed){66 (void)NotUsed;67 gInitCount++;68 return SQLITE_OK;69}70 71static void custom_xShutdown(void *NotUsed){72 (void)NotUsed;73 gShutdownCount++;74}75 76/* Global instance describing the custom allocator */77static sqlite3_mem_methods gCustomMethods = {78 custom_xMalloc,79 custom_xFree,80 custom_xRealloc,81 custom_xSize,82 custom_xRoundup,83 custom_xInit,84 custom_xShutdown,85 086};87 88/* Helper to zero counters */89static void reset_counters(void){90 gMallocCount = 0;91 gFreeCount = 0;92 gReallocCount = 0;93 gInitCount = 0;94 gShutdownCount = 0;95}96 97/* Helper to install the custom allocator (must be called while shut down) */98static void install_custom_allocator(void){99 int rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &gCustomMethods);100 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101}102 103/* Helper to fetch current allocator into out parameter */104static void get_current_allocator(sqlite3_mem_methods *pOut){105 memset(pOut, 0, sizeof(*pOut));106 int rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, pOut);107 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);108}109 110/* ----------------- Unity fixtures ----------------- */111 112void setUp(void) {113 /* Ensure a clean state for each test */114 sqlite3_shutdown();115 reset_counters();116 /* Start each test with default allocator unless the test changes it */117 sqlite3MemSetDefault();118}119 120void tearDown(void) {121 /* Reset to default allocator to avoid cross-test contamination */122 sqlite3_shutdown();123 sqlite3MemSetDefault();124}125 126/* ----------------- Tests ----------------- */127 128/* Test that sqlite3MemSetDefault replaces a custom allocator when allowed */129void test_sqlite3MemSetDefault_replaces_custom_allocator_after_shutdown(void){130 sqlite3_shutdown();131 reset_counters();132 133 /* Install custom allocator and verify it's active */134 install_custom_allocator();135 sqlite3_mem_methods before = {0};136 get_current_allocator(&before);137 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xMalloc, before.xMalloc);138 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xFree, before.xFree);139 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xRealloc, before.xRealloc);140 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xRoundup, before.xRoundup);141 142 /* Now set default allocator and verify it's no longer the custom one */143 sqlite3MemSetDefault();144 sqlite3_mem_methods after = {0};145 get_current_allocator(&after);146 TEST_ASSERT_NOT_EQUAL(gCustomMethods.xMalloc, after.xMalloc);147 TEST_ASSERT_NOT_EQUAL(gCustomMethods.xFree, after.xFree);148 TEST_ASSERT_NOT_EQUAL(gCustomMethods.xRealloc, after.xRealloc);149 TEST_ASSERT_NOT_EQUAL(gCustomMethods.xRoundup, after.xRoundup);150 151 /* Alloc/free something; counters should not increment (custom not in use) */152 unsigned beforeMallocs = gMallocCount;153 unsigned beforeFrees = gFreeCount;154 void *p = sqlite3_malloc(37);155 TEST_ASSERT_NOT_NULL(p);156 sqlite3_free(p);157 TEST_ASSERT_EQUAL_UINT(beforeMallocs, gMallocCount);158 TEST_ASSERT_EQUAL_UINT(beforeFrees, gFreeCount);159 160 /* Sanity-check xRoundup behavior matches ROUND8 macro after default */161 TEST_ASSERT_EQUAL_INT(ROUND8(1), after.xRoundup(1));162 TEST_ASSERT_EQUAL_INT(ROUND8(7), after.xRoundup(7));163 TEST_ASSERT_EQUAL_INT(ROUND8(8), after.xRoundup(8));164 TEST_ASSERT_EQUAL_INT(ROUND8(15), after.xRoundup(15));165}166 167/* Test that calling sqlite3MemSetDefault multiple times is idempotent */168void test_sqlite3MemSetDefault_idempotent_multiple_calls(void){169 sqlite3_shutdown();170 171 sqlite3MemSetDefault();172 sqlite3_mem_methods m1 = {0};173 get_current_allocator(&m1);174 175 sqlite3MemSetDefault();176 sqlite3_mem_methods m2 = {0};177 get_current_allocator(&m2);178 179 TEST_ASSERT_EQUAL_PTR(m1.xMalloc, m2.xMalloc);180 TEST_ASSERT_EQUAL_PTR(m1.xFree, m2.xFree);181 TEST_ASSERT_EQUAL_PTR(m1.xRealloc, m2.xRealloc);182 TEST_ASSERT_EQUAL_PTR(m1.xSize, m2.xSize);183 TEST_ASSERT_EQUAL_PTR(m1.xRoundup, m2.xRoundup);184 TEST_ASSERT_EQUAL_PTR(m1.xInit, m2.xInit);185 TEST_ASSERT_EQUAL_PTR(m1.xShutdown, m2.xShutdown);186}187 188/* Test that calling sqlite3MemSetDefault while initialized does not switch allocator */189void test_sqlite3MemSetDefault_during_initialized_state_no_effect(void){190 sqlite3_shutdown();191 reset_counters();192 193 install_custom_allocator();194 195 /* Initialize SQLite implicitly via allocation */196 void *p1 = sqlite3_malloc(24);197 TEST_ASSERT_NOT_NULL(p1);198 sqlite3_free(p1);199 unsigned baseMallocs = gMallocCount;200 unsigned baseFrees = gFreeCount;201 202 /* Attempt to switch to default while initialized */203 sqlite3MemSetDefault();204 205 /* Another allocation; it should still use the custom allocator */206 void *p2 = sqlite3_malloc(64);207 TEST_ASSERT_NOT_NULL(p2);208 sqlite3_free(p2);209 210 TEST_ASSERT_TRUE(gMallocCount > baseMallocs);211 TEST_ASSERT_TRUE(gFreeCount > baseFrees);212 213 /* Additionally, GETMALLOC should still report the custom allocator */214 sqlite3_mem_methods cur = {0};215 get_current_allocator(&cur);216 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xMalloc, cur.xMalloc);217 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xFree, cur.xFree);218 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xRealloc, cur.xRealloc);219 TEST_ASSERT_EQUAL_PTR(gCustomMethods.xRoundup, cur.xRoundup);220}221 222/* ----------------- Unity main ----------------- */223 224int main(void) {225 UNITY_BEGIN();226 RUN_TEST(test_sqlite3MemSetDefault_replaces_custom_allocator_after_shutdown);227 RUN_TEST(test_sqlite3MemSetDefault_idempotent_multiple_calls);228 RUN_TEST(test_sqlite3MemSetDefault_during_initialized_state_no_effect);229 return UNITY_END();230}