File size: 5,840 Bytes
7510827 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
#include "sqliteInt.h"
#include "unity.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/* Wrapper provided for static function under test */
extern void test_sqlite3ErrorIfNotEmpty(
Parse *pParse,
const char *zDb,
const char *zTab,
const char *zErr
);
static sqlite3 *gDb = NULL;
/* Helpers */
static void exec_or_fail(const char *zSql){
char *zErr = NULL;
int rc = sqlite3_exec(gDb, zSql, 0, 0, &zErr);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL failed: %s\n rc=%d err=%s\n", zSql, rc, zErr?zErr:"(null)");
}
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "exec_or_fail SQL error");
if( zErr ) sqlite3_free(zErr);
}
static void init_parse(Parse *p){
memset(p, 0, sizeof(*p));
p->db = gDb;
}
static void cleanup_parse(Parse *p){
if( p->pVdbe ){
sqlite3VdbeDelete(p->pVdbe);
p->pVdbe = NULL;
}
if( p->zErrMsg ){
sqlite3_free(p->zErrMsg);
p->zErrMsg = NULL;
}
}
/* Unity required hooks */
void setUp(void) {
int rc = sqlite3_open(":memory:", &gDb);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
}
void tearDown(void) {
if( gDb ){
int rc = sqlite3_close(gDb);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
gDb = NULL;
}
}
/* Tests */
void test_sqlite3ErrorIfNotEmpty_empty_table_executes_without_error(void){
/* Setup schema: empty table */
exec_or_fail("CREATE TABLE t(a)");
/* Call target wrapper to build nested parse (ensures it handles normal names) */
Parse s = {0};
init_parse(&s);
const char *zErr = "table is not empty";
test_sqlite3ErrorIfNotEmpty(&s, "main", "t", zErr);
cleanup_parse(&s);
/* Validate functional intent by running the equivalent SQL directly */
char *zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
zErr, "main", "t");
TEST_ASSERT_NOT_NULL(zSql);
char *zExecErr = NULL;
int rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);
/* On empty table, this should succeed with SQLITE_OK and no error message */
if( rc!=SQLITE_OK && zExecErr ){
fprintf(stderr, "Unexpected error on empty table: %s\n", zExecErr);
}
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
TEST_ASSERT_NULL(zExecErr);
sqlite3_free(zSql);
}
void test_sqlite3ErrorIfNotEmpty_nonempty_table_raises_error_with_message(void){
/* Setup schema: table with one row */
exec_or_fail("CREATE TABLE t2(a)");
exec_or_fail("INSERT INTO t2(a) VALUES(123)");
/* Call target wrapper */
Parse s = {0};
init_parse(&s);
const char *zErr = "cannot proceed: table has rows";
test_sqlite3ErrorIfNotEmpty(&s, "main", "t2", zErr);
cleanup_parse(&s);
/* Validate behavior by executing equivalent SQL directly */
char *zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
zErr, "main", "t2");
TEST_ASSERT_NOT_NULL(zSql);
char *zExecErr = NULL;
int rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);
/* Expect an error. Historically, RAISE(ABORT, ...) maps to a constraint error.
So just assert that it is not SQLITE_OK and that the message contains our text. */
TEST_ASSERT_NOT_EQUAL(INT, SQLITE_OK, rc);
TEST_ASSERT_NOT_NULL(zExecErr);
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(zExecErr, zErr), "Error message should contain the provided zErr text");
sqlite3_free(zExecErr);
sqlite3_free(zSql);
}
void test_sqlite3ErrorIfNotEmpty_identifier_quoting_with_special_table_name(void){
/* Create a table name with quotes and a dot to verify proper identifier escaping */
const char *zTab = "weird\"name.dot";
char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\"(a)", zTab);
TEST_ASSERT_NOT_NULL(zCreate);
exec_or_fail(zCreate);
sqlite3_free(zCreate);
/* Call target wrapper using an error message with embedded quotes to exercise %Q */
Parse s = {0};
init_parse(&s);
const char *zErr = "msg with \"quotes\" and 'single quotes'";
test_sqlite3ErrorIfNotEmpty(&s, "main", zTab, zErr);
cleanup_parse(&s);
/* Empty table: executing the equivalent SQL should succeed */
char *zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
zErr, "main", zTab);
TEST_ASSERT_NOT_NULL(zSql);
char *zExecErr = NULL;
int rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
TEST_ASSERT_NULL(zExecErr);
sqlite3_free(zSql);
/* Now insert a row and confirm it fails with the message */
char *zInsert = sqlite3_mprintf("INSERT INTO \"%w\"(a) VALUES(1)", zTab);
TEST_ASSERT_NOT_NULL(zInsert);
exec_or_fail(zInsert);
sqlite3_free(zInsert);
zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
zErr, "main", zTab);
TEST_ASSERT_NOT_NULL(zSql);
rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);
TEST_ASSERT_NOT_EQUAL(INT, SQLITE_OK, rc);
TEST_ASSERT_NOT_NULL(zExecErr);
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(zExecErr, zErr), "Error message should contain zErr");
sqlite3_free(zExecErr);
sqlite3_free(zSql);
}
void test_sqlite3ErrorIfNotEmpty_nonexistent_table_sets_parse_error(void){
/* Call target wrapper for a table that does not exist to ensure parse error captured */
Parse s = {0};
init_parse(&s);
test_sqlite3ErrorIfNotEmpty(&s, "main", "no_such_table_xyz", "any error");
/* We expect an error recorded by the nested parse */
TEST_ASSERT_TRUE(s.nErr > 0);
TEST_ASSERT_NOT_NULL(s.zErrMsg);
/* Avoid asserting exact text; just ensure an error was captured */
cleanup_parse(&s);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_sqlite3ErrorIfNotEmpty_empty_table_executes_without_error);
RUN_TEST(test_sqlite3ErrorIfNotEmpty_nonempty_table_raises_error_with_message);
RUN_TEST(test_sqlite3ErrorIfNotEmpty_identifier_quoting_with_special_table_name);
RUN_TEST(test_sqlite3ErrorIfNotEmpty_nonexistent_table_sets_parse_error);
return UNITY_END();
} |