AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* The static function under test is exposed via this test wrapper */7extern void test_attachFunc(sqlite3_context *context, int NotUsed, sqlite3_value **argv);8 9/* Helpers to create and manage sqlite3_value arguments */10static sqlite3_value *makeTextValue(sqlite3 *db, const char *z){11 sqlite3_value *v = sqlite3ValueNew(db);12 TEST_ASSERT_NOT_NULL(v);13 if( z ){14 sqlite3ValueSetStr(v, -1, z, SQLITE_UTF8, SQLITE_TRANSIENT);15 }else{16 /* Leave as NULL */17 }18 return v;19}20 21static sqlite3_value *makeNullValue(sqlite3 *db){22 sqlite3_value *v = sqlite3ValueNew(db);23 TEST_ASSERT_NOT_NULL(v);24 /* Leave uninitialized to represent SQL NULL */25 return v;26}27 28static void freeValue(sqlite3_value *v){29 if( v ) sqlite3ValueFree(v);30}31 32/* Initialize a minimal sqlite3_context with a valid output Mem */33static void initContext(sqlite3 *db, sqlite3_context *pCtx, Mem *pOut){34 memset(pCtx, 0, sizeof(*pCtx));35 memset(pOut, 0, sizeof(*pOut));36 sqlite3VdbeMemInit(pOut, db, 0);37 pCtx->db = db;38 pCtx->pOut = pOut;39}40 41void setUp(void) {42 /* No global setup required */43}44void tearDown(void) {45 /* No global teardown required */46}47 48/* Utility: assert that Mem contains a string with the given needle */49static void assertMemStringContains(Mem *pOut, const char *needle){50 TEST_ASSERT_NOT_NULL(pOut);51 TEST_ASSERT_TRUE(pOut->flags & MEM_Str);52 TEST_ASSERT_NOT_NULL(pOut->z);53 TEST_ASSERT_NOT_NULL(strstr(pOut->z, needle));54}55 56/* Test: basic successful attach of an in-memory database */57void test_attachFunc_basic_attach_memory(void){58 sqlite3 *db = 0;59 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));60 TEST_ASSERT_NOT_NULL(db);61 62 int nBefore = db->nDb;63 64 /* Prepare context and argv */65 sqlite3_context ctx;66 Mem out;67 initContext(db, &ctx, &out);68 69 sqlite3_value *argv[3];70 argv[0] = makeTextValue(db, ":memory:");71 argv[1] = makeTextValue(db, "aux1");72 argv[2] = makeNullValue(db); /* no key */73 74 test_attachFunc(&ctx, 0, argv);75 76 /* Verify success: nDb incremented and new Db slot initialized */77 TEST_ASSERT_EQUAL_INT(nBefore + 1, db->nDb);78 Db *pNew = &db->aDb[nBefore];79 TEST_ASSERT_NOT_NULL(pNew->pBt);80 TEST_ASSERT_NOT_NULL(pNew->pSchema);81 TEST_ASSERT_NOT_NULL(pNew->zDbSName);82 TEST_ASSERT_EQUAL_STRING("aux1", pNew->zDbSName);83 84 /* Cleanup */85 sqlite3VdbeMemRelease(&out);86 freeValue(argv[0]);87 freeValue(argv[1]);88 freeValue(argv[2]);89 sqlite3_close(db);90}91 92/* Test: duplicate database name error */93void test_attachFunc_duplicate_name_error(void){94 sqlite3 *db = 0;95 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));96 int nBefore = db->nDb;97 98 /* First attach succeeds */99 {100 sqlite3_context ctx;101 Mem out;102 initContext(db, &ctx, &out);103 sqlite3_value *argv[3];104 argv[0] = makeTextValue(db, ":memory:");105 argv[1] = makeTextValue(db, "dupdb");106 argv[2] = makeNullValue(db);107 test_attachFunc(&ctx, 0, argv);108 TEST_ASSERT_EQUAL_INT(nBefore + 1, db->nDb);109 sqlite3VdbeMemRelease(&out);110 freeValue(argv[0]);111 freeValue(argv[1]);112 freeValue(argv[2]);113 }114 115 /* Second attach with same name should fail */116 {117 sqlite3_context ctx;118 Mem out;119 initContext(db, &ctx, &out);120 sqlite3_value *argv[3];121 argv[0] = makeTextValue(db, ":memory:");122 argv[1] = makeTextValue(db, "dupdb");123 argv[2] = makeNullValue(db);124 test_attachFunc(&ctx, 0, argv);125 126 /* nDb unchanged */127 TEST_ASSERT_EQUAL_INT(nBefore + 1, db->nDb);128 129 /* Error message should mention "already in use" */130 assertMemStringContains(&out, "already in use");131 132 sqlite3VdbeMemRelease(&out);133 freeValue(argv[0]);134 freeValue(argv[1]);135 freeValue(argv[2]);136 }137 138 sqlite3_close(db);139}140 141/* Test: limit on number of attached databases */142void test_attachFunc_too_many_attached_limit(void){143 sqlite3 *db = 0;144 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));145 int nBefore = db->nDb;146 147 /* Set limit so that any new attach is forbidden */148 db->aLimit[SQLITE_LIMIT_ATTACHED] = 0; /* max attached dbs = 0 */149 150 sqlite3_context ctx;151 Mem out;152 initContext(db, &ctx, &out);153 154 sqlite3_value *argv[3];155 argv[0] = makeTextValue(db, ":memory:");156 argv[1] = makeTextValue(db, "toolarge");157 argv[2] = makeNullValue(db);158 159 test_attachFunc(&ctx, 0, argv);160 161 /* nDb must not change */162 TEST_ASSERT_EQUAL_INT(nBefore, db->nDb);163 164 /* Error message content */165 assertMemStringContains(&out, "too many attached databases");166 167 sqlite3VdbeMemRelease(&out);168 freeValue(argv[0]);169 freeValue(argv[1]);170 freeValue(argv[2]);171 sqlite3_close(db);172}173 174/* Test: empty/NULL filename leads to open error and no state change */175void test_attachFunc_null_filename_error(void){176 sqlite3 *db = 0;177 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));178 int nBefore = db->nDb;179 180 sqlite3_context ctx;181 Mem out;182 initContext(db, &ctx, &out);183 184 sqlite3_value *argv[3];185 argv[0] = makeNullValue(db); /* NULL filename => "" */186 argv[1] = makeTextValue(db, "badfiledb");187 argv[2] = makeNullValue(db);188 189 test_attachFunc(&ctx, 0, argv);190 191 /* nDb must remain unchanged after failure */192 TEST_ASSERT_EQUAL_INT(nBefore, db->nDb);193 194 /* Message should include "unable to open database" */195 assertMemStringContains(&out, "unable to open database");196 197 sqlite3VdbeMemRelease(&out);198 freeValue(argv[0]);199 freeValue(argv[1]);200 freeValue(argv[2]);201 sqlite3_close(db);202}203 204/* Test: attempting to attach using reserved name "main" triggers name-in-use */205void test_attachFunc_main_name_in_use(void){206 sqlite3 *db = 0;207 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));208 int nBefore = db->nDb;209 210 sqlite3_context ctx;211 Mem out;212 initContext(db, &ctx, &out);213 214 sqlite3_value *argv[3];215 argv[0] = makeTextValue(db, ":memory:");216 argv[1] = makeTextValue(db, "main"); /* reserved name */217 argv[2] = makeNullValue(db);218 219 test_attachFunc(&ctx, 0, argv);220 221 /* nDb must not change */222 TEST_ASSERT_EQUAL_INT(nBefore, db->nDb);223 224 /* Error message should say already in use */225 assertMemStringContains(&out, "already in use");226 227 sqlite3VdbeMemRelease(&out);228 freeValue(argv[0]);229 freeValue(argv[1]);230 freeValue(argv[2]);231 sqlite3_close(db);232}233 234int main(void) {235 UNITY_BEGIN();236 RUN_TEST(test_attachFunc_basic_attach_memory);237 RUN_TEST(test_attachFunc_duplicate_name_error);238 RUN_TEST(test_attachFunc_too_many_attached_limit);239 RUN_TEST(test_attachFunc_null_filename_error);240 RUN_TEST(test_attachFunc_main_name_in_use);241 return UNITY_END();242}