summaryrefslogtreecommitdiff
path: root/toolkit/components/places/tests/expiration
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/places/tests/expiration')
-rw-r--r--toolkit/components/places/tests/expiration/.eslintrc.js7
-rw-r--r--toolkit/components/places/tests/expiration/head_expiration.js124
-rw-r--r--toolkit/components/places/tests/expiration/test_analyze_runs.js118
-rw-r--r--toolkit/components/places/tests/expiration/test_annos_expire_history.js93
-rw-r--r--toolkit/components/places/tests/expiration/test_annos_expire_never.js95
-rw-r--r--toolkit/components/places/tests/expiration/test_annos_expire_policy.js189
-rw-r--r--toolkit/components/places/tests/expiration/test_annos_expire_session.js83
-rw-r--r--toolkit/components/places/tests/expiration/test_clearHistory.js157
-rw-r--r--toolkit/components/places/tests/expiration/test_debug_expiration.js225
-rw-r--r--toolkit/components/places/tests/expiration/test_idle_daily.js21
-rw-r--r--toolkit/components/places/tests/expiration/test_notifications.js38
-rw-r--r--toolkit/components/places/tests/expiration/test_notifications_onDeleteURI.js114
-rw-r--r--toolkit/components/places/tests/expiration/test_notifications_onDeleteVisits.js142
-rw-r--r--toolkit/components/places/tests/expiration/test_outdated_analyze.js72
-rw-r--r--toolkit/components/places/tests/expiration/test_pref_interval.js61
-rw-r--r--toolkit/components/places/tests/expiration/test_pref_maxpages.js124
-rw-r--r--toolkit/components/places/tests/expiration/xpcshell.ini22
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