test_history_tracker.js (7174B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const { PlacesDBUtils } = ChromeUtils.importESModule( 5 "resource://gre/modules/PlacesDBUtils.sys.mjs" 6 ); 7 const { HistoryEngine } = ChromeUtils.importESModule( 8 "resource://services-sync/engines/history.sys.mjs" 9 ); 10 const { Service } = ChromeUtils.importESModule( 11 "resource://services-sync/service.sys.mjs" 12 ); 13 14 let engine; 15 let tracker; 16 17 add_task(async function setup() { 18 await Service.engineManager.clear(); 19 await Service.engineManager.register(HistoryEngine); 20 engine = Service.engineManager.get("history"); 21 tracker = engine._tracker; 22 }); 23 24 async function verifyTrackerEmpty() { 25 let changes = await engine.pullNewChanges(); 26 do_check_empty(changes); 27 equal(tracker.score, 0); 28 } 29 30 async function verifyTrackedCount(expected) { 31 let changes = await engine.pullNewChanges(); 32 do_check_attribute_count(changes, expected); 33 } 34 35 async function verifyTrackedItems(tracked) { 36 let changes = await engine.pullNewChanges(); 37 let trackedIDs = new Set(Object.keys(changes)); 38 for (let guid of tracked) { 39 ok(guid in changes, `${guid} should be tracked`); 40 Assert.greater(changes[guid], 0, `${guid} should have a modified time`); 41 trackedIDs.delete(guid); 42 } 43 equal( 44 trackedIDs.size, 45 0, 46 `Unhandled tracked IDs: ${JSON.stringify(Array.from(trackedIDs))}` 47 ); 48 } 49 50 async function resetTracker() { 51 await tracker.clearChangedIDs(); 52 tracker.resetScore(); 53 } 54 55 async function cleanup() { 56 await PlacesUtils.history.clear(); 57 await resetTracker(); 58 await tracker.stop(); 59 } 60 61 add_task(async function test_empty() { 62 _("Verify we've got an empty, disabled tracker to work with."); 63 await verifyTrackerEmpty(); 64 Assert.ok(!tracker._isTracking); 65 66 await cleanup(); 67 }); 68 69 add_task(async function test_not_tracking() { 70 _("Create history item. Won't show because we haven't started tracking yet"); 71 await addVisit("not_tracking"); 72 await verifyTrackerEmpty(); 73 74 await cleanup(); 75 }); 76 77 add_task(async function test_start_tracking() { 78 _("Add hook for save completion."); 79 let savePromise = new Promise((resolve, reject) => { 80 let save = tracker._storage._save; 81 tracker._storage._save = async function () { 82 try { 83 await save.call(this); 84 resolve(); 85 } catch (ex) { 86 reject(ex); 87 } finally { 88 tracker._storage._save = save; 89 } 90 }; 91 }); 92 93 _("Tell the tracker to start tracking changes."); 94 tracker.start(); 95 let scorePromise = promiseOneObserver("weave:engine:score:updated"); 96 await addVisit("start_tracking"); 97 await scorePromise; 98 99 _("Score updated in test_start_tracking."); 100 await verifyTrackedCount(1); 101 Assert.equal(tracker.score, SCORE_INCREMENT_SMALL); 102 103 await savePromise; 104 105 _("changedIDs written to disk. Proceeding."); 106 await cleanup(); 107 }); 108 109 add_task(async function test_start_tracking_twice() { 110 _("Verifying preconditions."); 111 tracker.start(); 112 await addVisit("start_tracking_twice1"); 113 await verifyTrackedCount(1); 114 Assert.equal(tracker.score, SCORE_INCREMENT_SMALL); 115 116 _("Notifying twice won't do any harm."); 117 tracker.start(); 118 let scorePromise = promiseOneObserver("weave:engine:score:updated"); 119 await addVisit("start_tracking_twice2"); 120 await scorePromise; 121 122 _("Score updated in test_start_tracking_twice."); 123 await verifyTrackedCount(2); 124 Assert.equal(tracker.score, 2 * SCORE_INCREMENT_SMALL); 125 126 await cleanup(); 127 }); 128 129 add_task(async function test_track_delete() { 130 _("Deletions are tracked."); 131 132 // This isn't present because we weren't tracking when it was visited. 133 await addVisit("track_delete"); 134 let uri = CommonUtils.makeURI("http://getfirefox.com/track_delete"); 135 let guid = await engine._store.GUIDForUri(uri.spec); 136 await verifyTrackerEmpty(); 137 138 tracker.start(); 139 let visitRemovedPromise = promiseVisit("removed", uri); 140 let scorePromise = promiseOneObserver("weave:engine:score:updated"); 141 await PlacesUtils.history.remove(uri); 142 await Promise.all([scorePromise, visitRemovedPromise]); 143 144 await verifyTrackedItems([guid]); 145 Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE); 146 147 await cleanup(); 148 }); 149 150 add_task(async function test_dont_track_expiration() { 151 _("Expirations are not tracked."); 152 let uriToRemove = await addVisit("to_remove"); 153 let guidToRemove = await engine._store.GUIDForUri(uriToRemove.spec); 154 155 await resetTracker(); 156 await verifyTrackerEmpty(); 157 158 tracker.start(); 159 let visitRemovedPromise = promiseVisit("removed", uriToRemove); 160 let scorePromise = promiseOneObserver("weave:engine:score:updated"); 161 162 // Observe expiration. 163 Services.obs.addObserver(function onExpiration(aSubject, aTopic) { 164 Services.obs.removeObserver(onExpiration, aTopic); 165 // Remove the remaining page to update its score. 166 PlacesUtils.history.remove(uriToRemove); 167 }, PlacesUtils.TOPIC_EXPIRATION_FINISHED); 168 169 // Force expiration of 1 entry. 170 Services.prefs.setIntPref("places.history.expiration.max_pages", 0); 171 Cc["@mozilla.org/places/expiration;1"] 172 .getService(Ci.nsIObserver) 173 .observe(null, "places-debug-start-expiration", 1); 174 175 await Promise.all([scorePromise, visitRemovedPromise]); 176 await verifyTrackedItems([guidToRemove]); 177 178 await cleanup(); 179 }); 180 181 add_task(async function test_stop_tracking() { 182 _("Let's stop tracking again."); 183 await tracker.stop(); 184 await addVisit("stop_tracking"); 185 await verifyTrackerEmpty(); 186 187 await cleanup(); 188 }); 189 190 add_task(async function test_stop_tracking_twice() { 191 await tracker.stop(); 192 await addVisit("stop_tracking_twice1"); 193 194 _("Notifying twice won't do any harm."); 195 await tracker.stop(); 196 await addVisit("stop_tracking_twice2"); 197 await verifyTrackerEmpty(); 198 199 await cleanup(); 200 }); 201 202 add_task(async function test_filter_file_uris() { 203 tracker.start(); 204 205 let uri = CommonUtils.makeURI("file:///Users/eoger/tps/config.json"); 206 let visitAddedPromise = promiseVisit("added", uri); 207 await PlacesTestUtils.addVisits({ 208 uri, 209 visitDate: Date.now() * 1000, 210 transition: PlacesUtils.history.TRANSITION_LINK, 211 }); 212 await visitAddedPromise; 213 214 await verifyTrackerEmpty(); 215 await tracker.stop(); 216 await cleanup(); 217 }); 218 219 add_task(async function test_filter_hidden() { 220 tracker.start(); 221 222 _("Add visit; should be hidden by the redirect"); 223 let hiddenURI = await addVisit("hidden"); 224 let hiddenGUID = await engine._store.GUIDForUri(hiddenURI.spec); 225 _(`Hidden visit GUID: ${hiddenGUID}`); 226 227 _("Add redirect visit; should be tracked"); 228 let trackedURI = await addVisit( 229 "redirect", 230 hiddenURI.spec, 231 PlacesUtils.history.TRANSITION_REDIRECT_PERMANENT 232 ); 233 let trackedGUID = await engine._store.GUIDForUri(trackedURI.spec); 234 _(`Tracked visit GUID: ${trackedGUID}`); 235 236 _("Add visit for framed link; should be ignored"); 237 let embedURI = await addVisit( 238 "framed_link", 239 null, 240 PlacesUtils.history.TRANSITION_FRAMED_LINK 241 ); 242 let embedGUID = await engine._store.GUIDForUri(embedURI.spec); 243 _(`Framed link visit GUID: ${embedGUID}`); 244 245 _("Run Places maintenance to mark redirect visit as hidden"); 246 await PlacesDBUtils.maintenanceOnIdle(); 247 248 await verifyTrackedItems([trackedGUID]); 249 250 await cleanup(); 251 });