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