File size: 4,963 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
#include "sqliteInt.h"
#include "unity.h"
#include <string.h>

/* The wrapper for the static function under test (provided in the module). */
extern void test_renameParseCleanup(Parse *pParse);

static void init_parse_with_allocs(sqlite3 *db, Parse *pParse){
  memset(pParse, 0, sizeof(*pParse));
  pParse->db = db;

  /* Create a VDBE so that finalize is exercised. */
  pParse->pVdbe = sqlite3VdbeCreate(db);

  /* Allocate a new Table */
  pParse->pNewTable = (Table*)sqlite3DbMallocZero(db, sizeof(Table));

  /* Allocate a linked list of Index objects */
  Index *pIdx1 = (Index*)sqlite3DbMallocZero(db, sizeof(Index));
  Index *pIdx2 = (Index*)sqlite3DbMallocZero(db, sizeof(Index));
  if( pIdx1 ) pIdx1->pNext = pIdx2;
  pParse->pNewIndex = pIdx1;

  /* Allocate a Trigger */
  pParse->pNewTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));

  /* Allocate an error message */
  pParse->zErrMsg = sqlite3DbStrDup(db, "unit-test-error");

  /* Allocate a linked list of RenameToken */
  RenameToken *t1 = (RenameToken*)sqlite3DbMallocZero(db, sizeof(RenameToken));
  RenameToken *t2 = (RenameToken*)sqlite3DbMallocZero(db, sizeof(RenameToken));
  if( t1 ) t1->pNext = t2;
  pParse->pRename = t1;
}

void setUp(void) {
  /* No-op */
}

void tearDown(void) {
  /* No-op */
}

static void assert_parse_pointers_cleared(Parse *p){
  TEST_ASSERT_NULL(p->pVdbe);
  TEST_ASSERT_NULL(p->pNewTable);
  TEST_ASSERT_NULL(p->pNewIndex);
  TEST_ASSERT_NULL(p->pNewTrigger);
  TEST_ASSERT_NULL(p->zErrMsg);
  TEST_ASSERT_NULL(p->pRename);
}

/* Test that renameParseCleanup frees all populated fields and resets them. */
void test_renameParseCleanup_clears_all_fields(void){
  sqlite3 *db = 0;
  int rc = sqlite3_open(":memory:", &db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(db);

  Parse p;
  init_parse_with_allocs(db, &p);

  test_renameParseCleanup(&p);

  /* After cleanup, all relevant pointers should be NULL. */
  assert_parse_pointers_cleared(&p);

  /* Database should be cleanly closable (VDBE finalized). */
  rc = sqlite3_close(db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
}

/* Test that renameParseCleanup is safe when all fields are NULL. */
void test_renameParseCleanup_handles_null_fields(void){
  sqlite3 *db = 0;
  int rc = sqlite3_open(":memory:", &db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(db);

  Parse p;
  memset(&p, 0, sizeof(p));
  p.db = db;

  /* Everything NULL already */
  test_renameParseCleanup(&p);

  /* Still NULL after cleanup */
  assert_parse_pointers_cleared(&p);

  rc = sqlite3_close(db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
}

/* Test with longer lists of Index and RenameToken to ensure full traversal/free. */
void test_renameParseCleanup_multiple_indexes_and_tokens(void){
  sqlite3 *db = 0;
  int rc = sqlite3_open(":memory:", &db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(db);

  Parse p;
  memset(&p, 0, sizeof(p));
  p.db = db;

  /* Create a VDBE */
  p.pVdbe = sqlite3VdbeCreate(db);

  /* Create 3 Index nodes */
  Index *i1 = (Index*)sqlite3DbMallocZero(db, sizeof(Index));
  Index *i2 = (Index*)sqlite3DbMallocZero(db, sizeof(Index));
  Index *i3 = (Index*)sqlite3DbMallocZero(db, sizeof(Index));
  if( i1 ) i1->pNext = i2;
  if( i2 ) i2->pNext = i3;
  p.pNewIndex = i1;

  /* Create 3 RenameToken nodes */
  RenameToken *t1 = (RenameToken*)sqlite3DbMallocZero(db, sizeof(RenameToken));
  RenameToken *t2 = (RenameToken*)sqlite3DbMallocZero(db, sizeof(RenameToken));
  RenameToken *t3 = (RenameToken*)sqlite3DbMallocZero(db, sizeof(RenameToken));
  if( t1 ) t1->pNext = t2;
  if( t2 ) t2->pNext = t3;
  p.pRename = t1;

  /* Also set a table, trigger, and error message */
  p.pNewTable = (Table*)sqlite3DbMallocZero(db, sizeof(Table));
  p.pNewTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
  p.zErrMsg = sqlite3DbStrDup(db, "err");

  test_renameParseCleanup(&p);

  /* Ensure all pointers are cleared */
  assert_parse_pointers_cleared(&p);

  rc = sqlite3_close(db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
}

/* Test idempotency: calling cleanup twice should be safe and keep pointers NULL. */
void test_renameParseCleanup_idempotent_double_call(void){
  sqlite3 *db = 0;
  int rc = sqlite3_open(":memory:", &db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(db);

  Parse p;
  init_parse_with_allocs(db, &p);

  /* First cleanup */
  test_renameParseCleanup(&p);
  assert_parse_pointers_cleared(&p);

  /* Second cleanup - should be a no-op and not crash */
  test_renameParseCleanup(&p);
  assert_parse_pointers_cleared(&p);

  rc = sqlite3_close(db);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
}

int main(void){
  UNITY_BEGIN();
  RUN_TEST(test_renameParseCleanup_clears_all_fields);
  RUN_TEST(test_renameParseCleanup_handles_null_fields);
  RUN_TEST(test_renameParseCleanup_multiple_indexes_and_tokens);
  RUN_TEST(test_renameParseCleanup_idempotent_double_call);
  return UNITY_END();
}