diff options
Diffstat (limited to 'toolkit/components/places/tests/expiration')
17 files changed, 1685 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/expiration/.eslintrc.js b/toolkit/components/places/tests/expiration/.eslintrc.js new file mode 100644 index 0000000000..d35787cd2c --- /dev/null +++ b/toolkit/components/places/tests/expiration/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "../../../../../testing/xpcshell/xpcshell.eslintrc.js" + ] +}; diff --git a/toolkit/components/places/tests/expiration/head_expiration.js b/toolkit/components/places/tests/expiration/head_expiration.js new file mode 100644 index 0000000000..2be4af307c --- /dev/null +++ b/toolkit/components/places/tests/expiration/head_expiration.js @@ -0,0 +1,124 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var Ci = Components.interfaces; +var Cc = Components.classes; +var Cr = Components.results; +var Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); + +// Import common head. +{ + let commonFile = do_get_file("../head_common.js", false); + let uri = Services.io.newFileURI(commonFile); + Services.scriptloader.loadSubScript(uri.spec, this); +} + +// Put any other stuff relative to this test folder below. + + +// Simulates an expiration at shutdown. +function shutdownExpiration() +{ + let expire = Cc["@mozilla.org/places/expiration;1"].getService(Ci.nsIObserver); + expire.observe(null, "places-will-close-connection", null); +} + + +/** + * Causes expiration component to start, otherwise it would wait for the first + * history notification. + */ +function force_expiration_start() { + Cc["@mozilla.org/places/expiration;1"] + .getService(Ci.nsIObserver) + .observe(null, "testing-mode", null); +} + + +/** + * Forces an expiration run. + * + * @param [optional] aLimit + * Limit for the expiration. Pass -1 for unlimited. + * Any other non-positive value will just expire orphans. + * + * @return {Promise} + * @resolves When expiration finishes. + * @rejects Never. + */ +function promiseForceExpirationStep(aLimit) { + let promise = promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED); + let expire = Cc["@mozilla.org/places/expiration;1"].getService(Ci.nsIObserver); + expire.observe(null, "places-debug-start-expiration", aLimit); + return promise; +} + + +/** + * Expiration preferences helpers. + */ + +function setInterval(aNewInterval) { + Services.prefs.setIntPref("places.history.expiration.interval_seconds", aNewInterval); +} +function getInterval() { + return Services.prefs.getIntPref("places.history.expiration.interval_seconds"); +} +function clearInterval() { + try { + Services.prefs.clearUserPref("places.history.expiration.interval_seconds"); + } + catch (ex) {} +} + + +function setMaxPages(aNewMaxPages) { + Services.prefs.setIntPref("places.history.expiration.max_pages", aNewMaxPages); +} +function getMaxPages() { + return Services.prefs.getIntPref("places.history.expiration.max_pages"); +} +function clearMaxPages() { + try { + Services.prefs.clearUserPref("places.history.expiration.max_pages"); + } + catch (ex) {} +} + + +function setHistoryEnabled(aHistoryEnabled) { + Services.prefs.setBoolPref("places.history.enabled", aHistoryEnabled); +} +function getHistoryEnabled() { + return Services.prefs.getBoolPref("places.history.enabled"); +} +function clearHistoryEnabled() { + try { + Services.prefs.clearUserPref("places.history.enabled"); + } + catch (ex) {} +} + +/** + * Returns a PRTime in the past usable to add expirable visits. + * + * param [optional] daysAgo + * Expiration ignores any visit added in the last 7 days, so by default + * this will be set to 7. + * @note to be safe against DST issues we go back one day more. + */ +function getExpirablePRTime(daysAgo = 7) { + let dateObj = new Date(); + // Normalize to midnight + dateObj.setHours(0); + dateObj.setMinutes(0); + dateObj.setSeconds(0); + dateObj.setMilliseconds(0); + dateObj = new Date(dateObj.getTime() - (daysAgo + 1) * 86400000); + return dateObj.getTime() * 1000; +} diff --git a/toolkit/components/places/tests/expiration/test_analyze_runs.js b/toolkit/components/places/tests/expiration/test_analyze_runs.js new file mode 100644 index 0000000000..1a84e1b38c --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_analyze_runs.js @@ -0,0 +1,118 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Constants + +const TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING = "autocomplete-will-enter-text"; + +// Helpers + +/** + * Ensures that we have no data in the tables created by ANALYZE. + */ +function clearAnalyzeData() { + let db = DBConn(); + if (!db.tableExists("sqlite_stat1")) { + return; + } + db.executeSimpleSQL("DELETE FROM sqlite_stat1"); +} + +/** + * Checks that we ran ANALYZE on the specified table. + * + * @param aTableName + * The table to check if ANALYZE was ran. + * @param aRan + * True if it was expected to run, false otherwise + */ +function do_check_analyze_ran(aTableName, aRan) { + let db = DBConn(); + do_check_true(db.tableExists("sqlite_stat1")); + let stmt = db.createStatement("SELECT idx FROM sqlite_stat1 WHERE tbl = :table"); + stmt.params.table = aTableName; + try { + if (aRan) { + do_check_true(stmt.executeStep()); + do_check_neq(stmt.row.idx, null); + } + else { + do_check_false(stmt.executeStep()); + } + } + finally { + stmt.finalize(); + } +} + +// Tests + +function run_test() { + run_next_test(); +} + +add_task(function* init_tests() { + const TEST_URI = NetUtil.newURI("http://mozilla.org/"); + const TEST_TITLE = "This is a test"; + + yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: TEST_TITLE, + url: TEST_URI + }); + yield PlacesTestUtils.addVisits(TEST_URI); + let thing = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput, + Ci.nsIAutoCompletePopup, + Ci.nsIAutoCompleteController]), + get popup() { return thing; }, + get controller() { return thing; }, + popupOpen: true, + selectedIndex: 0, + getValueAt: function() { return TEST_URI.spec; }, + searchString: TEST_TITLE, + }; + Services.obs.notifyObservers(thing, TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING, + null); +}); + +add_task(function* test_timed() { + clearAnalyzeData(); + + // Set a low interval and wait for the timed expiration to start. + let promise = promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED); + setInterval(3); + yield promise; + setInterval(3600); + + do_check_analyze_ran("moz_places", false); + do_check_analyze_ran("moz_bookmarks", false); + do_check_analyze_ran("moz_historyvisits", false); + do_check_analyze_ran("moz_inputhistory", true); +}); + +add_task(function* test_debug() { + clearAnalyzeData(); + + yield promiseForceExpirationStep(1); + + do_check_analyze_ran("moz_places", true); + do_check_analyze_ran("moz_bookmarks", true); + do_check_analyze_ran("moz_historyvisits", true); + do_check_analyze_ran("moz_inputhistory", true); +}); + +add_task(function* test_clear_history() { + clearAnalyzeData(); + + let promise = promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED); + let listener = Cc["@mozilla.org/places/expiration;1"] + .getService(Ci.nsINavHistoryObserver); + listener.onClearHistory(); + yield promise; + + do_check_analyze_ran("moz_places", true); + do_check_analyze_ran("moz_bookmarks", false); + do_check_analyze_ran("moz_historyvisits", true); + do_check_analyze_ran("moz_inputhistory", true); +}); diff --git a/toolkit/components/places/tests/expiration/test_annos_expire_history.js b/toolkit/components/places/tests/expiration/test_annos_expire_history.js new file mode 100644 index 0000000000..f9568a769c --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_annos_expire_history.js @@ -0,0 +1,93 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * EXPIRE_WITH_HISTORY annotations should be expired when a page has no more + * visits, even if the page still exists in the database. + * This expiration policy is only valid for page annotations. + */ + +var as = Cc["@mozilla.org/browser/annotation-service;1"]. + getService(Ci.nsIAnnotationService); + +function run_test() { + run_next_test(); +} + +add_task(function* test_annos_expire_history() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + // Expire all expirable pages. + setMaxPages(0); + + // Add some visited page and a couple expire with history annotations for each. + let now = getExpirablePRTime(); + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://page_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now++ }); + as.setPageAnnotation(pageURI, "page_expire1", "test", 0, as.EXPIRE_WITH_HISTORY); + as.setPageAnnotation(pageURI, "page_expire2", "test", 0, as.EXPIRE_WITH_HISTORY); + } + + let pages = as.getPagesWithAnnotation("page_expire1"); + do_check_eq(pages.length, 5); + pages = as.getPagesWithAnnotation("page_expire2"); + do_check_eq(pages.length, 5); + + // Add some bookmarked page and a couple session annotations for each. + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://item_anno." + i + ".mozilla.org/"); + // We also add a visit before bookmarking. + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now++ }); + yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: pageURI, + title: null + }); + // Notice we use page annotations here, items annotations can't use this + // kind of expiration policy. + as.setPageAnnotation(pageURI, "item_persist1", "test", 0, as.EXPIRE_WITH_HISTORY); + as.setPageAnnotation(pageURI, "item_persist2", "test", 0, as.EXPIRE_WITH_HISTORY); + } + + let items = as.getPagesWithAnnotation("item_persist1"); + do_check_eq(items.length, 5); + items = as.getPagesWithAnnotation("item_persist2"); + do_check_eq(items.length, 5); + + // Add other visited page and a couple expire with history annotations for each. + // We won't expire these visits, so the annotations should survive. + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://persist_page_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now++ }); + as.setPageAnnotation(pageURI, "page_persist1", "test", 0, as.EXPIRE_WITH_HISTORY); + as.setPageAnnotation(pageURI, "page_persist2", "test", 0, as.EXPIRE_WITH_HISTORY); + } + + pages = as.getPagesWithAnnotation("page_persist1"); + do_check_eq(pages.length, 5); + pages = as.getPagesWithAnnotation("page_persist2"); + do_check_eq(pages.length, 5); + + // Expire all visits for the first 5 pages and the bookmarks. + yield promiseForceExpirationStep(10); + + pages = as.getPagesWithAnnotation("page_expire1"); + do_check_eq(pages.length, 0); + pages = as.getPagesWithAnnotation("page_expire2"); + do_check_eq(pages.length, 0); + items = as.getItemsWithAnnotation("item_persist1"); + do_check_eq(items.length, 0); + items = as.getItemsWithAnnotation("item_persist2"); + do_check_eq(items.length, 0); + pages = as.getPagesWithAnnotation("page_persist1"); + do_check_eq(pages.length, 5); + pages = as.getPagesWithAnnotation("page_persist2"); + do_check_eq(pages.length, 5); +}); diff --git a/toolkit/components/places/tests/expiration/test_annos_expire_never.js b/toolkit/components/places/tests/expiration/test_annos_expire_never.js new file mode 100644 index 0000000000..f146f25b5e --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_annos_expire_never.js @@ -0,0 +1,95 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * EXPIRE_NEVER annotations should be expired when a page is removed from the + * database. + * If the annotation is a page annotation this will happen when the page is + * expired, namely when the page has no visits and is not bookmarked. + * Otherwise if it's an item annotation the annotation will be expired when + * the item is removed, thus expiration won't handle this case at all. + */ + +var as = Cc["@mozilla.org/browser/annotation-service;1"]. + getService(Ci.nsIAnnotationService); + +function run_test() { + run_next_test(); +} + +add_task(function* test_annos_expire_never() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + // Expire all expirable pages. + setMaxPages(0); + + // Add some visited page and a couple expire never annotations for each. + let now = getExpirablePRTime(); + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://page_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now++ }); + as.setPageAnnotation(pageURI, "page_expire1", "test", 0, as.EXPIRE_NEVER); + as.setPageAnnotation(pageURI, "page_expire2", "test", 0, as.EXPIRE_NEVER); + } + + let pages = as.getPagesWithAnnotation("page_expire1"); + do_check_eq(pages.length, 5); + pages = as.getPagesWithAnnotation("page_expire2"); + do_check_eq(pages.length, 5); + + // Add some bookmarked page and a couple expire never annotations for each. + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://item_anno." + i + ".mozilla.org/"); + // We also add a visit before bookmarking. + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now++ }); + let bm = yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: pageURI, + title: null + }); + let id = yield PlacesUtils.promiseItemId(bm.guid); + as.setItemAnnotation(id, "item_persist1", "test", 0, as.EXPIRE_NEVER); + as.setItemAnnotation(id, "item_persist2", "test", 0, as.EXPIRE_NEVER); + } + + let items = as.getItemsWithAnnotation("item_persist1"); + do_check_eq(items.length, 5); + items = as.getItemsWithAnnotation("item_persist2"); + do_check_eq(items.length, 5); + + // Add other visited page and a couple expire never annotations for each. + // We won't expire these visits, so the annotations should survive. + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://persist_page_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now++ }); + as.setPageAnnotation(pageURI, "page_persist1", "test", 0, as.EXPIRE_NEVER); + as.setPageAnnotation(pageURI, "page_persist2", "test", 0, as.EXPIRE_NEVER); + } + + pages = as.getPagesWithAnnotation("page_persist1"); + do_check_eq(pages.length, 5); + pages = as.getPagesWithAnnotation("page_persist2"); + do_check_eq(pages.length, 5); + + // Expire all visits for the first 5 pages and the bookmarks. + yield promiseForceExpirationStep(10); + + pages = as.getPagesWithAnnotation("page_expire1"); + do_check_eq(pages.length, 0); + pages = as.getPagesWithAnnotation("page_expire2"); + do_check_eq(pages.length, 0); + items = as.getItemsWithAnnotation("item_persist1"); + do_check_eq(items.length, 5); + items = as.getItemsWithAnnotation("item_persist2"); + do_check_eq(items.length, 5); + pages = as.getPagesWithAnnotation("page_persist1"); + do_check_eq(pages.length, 5); + pages = as.getPagesWithAnnotation("page_persist2"); + do_check_eq(pages.length, 5); +}); diff --git a/toolkit/components/places/tests/expiration/test_annos_expire_policy.js b/toolkit/components/places/tests/expiration/test_annos_expire_policy.js new file mode 100644 index 0000000000..2fe50e13e5 --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_annos_expire_policy.js @@ -0,0 +1,189 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * Annotations can be set with a timed expiration policy. + * Supported policies are: + * - EXPIRE_DAYS: annotation would be expired after 7 days + * - EXPIRE_WEEKS: annotation would be expired after 30 days + * - EXPIRE_MONTHS: annotation would be expired after 180 days + */ + +var as = Cc["@mozilla.org/browser/annotation-service;1"]. + getService(Ci.nsIAnnotationService); + +/** + * Creates an aged annotation. + * + * @param aIdentifier Either a page url or an item id. + * @param aIdentifier Name of the annotation. + * @param aValue Value for the annotation. + * @param aExpirePolicy Expiration policy of the annotation. + * @param aAgeInDays Age in days of the annotation. + * @param [optional] aLastModifiedAgeInDays Age in days of the annotation, for lastModified. + */ +var now = Date.now(); +function add_old_anno(aIdentifier, aName, aValue, aExpirePolicy, + aAgeInDays, aLastModifiedAgeInDays) { + let expireDate = (now - (aAgeInDays * 86400 * 1000)) * 1000; + let lastModifiedDate = 0; + if (aLastModifiedAgeInDays) + lastModifiedDate = (now - (aLastModifiedAgeInDays * 86400 * 1000)) * 1000; + + let sql; + if (typeof(aIdentifier) == "number") { + // Item annotation. + as.setItemAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy); + // Update dateAdded for the last added annotation. + sql = "UPDATE moz_items_annos SET dateAdded = :expire_date, lastModified = :last_modified " + + "WHERE id = (SELECT id FROM moz_items_annos " + + "WHERE item_id = :id " + + "ORDER BY dateAdded DESC LIMIT 1)"; + } + else if (aIdentifier instanceof Ci.nsIURI) { + // Page annotation. + as.setPageAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy); + // Update dateAdded for the last added annotation. + sql = "UPDATE moz_annos SET dateAdded = :expire_date, lastModified = :last_modified " + + "WHERE id = (SELECT a.id FROM moz_annos a " + + "LEFT JOIN moz_places h on h.id = a.place_id " + + "WHERE h.url_hash = hash(:id) AND h.url = :id " + + "ORDER BY a.dateAdded DESC LIMIT 1)"; + } + else + do_throw("Wrong identifier type"); + + let stmt = DBConn().createStatement(sql); + stmt.params.id = (typeof(aIdentifier) == "number") ? aIdentifier + : aIdentifier.spec; + stmt.params.expire_date = expireDate; + stmt.params.last_modified = lastModifiedDate; + try { + stmt.executeStep(); + } + finally { + stmt.finalize(); + } +} + +function run_test() { + run_next_test(); +} + +add_task(function* test_annos_expire_policy() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + // Expire all expirable pages. + setMaxPages(0); + + let now_specific_to_test = getExpirablePRTime(); + // Add some bookmarked page and timed annotations for each. + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://item_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now_specific_to_test++ }); + let bm = yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: pageURI, + title: null + }); + let id = yield PlacesUtils.promiseItemId(bm.guid); + // Add a 6 days old anno. + add_old_anno(id, "persist_days", "test", as.EXPIRE_DAYS, 6); + // Add a 8 days old anno, modified 5 days ago. + add_old_anno(id, "persist_lm_days", "test", as.EXPIRE_DAYS, 8, 6); + // Add a 8 days old anno. + add_old_anno(id, "expire_days", "test", as.EXPIRE_DAYS, 8); + + // Add a 29 days old anno. + add_old_anno(id, "persist_weeks", "test", as.EXPIRE_WEEKS, 29); + // Add a 31 days old anno, modified 29 days ago. + add_old_anno(id, "persist_lm_weeks", "test", as.EXPIRE_WEEKS, 31, 29); + // Add a 31 days old anno. + add_old_anno(id, "expire_weeks", "test", as.EXPIRE_WEEKS, 31); + + // Add a 179 days old anno. + add_old_anno(id, "persist_months", "test", as.EXPIRE_MONTHS, 179); + // Add a 181 days old anno, modified 179 days ago. + add_old_anno(id, "persist_lm_months", "test", as.EXPIRE_MONTHS, 181, 179); + // Add a 181 days old anno. + add_old_anno(id, "expire_months", "test", as.EXPIRE_MONTHS, 181); + + // Add a 6 days old anno. + add_old_anno(pageURI, "persist_days", "test", as.EXPIRE_DAYS, 6); + // Add a 8 days old anno, modified 5 days ago. + add_old_anno(pageURI, "persist_lm_days", "test", as.EXPIRE_DAYS, 8, 6); + // Add a 8 days old anno. + add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8); + + // Add a 29 days old anno. + add_old_anno(pageURI, "persist_weeks", "test", as.EXPIRE_WEEKS, 29); + // Add a 31 days old anno, modified 29 days ago. + add_old_anno(pageURI, "persist_lm_weeks", "test", as.EXPIRE_WEEKS, 31, 29); + // Add a 31 days old anno. + add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31); + + // Add a 179 days old anno. + add_old_anno(pageURI, "persist_months", "test", as.EXPIRE_MONTHS, 179); + // Add a 181 days old anno, modified 179 days ago. + add_old_anno(pageURI, "persist_lm_months", "test", as.EXPIRE_MONTHS, 181, 179); + // Add a 181 days old anno. + add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181); + } + + // Add some visited page and timed annotations for each. + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://page_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now_specific_to_test++ }); + // Add a 6 days old anno. + add_old_anno(pageURI, "persist_days", "test", as.EXPIRE_DAYS, 6); + // Add a 8 days old anno, modified 5 days ago. + add_old_anno(pageURI, "persist_lm_days", "test", as.EXPIRE_DAYS, 8, 6); + // Add a 8 days old anno. + add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8); + + // Add a 29 days old anno. + add_old_anno(pageURI, "persist_weeks", "test", as.EXPIRE_WEEKS, 29); + // Add a 31 days old anno, modified 29 days ago. + add_old_anno(pageURI, "persist_lm_weeks", "test", as.EXPIRE_WEEKS, 31, 29); + // Add a 31 days old anno. + add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31); + + // Add a 179 days old anno. + add_old_anno(pageURI, "persist_months", "test", as.EXPIRE_MONTHS, 179); + // Add a 181 days old anno, modified 179 days ago. + add_old_anno(pageURI, "persist_lm_months", "test", as.EXPIRE_MONTHS, 181, 179); + // Add a 181 days old anno. + add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181); + } + + // Expire all visits for the bookmarks. + yield promiseForceExpirationStep(5); + + ["expire_days", "expire_weeks", "expire_months"].forEach(function(aAnno) { + let pages = as.getPagesWithAnnotation(aAnno); + do_check_eq(pages.length, 0); + }); + + ["expire_days", "expire_weeks", "expire_months"].forEach(function(aAnno) { + let items = as.getItemsWithAnnotation(aAnno); + do_check_eq(items.length, 0); + }); + + ["persist_days", "persist_lm_days", "persist_weeks", "persist_lm_weeks", + "persist_months", "persist_lm_months"].forEach(function(aAnno) { + let pages = as.getPagesWithAnnotation(aAnno); + do_check_eq(pages.length, 10); + }); + + ["persist_days", "persist_lm_days", "persist_weeks", "persist_lm_weeks", + "persist_months", "persist_lm_months"].forEach(function(aAnno) { + let items = as.getItemsWithAnnotation(aAnno); + do_check_eq(items.length, 5); + }); +}); diff --git a/toolkit/components/places/tests/expiration/test_annos_expire_session.js b/toolkit/components/places/tests/expiration/test_annos_expire_session.js new file mode 100644 index 0000000000..68c995f80e --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_annos_expire_session.js @@ -0,0 +1,83 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * Session annotations should be expired when browsing session ends. + */ + +var as = Cc["@mozilla.org/browser/annotation-service;1"]. + getService(Ci.nsIAnnotationService); + +function run_test() { + run_next_test(); +} + +add_task(function* test_annos_expire_session() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + // Add some visited page and a couple session annotations for each. + let now = Date.now() * 1000; + for (let i = 0; i < 10; i++) { + let pageURI = uri("http://session_page_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI, visitDate: now++ }); + as.setPageAnnotation(pageURI, "test1", "test", 0, as.EXPIRE_SESSION); + as.setPageAnnotation(pageURI, "test2", "test", 0, as.EXPIRE_SESSION); + } + + // Add some bookmarked page and a couple session annotations for each. + for (let i = 0; i < 10; i++) { + let pageURI = uri("http://session_item_anno." + i + ".mozilla.org/"); + let bm = yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: pageURI, + title: null + }); + let id = yield PlacesUtils.promiseItemId(bm.guid); + as.setItemAnnotation(id, "test1", "test", 0, as.EXPIRE_SESSION); + as.setItemAnnotation(id, "test2", "test", 0, as.EXPIRE_SESSION); + } + + + let pages = as.getPagesWithAnnotation("test1"); + do_check_eq(pages.length, 10); + pages = as.getPagesWithAnnotation("test2"); + do_check_eq(pages.length, 10); + let items = as.getItemsWithAnnotation("test1"); + do_check_eq(items.length, 10); + items = as.getItemsWithAnnotation("test2"); + do_check_eq(items.length, 10); + + let deferred = Promise.defer(); + waitForConnectionClosed(function() { + let stmt = DBConn(true).createAsyncStatement( + `SELECT id FROM moz_annos + UNION ALL + SELECT id FROM moz_items_annos + WHERE expiration = :expiration` + ); + stmt.params.expiration = as.EXPIRE_SESSION; + stmt.executeAsync({ + handleResult: function(aResultSet) { + dump_table("moz_annos"); + dump_table("moz_items_annos"); + do_throw("Should not find any leftover session annotations"); + }, + handleError: function(aError) { + do_throw("Error code " + aError.result + " with message '" + + aError.message + "' returned."); + }, + handleCompletion: function(aReason) { + do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); + deferred.resolve(); + } + }); + stmt.finalize(); + }); + yield deferred.promise; +}); diff --git a/toolkit/components/places/tests/expiration/test_clearHistory.js b/toolkit/components/places/tests/expiration/test_clearHistory.js new file mode 100644 index 0000000000..d3879d7ad9 --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_clearHistory.js @@ -0,0 +1,157 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * History.clear() should expire everything but bookmarked pages and valid + * annos. + */ + +var hs = PlacesUtils.history; +var as = PlacesUtils.annotations; + +/** + * Creates an aged annotation. + * + * @param aIdentifier Either a page url or an item id. + * @param aIdentifier Name of the annotation. + * @param aValue Value for the annotation. + * @param aExpirePolicy Expiration policy of the annotation. + * @param aAgeInDays Age in days of the annotation. + * @param [optional] aLastModifiedAgeInDays Age in days of the annotation, for lastModified. + */ +var now = Date.now(); +function add_old_anno(aIdentifier, aName, aValue, aExpirePolicy, + aAgeInDays, aLastModifiedAgeInDays) { + let expireDate = (now - (aAgeInDays * 86400 * 1000)) * 1000; + let lastModifiedDate = 0; + if (aLastModifiedAgeInDays) + lastModifiedDate = (now - (aLastModifiedAgeInDays * 86400 * 1000)) * 1000; + + let sql; + if (typeof(aIdentifier) == "number") { + // Item annotation. + as.setItemAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy); + // Update dateAdded for the last added annotation. + sql = "UPDATE moz_items_annos SET dateAdded = :expire_date, lastModified = :last_modified " + + "WHERE id = ( " + + "SELECT a.id FROM moz_items_annos a " + + "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " + + "WHERE a.item_id = :id " + + "AND n.name = :anno_name " + + "ORDER BY a.dateAdded DESC LIMIT 1 " + + ")"; + } + else if (aIdentifier instanceof Ci.nsIURI) { + // Page annotation. + as.setPageAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy); + // Update dateAdded for the last added annotation. + sql = "UPDATE moz_annos SET dateAdded = :expire_date, lastModified = :last_modified " + + "WHERE id = ( " + + "SELECT a.id FROM moz_annos a " + + "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " + + "JOIN moz_places h on h.id = a.place_id " + + "WHERE h.url_hash = hash(:id) AND h.url = :id " + + "AND n.name = :anno_name " + + "ORDER BY a.dateAdded DESC LIMIT 1 " + + ")"; + } + else + do_throw("Wrong identifier type"); + + let stmt = DBConn().createStatement(sql); + stmt.params.id = (typeof(aIdentifier) == "number") ? aIdentifier + : aIdentifier.spec; + stmt.params.expire_date = expireDate; + stmt.params.last_modified = lastModifiedDate; + stmt.params.anno_name = aName; + try { + stmt.executeStep(); + } + finally { + stmt.finalize(); + } +} + +function run_test() { + run_next_test(); +} + +add_task(function* test_historyClear() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + // Expire all expirable pages. + setMaxPages(0); + + // Add some bookmarked page with visit and annotations. + for (let i = 0; i < 5; i++) { + let pageURI = uri("http://item_anno." + i + ".mozilla.org/"); + // This visit will be expired. + yield PlacesTestUtils.addVisits({ uri: pageURI }); + let bm = yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: pageURI, + title: null + }); + let id = yield PlacesUtils.promiseItemId(bm.guid); + // Will persist because it's an EXPIRE_NEVER item anno. + as.setItemAnnotation(id, "persist", "test", 0, as.EXPIRE_NEVER); + // Will persist because the page is bookmarked. + as.setPageAnnotation(pageURI, "persist", "test", 0, as.EXPIRE_NEVER); + // All EXPIRE_SESSION annotations are expected to expire on clear history. + as.setItemAnnotation(id, "expire_session", "test", 0, as.EXPIRE_SESSION); + as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION); + // Annotations with timed policy will expire regardless bookmarked status. + add_old_anno(id, "expire_days", "test", as.EXPIRE_DAYS, 8); + add_old_anno(id, "expire_weeks", "test", as.EXPIRE_WEEKS, 31); + add_old_anno(id, "expire_months", "test", as.EXPIRE_MONTHS, 181); + add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8); + add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31); + add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181); + } + + // Add some visited page and annotations for each. + for (let i = 0; i < 5; i++) { + // All page annotations related to these expired pages are expected to + // expire as well. + let pageURI = uri("http://page_anno." + i + ".mozilla.org/"); + yield PlacesTestUtils.addVisits({ uri: pageURI }); + as.setPageAnnotation(pageURI, "expire", "test", 0, as.EXPIRE_NEVER); + as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION); + add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8); + add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31); + add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181); + } + + // Expire all visits for the bookmarks + yield PlacesUtils.history.clear(); + + ["expire_days", "expire_weeks", "expire_months", "expire_session", + "expire"].forEach(function(aAnno) { + let pages = as.getPagesWithAnnotation(aAnno); + do_check_eq(pages.length, 0); + }); + + ["expire_days", "expire_weeks", "expire_months", "expire_session", + "expire"].forEach(function(aAnno) { + let items = as.getItemsWithAnnotation(aAnno); + do_check_eq(items.length, 0); + }); + + let pages = as.getPagesWithAnnotation("persist"); + do_check_eq(pages.length, 5); + + let items = as.getItemsWithAnnotation("persist"); + do_check_eq(items.length, 5); + + for (let itemId of items) { + // Check item exists. + let guid = yield PlacesUtils.promiseItemGuid(itemId); + do_check_true((yield PlacesUtils.bookmarks.fetch({guid})), "item exists"); + } +}); diff --git a/toolkit/components/places/tests/expiration/test_debug_expiration.js b/toolkit/components/places/tests/expiration/test_debug_expiration.js new file mode 100644 index 0000000000..456c03363e --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_debug_expiration.js @@ -0,0 +1,225 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * What this is aimed to test: + * + * Expiration can be manually triggered through a debug topic, but that should + * only expire orphan entries, unless -1 is passed as limit. + */ + +var gNow = getExpirablePRTime(60); + +add_task(function* test_expire_orphans() +{ + // Add visits to 2 pages and force a orphan expiration. Visits should survive. + yield PlacesTestUtils.addVisits({ + uri: uri("http://page1.mozilla.org/"), + visitDate: gNow++ + }); + yield PlacesTestUtils.addVisits({ + uri: uri("http://page2.mozilla.org/"), + visitDate: gNow++ + }); + // Create a orphan place. + let bm = yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://page3.mozilla.org/", + title: "" + }); + yield PlacesUtils.bookmarks.remove(bm); + + // Expire now. + yield promiseForceExpirationStep(0); + + // Check that visits survived. + do_check_eq(visits_in_database("http://page1.mozilla.org/"), 1); + do_check_eq(visits_in_database("http://page2.mozilla.org/"), 1); + do_check_false(page_in_database("http://page3.mozilla.org/")); + + // Clean up. + yield PlacesTestUtils.clearHistory(); +}); + +add_task(function* test_expire_orphans_optionalarg() +{ + // Add visits to 2 pages and force a orphan expiration. Visits should survive. + yield PlacesTestUtils.addVisits({ + uri: uri("http://page1.mozilla.org/"), + visitDate: gNow++ + }); + yield PlacesTestUtils.addVisits({ + uri: uri("http://page2.mozilla.org/"), + visitDate: gNow++ + }); + // Create a orphan place. + let bm = yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://page3.mozilla.org/", + title: "" + }); + yield PlacesUtils.bookmarks.remove(bm); + + // Expire now. + yield promiseForceExpirationStep(); + + // Check that visits survived. + do_check_eq(visits_in_database("http://page1.mozilla.org/"), 1); + do_check_eq(visits_in_database("http://page2.mozilla.org/"), 1); + do_check_false(page_in_database("http://page3.mozilla.org/")); + + // Clean up. + yield PlacesTestUtils.clearHistory(); +}); + +add_task(function* test_expire_limited() +{ + yield PlacesTestUtils.addVisits([ + { // Should be expired cause it's the oldest visit + uri: "http://old.mozilla.org/", + visitDate: gNow++ + }, + { // Should not be expired cause we limit 1 + uri: "http://new.mozilla.org/", + visitDate: gNow++ + }, + ]); + + // Expire now. + yield promiseForceExpirationStep(1); + + // Check that newer visit survived. + do_check_eq(visits_in_database("http://new.mozilla.org/"), 1); + // Other visits should have been expired. + do_check_false(page_in_database("http://old.mozilla.org/")); + + // Clean up. + yield PlacesTestUtils.clearHistory(); +}); + +add_task(function* test_expire_limited_longurl() +{ + let longurl = "http://long.mozilla.org/" + "a".repeat(232); + yield PlacesTestUtils.addVisits([ + { // Should be expired cause it's the oldest visit + uri: "http://old.mozilla.org/", + visitDate: gNow++ + }, + { // Should be expired cause it's a long url older than 60 days. + uri: longurl, + visitDate: gNow++ + }, + { // Should not be expired cause younger than 60 days. + uri: longurl, + visitDate: getExpirablePRTime(58) + } + ]); + + yield promiseForceExpirationStep(1); + + // Check that some visits survived. + do_check_eq(visits_in_database(longurl), 1); + // Other visits should have been expired. + do_check_false(page_in_database("http://old.mozilla.org/")); + + // Clean up. + yield PlacesTestUtils.clearHistory(); +}); + +add_task(function* test_expire_limited_exoticurl() +{ + yield PlacesTestUtils.addVisits([ + { // Should be expired cause it's the oldest visit + uri: "http://old.mozilla.org/", + visitDate: gNow++ + }, + { // Should be expired cause it's a long url older than 60 days. + uri: "http://download.mozilla.org", + visitDate: gNow++, + transition: 7 + }, + { // Should not be expired cause younger than 60 days. + uri: "http://nonexpirable-download.mozilla.org", + visitDate: getExpirablePRTime(58), + transition: 7 + } + ]); + + yield promiseForceExpirationStep(1); + + // Check that some visits survived. + do_check_eq(visits_in_database("http://nonexpirable-download.mozilla.org/"), 1); + // The visits are gone, the url is not yet, cause we limited the expiration + // to one entry, and we already removed http://old.mozilla.org/. + // The page normally would be expired by the next expiration run. + do_check_eq(visits_in_database("http://download.mozilla.org/"), 0); + // Other visits should have been expired. + do_check_false(page_in_database("http://old.mozilla.org/")); + + // Clean up. + yield PlacesTestUtils.clearHistory(); +}); + +add_task(function* test_expire_unlimited() +{ + let longurl = "http://long.mozilla.org/" + "a".repeat(232); + yield PlacesTestUtils.addVisits([ + { + uri: "http://old.mozilla.org/", + visitDate: gNow++ + }, + { + uri: "http://new.mozilla.org/", + visitDate: gNow++ + }, + // Add expirable visits. + { + uri: "http://download.mozilla.org/", + visitDate: gNow++, + transition: PlacesUtils.history.TRANSITION_DOWNLOAD + }, + { + uri: longurl, + visitDate: gNow++ + }, + + // Add non-expirable visits + { + uri: "http://nonexpirable.mozilla.org/", + visitDate: getExpirablePRTime(5) + }, + { + uri: "http://nonexpirable-download.mozilla.org/", + visitDate: getExpirablePRTime(5), + transition: PlacesUtils.history.TRANSITION_DOWNLOAD + }, + { + uri: longurl, + visitDate: getExpirablePRTime(5) + } + ]); + + yield promiseForceExpirationStep(-1); + + // Check that some visits survived. + do_check_eq(visits_in_database("http://nonexpirable.mozilla.org/"), 1); + do_check_eq(visits_in_database("http://nonexpirable-download.mozilla.org/"), 1); + do_check_eq(visits_in_database(longurl), 1); + // Other visits should have been expired. + do_check_false(page_in_database("http://old.mozilla.org/")); + do_check_false(page_in_database("http://download.mozilla.org/")); + do_check_false(page_in_database("http://new.mozilla.org/")); + + // Clean up. + yield PlacesTestUtils.clearHistory(); +}); + +function run_test() +{ + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + // Set maxPages to a low value, so it's easy to go over it. + setMaxPages(1); + + run_next_test(); +} diff --git a/toolkit/components/places/tests/expiration/test_idle_daily.js b/toolkit/components/places/tests/expiration/test_idle_daily.js new file mode 100644 index 0000000000..05e5a8125a --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_idle_daily.js @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that expiration runs on idle-daily. + +function run_test() { + do_test_pending(); + + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + Services.obs.addObserver(function observeExpiration(aSubject, aTopic, aData) { + Services.obs.removeObserver(observeExpiration, + PlacesUtils.TOPIC_EXPIRATION_FINISHED); + do_test_finished(); + }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false); + + let expire = Cc["@mozilla.org/places/expiration;1"]. + getService(Ci.nsIObserver); + expire.observe(null, "idle-daily", null); +} diff --git a/toolkit/components/places/tests/expiration/test_notifications.js b/toolkit/components/places/tests/expiration/test_notifications.js new file mode 100644 index 0000000000..06e585c6cd --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_notifications.js @@ -0,0 +1,38 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * Ensure that History (through category cache) notifies us just once. + */ + +var os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + +var gObserver = { + notifications: 0, + observe: function(aSubject, aTopic, aData) { + this.notifications++; + } +}; +os.addObserver(gObserver, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false); + +function run_test() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + PlacesTestUtils.clearHistory(); + + do_timeout(2000, check_result); + do_test_pending(); +} + +function check_result() { + os.removeObserver(gObserver, PlacesUtils.TOPIC_EXPIRATION_FINISHED); + do_check_eq(gObserver.notifications, 1); + do_test_finished(); +} diff --git a/toolkit/components/places/tests/expiration/test_notifications_onDeleteURI.js b/toolkit/components/places/tests/expiration/test_notifications_onDeleteURI.js new file mode 100644 index 0000000000..f70cd2b586 --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_notifications_onDeleteURI.js @@ -0,0 +1,114 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * Expiring a full page should fire an onDeleteURI notification. + */ + +var hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + +var tests = [ + + { desc: "Add 1 bookmarked page.", + addPages: 1, + addBookmarks: 1, + expectedNotifications: 0, // No expirable pages. + }, + + { desc: "Add 2 pages, 1 bookmarked.", + addPages: 2, + addBookmarks: 1, + expectedNotifications: 1, // Only one expirable page. + }, + + { desc: "Add 10 pages, none bookmarked.", + addPages: 10, + addBookmarks: 0, + expectedNotifications: 10, // Will expire everything. + }, + + { desc: "Add 10 pages, all bookmarked.", + addPages: 10, + addBookmarks: 10, + expectedNotifications: 0, // No expirable pages. + }, + +]; + +function run_test() { + run_next_test(); +} + +add_task(function* test_notifications_onDeleteURI() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + // Expire anything that is expirable. + setMaxPages(0); + + for (let testIndex = 1; testIndex <= tests.length; testIndex++) { + let currentTest = tests[testIndex -1]; + print("\nTEST " + testIndex + ": " + currentTest.desc); + currentTest.receivedNotifications = 0; + + // Setup visits. + let now = getExpirablePRTime(); + for (let i = 0; i < currentTest.addPages; i++) { + let page = "http://" + testIndex + "." + i + ".mozilla.org/"; + yield PlacesTestUtils.addVisits({ uri: uri(page), visitDate: now++ }); + } + + // Setup bookmarks. + currentTest.bookmarks = []; + for (let i = 0; i < currentTest.addBookmarks; i++) { + let page = "http://" + testIndex + "." + i + ".mozilla.org/"; + yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: null, + url: page + }); + currentTest.bookmarks.push(page); + } + + // Observe history. + historyObserver = { + onBeginUpdateBatch: function PEX_onBeginUpdateBatch() {}, + onEndUpdateBatch: function PEX_onEndUpdateBatch() {}, + onClearHistory: function() {}, + onVisit: function() {}, + onTitleChanged: function() {}, + onDeleteURI: function(aURI, aGUID, aReason) { + currentTest.receivedNotifications++; + // Check this uri was not bookmarked. + do_check_eq(currentTest.bookmarks.indexOf(aURI.spec), -1); + do_check_valid_places_guid(aGUID); + do_check_eq(aReason, Ci.nsINavHistoryObserver.REASON_EXPIRED); + }, + onPageChanged: function() {}, + onDeleteVisits: function(aURI, aTime) { }, + }; + hs.addObserver(historyObserver, false); + + // Expire now. + yield promiseForceExpirationStep(-1); + + hs.removeObserver(historyObserver, false); + + do_check_eq(currentTest.receivedNotifications, + currentTest.expectedNotifications); + + // Clean up. + yield PlacesUtils.bookmarks.eraseEverything(); + yield PlacesTestUtils.clearHistory(); + } + + clearMaxPages(); + yield PlacesUtils.bookmarks.eraseEverything(); + yield PlacesTestUtils.clearHistory(); +}); diff --git a/toolkit/components/places/tests/expiration/test_notifications_onDeleteVisits.js b/toolkit/components/places/tests/expiration/test_notifications_onDeleteVisits.js new file mode 100644 index 0000000000..e6b99ff8ba --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_notifications_onDeleteVisits.js @@ -0,0 +1,142 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * Expiring only visits for a page, but not the full page, should fire an + * onDeleteVisits notification. + */ + +var hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + +var tests = [ + + { desc: "Add 1 bookmarked page.", + addPages: 1, + visitsPerPage: 1, + addBookmarks: 1, + limitExpiration: -1, + expectedNotifications: 1, // Will expire visits for 1 page. + }, + + { desc: "Add 2 pages, 1 bookmarked.", + addPages: 2, + visitsPerPage: 1, + addBookmarks: 1, + limitExpiration: -1, + expectedNotifications: 1, // Will expire visits for 1 page. + }, + + { desc: "Add 10 pages, none bookmarked.", + addPages: 10, + visitsPerPage: 1, + addBookmarks: 0, + limitExpiration: -1, + expectedNotifications: 0, // Will expire only full pages. + }, + + { desc: "Add 10 pages, all bookmarked.", + addPages: 10, + visitsPerPage: 1, + addBookmarks: 10, + limitExpiration: -1, + expectedNotifications: 10, // Will expire visist for all pages. + }, + + { desc: "Add 10 pages with lot of visits, none bookmarked.", + addPages: 10, + visitsPerPage: 10, + addBookmarks: 0, + limitExpiration: 10, + expectedNotifications: 10, // Will expire 1 visist for each page, but won't + }, // expire pages since they still have visits. + +]; + +function run_test() { + run_next_test(); +} + +add_task(function* test_notifications_onDeleteVisits() { + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + // Expire anything that is expirable. + setMaxPages(0); + + for (let testIndex = 1; testIndex <= tests.length; testIndex++) { + let currentTest = tests[testIndex -1]; + print("\nTEST " + testIndex + ": " + currentTest.desc); + currentTest.receivedNotifications = 0; + + // Setup visits. + let timeInMicroseconds = getExpirablePRTime(8); + + function newTimeInMicroseconds() { + timeInMicroseconds = timeInMicroseconds + 1000; + return timeInMicroseconds; + } + + for (let j = 0; j < currentTest.visitsPerPage; j++) { + for (let i = 0; i < currentTest.addPages; i++) { + let page = "http://" + testIndex + "." + i + ".mozilla.org/"; + yield PlacesTestUtils.addVisits({ uri: uri(page), visitDate: newTimeInMicroseconds() }); + } + } + + // Setup bookmarks. + currentTest.bookmarks = []; + for (let i = 0; i < currentTest.addBookmarks; i++) { + let page = "http://" + testIndex + "." + i + ".mozilla.org/"; + yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: null, + url: page + }); + currentTest.bookmarks.push(page); + } + + // Observe history. + historyObserver = { + onBeginUpdateBatch: function PEX_onBeginUpdateBatch() {}, + onEndUpdateBatch: function PEX_onEndUpdateBatch() {}, + onClearHistory: function() {}, + onVisit: function() {}, + onTitleChanged: function() {}, + onDeleteURI: function(aURI, aGUID, aReason) { + // Check this uri was not bookmarked. + do_check_eq(currentTest.bookmarks.indexOf(aURI.spec), -1); + do_check_valid_places_guid(aGUID); + do_check_eq(aReason, Ci.nsINavHistoryObserver.REASON_EXPIRED); + }, + onPageChanged: function() {}, + onDeleteVisits: function(aURI, aTime, aGUID, aReason) { + currentTest.receivedNotifications++; + do_check_guid_for_uri(aURI, aGUID); + do_check_eq(aReason, Ci.nsINavHistoryObserver.REASON_EXPIRED); + }, + }; + hs.addObserver(historyObserver, false); + + // Expire now. + yield promiseForceExpirationStep(currentTest.limitExpiration); + + hs.removeObserver(historyObserver, false); + + do_check_eq(currentTest.receivedNotifications, + currentTest.expectedNotifications); + + // Clean up. + yield PlacesUtils.bookmarks.eraseEverything(); + yield PlacesTestUtils.clearHistory(); + } + + clearMaxPages(); + yield PlacesUtils.bookmarks.eraseEverything(); + yield PlacesTestUtils.clearHistory(); +}); diff --git a/toolkit/components/places/tests/expiration/test_outdated_analyze.js b/toolkit/components/places/tests/expiration/test_outdated_analyze.js new file mode 100644 index 0000000000..9cf61f06b2 --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_outdated_analyze.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that expiration executes ANALYZE when statistics are outdated. + +const TEST_URL = "http://www.mozilla.org/"; + +XPCOMUtils.defineLazyServiceGetter(this, "gHistory", + "@mozilla.org/browser/history;1", + "mozIAsyncHistory"); + +/** + * Object that represents a mozIVisitInfo object. + * + * @param [optional] aTransitionType + * The transition type of the visit. Defaults to TRANSITION_LINK if not + * provided. + * @param [optional] aVisitTime + * The time of the visit. Defaults to now if not provided. + */ +function VisitInfo(aTransitionType, aVisitTime) { + this.transitionType = + aTransitionType === undefined ? TRANSITION_LINK : aTransitionType; + this.visitDate = aVisitTime || Date.now() * 1000; +} + +function run_test() { + do_test_pending(); + + // Init expiration before "importing". + force_expiration_start(); + + // Add a bunch of pages (at laast IMPORT_PAGES_THRESHOLD pages). + let places = []; + for (let i = 0; i < 100; i++) { + places.push({ + uri: NetUtil.newURI(TEST_URL + i), + title: "Title" + i, + visits: [new VisitInfo] + }); + } + gHistory.updatePlaces(places); + + // Set interval to a small value to expire on it. + setInterval(1); // 1s + + Services.obs.addObserver(function observeExpiration(aSubject, aTopic, aData) { + Services.obs.removeObserver(observeExpiration, + PlacesUtils.TOPIC_EXPIRATION_FINISHED); + + // Check that statistica are up-to-date. + let stmt = DBConn().createAsyncStatement( + "SELECT (SELECT COUNT(*) FROM moz_places) - " + + "(SELECT SUBSTR(stat,1,LENGTH(stat)-2) FROM sqlite_stat1 " + + "WHERE idx = 'moz_places_url_hashindex')" + ); + stmt.executeAsync({ + handleResult: function(aResultSet) { + let row = aResultSet.getNextRow(); + this._difference = row.getResultByIndex(0); + }, + handleError: function(aError) { + do_throw("Unexpected error (" + aError.result + "): " + aError.message); + }, + handleCompletion: function(aReason) { + do_check_true(this._difference === 0); + do_test_finished(); + } + }); + stmt.finalize(); + }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false); +} diff --git a/toolkit/components/places/tests/expiration/test_pref_interval.js b/toolkit/components/places/tests/expiration/test_pref_interval.js new file mode 100644 index 0000000000..44c749d7a0 --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_pref_interval.js @@ -0,0 +1,61 @@ +/** + * What this is aimed to test: + * + * Expiration relies on an interval, that is user-preffable setting + * "places.history.expiration.interval_seconds". + * On pref change it will stop current interval timer and fire a new one, + * that will obey the new value. + * If the pref is set to a number <= 0 we will use the default value. + */ + +// Default timer value for expiration in seconds. Must have same value as +// PREF_INTERVAL_SECONDS_NOTSET in nsPlacesExpiration. +const DEFAULT_TIMER_DELAY_SECONDS = 3 * 60; + +// Sync this with the const value in the component. +const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3; + +var tests = [ + + // This test should be the first, so the interval won't be influenced by + // status of history. + { desc: "Set interval to 1s.", + interval: 1, + expectedTimerDelay: 1 + }, + + { desc: "Set interval to a negative value.", + interval: -1, + expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS + }, + + { desc: "Set interval to 0.", + interval: 0, + expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS + }, + + { desc: "Set interval to a large value.", + interval: 100, + expectedTimerDelay: 100 + }, + +]; + +add_task(function* test() { + // The pref should not exist by default. + Assert.throws(() => getInterval()); + + // Force the component, so it will start observing preferences. + force_expiration_start(); + + for (let currentTest of tests) { + print(currentTest.desc); + let promise = promiseTopicObserved("test-interval-changed"); + setInterval(currentTest.interval); + let [, data] = yield promise; + Assert.equal(data, currentTest.expectedTimerDelay * EXPIRE_AGGRESSIVITY_MULTIPLIER); + } + + clearInterval(); +}); + diff --git a/toolkit/components/places/tests/expiration/test_pref_maxpages.js b/toolkit/components/places/tests/expiration/test_pref_maxpages.js new file mode 100644 index 0000000000..6a237afbb9 --- /dev/null +++ b/toolkit/components/places/tests/expiration/test_pref_maxpages.js @@ -0,0 +1,124 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * What this is aimed to test: + * + * Expiration will obey to hardware spec, but user can set a custom maximum + * number of pages to retain, to restrict history, through + * "places.history.expiration.max_pages". + * This limit is used at next expiration run. + * If the pref is set to a number < 0 we will use the default value. + */ + +var hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + +var tests = [ + + { desc: "Set max_pages to a negative value, with 1 page.", + maxPages: -1, + addPages: 1, + expectedNotifications: 0, // Will ignore and won't expire anything. + }, + + { desc: "Set max_pages to 0.", + maxPages: 0, + addPages: 1, + expectedNotifications: 1, + }, + + { desc: "Set max_pages to 0, with 2 pages.", + maxPages: 0, + addPages: 2, + expectedNotifications: 2, // Will expire everything. + }, + + // Notice if we are over limit we do a full step of expiration. So we ensure + // that we will expire if we are over the limit, but we don't ensure that we + // will expire exactly up to the limit. Thus in this case we expire + // everything. + { desc: "Set max_pages to 1 with 2 pages.", + maxPages: 1, + addPages: 2, + expectedNotifications: 2, // Will expire everything (in this case). + }, + + { desc: "Set max_pages to 10, with 9 pages.", + maxPages: 10, + addPages: 9, + expectedNotifications: 0, // We are at the limit, won't expire anything. + }, + + { desc: "Set max_pages to 10 with 10 pages.", + maxPages: 10, + addPages: 10, + expectedNotifications: 0, // We are below the limit, won't expire anything. + }, +]; + +function run_test() { + run_next_test(); +} + +add_task(function* test_pref_maxpages() { + // The pref should not exist by default. + try { + getMaxPages(); + do_throw("interval pref should not exist by default"); + } + catch (ex) {} + + // Set interval to a large value so we don't expire on it. + setInterval(3600); // 1h + + for (let testIndex = 1; testIndex <= tests.length; testIndex++) { + let currentTest = tests[testIndex -1]; + print("\nTEST " + testIndex + ": " + currentTest.desc); + currentTest.receivedNotifications = 0; + + // Setup visits. + let now = getExpirablePRTime(); + for (let i = 0; i < currentTest.addPages; i++) { + let page = "http://" + testIndex + "." + i + ".mozilla.org/"; + yield PlacesTestUtils.addVisits({ uri: uri(page), visitDate: now++ }); + } + + // Observe history. + let historyObserver = { + onBeginUpdateBatch: function PEX_onBeginUpdateBatch() {}, + onEndUpdateBatch: function PEX_onEndUpdateBatch() {}, + onClearHistory: function() {}, + onVisit: function() {}, + onTitleChanged: function() {}, + onDeleteURI: function(aURI) { + print("onDeleteURI " + aURI.spec); + currentTest.receivedNotifications++; + }, + onPageChanged: function() {}, + onDeleteVisits: function(aURI, aTime) { + print("onDeleteVisits " + aURI.spec + " " + aTime); + }, + }; + hs.addObserver(historyObserver, false); + + setMaxPages(currentTest.maxPages); + + // Expire now. + yield promiseForceExpirationStep(-1); + + hs.removeObserver(historyObserver, false); + + do_check_eq(currentTest.receivedNotifications, + currentTest.expectedNotifications); + + // Clean up. + yield PlacesTestUtils.clearHistory(); + } + + clearMaxPages(); + yield PlacesTestUtils.clearHistory(); +}); diff --git a/toolkit/components/places/tests/expiration/xpcshell.ini b/toolkit/components/places/tests/expiration/xpcshell.ini new file mode 100644 index 0000000000..cda7ac052c --- /dev/null +++ b/toolkit/components/places/tests/expiration/xpcshell.ini @@ -0,0 +1,22 @@ +[DEFAULT] +head = head_expiration.js +tail = +skip-if = toolkit == 'android' + +[test_analyze_runs.js] +# Bug 676989: test hangs consistently on Android +skip-if = os == "android" +[test_annos_expire_history.js] +[test_annos_expire_never.js] +[test_annos_expire_policy.js] +[test_annos_expire_session.js] +[test_clearHistory.js] +[test_debug_expiration.js] +[test_idle_daily.js] +[test_notifications.js] +[test_notifications_onDeleteURI.js] +[test_notifications_onDeleteVisits.js] +[test_outdated_analyze.js] +[test_pref_interval.js] +[test_pref_maxpages.js] +skip-if = os == "linux" # bug 1284083 |