Skip to content

Commit 3dd4ad3

Browse files
author
Chris Brody
committed
Some testing of custom corrupt database error handler functionality
1 parent b781b1a commit 3dd4ad3

File tree

2 files changed

+394
-0
lines changed

2 files changed

+394
-0
lines changed
Lines changed: 393 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,393 @@
1+
package net.zetetic.tests;
2+
3+
import android.util.Log;
4+
5+
import java.io.File;
6+
import java.io.IOException;
7+
8+
import java.util.Locale;
9+
10+
import net.sqlcipher.DatabaseErrorHandler;
11+
import net.sqlcipher.database.SQLiteDatabase;
12+
import net.sqlcipher.database.SQLiteException;
13+
import net.sqlcipher.database.SQLiteDatabaseCorruptException;
14+
15+
import net.zetetic.ZeteticApplication;
16+
17+
public class CustomCorruptionHandlerTest extends SQLCipherTest {
18+
19+
@Override
20+
public TestResult run() {
21+
22+
TestResult result = new TestResult(getName(), false);
23+
try {
24+
result.setResult(execute(null));
25+
SQLiteDatabase.releaseMemory();
26+
} catch (Exception e) {
27+
Log.v(ZeteticApplication.TAG, e.toString());
28+
}
29+
return result;
30+
}
31+
32+
@Override
33+
public boolean execute(SQLiteDatabase null_database_ignored) {
34+
35+
boolean status = false;
36+
37+
final File corruptDatabase = ZeteticApplication.getInstance().getDatabasePath("corrupt.db");
38+
39+
// ugly trick from: http://stackoverflow.com/questions/5977735/setting-outer-variable-from-anonymous-inner-class
40+
final boolean[] inner_status_slot = new boolean[1];
41+
42+
// normal recovery test with custom error handler that actually cleans it up:
43+
try {
44+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
45+
46+
// make sure custom onCorruption() function is called:
47+
SQLiteDatabase database = SQLiteDatabase.openDatabase(corruptDatabase.getPath(), "", null, SQLiteDatabase.CREATE_IF_NECESSARY, null, new DatabaseErrorHandler() {
48+
@Override
49+
public void onCorruption(SQLiteDatabase db) {
50+
try {
51+
Log.i(TAG, "Custom onCorruption() called");
52+
inner_status_slot[0] = true;
53+
} catch (Exception ex) {
54+
// Uncaught exception (not expected):
55+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
56+
}
57+
58+
try {
59+
// clean it up!
60+
corruptDatabase.delete();
61+
} catch (Exception ex) {
62+
// Uncaught exception (not expected):
63+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
64+
inner_status_slot[0] = false;
65+
}
66+
}
67+
});
68+
69+
status = inner_status_slot[0];
70+
71+
// NOTE: database not expected to be null, but double-check:
72+
if (database == null) {
73+
Log.e(TAG, "ERROR: got null database object");
74+
return false;
75+
}
76+
77+
database.close();
78+
} catch (Exception ex) {
79+
// Uncaught exception (not expected):
80+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
81+
return false;
82+
}
83+
finally {
84+
corruptDatabase.delete();
85+
}
86+
87+
if (!status) return false;
88+
inner_status_slot[0] = status = false;
89+
90+
// does not recover due to missing SQLiteDatabase.CREATE_IF_NECESSARY flag:
91+
try {
92+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
93+
94+
// make sure custom onCorruption() function is called:
95+
SQLiteDatabase database = SQLiteDatabase.openDatabase(corruptDatabase.getPath(), "", null, 0, null, new DatabaseErrorHandler() {
96+
@Override
97+
public void onCorruption(SQLiteDatabase db) {
98+
try {
99+
Log.i(TAG, "Custom onCorruption() called");
100+
inner_status_slot[0] = true;
101+
} catch (Exception ex) {
102+
// Uncaught exception (not expected):
103+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
104+
}
105+
106+
try {
107+
// clean it up!
108+
corruptDatabase.delete();
109+
} catch (Exception ex) {
110+
// Uncaught exception (not expected):
111+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
112+
inner_status_slot[0] = false;
113+
}
114+
}
115+
});
116+
117+
// should not get here:
118+
Log.e(TAG, "UNEXPECTED RESULT: recovered from corrupt database without SQLiteDatabase.CREATE_IF_NECESSARY flag");
119+
database.close();
120+
corruptDatabase.delete();
121+
return false;
122+
} catch (SQLiteException ex) {
123+
Log.i(TAG, "Could not recover, as expected OK", ex);
124+
//Log.i(TAG, "inner_status_slot[0]: " + inner_status_slot[0])
125+
status = inner_status_slot[0];
126+
} catch (Exception ex) {
127+
// Uncaught exception (not expected):
128+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
129+
return false;
130+
}
131+
132+
if (!status) return false;
133+
inner_status_slot[0] = status = false;
134+
135+
// attempt to recover with custom error handler but database is not cleaned up:
136+
try {
137+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
138+
139+
// make sure custom onCorruption() function is called:
140+
SQLiteDatabase database = SQLiteDatabase.openDatabase(corruptDatabase.getPath(), "", null, SQLiteDatabase.CREATE_IF_NECESSARY, null, new DatabaseErrorHandler() {
141+
@Override
142+
public void onCorruption(SQLiteDatabase db) {
143+
try {
144+
inner_status_slot[0] = true;
145+
} catch (Exception ex) {
146+
// Uncaught exception (not expected):
147+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
148+
}
149+
}
150+
});
151+
152+
// should not get here:
153+
Log.e(TAG, "UNEXPECTED RESULT: recovered from corrupt database that was not cleaned up");
154+
database.close();
155+
corruptDatabase.delete();
156+
return false;
157+
} catch (SQLiteException ex) {
158+
Log.i(TAG, "Could not recover, as expected OK", ex);
159+
status = inner_status_slot[0];
160+
} catch (Exception ex) {
161+
// Uncaught exception (not expected):
162+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
163+
return false;
164+
}
165+
166+
if (!status) return false;
167+
inner_status_slot[0] = status = false;
168+
169+
// custom error handler throws:
170+
try {
171+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
172+
173+
// make sure custom onCorruption() function is called:
174+
SQLiteDatabase database = SQLiteDatabase.openDatabase(corruptDatabase.getPath(), "", null, SQLiteDatabase.CREATE_IF_NECESSARY, null, new DatabaseErrorHandler() {
175+
@Override
176+
public void onCorruption(SQLiteDatabase db) {
177+
try {
178+
inner_status_slot[0] = true;
179+
} catch (Exception ex) {
180+
// Uncaught exception (not expected):
181+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
182+
}
183+
184+
throw new RuntimeException("abort");
185+
}
186+
});
187+
188+
// should not get here:
189+
Log.e(TAG, "UNEXPECTED RESULT: recovered from corrupt database that was not cleaned up");
190+
database.close();
191+
corruptDatabase.delete();
192+
return false;
193+
} catch (RuntimeException ex) {
194+
Log.v(TAG, "Caught RuntimeException as thrown by custom error handler OK");
195+
status = inner_status_slot[0];
196+
} catch (Exception ex) {
197+
// Uncaught exception (not expected):
198+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
199+
return false;
200+
}
201+
202+
if (!status) return false;
203+
inner_status_slot[0] = status = false;
204+
205+
// extra fun:
206+
final boolean[] inner_fun_slot = new boolean[1];
207+
208+
// tell custom error handler NOT to delete corrupt database during sql query:
209+
try {
210+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
211+
212+
// make sure custom onCorruption() function is called:
213+
SQLiteDatabase database = SQLiteDatabase.openDatabase(corruptDatabase.getPath(), "", null, SQLiteDatabase.CREATE_IF_NECESSARY, null, new DatabaseErrorHandler() {
214+
@Override
215+
public void onCorruption(SQLiteDatabase db) {
216+
try {
217+
Log.i(TAG, "Custom onCorruption() called");
218+
inner_status_slot[0] = true;
219+
} catch (Exception ex) {
220+
// Uncaught exception (not expected):
221+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
222+
}
223+
224+
if (inner_fun_slot[0]) return;
225+
226+
try {
227+
// clean it up!
228+
corruptDatabase.delete();
229+
} catch (Exception ex) {
230+
// Uncaught exception (not expected):
231+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
232+
inner_status_slot[0] = false;
233+
}
234+
}
235+
});
236+
237+
// NOTE: database not expected to be null, but double-check:
238+
if (database == null) {
239+
Log.e(TAG, "ERROR: got null database object");
240+
return false;
241+
}
242+
243+
if (!inner_status_slot[0]) {
244+
Log.e(TAG, "ERROR: Custom onCorruption() NOT called");
245+
database.close();
246+
return false;
247+
}
248+
249+
// tell custom error handler NOT to delete database:
250+
inner_fun_slot[0] = true;
251+
252+
// *Should* corrupt the database file that is already open:
253+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
254+
255+
try {
256+
// Attempt to write to corrupt database file *should* fail:
257+
database.execSQL("CREATE TABLE t1(a,b);");
258+
259+
// NOT EXPECTED to get here:
260+
Log.e(TAG, "NOT EXPECTED: CREATE TABLE succeeded ");
261+
return false;
262+
} catch (SQLiteDatabaseCorruptException ex) {
263+
Log.v(TAG, "Caught SQLiteDatabaseCorruptException as expected OK");
264+
}
265+
266+
// NOTE: the database is NOT closed, try it again
267+
if (!database.isOpen()) {
268+
Log.e(TAG, "BEHAVIOR CHANGED: database was closed after sql encountered corruption but error handler did not delete the database");
269+
return false;
270+
}
271+
272+
// custom error handler back to normal:
273+
inner_fun_slot[0] = false;
274+
275+
try {
276+
// Attempt to write to corrupt database file *should* fail:
277+
database.execSQL("CREATE TABLE t1(a,b);");
278+
279+
// NOT EXPECTED to get here:
280+
Log.e(TAG, "NOT EXPECTED: CREATE TABLE succeeded ");
281+
return false;
282+
} catch (SQLiteDatabaseCorruptException ex) {
283+
Log.v(TAG, "Caught SQLiteDatabaseCorruptException as expected OK");
284+
}
285+
286+
// For some reason, database is still open here.
287+
if (!database.isOpen()) {
288+
Log.e(TAG, "BEHAVIOR CHANGED: database closed here");
289+
return false;
290+
}
291+
292+
database.close();
293+
294+
status = true;
295+
296+
} catch (Exception ex) {
297+
// Uncaught exception (not expected):
298+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
299+
return false;
300+
}
301+
finally {
302+
corruptDatabase.delete();
303+
}
304+
305+
if (!status) return false;
306+
inner_status_slot[0] = status = false;
307+
inner_fun_slot[0] = false;
308+
309+
// tell custom error handler to throw runtime error during sql query:
310+
try {
311+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
312+
313+
// make sure custom onCorruption() function is called:
314+
SQLiteDatabase database = SQLiteDatabase.openDatabase(corruptDatabase.getPath(), "", null, SQLiteDatabase.CREATE_IF_NECESSARY, null, new DatabaseErrorHandler() {
315+
@Override
316+
public void onCorruption(SQLiteDatabase db) {
317+
try {
318+
Log.i(TAG, "Custom onCorruption() called");
319+
inner_status_slot[0] = true;
320+
} catch (Exception ex) {
321+
// Uncaught exception (not expected):
322+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
323+
}
324+
325+
if (inner_fun_slot[0]) throw new RuntimeException("abort");
326+
327+
try {
328+
// clean it up!
329+
corruptDatabase.delete();
330+
} catch (Exception ex) {
331+
// Uncaught exception (not expected):
332+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
333+
inner_status_slot[0] = false;
334+
}
335+
}
336+
});
337+
338+
// NOTE: database not expected to be null, but double-check:
339+
if (database == null) {
340+
Log.e(TAG, "ERROR: got null database object");
341+
return false;
342+
}
343+
344+
if (!inner_status_slot[0]) {
345+
Log.e(TAG, "ERROR: Custom onCorruption() NOT called");
346+
database.close();
347+
return false;
348+
}
349+
350+
// tell custom error handler NOT to delete database:
351+
inner_fun_slot[0] = true;
352+
353+
// *Should* corrupt the database file that is already open:
354+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory("corrupt.db");
355+
356+
try {
357+
// Attempt to write to corrupt database file *should* fail:
358+
database.execSQL("CREATE TABLE t1(a,b);");
359+
360+
// NOT EXPECTED to get here:
361+
Log.e(TAG, "NOT EXPECTED: CREATE TABLE succeeded ");
362+
return false;
363+
} catch (RuntimeException ex) {
364+
Log.v(TAG, "Caught RuntimeException as thrown by custom error handler OK");
365+
}
366+
367+
// NOTE: the database is NOT expected to closed
368+
if (!database.isOpen()) {
369+
Log.e(TAG, "BEHAVIOR CHANGED: database was closed after sql encountered corruption but error handler did not delete the database");
370+
return false;
371+
}
372+
373+
database.close();
374+
375+
status = true;
376+
377+
} catch (Exception ex) {
378+
// Uncaught exception (not expected):
379+
Log.e(TAG, "UNEXPECTED EXCEPTION", ex);
380+
return false;
381+
}
382+
finally {
383+
corruptDatabase.delete();
384+
}
385+
386+
return status;
387+
}
388+
389+
@Override
390+
public String getName() {
391+
return "Custom Corruption Handler Test";
392+
}
393+
}

0 commit comments

Comments
 (0)