services/sync/tests/unit/test_history_store.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 Cu.import("resource://gre/modules/PlacesUtils.jsm");
michael@0 5 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 6 Cu.import("resource://services-common/async.js");
michael@0 7 Cu.import("resource://services-sync/engines/history.js");
michael@0 8 Cu.import("resource://services-sync/service.js");
michael@0 9 Cu.import("resource://services-sync/util.js");
michael@0 10
michael@0 11 const TIMESTAMP1 = (Date.now() - 103406528) * 1000;
michael@0 12 const TIMESTAMP2 = (Date.now() - 6592903) * 1000;
michael@0 13 const TIMESTAMP3 = (Date.now() - 123894) * 1000;
michael@0 14
michael@0 15 function queryPlaces(uri, options) {
michael@0 16 let query = PlacesUtils.history.getNewQuery();
michael@0 17 query.uri = uri;
michael@0 18 let res = PlacesUtils.history.executeQuery(query, options);
michael@0 19 res.root.containerOpen = true;
michael@0 20
michael@0 21 let results = [];
michael@0 22 for (let i = 0; i < res.root.childCount; i++)
michael@0 23 results.push(res.root.getChild(i));
michael@0 24 res.root.containerOpen = false;
michael@0 25 return results;
michael@0 26 }
michael@0 27
michael@0 28 function queryHistoryVisits(uri) {
michael@0 29 let options = PlacesUtils.history.getNewQueryOptions();
michael@0 30 options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
michael@0 31 options.resultType = Ci.nsINavHistoryQueryOptions.RESULTS_AS_VISIT;
michael@0 32 options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING;
michael@0 33 return queryPlaces(uri, options);
michael@0 34 }
michael@0 35
michael@0 36 function onNextTitleChanged(callback) {
michael@0 37 PlacesUtils.history.addObserver({
michael@0 38 onBeginUpdateBatch: function onBeginUpdateBatch() {},
michael@0 39 onEndUpdateBatch: function onEndUpdateBatch() {},
michael@0 40 onPageChanged: function onPageChanged() {},
michael@0 41 onTitleChanged: function onTitleChanged() {
michael@0 42 PlacesUtils.history.removeObserver(this);
michael@0 43 Utils.nextTick(callback);
michael@0 44 },
michael@0 45 onVisit: function onVisit() {},
michael@0 46 onDeleteVisits: function onDeleteVisits() {},
michael@0 47 onPageExpired: function onPageExpired() {},
michael@0 48 onDeleteURI: function onDeleteURI() {},
michael@0 49 onClearHistory: function onClearHistory() {},
michael@0 50 QueryInterface: XPCOMUtils.generateQI([
michael@0 51 Ci.nsINavHistoryObserver,
michael@0 52 Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS,
michael@0 53 Ci.nsISupportsWeakReference
michael@0 54 ])
michael@0 55 }, true);
michael@0 56 }
michael@0 57
michael@0 58 // Ensure exceptions from inside callbacks leads to test failures while
michael@0 59 // we still clean up properly.
michael@0 60 function ensureThrows(func) {
michael@0 61 return function() {
michael@0 62 try {
michael@0 63 func.apply(this, arguments);
michael@0 64 } catch (ex) {
michael@0 65 PlacesUtils.history.removeAllPages();
michael@0 66 do_throw(ex);
michael@0 67 }
michael@0 68 };
michael@0 69 }
michael@0 70
michael@0 71 let store = new HistoryEngine(Service)._store;
michael@0 72 function applyEnsureNoFailures(records) {
michael@0 73 do_check_eq(store.applyIncomingBatch(records).length, 0);
michael@0 74 }
michael@0 75
michael@0 76 let fxuri, fxguid, tburi, tbguid;
michael@0 77
michael@0 78 function run_test() {
michael@0 79 initTestLogging("Trace");
michael@0 80 run_next_test();
michael@0 81 }
michael@0 82
michael@0 83 add_test(function test_store() {
michael@0 84 _("Verify that we've got an empty store to work with.");
michael@0 85 do_check_empty(store.getAllIDs());
michael@0 86
michael@0 87 _("Let's create an entry in the database.");
michael@0 88 fxuri = Utils.makeURI("http://getfirefox.com/");
michael@0 89
michael@0 90 let place = {
michael@0 91 uri: fxuri,
michael@0 92 title: "Get Firefox!",
michael@0 93 visits: [{
michael@0 94 visitDate: TIMESTAMP1,
michael@0 95 transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
michael@0 96 }]
michael@0 97 };
michael@0 98 PlacesUtils.asyncHistory.updatePlaces(place, {
michael@0 99 handleError: function handleError() {
michael@0 100 do_throw("Unexpected error in adding visit.");
michael@0 101 },
michael@0 102 handleResult: function handleResult() {},
michael@0 103 handleCompletion: onVisitAdded
michael@0 104 });
michael@0 105
michael@0 106 function onVisitAdded() {
michael@0 107 _("Verify that the entry exists.");
michael@0 108 let ids = Object.keys(store.getAllIDs());
michael@0 109 do_check_eq(ids.length, 1);
michael@0 110 fxguid = ids[0];
michael@0 111 do_check_true(store.itemExists(fxguid));
michael@0 112
michael@0 113 _("If we query a non-existent record, it's marked as deleted.");
michael@0 114 let record = store.createRecord("non-existent");
michael@0 115 do_check_true(record.deleted);
michael@0 116
michael@0 117 _("Verify createRecord() returns a complete record.");
michael@0 118 record = store.createRecord(fxguid);
michael@0 119 do_check_eq(record.histUri, fxuri.spec);
michael@0 120 do_check_eq(record.title, "Get Firefox!");
michael@0 121 do_check_eq(record.visits.length, 1);
michael@0 122 do_check_eq(record.visits[0].date, TIMESTAMP1);
michael@0 123 do_check_eq(record.visits[0].type, Ci.nsINavHistoryService.TRANSITION_LINK);
michael@0 124
michael@0 125 _("Let's modify the record and have the store update the database.");
michael@0 126 let secondvisit = {date: TIMESTAMP2,
michael@0 127 type: Ci.nsINavHistoryService.TRANSITION_TYPED};
michael@0 128 onNextTitleChanged(ensureThrows(function() {
michael@0 129 let queryres = queryHistoryVisits(fxuri);
michael@0 130 do_check_eq(queryres.length, 2);
michael@0 131 do_check_eq(queryres[0].time, TIMESTAMP1);
michael@0 132 do_check_eq(queryres[0].title, "Hol Dir Firefox!");
michael@0 133 do_check_eq(queryres[1].time, TIMESTAMP2);
michael@0 134 do_check_eq(queryres[1].title, "Hol Dir Firefox!");
michael@0 135 run_next_test();
michael@0 136 }));
michael@0 137 applyEnsureNoFailures([
michael@0 138 {id: fxguid,
michael@0 139 histUri: record.histUri,
michael@0 140 title: "Hol Dir Firefox!",
michael@0 141 visits: [record.visits[0], secondvisit]}
michael@0 142 ]);
michael@0 143 }
michael@0 144 });
michael@0 145
michael@0 146 add_test(function test_store_create() {
michael@0 147 _("Create a brand new record through the store.");
michael@0 148 tbguid = Utils.makeGUID();
michael@0 149 tburi = Utils.makeURI("http://getthunderbird.com");
michael@0 150 onNextTitleChanged(ensureThrows(function() {
michael@0 151 do_check_attribute_count(store.getAllIDs(), 2);
michael@0 152 let queryres = queryHistoryVisits(tburi);
michael@0 153 do_check_eq(queryres.length, 1);
michael@0 154 do_check_eq(queryres[0].time, TIMESTAMP3);
michael@0 155 do_check_eq(queryres[0].title, "The bird is the word!");
michael@0 156 run_next_test();
michael@0 157 }));
michael@0 158 applyEnsureNoFailures([
michael@0 159 {id: tbguid,
michael@0 160 histUri: tburi.spec,
michael@0 161 title: "The bird is the word!",
michael@0 162 visits: [{date: TIMESTAMP3,
michael@0 163 type: Ci.nsINavHistoryService.TRANSITION_TYPED}]}
michael@0 164 ]);
michael@0 165 });
michael@0 166
michael@0 167 add_test(function test_null_title() {
michael@0 168 _("Make sure we handle a null title gracefully (it can happen in some cases, e.g. for resource:// URLs)");
michael@0 169 let resguid = Utils.makeGUID();
michael@0 170 let resuri = Utils.makeURI("unknown://title");
michael@0 171 applyEnsureNoFailures([
michael@0 172 {id: resguid,
michael@0 173 histUri: resuri.spec,
michael@0 174 title: null,
michael@0 175 visits: [{date: TIMESTAMP3,
michael@0 176 type: Ci.nsINavHistoryService.TRANSITION_TYPED}]}
michael@0 177 ]);
michael@0 178 do_check_attribute_count(store.getAllIDs(), 3);
michael@0 179 let queryres = queryHistoryVisits(resuri);
michael@0 180 do_check_eq(queryres.length, 1);
michael@0 181 do_check_eq(queryres[0].time, TIMESTAMP3);
michael@0 182 run_next_test();
michael@0 183 });
michael@0 184
michael@0 185 add_test(function test_invalid_records() {
michael@0 186 _("Make sure we handle invalid URLs in places databases gracefully.");
michael@0 187 let connection = PlacesUtils.history
michael@0 188 .QueryInterface(Ci.nsPIPlacesDatabase)
michael@0 189 .DBConnection;
michael@0 190 let stmt = connection.createAsyncStatement(
michael@0 191 "INSERT INTO moz_places "
michael@0 192 + "(url, title, rev_host, visit_count, last_visit_date) "
michael@0 193 + "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"
michael@0 194 );
michael@0 195 Async.querySpinningly(stmt);
michael@0 196 stmt.finalize();
michael@0 197 // Add the corresponding visit to retain database coherence.
michael@0 198 stmt = connection.createAsyncStatement(
michael@0 199 "INSERT INTO moz_historyvisits "
michael@0 200 + "(place_id, visit_date, visit_type, session) "
michael@0 201 + "VALUES ((SELECT id FROM moz_places WHERE url = 'invalid-uri'), "
michael@0 202 + TIMESTAMP3 + ", " + Ci.nsINavHistoryService.TRANSITION_TYPED + ", 1)"
michael@0 203 );
michael@0 204 Async.querySpinningly(stmt);
michael@0 205 stmt.finalize();
michael@0 206 do_check_attribute_count(store.getAllIDs(), 4);
michael@0 207
michael@0 208 _("Make sure we report records with invalid URIs.");
michael@0 209 let invalid_uri_guid = Utils.makeGUID();
michael@0 210 let failed = store.applyIncomingBatch([{
michael@0 211 id: invalid_uri_guid,
michael@0 212 histUri: ":::::::::::::::",
michael@0 213 title: "Doesn't have a valid URI",
michael@0 214 visits: [{date: TIMESTAMP3,
michael@0 215 type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
michael@0 216 ]);
michael@0 217 do_check_eq(failed.length, 1);
michael@0 218 do_check_eq(failed[0], invalid_uri_guid);
michael@0 219
michael@0 220 _("Make sure we handle records with invalid GUIDs gracefully (ignore).");
michael@0 221 applyEnsureNoFailures([
michael@0 222 {id: "invalid",
michael@0 223 histUri: "http://invalid.guid/",
michael@0 224 title: "Doesn't have a valid GUID",
michael@0 225 visits: [{date: TIMESTAMP3,
michael@0 226 type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
michael@0 227 ]);
michael@0 228
michael@0 229 _("Make sure we report records with invalid visits, gracefully handle non-integer dates.");
michael@0 230 let no_date_visit_guid = Utils.makeGUID();
michael@0 231 let no_type_visit_guid = Utils.makeGUID();
michael@0 232 let invalid_type_visit_guid = Utils.makeGUID();
michael@0 233 let non_integer_visit_guid = Utils.makeGUID();
michael@0 234 failed = store.applyIncomingBatch([
michael@0 235 {id: no_date_visit_guid,
michael@0 236 histUri: "http://no.date.visit/",
michael@0 237 title: "Visit has no date",
michael@0 238 visits: [{date: TIMESTAMP3}]},
michael@0 239 {id: no_type_visit_guid,
michael@0 240 histUri: "http://no.type.visit/",
michael@0 241 title: "Visit has no type",
michael@0 242 visits: [{type: Ci.nsINavHistoryService.TRANSITION_EMBED}]},
michael@0 243 {id: invalid_type_visit_guid,
michael@0 244 histUri: "http://invalid.type.visit/",
michael@0 245 title: "Visit has invalid type",
michael@0 246 visits: [{date: TIMESTAMP3,
michael@0 247 type: Ci.nsINavHistoryService.TRANSITION_LINK - 1}]},
michael@0 248 {id: non_integer_visit_guid,
michael@0 249 histUri: "http://non.integer.visit/",
michael@0 250 title: "Visit has non-integer date",
michael@0 251 visits: [{date: 1234.567,
michael@0 252 type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
michael@0 253 ]);
michael@0 254 do_check_eq(failed.length, 3);
michael@0 255 failed.sort();
michael@0 256 let expected = [no_date_visit_guid,
michael@0 257 no_type_visit_guid,
michael@0 258 invalid_type_visit_guid].sort();
michael@0 259 for (let i = 0; i < expected.length; i++) {
michael@0 260 do_check_eq(failed[i], expected[i]);
michael@0 261 }
michael@0 262
michael@0 263 _("Make sure we handle records with javascript: URLs gracefully.");
michael@0 264 applyEnsureNoFailures([
michael@0 265 {id: Utils.makeGUID(),
michael@0 266 histUri: "javascript:''",
michael@0 267 title: "javascript:''",
michael@0 268 visits: [{date: TIMESTAMP3,
michael@0 269 type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
michael@0 270 ]);
michael@0 271
michael@0 272 _("Make sure we handle records without any visits gracefully.");
michael@0 273 applyEnsureNoFailures([
michael@0 274 {id: Utils.makeGUID(),
michael@0 275 histUri: "http://getfirebug.com",
michael@0 276 title: "Get Firebug!",
michael@0 277 visits: []}
michael@0 278 ]);
michael@0 279
michael@0 280 run_next_test();
michael@0 281 });
michael@0 282
michael@0 283 add_test(function test_remove() {
michael@0 284 _("Remove an existent record and a non-existent from the store.");
michael@0 285 applyEnsureNoFailures([{id: fxguid, deleted: true},
michael@0 286 {id: Utils.makeGUID(), deleted: true}]);
michael@0 287 do_check_false(store.itemExists(fxguid));
michael@0 288 let queryres = queryHistoryVisits(fxuri);
michael@0 289 do_check_eq(queryres.length, 0);
michael@0 290
michael@0 291 _("Make sure wipe works.");
michael@0 292 store.wipe();
michael@0 293 do_check_empty(store.getAllIDs());
michael@0 294 queryres = queryHistoryVisits(fxuri);
michael@0 295 do_check_eq(queryres.length, 0);
michael@0 296 queryres = queryHistoryVisits(tburi);
michael@0 297 do_check_eq(queryres.length, 0);
michael@0 298 run_next_test();
michael@0 299 });
michael@0 300
michael@0 301 add_test(function cleanup() {
michael@0 302 _("Clean up.");
michael@0 303 PlacesUtils.history.removeAllPages();
michael@0 304 run_next_test();
michael@0 305 });

mercurial