1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/satchel/test/unit/test_history_api.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,417 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +var testnum = 0; 1.9 +let dbConnection; // used for deleted table tests 1.10 + 1.11 +Cu.import("resource://gre/modules/Promise.jsm"); 1.12 + 1.13 +function countDeletedEntries(expected) 1.14 +{ 1.15 + let deferred = Promise.defer(); 1.16 + let stmt = dbConnection.createAsyncStatement("SELECT COUNT(*) AS numEntries FROM moz_deleted_formhistory"); 1.17 + stmt.executeAsync({ 1.18 + handleResult: function(resultSet) { 1.19 + do_check_eq(expected, resultSet.getNextRow().getResultByName("numEntries")); 1.20 + deferred.resolve(); 1.21 + }, 1.22 + handleError : function () { 1.23 + do_throw("Error occurred counting deleted entries: " + error); 1.24 + deferred.reject(); 1.25 + }, 1.26 + handleCompletion : function () { 1.27 + stmt.finalize(); 1.28 + } 1.29 + }); 1.30 + return deferred.promise; 1.31 +} 1.32 + 1.33 +function checkTimeDeleted(guid, checkFunction) 1.34 +{ 1.35 + let deferred = Promise.defer(); 1.36 + let stmt = dbConnection.createAsyncStatement("SELECT timeDeleted FROM moz_deleted_formhistory WHERE guid = :guid"); 1.37 + stmt.params.guid = guid; 1.38 + stmt.executeAsync({ 1.39 + handleResult: function(resultSet) { 1.40 + checkFunction(resultSet.getNextRow().getResultByName("timeDeleted")); 1.41 + deferred.resolve(); 1.42 + }, 1.43 + handleError : function () { 1.44 + do_throw("Error occurred getting deleted entries: " + error); 1.45 + deferred.reject(); 1.46 + }, 1.47 + handleCompletion : function () { 1.48 + stmt.finalize(); 1.49 + } 1.50 + }); 1.51 + return deferred.promise; 1.52 +} 1.53 + 1.54 +function promiseUpdateEntry(op, name, value) 1.55 +{ 1.56 + var change = { op: op }; 1.57 + if (name !== null) 1.58 + change.fieldname = name; 1.59 + if (value !== null) 1.60 + change.value = value; 1.61 + return promiseUpdate(change); 1.62 +} 1.63 + 1.64 +function promiseUpdate(change) 1.65 +{ 1.66 + let deferred = Promise.defer(); 1.67 + FormHistory.update(change, 1.68 + { handleError: function (error) { 1.69 + do_throw("Error occurred updating form history: " + error); 1.70 + deferred.reject(error); 1.71 + }, 1.72 + handleCompletion: function (reason) { if (!reason) deferred.resolve(); } 1.73 + }); 1.74 + return deferred.promise; 1.75 +} 1.76 + 1.77 +function promiseSearchEntries(terms, params) 1.78 +{ 1.79 + let deferred = Promise.defer(); 1.80 + let results = []; 1.81 + FormHistory.search(terms, params, 1.82 + { handleResult: function(result) results.push(result), 1.83 + handleError: function (error) { 1.84 + do_throw("Error occurred searching form history: " + error); 1.85 + deferred.reject(error); 1.86 + }, 1.87 + handleCompletion: function (reason) { if (!reason) deferred.resolve(results); } 1.88 + }); 1.89 + return deferred.promise; 1.90 +} 1.91 + 1.92 +function promiseCountEntries(name, value, checkFn) 1.93 +{ 1.94 + let deferred = Promise.defer(); 1.95 + countEntries(name, value, function (result) { checkFn(result); deferred.resolve(); } ); 1.96 + return deferred.promise; 1.97 +} 1.98 + 1.99 +add_task(function () 1.100 +{ 1.101 + let oldSupportsDeletedTable = FormHistory._supportsDeletedTable; 1.102 + FormHistory._supportsDeletedTable = true; 1.103 + 1.104 + try { 1.105 + 1.106 + // ===== test init ===== 1.107 + var testfile = do_get_file("formhistory_apitest.sqlite"); 1.108 + var profileDir = dirSvc.get("ProfD", Ci.nsIFile); 1.109 + 1.110 + // Cleanup from any previous tests or failures. 1.111 + var destFile = profileDir.clone(); 1.112 + destFile.append("formhistory.sqlite"); 1.113 + if (destFile.exists()) 1.114 + destFile.remove(false); 1.115 + 1.116 + testfile.copyTo(profileDir, "formhistory.sqlite"); 1.117 + 1.118 + function checkExists(num) { do_check_true(num > 0); } 1.119 + function checkNotExists(num) { do_check_true(num == 0); } 1.120 + 1.121 + // ===== 1 ===== 1.122 + // Check initial state is as expected 1.123 + testnum++; 1.124 + yield promiseCountEntries("name-A", null, checkExists); 1.125 + yield promiseCountEntries("name-B", null, checkExists); 1.126 + yield promiseCountEntries("name-C", null, checkExists); 1.127 + yield promiseCountEntries("name-D", null, checkExists); 1.128 + yield promiseCountEntries("name-A", "value-A", checkExists); 1.129 + yield promiseCountEntries("name-B", "value-B1", checkExists); 1.130 + yield promiseCountEntries("name-B", "value-B2", checkExists); 1.131 + yield promiseCountEntries("name-C", "value-C", checkExists); 1.132 + yield promiseCountEntries("name-D", "value-D", checkExists); 1.133 + // time-A/B/C/D checked below. 1.134 + 1.135 + // Delete anything from the deleted table 1.136 + let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone(); 1.137 + dbFile.append("formhistory.sqlite"); 1.138 + dbConnection = Services.storage.openUnsharedDatabase(dbFile); 1.139 + 1.140 + let deferred = Promise.defer(); 1.141 + 1.142 + let stmt = dbConnection.createAsyncStatement("DELETE FROM moz_deleted_formhistory"); 1.143 + stmt.executeAsync({ 1.144 + handleResult: function(resultSet) { }, 1.145 + handleError : function () { 1.146 + do_throw("Error occurred counting deleted all entries: " + error); 1.147 + }, 1.148 + handleCompletion : function () { 1.149 + stmt.finalize(); 1.150 + deferred.resolve(); 1.151 + } 1.152 + }); 1.153 + yield deferred.promise; 1.154 + 1.155 + // ===== 2 ===== 1.156 + // Test looking for nonexistent / bogus data. 1.157 + testnum++; 1.158 + yield promiseCountEntries("blah", null, checkNotExists); 1.159 + yield promiseCountEntries("", null, checkNotExists); 1.160 + yield promiseCountEntries("name-A", "blah", checkNotExists); 1.161 + yield promiseCountEntries("name-A", "", checkNotExists); 1.162 + yield promiseCountEntries("name-A", null, checkExists); 1.163 + yield promiseCountEntries("blah", "value-A", checkNotExists); 1.164 + yield promiseCountEntries("", "value-A", checkNotExists); 1.165 + yield promiseCountEntries(null, "value-A", checkExists); 1.166 + 1.167 + // Cannot use promiseCountEntries when name and value are null because it treats null values as not set 1.168 + // and here a search should be done explicity for null. 1.169 + deferred = Promise.defer(); 1.170 + yield FormHistory.count({ fieldname: null, value: null }, 1.171 + { handleResult: function(result) checkNotExists(result), 1.172 + handleError: function (error) { 1.173 + do_throw("Error occurred searching form history: " + error); 1.174 + }, 1.175 + handleCompletion: function(reason) { if (!reason) deferred.resolve() } 1.176 + }); 1.177 + yield deferred.promise; 1.178 + 1.179 + // ===== 3 ===== 1.180 + // Test removeEntriesForName with a single matching value 1.181 + testnum++; 1.182 + yield promiseUpdateEntry("remove", "name-A", null); 1.183 + 1.184 + yield promiseCountEntries("name-A", "value-A", checkNotExists); 1.185 + yield promiseCountEntries("name-B", "value-B1", checkExists); 1.186 + yield promiseCountEntries("name-B", "value-B2", checkExists); 1.187 + yield promiseCountEntries("name-C", "value-C", checkExists); 1.188 + yield promiseCountEntries("name-D", "value-D", checkExists); 1.189 + yield countDeletedEntries(1); 1.190 + 1.191 + // ===== 4 ===== 1.192 + // Test removeEntriesForName with multiple matching values 1.193 + testnum++; 1.194 + yield promiseUpdateEntry("remove", "name-B", null); 1.195 + 1.196 + yield promiseCountEntries("name-A", "value-A", checkNotExists); 1.197 + yield promiseCountEntries("name-B", "value-B1", checkNotExists); 1.198 + yield promiseCountEntries("name-B", "value-B2", checkNotExists); 1.199 + yield promiseCountEntries("name-C", "value-C", checkExists); 1.200 + yield promiseCountEntries("name-D", "value-D", checkExists); 1.201 + yield countDeletedEntries(3); 1.202 + 1.203 + // ===== 5 ===== 1.204 + // Test removing by time range (single entry, not surrounding entries) 1.205 + testnum++; 1.206 + yield promiseCountEntries("time-A", null, checkExists); // firstUsed=1000, lastUsed=1000 1.207 + yield promiseCountEntries("time-B", null, checkExists); // firstUsed=1000, lastUsed=1099 1.208 + yield promiseCountEntries("time-C", null, checkExists); // firstUsed=1099, lastUsed=1099 1.209 + yield promiseCountEntries("time-D", null, checkExists); // firstUsed=2001, lastUsed=2001 1.210 + yield promiseUpdate({ op : "remove", firstUsedStart: 1050, firstUsedEnd: 2000 }); 1.211 + 1.212 + yield promiseCountEntries("time-A", null, checkExists); 1.213 + yield promiseCountEntries("time-B", null, checkExists); 1.214 + yield promiseCountEntries("time-C", null, checkNotExists); 1.215 + yield promiseCountEntries("time-D", null, checkExists); 1.216 + yield countDeletedEntries(4); 1.217 + 1.218 + // ===== 6 ===== 1.219 + // Test removing by time range (multiple entries) 1.220 + testnum++; 1.221 + yield promiseUpdate({ op : "remove", firstUsedStart: 1000, firstUsedEnd: 2000 }); 1.222 + 1.223 + yield promiseCountEntries("time-A", null, checkNotExists); 1.224 + yield promiseCountEntries("time-B", null, checkNotExists); 1.225 + yield promiseCountEntries("time-C", null, checkNotExists); 1.226 + yield promiseCountEntries("time-D", null, checkExists); 1.227 + yield countDeletedEntries(6); 1.228 + 1.229 + // ===== 7 ===== 1.230 + // test removeAllEntries 1.231 + testnum++; 1.232 + yield promiseUpdateEntry("remove", null, null); 1.233 + 1.234 + yield promiseCountEntries("name-C", null, checkNotExists); 1.235 + yield promiseCountEntries("name-D", null, checkNotExists); 1.236 + yield promiseCountEntries("name-C", "value-C", checkNotExists); 1.237 + yield promiseCountEntries("name-D", "value-D", checkNotExists); 1.238 + 1.239 + yield promiseCountEntries(null, null, checkNotExists); 1.240 + yield countDeletedEntries(6); 1.241 + 1.242 + // ===== 8 ===== 1.243 + // Add a single entry back 1.244 + testnum++; 1.245 + yield promiseUpdateEntry("add", "newname-A", "newvalue-A"); 1.246 + yield promiseCountEntries("newname-A", "newvalue-A", checkExists); 1.247 + 1.248 + // ===== 9 ===== 1.249 + // Remove the single entry 1.250 + testnum++; 1.251 + yield promiseUpdateEntry("remove", "newname-A", "newvalue-A"); 1.252 + yield promiseCountEntries("newname-A", "newvalue-A", checkNotExists); 1.253 + 1.254 + // ===== 10 ===== 1.255 + // Add a single entry 1.256 + testnum++; 1.257 + yield promiseUpdateEntry("add", "field1", "value1"); 1.258 + yield promiseCountEntries("field1", "value1", checkExists); 1.259 + 1.260 + let processFirstResult = function processResults(results) 1.261 + { 1.262 + // Only handle the first result 1.263 + if (results.length > 0) { 1.264 + let result = results[0]; 1.265 + return [result.timesUsed, result.firstUsed, result.lastUsed, result.guid]; 1.266 + } 1.267 + } 1.268 + 1.269 + results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"], 1.270 + { fieldname: "field1", value: "value1" }); 1.271 + let [timesUsed, firstUsed, lastUsed] = processFirstResult(results); 1.272 + do_check_eq(1, timesUsed); 1.273 + do_check_true(firstUsed > 0); 1.274 + do_check_true(lastUsed > 0); 1.275 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 1)); 1.276 + 1.277 + // ===== 11 ===== 1.278 + // Add another single entry 1.279 + testnum++; 1.280 + yield promiseUpdateEntry("add", "field1", "value1b"); 1.281 + yield promiseCountEntries("field1", "value1", checkExists); 1.282 + yield promiseCountEntries("field1", "value1b", checkExists); 1.283 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 2)); 1.284 + 1.285 + // ===== 12 ===== 1.286 + // Update a single entry 1.287 + testnum++; 1.288 + 1.289 + results = yield promiseSearchEntries(["guid"], { fieldname: "field1", value: "value1" }); 1.290 + let guid = processFirstResult(results)[3]; 1.291 + 1.292 + yield promiseUpdate({ op : "update", guid: guid, value: "modifiedValue" }); 1.293 + yield promiseCountEntries("field1", "modifiedValue", checkExists); 1.294 + yield promiseCountEntries("field1", "value1", checkNotExists); 1.295 + yield promiseCountEntries("field1", "value1b", checkExists); 1.296 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 2)); 1.297 + 1.298 + // ===== 13 ===== 1.299 + // Add a single entry with times 1.300 + testnum++; 1.301 + yield promiseUpdate({ op : "add", fieldname: "field2", value: "value2", 1.302 + timesUsed: 20, firstUsed: 100, lastUsed: 500 }); 1.303 + 1.304 + results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"], 1.305 + { fieldname: "field2", value: "value2" }); 1.306 + [timesUsed, firstUsed, lastUsed] = processFirstResult(results); 1.307 + 1.308 + do_check_eq(20, timesUsed); 1.309 + do_check_eq(100, firstUsed); 1.310 + do_check_eq(500, lastUsed); 1.311 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 3)); 1.312 + 1.313 + // ===== 14 ===== 1.314 + // Bump an entry, which updates its lastUsed field 1.315 + testnum++; 1.316 + yield promiseUpdate({ op : "bump", fieldname: "field2", value: "value2", 1.317 + timesUsed: 20, firstUsed: 100, lastUsed: 500 }); 1.318 + results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"], 1.319 + { fieldname: "field2", value: "value2" }); 1.320 + [timesUsed, firstUsed, lastUsed] = processFirstResult(results); 1.321 + do_check_eq(21, timesUsed); 1.322 + do_check_eq(100, firstUsed); 1.323 + do_check_true(lastUsed > 500); 1.324 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 3)); 1.325 + 1.326 + // ===== 15 ===== 1.327 + // Bump an entry that does not exist 1.328 + testnum++; 1.329 + yield promiseUpdate({ op : "bump", fieldname: "field3", value: "value3", 1.330 + timesUsed: 10, firstUsed: 50, lastUsed: 400 }); 1.331 + results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"], 1.332 + { fieldname: "field3", value: "value3" }); 1.333 + [timesUsed, firstUsed, lastUsed] = processFirstResult(results); 1.334 + do_check_eq(10, timesUsed); 1.335 + do_check_eq(50, firstUsed); 1.336 + do_check_eq(400, lastUsed); 1.337 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4)); 1.338 + 1.339 + // ===== 16 ===== 1.340 + // Bump an entry with a guid 1.341 + testnum++; 1.342 + results = yield promiseSearchEntries(["guid"], { fieldname: "field3", value: "value3" }); 1.343 + guid = processFirstResult(results)[3]; 1.344 + yield promiseUpdate({ op : "bump", guid: guid, timesUsed: 20, firstUsed: 55, lastUsed: 400 }); 1.345 + results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"], 1.346 + { fieldname: "field3", value: "value3" }); 1.347 + [timesUsed, firstUsed, lastUsed] = processFirstResult(results); 1.348 + do_check_eq(11, timesUsed); 1.349 + do_check_eq(50, firstUsed); 1.350 + do_check_true(lastUsed > 400); 1.351 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4)); 1.352 + 1.353 + // ===== 17 ===== 1.354 + // Remove an entry 1.355 + testnum++; 1.356 + yield countDeletedEntries(7); 1.357 + 1.358 + results = yield promiseSearchEntries(["guid"], { fieldname: "field1", value: "value1b" }); 1.359 + guid = processFirstResult(results)[3]; 1.360 + 1.361 + yield promiseUpdate({ op : "remove", guid: guid}); 1.362 + yield promiseCountEntries("field1", "modifiedValue", checkExists); 1.363 + yield promiseCountEntries("field1", "value1b", checkNotExists); 1.364 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 3)); 1.365 + 1.366 + yield countDeletedEntries(8); 1.367 + yield checkTimeDeleted(guid, function (timeDeleted) do_check_true(timeDeleted > 10000)); 1.368 + 1.369 + // ===== 18 ===== 1.370 + // Add yet another single entry 1.371 + testnum++; 1.372 + yield promiseUpdate({ op : "add", fieldname: "field4", value: "value4", 1.373 + timesUsed: 5, firstUsed: 230, lastUsed: 600 }); 1.374 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4)); 1.375 + 1.376 + // ===== 19 ===== 1.377 + // Remove an entry by time 1.378 + testnum++; 1.379 + yield promiseUpdate({ op : "remove", firstUsedStart: 60, firstUsedEnd: 250 }); 1.380 + yield promiseCountEntries("field1", "modifiedValue", checkExists); 1.381 + yield promiseCountEntries("field2", "value2", checkNotExists); 1.382 + yield promiseCountEntries("field3", "value3", checkExists); 1.383 + yield promiseCountEntries("field4", "value4", checkNotExists); 1.384 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 2)); 1.385 + yield countDeletedEntries(10); 1.386 + 1.387 + // ===== 20 ===== 1.388 + // Bump multiple existing entries at once 1.389 + testnum++; 1.390 + 1.391 + yield promiseUpdate([{ op : "add", fieldname: "field5", value: "value5", 1.392 + timesUsed: 5, firstUsed: 230, lastUsed: 600 }, 1.393 + { op : "add", fieldname: "field6", value: "value6", 1.394 + timesUsed: 12, firstUsed: 430, lastUsed: 700 }]); 1.395 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4)); 1.396 + 1.397 + yield promiseUpdate([ 1.398 + { op : "bump", fieldname: "field5", value: "value5" }, 1.399 + { op : "bump", fieldname: "field6", value: "value6" }]); 1.400 + results = yield promiseSearchEntries(["fieldname", "timesUsed", "firstUsed", "lastUsed"], { }); 1.401 + 1.402 + do_check_eq(6, results[2].timesUsed); 1.403 + do_check_eq(13, results[3].timesUsed); 1.404 + do_check_eq(230, results[2].firstUsed); 1.405 + do_check_eq(430, results[3].firstUsed); 1.406 + do_check_true(results[2].lastUsed > 600); 1.407 + do_check_true(results[3].lastUsed > 700); 1.408 + 1.409 + yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4)); 1.410 + 1.411 + } catch (e) { 1.412 + throw "FAILED in test #" + testnum + " -- " + e; 1.413 + } 1.414 + finally { 1.415 + FormHistory._supportsDeletedTable = oldSupportsDeletedTable; 1.416 + dbConnection.asyncClose(do_test_finished); 1.417 + } 1.418 +}); 1.419 + 1.420 +function run_test() run_next_test();