/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests alternative pages frecency. // Note: the order of the tests here matters, since we are emulating subsquent // starts of the recalculator component with different initial conditions. const FEATURE_PREF = "places.frecency.pages.alternative.featureGate"; async function restartRecalculator() { let subject = {}; PlacesFrecencyRecalculator.observe( subject, "test-alternative-frecency-init", "" ); await subject.promise; } async function getAllPages() { let db = await PlacesUtils.promiseDBConnection(); let rows = await db.execute(`SELECT * FROM moz_places`); Assert.greater(rows.length, 0); return rows.map(r => ({ url: r.getResultByName("url"), frecency: r.getResultByName("frecency"), recalc_frecency: r.getResultByName("recalc_frecency"), alt_frecency: r.getResultByName("alt_frecency"), recalc_alt_frecency: r.getResultByName("recalc_alt_frecency"), })); } add_setup(async function () { await PlacesTestUtils.addVisits([ "https://testdomain1.moz.org", "https://testdomain2.moz.org", "https://testdomain3.moz.org", ]); registerCleanupFunction(PlacesUtils.history.clear); }); add_task( { pref_set: [[FEATURE_PREF, false]], }, async function test_normal_init() { // The test starts with the pref enabled, otherwise we'd not have the SQL // function defined. So here we disable it, then enable again later. await restartRecalculator(); // Avoid hitting the cache, we want to check the actual database value. PlacesUtils.metadata.cache.clear(); Assert.ok( ObjectUtils.isEmpty( await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ) ), "Check there's no variables stored" ); } ); add_task( { pref_set: [[FEATURE_PREF, true]], }, async function test_enable_init() { // Set alt_frecency to NULL and recalc_alt_frecency = 0 for the entries in // moz_places to verify they are recalculated. await PlacesTestUtils.updateDatabaseValues("moz_places", { alt_frecency: null, recalc_alt_frecency: 0, }); await restartRecalculator(); // Ensure moz_meta doesn't report anything. Assert.ok( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.enabled, "Check the pref is enabled" ); // Avoid hitting the cache, we want to check the actual database value. PlacesUtils.metadata.cache.clear(); Assert.equal( ( await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ) ).version, PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.variables .version, "Check the algorithm version has been stored" ); // Check all alternative frecencies have been calculated, since we just have // a few. let pages = await getAllPages(); Assert.ok( pages.every(p => p.recalc_alt_frecency == 0), "All the entries have been recalculated" ); Assert.ok( pages.every(p => p.alt_frecency > 0), "All the entries have been recalculated" ); Assert.ok( PlacesFrecencyRecalculator.isRecalculationPending, "Recalculation should be pending" ); } ); add_task( { pref_set: [[FEATURE_PREF, true]], }, async function test_different_version() { let pages = await getAllPages(); Assert.ok( pages.every(p => p.recalc_alt_frecency == 0), "All the entries should not need recalculation" ); // Set alt_frecency to NULL to verify all the entries are recalculated. await PlacesTestUtils.updateDatabaseValues("moz_places", { alt_frecency: null, }); // It doesn't matter that the version is, it just have to be different. let variables = await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ); variables.version = 999; await PlacesUtils.metadata.set( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, variables ); await restartRecalculator(); // Check alternative frecency has been marked for recalculation. // Note just after init we reculate a chunk, and this test code is expected // to run before that... though we can't be sure, so if this starts failing // intermittently we'll have to add more synchronization test code. pages = await getAllPages(); // Ensure moz_meta has been updated. Assert.ok( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.enabled, "Check the pref is enabled" ); // Avoid hitting the cache, we want to check the actual database value. PlacesUtils.metadata.cache.clear(); Assert.deepEqual( ( await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ) ).version, PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.variables .version, "Check the algorithm version has been stored" ); // Check all alternative frecencies have been calculated, since we just have // a few. pages = await getAllPages(); Assert.ok( pages.every(p => p.recalc_alt_frecency == 0), "All the entries have been recalculated" ); Assert.ok( pages.every(p => p.alt_frecency > 0), "All the entries have been recalculated" ); Assert.ok( PlacesFrecencyRecalculator.isRecalculationPending, "Recalculation should be pending" ); } ); add_task( { pref_set: [[FEATURE_PREF, true]], }, async function test_different_variables() { let pages = await getAllPages(); Assert.ok( pages.every(p => p.recalc_alt_frecency == 0), "All the entries should not need recalculation" ); // Set alt_frecency to NULL to verify all the entries are recalculated. await PlacesTestUtils.updateDatabaseValues("moz_places", { alt_frecency: null, }); // Change variables. let variables = await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ); Assert.greater(Object.keys(variables).length, 1); Assert.ok("version" in variables, "At least the version is always present"); await PlacesUtils.metadata.set( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, { version: PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.variables .version, someVar: 1, } ); await restartRecalculator(); // Ensure moz_meta has been updated. Assert.ok( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.enabled, "Check the pref is enabled" ); // Avoid hitting the cache, we want to check the actual database value. PlacesUtils.metadata.cache.clear(); Assert.deepEqual( await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ), variables, "Check the algorithm variables have been stored" ); // Check all alternative frecencies have been calculated, since we just have // a few. pages = await getAllPages(); Assert.ok( pages.every(p => p.recalc_alt_frecency == 0), "All the entries have been recalculated" ); Assert.ok( pages.every(p => p.alt_frecency > 0), "All the entries have been recalculated" ); Assert.ok( PlacesFrecencyRecalculator.isRecalculationPending, "Recalculation should be pending" ); } ); add_task( { pref_set: [[FEATURE_PREF, false]], }, async function test_disable() { let pages = await getAllPages(); Assert.ok( pages.every(p => p.recalc_alt_frecency == 0), "All the entries should not need recalculation" ); // Avoid hitting the cache, we want to check the actual database value. PlacesUtils.metadata.cache.clear(); Assert.equal( ( await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ) ).version, PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.variables .version, "Check the algorithm version has been stored" ); await restartRecalculator(); // Check alternative frecency has not been marked for recalculation. pages = await getAllPages(); Assert.ok( pages.every(p => p.recalc_alt_frecency == 0), "The entries not have been marked for recalc" ); Assert.ok( pages.every(p => p.alt_frecency === null), "All the alt_frecency values should have been nullified" ); // Ensure moz_meta has been updated. // Avoid hitting the cache, we want to check the actual database value. PlacesUtils.metadata.cache.clear(); Assert.ok( ObjectUtils.isEmpty( await PlacesUtils.metadata.get( PlacesFrecencyRecalculator.alternativeFrecencyInfo.pages.metadataKey, Object.create(null) ) ), "Check the algorithm variables has been removed" ); } ); add_task( { pref_set: [[FEATURE_PREF, true]], }, async function test_score() { await restartRecalculator(); // This is not intended to cover the algorithm as a whole, but just as a // sanity check for scores. // Add before visits to properly set visit source. await PlacesUtils.bookmarks.insert({ url: "https://visitedbookmark.moz.org", parentGuid: PlacesUtils.bookmarks.toolbarGuid, }); await PlacesTestUtils.addVisits([ { url: "https://low.moz.org", transition: PlacesUtils.history.TRANSITIONS.FRAMED_LINK, }, { url: "https://visitedbookmark.moz.org", transition: PlacesUtils.history.TRANSITIONS.FRAMED_LINK, }, { url: "https://old.moz.org", visitDate: (Date.now() - 2 * 86400000) * 1000, }, { url: "https://base.moz.org" }, { url: "https://manyvisits.moz.org" }, { url: "https://manyvisits.moz.org" }, { url: "https://manyvisits.moz.org" }, { url: "https://manyvisits.moz.org" }, ]); await PlacesUtils.bookmarks.insert({ url: "https://unvisitedbookmark.moz.org", parentGuid: PlacesUtils.bookmarks.toolbarGuid, }); await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies(); let getFrecency = url => PlacesTestUtils.getDatabaseValue("moz_places", "alt_frecency", { url, }); let low = await getFrecency("https://low.moz.org/"); let old = await getFrecency("https://old.moz.org/"); Assert.greater(old, low); let base = await getFrecency("https://base.moz.org/"); Assert.greater(base, old); let unvisitedBm = await getFrecency("https://unvisitedbookmark.moz.org/"); Assert.greater(unvisitedBm, base); let manyVisits = await getFrecency("https://manyvisits.moz.org/"); Assert.greater(manyVisits, unvisitedBm); let visitedBm = await getFrecency("https://visitedbookmark.moz.org/"); Assert.greater(visitedBm, base); } );