|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 /** |
|
5 * This file tests migration invariants from schema version 10 to the current |
|
6 * schema version. |
|
7 */ |
|
8 |
|
9 //////////////////////////////////////////////////////////////////////////////// |
|
10 //// Constants |
|
11 |
|
12 const kGuidAnnotationName = "sync/guid"; |
|
13 const kExpectedAnnotations = 5; |
|
14 const kExpectedValidGuids = 2; |
|
15 |
|
16 //////////////////////////////////////////////////////////////////////////////// |
|
17 //// Globals |
|
18 |
|
19 // Set in test_initial_state to the value in the database. |
|
20 var gItemGuid = []; |
|
21 var gItemId = []; |
|
22 var gPlaceGuid = []; |
|
23 var gPlaceId = []; |
|
24 |
|
25 //////////////////////////////////////////////////////////////////////////////// |
|
26 //// Helpers |
|
27 |
|
28 /** |
|
29 * Determines if a guid is valid or not. |
|
30 * |
|
31 * @return true if it is a valid guid, false otherwise. |
|
32 */ |
|
33 function isValidGuid(aGuid) |
|
34 { |
|
35 return /^[a-zA-Z0-9\-_]{12}$/.test(aGuid); |
|
36 } |
|
37 |
|
38 //////////////////////////////////////////////////////////////////////////////// |
|
39 //// Test Functions |
|
40 |
|
41 function test_initial_state() |
|
42 { |
|
43 // Mostly sanity checks our starting DB to make sure it's setup as we expect |
|
44 // it to be. |
|
45 let dbFile = gProfD.clone(); |
|
46 dbFile.append(kDBName); |
|
47 let db = Services.storage.openUnsharedDatabase(dbFile); |
|
48 |
|
49 let stmt = db.createStatement("PRAGMA journal_mode"); |
|
50 do_check_true(stmt.executeStep()); |
|
51 // WAL journal mode should not be set on this database. |
|
52 do_check_neq(stmt.getString(0).toLowerCase(), "wal"); |
|
53 stmt.finalize(); |
|
54 |
|
55 do_check_false(db.indexExists("moz_bookmarks_guid_uniqueindex")); |
|
56 do_check_false(db.indexExists("moz_places_guid_uniqueindex")); |
|
57 |
|
58 // There should be five item annotations for a bookmark guid. |
|
59 stmt = db.createStatement( |
|
60 "SELECT content AS guid, item_id " |
|
61 + "FROM moz_items_annos " |
|
62 + "WHERE anno_attribute_id = ( " |
|
63 + "SELECT id " |
|
64 + "FROM moz_anno_attributes " |
|
65 + "WHERE name = :attr_name " |
|
66 + ") " |
|
67 ); |
|
68 stmt.params.attr_name = kGuidAnnotationName; |
|
69 while (stmt.executeStep()) { |
|
70 gItemGuid.push(stmt.row.guid); |
|
71 gItemId.push(stmt.row.item_id) |
|
72 } |
|
73 do_check_eq(gItemGuid.length, gItemId.length); |
|
74 do_check_eq(gItemGuid.length, kExpectedAnnotations); |
|
75 stmt.finalize(); |
|
76 |
|
77 // There should be five item annotations for a place guid. |
|
78 stmt = db.createStatement( |
|
79 "SELECT content AS guid, place_id " |
|
80 + "FROM moz_annos " |
|
81 + "WHERE anno_attribute_id = ( " |
|
82 + "SELECT id " |
|
83 + "FROM moz_anno_attributes " |
|
84 + "WHERE name = :attr_name " |
|
85 + ") " |
|
86 ); |
|
87 stmt.params.attr_name = kGuidAnnotationName; |
|
88 while (stmt.executeStep()) { |
|
89 gPlaceGuid.push(stmt.row.guid); |
|
90 gPlaceId.push(stmt.row.place_id) |
|
91 } |
|
92 do_check_eq(gPlaceGuid.length, gPlaceId.length); |
|
93 do_check_eq(gPlaceGuid.length, kExpectedAnnotations); |
|
94 stmt.finalize(); |
|
95 |
|
96 // Check our schema version to make sure it is actually at 10. |
|
97 do_check_eq(db.schemaVersion, 10); |
|
98 |
|
99 db.close(); |
|
100 run_next_test(); |
|
101 } |
|
102 |
|
103 function test_moz_bookmarks_guid_exists() |
|
104 { |
|
105 // This will throw if the column does not exist |
|
106 let stmt = DBConn().createStatement( |
|
107 "SELECT guid " |
|
108 + "FROM moz_bookmarks " |
|
109 ); |
|
110 stmt.finalize(); |
|
111 |
|
112 run_next_test(); |
|
113 } |
|
114 |
|
115 function test_bookmark_guids_non_null() |
|
116 { |
|
117 // First, sanity check that we have a non-zero amount of bookmarks. |
|
118 let stmt = DBConn().createStatement( |
|
119 "SELECT COUNT(1) " |
|
120 + "FROM moz_bookmarks " |
|
121 ); |
|
122 do_check_true(stmt.executeStep()); |
|
123 do_check_neq(stmt.getInt32(0), 0); |
|
124 stmt.finalize(); |
|
125 |
|
126 // Now, make sure we have no NULL guid entry. |
|
127 stmt = DBConn().createStatement( |
|
128 "SELECT guid " |
|
129 + "FROM moz_bookmarks " |
|
130 + "WHERE guid IS NULL " |
|
131 ); |
|
132 do_check_false(stmt.executeStep()); |
|
133 stmt.finalize(); |
|
134 run_next_test(); |
|
135 } |
|
136 |
|
137 function test_bookmark_guid_annotation_imported() |
|
138 { |
|
139 // Make sure we have the imported guid; not a newly generated one. |
|
140 let stmt = DBConn().createStatement( |
|
141 "SELECT id " |
|
142 + "FROM moz_bookmarks " |
|
143 + "WHERE guid = :guid " |
|
144 + "AND id = :item_id " |
|
145 ); |
|
146 let validGuids = 0; |
|
147 let seenGuids = []; |
|
148 for (let i = 0; i < gItemGuid.length; i++) { |
|
149 let guid = gItemGuid[i]; |
|
150 stmt.params.guid = guid; |
|
151 stmt.params.item_id = gItemId[i]; |
|
152 |
|
153 // Check that it is a valid guid that we expect, and that it is not a |
|
154 // duplicate (which would violate the unique constraint). |
|
155 let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1; |
|
156 seenGuids.push(guid); |
|
157 |
|
158 if (valid) { |
|
159 validGuids++; |
|
160 do_check_true(stmt.executeStep()); |
|
161 } |
|
162 else { |
|
163 do_check_false(stmt.executeStep()); |
|
164 } |
|
165 stmt.reset(); |
|
166 } |
|
167 do_check_eq(validGuids, kExpectedValidGuids); |
|
168 stmt.finalize(); |
|
169 |
|
170 run_next_test(); |
|
171 } |
|
172 |
|
173 function test_bookmark_guid_annotation_removed() |
|
174 { |
|
175 let stmt = DBConn().createStatement( |
|
176 "SELECT COUNT(1) " |
|
177 + "FROM moz_items_annos " |
|
178 + "WHERE anno_attribute_id = ( " |
|
179 + "SELECT id " |
|
180 + "FROM moz_anno_attributes " |
|
181 + "WHERE name = :attr_name " |
|
182 + ") " |
|
183 ); |
|
184 stmt.params.attr_name = kGuidAnnotationName; |
|
185 do_check_true(stmt.executeStep()); |
|
186 do_check_eq(stmt.getInt32(0), 0); |
|
187 stmt.finalize(); |
|
188 |
|
189 run_next_test(); |
|
190 } |
|
191 |
|
192 function test_moz_places_guid_exists() |
|
193 { |
|
194 // This will throw if the column does not exist |
|
195 let stmt = DBConn().createStatement( |
|
196 "SELECT guid " |
|
197 + "FROM moz_places " |
|
198 ); |
|
199 stmt.finalize(); |
|
200 |
|
201 run_next_test(); |
|
202 } |
|
203 |
|
204 function test_place_guids_non_null() |
|
205 { |
|
206 // First, sanity check that we have a non-zero amount of places. |
|
207 let stmt = DBConn().createStatement( |
|
208 "SELECT COUNT(1) " |
|
209 + "FROM moz_places " |
|
210 ); |
|
211 do_check_true(stmt.executeStep()); |
|
212 do_check_neq(stmt.getInt32(0), 0); |
|
213 stmt.finalize(); |
|
214 |
|
215 // Now, make sure we have no NULL guid entry. |
|
216 stmt = DBConn().createStatement( |
|
217 "SELECT guid " |
|
218 + "FROM moz_places " |
|
219 + "WHERE guid IS NULL " |
|
220 ); |
|
221 do_check_false(stmt.executeStep()); |
|
222 stmt.finalize(); |
|
223 run_next_test(); |
|
224 } |
|
225 |
|
226 function test_place_guid_annotation_imported() |
|
227 { |
|
228 // Make sure we have the imported guid; not a newly generated one. |
|
229 let stmt = DBConn().createStatement( |
|
230 "SELECT id " |
|
231 + "FROM moz_places " |
|
232 + "WHERE guid = :guid " |
|
233 + "AND id = :item_id " |
|
234 ); |
|
235 let validGuids = 0; |
|
236 let seenGuids = []; |
|
237 for (let i = 0; i < gPlaceGuid.length; i++) { |
|
238 let guid = gPlaceGuid[i]; |
|
239 stmt.params.guid = guid; |
|
240 stmt.params.item_id = gPlaceId[i]; |
|
241 |
|
242 // Check that it is a valid guid that we expect, and that it is not a |
|
243 // duplicate (which would violate the unique constraint). |
|
244 let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1; |
|
245 seenGuids.push(guid); |
|
246 |
|
247 if (valid) { |
|
248 validGuids++; |
|
249 do_check_true(stmt.executeStep()); |
|
250 } |
|
251 else { |
|
252 do_check_false(stmt.executeStep()); |
|
253 } |
|
254 stmt.reset(); |
|
255 } |
|
256 do_check_eq(validGuids, kExpectedValidGuids); |
|
257 stmt.finalize(); |
|
258 |
|
259 run_next_test(); |
|
260 } |
|
261 |
|
262 function test_place_guid_annotation_removed() |
|
263 { |
|
264 let stmt = DBConn().createStatement( |
|
265 "SELECT COUNT(1) " |
|
266 + "FROM moz_annos " |
|
267 + "WHERE anno_attribute_id = ( " |
|
268 + "SELECT id " |
|
269 + "FROM moz_anno_attributes " |
|
270 + "WHERE name = :attr_name " |
|
271 + ") " |
|
272 ); |
|
273 stmt.params.attr_name = kGuidAnnotationName; |
|
274 do_check_true(stmt.executeStep()); |
|
275 do_check_eq(stmt.getInt32(0), 0); |
|
276 stmt.finalize(); |
|
277 |
|
278 run_next_test(); |
|
279 } |
|
280 |
|
281 function test_moz_hosts() |
|
282 { |
|
283 // This will throw if the column does not exist |
|
284 let stmt = DBConn().createStatement( |
|
285 "SELECT host, frecency, typed, prefix " |
|
286 + "FROM moz_hosts " |
|
287 ); |
|
288 stmt.finalize(); |
|
289 |
|
290 // moz_hosts is populated asynchronously, so query asynchronously to serialize |
|
291 // to that. |
|
292 // check the number of entries in moz_hosts equals the number of |
|
293 // unique rev_host in moz_places |
|
294 stmt = DBConn().createAsyncStatement( |
|
295 "SELECT (SELECT COUNT(host) FROM moz_hosts), " + |
|
296 "(SELECT COUNT(DISTINCT rev_host) " + |
|
297 "FROM moz_places " + |
|
298 "WHERE LENGTH(rev_host) > 1)"); |
|
299 try { |
|
300 stmt.executeAsync({ |
|
301 handleResult: function (aResult) { |
|
302 this._hasResults = true; |
|
303 let row = aResult.getNextRow(); |
|
304 let mozHostsCount = row.getResultByIndex(0); |
|
305 let mozPlacesCount = row.getResultByIndex(1); |
|
306 do_check_true(mozPlacesCount > 0); |
|
307 do_check_eq(mozPlacesCount, mozHostsCount); |
|
308 }, |
|
309 handleError: function () {}, |
|
310 handleCompletion: function (aReason) { |
|
311 do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); |
|
312 do_check_true(this._hasResults); |
|
313 run_next_test(); |
|
314 } |
|
315 }); |
|
316 } |
|
317 finally { |
|
318 stmt.finalize(); |
|
319 } |
|
320 } |
|
321 |
|
322 function test_final_state() |
|
323 { |
|
324 // We open a new database mostly so that we can check that the settings were |
|
325 // actually saved. |
|
326 let dbFile = gProfD.clone(); |
|
327 dbFile.append(kDBName); |
|
328 let db = Services.storage.openUnsharedDatabase(dbFile); |
|
329 |
|
330 let (stmt = db.createStatement("PRAGMA journal_mode")) { |
|
331 do_check_true(stmt.executeStep()); |
|
332 // WAL journal mode should be set on this database. |
|
333 do_check_eq(stmt.getString(0).toLowerCase(), "wal"); |
|
334 stmt.finalize(); |
|
335 } |
|
336 |
|
337 do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex")); |
|
338 do_check_true(db.indexExists("moz_places_guid_uniqueindex")); |
|
339 do_check_true(db.indexExists("moz_favicons_guid_uniqueindex")); |
|
340 |
|
341 do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION); |
|
342 |
|
343 db.close(); |
|
344 run_next_test(); |
|
345 } |
|
346 |
|
347 //////////////////////////////////////////////////////////////////////////////// |
|
348 //// Test Runner |
|
349 |
|
350 [ |
|
351 test_initial_state, |
|
352 test_moz_bookmarks_guid_exists, |
|
353 test_bookmark_guids_non_null, |
|
354 test_bookmark_guid_annotation_imported, |
|
355 test_bookmark_guid_annotation_removed, |
|
356 test_moz_places_guid_exists, |
|
357 test_place_guids_non_null, |
|
358 test_place_guid_annotation_imported, |
|
359 test_place_guid_annotation_removed, |
|
360 test_moz_hosts, |
|
361 test_final_state, |
|
362 ].forEach(add_test); |
|
363 |
|
364 function run_test() |
|
365 { |
|
366 setPlacesDatabase("places_v10.sqlite"); |
|
367 run_next_test(); |
|
368 } |