michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ michael@0: */ michael@0: michael@0: // This file tests the Vacuum Manager. michael@0: michael@0: Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Components.utils.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: /** michael@0: * Loads a test component that will register as a vacuum-participant. michael@0: * If other participants are found they will be unregistered, to avoid conflicts michael@0: * with the test itself. michael@0: */ michael@0: function load_test_vacuum_component() michael@0: { michael@0: const CATEGORY_NAME = "vacuum-participant"; michael@0: michael@0: do_load_manifest("vacuumParticipant.manifest"); michael@0: michael@0: // This is a lazy check, there could be more participants than just this test michael@0: // we just mind that the test exists though. michael@0: const EXPECTED_ENTRIES = ["vacuumParticipant"]; michael@0: let catMan = Cc["@mozilla.org/categorymanager;1"]. michael@0: getService(Ci.nsICategoryManager); michael@0: let found = false; michael@0: let entries = catMan.enumerateCategory(CATEGORY_NAME); michael@0: while (entries.hasMoreElements()) { michael@0: let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data; michael@0: print("Check if the found category entry (" + entry + ") is expected."); michael@0: if (EXPECTED_ENTRIES.indexOf(entry) != -1) { michael@0: print("Check that only one test entry exists."); michael@0: do_check_false(found); michael@0: found = true; michael@0: } michael@0: else { michael@0: // Temporary unregister other participants for this test. michael@0: catMan.deleteCategoryEntry("vacuum-participant", entry, false); michael@0: } michael@0: } michael@0: print("Check the test entry exists."); michael@0: do_check_true(found); michael@0: } michael@0: michael@0: /** michael@0: * Sends a fake idle-daily notification to the VACUUM Manager. michael@0: */ michael@0: function synthesize_idle_daily() michael@0: { michael@0: let vm = Cc["@mozilla.org/storage/vacuum;1"].getService(Ci.nsIObserver); michael@0: vm.observe(null, "idle-daily", null); michael@0: } michael@0: michael@0: /** michael@0: * Returns a new nsIFile reference for a profile database. michael@0: * @param filename for the database, excluded the .sqlite extension. michael@0: */ michael@0: function new_db_file(name) michael@0: { michael@0: let file = Services.dirsvc.get("ProfD", Ci.nsIFile); michael@0: file.append(name + ".sqlite"); michael@0: return file; michael@0: } michael@0: michael@0: function run_test() michael@0: { michael@0: do_test_pending(); michael@0: michael@0: // Change initial page size. Do it immediately since it would require an michael@0: // additional vacuum op to do it later. As a bonus this makes the page size michael@0: // change test really fast since it only has to check results. michael@0: let conn = getDatabase(new_db_file("testVacuum")); michael@0: conn.executeSimpleSQL("PRAGMA page_size = 1024"); michael@0: print("Check current page size."); michael@0: let stmt = conn.createStatement("PRAGMA page_size"); michael@0: try { michael@0: while (stmt.executeStep()) { michael@0: do_check_eq(stmt.row.page_size, 1024); michael@0: } michael@0: } michael@0: finally { michael@0: stmt.finalize(); michael@0: } michael@0: michael@0: load_test_vacuum_component(); michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: function run_next_test() michael@0: { michael@0: if (TESTS.length == 0) { michael@0: Services.obs.notifyObservers(null, "test-options", "dispose"); michael@0: do_test_finished(); michael@0: } michael@0: else { michael@0: // Set last VACUUM to a date in the past. michael@0: Services.prefs.setIntPref("storage.vacuum.last.testVacuum.sqlite", michael@0: parseInt(Date.now() / 1000 - 31 * 86400)); michael@0: do_execute_soon(TESTS.shift()); michael@0: } michael@0: } michael@0: michael@0: const TESTS = [ michael@0: michael@0: function test_common_vacuum() michael@0: { michael@0: print("\n*** Test that a VACUUM correctly happens and all notifications are fired."); michael@0: // Wait for VACUUM begin. michael@0: let beginVacuumReceived = false; michael@0: Services.obs.addObserver(function onVacuum(aSubject, aTopic, aData) { michael@0: Services.obs.removeObserver(onVacuum, aTopic); michael@0: beginVacuumReceived = true; michael@0: }, "test-begin-vacuum", false); michael@0: michael@0: // Wait for heavy IO notifications. michael@0: let heavyIOTaskBeginReceived = false; michael@0: let heavyIOTaskEndReceived = false; michael@0: Services.obs.addObserver(function onVacuum(aSubject, aTopic, aData) { michael@0: if (heavyIOTaskBeginReceived && heavyIOTaskEndReceived) { michael@0: Services.obs.removeObserver(onVacuum, aTopic); michael@0: } michael@0: michael@0: if (aData == "vacuum-begin") { michael@0: heavyIOTaskBeginReceived = true; michael@0: } michael@0: else if (aData == "vacuum-end") { michael@0: heavyIOTaskEndReceived = true; michael@0: } michael@0: }, "heavy-io-task", false); michael@0: michael@0: // Wait for VACUUM end. michael@0: Services.obs.addObserver(function onVacuum(aSubject, aTopic, aData) { michael@0: Services.obs.removeObserver(onVacuum, aTopic); michael@0: print("Check we received onBeginVacuum"); michael@0: do_check_true(beginVacuumReceived); michael@0: print("Check we received heavy-io-task notifications"); michael@0: do_check_true(heavyIOTaskBeginReceived); michael@0: do_check_true(heavyIOTaskEndReceived); michael@0: print("Received onEndVacuum"); michael@0: run_next_test(); michael@0: }, "test-end-vacuum", false); michael@0: michael@0: synthesize_idle_daily(); michael@0: }, michael@0: michael@0: function test_skipped_if_recent_vacuum() michael@0: { michael@0: print("\n*** Test that a VACUUM is skipped if it was run recently."); michael@0: Services.prefs.setIntPref("storage.vacuum.last.testVacuum.sqlite", michael@0: parseInt(Date.now() / 1000)); michael@0: michael@0: // Wait for VACUUM begin. michael@0: let vacuumObserver = { michael@0: gotNotification: false, michael@0: observe: function VO_observe(aSubject, aTopic, aData) { michael@0: this.gotNotification = true; michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) michael@0: } michael@0: Services.obs.addObserver(vacuumObserver, "test-begin-vacuum", false); michael@0: michael@0: // Check after a couple seconds that no VACUUM has been run. michael@0: do_timeout(2000, function () { michael@0: print("Check VACUUM did not run."); michael@0: do_check_false(vacuumObserver.gotNotification); michael@0: Services.obs.removeObserver(vacuumObserver, "test-begin-vacuum"); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: synthesize_idle_daily(); michael@0: }, michael@0: michael@0: function test_page_size_change() michael@0: { michael@0: print("\n*** Test that a VACUUM changes page_size"); michael@0: michael@0: // We did setup the database with a small page size, the previous vacuum michael@0: // should have updated it. michael@0: print("Check that page size was updated."); michael@0: let conn = getDatabase(new_db_file("testVacuum")); michael@0: let stmt = conn.createStatement("PRAGMA page_size"); michael@0: try { michael@0: while (stmt.executeStep()) { michael@0: do_check_eq(stmt.row.page_size, conn.defaultPageSize); michael@0: } michael@0: } michael@0: finally { michael@0: stmt.finalize(); michael@0: } michael@0: michael@0: run_next_test(); michael@0: }, michael@0: michael@0: function test_skipped_optout_vacuum() michael@0: { michael@0: print("\n*** Test that a VACUUM is skipped if the participant wants to opt-out."); michael@0: Services.obs.notifyObservers(null, "test-options", "opt-out"); michael@0: michael@0: // Wait for VACUUM begin. michael@0: let vacuumObserver = { michael@0: gotNotification: false, michael@0: observe: function VO_observe(aSubject, aTopic, aData) { michael@0: this.gotNotification = true; michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) michael@0: } michael@0: Services.obs.addObserver(vacuumObserver, "test-begin-vacuum", false); michael@0: michael@0: // Check after a couple seconds that no VACUUM has been run. michael@0: do_timeout(2000, function () { michael@0: print("Check VACUUM did not run."); michael@0: do_check_false(vacuumObserver.gotNotification); michael@0: Services.obs.removeObserver(vacuumObserver, "test-begin-vacuum"); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: synthesize_idle_daily(); michael@0: }, michael@0: michael@0: /* Changing page size on WAL is not supported till Bug 634374 is properly fixed. michael@0: function test_page_size_change_with_wal() michael@0: { michael@0: print("\n*** Test that a VACUUM changes page_size with WAL mode"); michael@0: Services.obs.notifyObservers(null, "test-options", "wal"); michael@0: michael@0: // Set a small page size. michael@0: let conn = getDatabase(new_db_file("testVacuum2")); michael@0: conn.executeSimpleSQL("PRAGMA page_size = 1024"); michael@0: let stmt = conn.createStatement("PRAGMA page_size"); michael@0: try { michael@0: while (stmt.executeStep()) { michael@0: do_check_eq(stmt.row.page_size, 1024); michael@0: } michael@0: } michael@0: finally { michael@0: stmt.finalize(); michael@0: } michael@0: michael@0: // Use WAL journal mode. michael@0: conn.executeSimpleSQL("PRAGMA journal_mode = WAL"); michael@0: stmt = conn.createStatement("PRAGMA journal_mode"); michael@0: try { michael@0: while (stmt.executeStep()) { michael@0: do_check_eq(stmt.row.journal_mode, "wal"); michael@0: } michael@0: } michael@0: finally { michael@0: stmt.finalize(); michael@0: } michael@0: michael@0: // Wait for VACUUM end. michael@0: let vacuumObserver = { michael@0: observe: function VO_observe(aSubject, aTopic, aData) { michael@0: Services.obs.removeObserver(this, aTopic); michael@0: print("Check page size has been updated."); michael@0: let stmt = conn.createStatement("PRAGMA page_size"); michael@0: try { michael@0: while (stmt.executeStep()) { michael@0: do_check_eq(stmt.row.page_size, Ci.mozIStorageConnection.DEFAULT_PAGE_SIZE); michael@0: } michael@0: } michael@0: finally { michael@0: stmt.finalize(); michael@0: } michael@0: michael@0: print("Check journal mode has been restored."); michael@0: stmt = conn.createStatement("PRAGMA journal_mode"); michael@0: try { michael@0: while (stmt.executeStep()) { michael@0: do_check_eq(stmt.row.journal_mode, "wal"); michael@0: } michael@0: } michael@0: finally { michael@0: stmt.finalize(); michael@0: } michael@0: michael@0: run_next_test(); michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) michael@0: } michael@0: Services.obs.addObserver(vacuumObserver, "test-end-vacuum", false); michael@0: michael@0: synthesize_idle_daily(); michael@0: }, michael@0: */ michael@0: michael@0: function test_memory_database_crash() michael@0: { michael@0: print("\n*** Test that we don't crash trying to vacuum a memory database"); michael@0: Services.obs.notifyObservers(null, "test-options", "memory"); michael@0: michael@0: // Wait for VACUUM begin. michael@0: let vacuumObserver = { michael@0: gotNotification: false, michael@0: observe: function VO_observe(aSubject, aTopic, aData) { michael@0: this.gotNotification = true; michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) michael@0: } michael@0: Services.obs.addObserver(vacuumObserver, "test-begin-vacuum", false); michael@0: michael@0: // Check after a couple seconds that no VACUUM has been run. michael@0: do_timeout(2000, function () { michael@0: print("Check VACUUM did not run."); michael@0: do_check_false(vacuumObserver.gotNotification); michael@0: Services.obs.removeObserver(vacuumObserver, "test-begin-vacuum"); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: synthesize_idle_daily(); michael@0: }, michael@0: michael@0: /* Changing page size on WAL is not supported till Bug 634374 is properly fixed. michael@0: function test_wal_restore_fail() michael@0: { michael@0: print("\n*** Test that a failing WAL restoration notifies failure"); michael@0: Services.obs.notifyObservers(null, "test-options", "wal-fail"); michael@0: michael@0: // Wait for VACUUM end. michael@0: let vacuumObserver = { michael@0: observe: function VO_observe(aSubject, aTopic, aData) { michael@0: Services.obs.removeObserver(vacuumObserver, "test-end-vacuum"); michael@0: print("Check WAL restoration failed."); michael@0: do_check_false(aData); michael@0: run_next_test(); michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) michael@0: } michael@0: Services.obs.addObserver(vacuumObserver, "test-end-vacuum", false); michael@0: michael@0: synthesize_idle_daily(); michael@0: }, michael@0: */ michael@0: ];