test_SessionStoreBackupResource.js (8739B)
1 /* Any copyright is dedicated to the Public Domain. 2 https://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { SessionStoreBackupResource } = ChromeUtils.importESModule( 7 "resource:///modules/backup/SessionStoreBackupResource.sys.mjs" 8 ); 9 const { SessionStore } = ChromeUtils.importESModule( 10 "resource:///modules/sessionstore/SessionStore.sys.mjs" 11 ); 12 const { NetUtil } = ChromeUtils.importESModule( 13 "resource://gre/modules/NetUtil.sys.mjs" 14 ); 15 16 const TOTAL_COOKIES = 10; 17 18 add_setup(async () => { 19 // Start the cookieservice, to force creation of a database. 20 // Get the sessionCookies to join the initialization in cookie thread 21 Services.cookies.sessionCookies; 22 23 Services.prefs.setBoolPref( 24 "network.cookieJarSettings.unblocked_for_testing", 25 true 26 ); 27 28 let uri = NetUtil.newURI("https://foo.com/"); 29 let channel = NetUtil.newChannel({ 30 uri, 31 loadUsingSystemPrincipal: true, 32 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 33 }); 34 for (let i = 0; i < TOTAL_COOKIES; ++i) { 35 uri = NetUtil.newURI("https://" + i + ".com/"); 36 Services.cookies.setCookieStringFromHttp(uri, "oh=hai", channel); 37 } 38 39 Assert.equal(Services.cookies.sessionCookies.length, TOTAL_COOKIES); 40 41 let state = SessionStore.getCurrentState(true); 42 Assert.equal( 43 state.cookies.length, 44 TOTAL_COOKIES, 45 "The cookies are part of the session" 46 ); 47 }); 48 49 /** 50 * Tests that we can measure the Session Store JSON and backups directory. 51 */ 52 add_task(async function test_measure() { 53 const EXPECTED_KILOBYTES_FOR_BACKUPS_DIR = 1000; 54 Services.fog.testResetFOG(); 55 56 // Create the sessionstore-backups directory. 57 let tempDir = PathUtils.tempDir; 58 let sessionStoreBackupsPath = PathUtils.join( 59 tempDir, 60 "sessionstore-backups", 61 "restore.jsonlz4" 62 ); 63 await createKilobyteSizedFile( 64 sessionStoreBackupsPath, 65 EXPECTED_KILOBYTES_FOR_BACKUPS_DIR 66 ); 67 68 let sessionStoreBackupResource = new SessionStoreBackupResource(); 69 await sessionStoreBackupResource.measure(tempDir); 70 71 let sessionStoreBackupsDirectoryMeasurement = 72 Glean.browserBackup.sessionStoreBackupsDirectorySize.testGetValue(); 73 let sessionStoreMeasurement = 74 Glean.browserBackup.sessionStoreSize.testGetValue(); 75 let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false); 76 77 // Compare glean vs telemetry measurements 78 TelemetryTestUtils.assertScalar( 79 scalars, 80 "browser.backup.session_store_backups_directory_size", 81 sessionStoreBackupsDirectoryMeasurement, 82 "Glean and telemetry measurements for session store backups directory should be equal" 83 ); 84 TelemetryTestUtils.assertScalar( 85 scalars, 86 "browser.backup.session_store_size", 87 sessionStoreMeasurement, 88 "Glean and telemetry measurements for session store should be equal" 89 ); 90 91 // Compare glean measurements vs actual file sizes 92 Assert.equal( 93 sessionStoreBackupsDirectoryMeasurement, 94 EXPECTED_KILOBYTES_FOR_BACKUPS_DIR, 95 "Should have collected the correct glean measurement for the sessionstore-backups directory" 96 ); 97 98 // Session store measurement is from `getCurrentState`, so exact size is unknown. 99 Assert.greater( 100 sessionStoreMeasurement, 101 0, 102 "Should have collected a measurement for the session store" 103 ); 104 105 await IOUtils.remove(sessionStoreBackupsPath); 106 }); 107 108 /** 109 * Test that the backup method correctly copies items from the profile directory 110 * into the staging directory when the backup is encrypted. 111 */ 112 add_task(async function test_backup_encrypted() { 113 await testBackupHelper(true /* isEncrypted */); 114 }); 115 116 /** 117 * Test that the backup method correctly copies items from the profile directory 118 * into the staging directory when the backup is not encrypted. 119 */ 120 add_task(async function test_backup_not_encrypted() { 121 await testBackupHelper(false /* isEncrypted */); 122 }); 123 124 /** 125 * A helper method that does the work of setting up a SessionStoreBackupResource 126 * and all of the testing infrastructure needed to test its backup() method. 127 * 128 * @param {boolean} isEncrypted 129 * True if the test is for an encrypted backup. 130 */ 131 async function testBackupHelper(isEncrypted) { 132 let sandbox = sinon.createSandbox(); 133 134 let sessionStoreBackupResource = new SessionStoreBackupResource(); 135 let sourcePath = await IOUtils.createUniqueDirectory( 136 PathUtils.tempDir, 137 "SessionStoreBackupResource-source-test" 138 ); 139 let stagingPath = await IOUtils.createUniqueDirectory( 140 PathUtils.tempDir, 141 "SessionStoreBackupResource-staging-test" 142 ); 143 144 const simpleCopyFiles = [ 145 { path: ["sessionstore-backups", "test-sessionstore-backup.jsonlz4"] }, 146 { path: ["sessionstore-backups", "test-sessionstore-recovery.baklz4"] }, 147 ]; 148 await createTestFiles(sourcePath, simpleCopyFiles); 149 150 let sessionStoreState = SessionStore.getCurrentState(true); 151 let manifestEntry = await sessionStoreBackupResource.backup( 152 stagingPath, 153 sourcePath, 154 isEncrypted 155 ); 156 Assert.equal( 157 manifestEntry, 158 null, 159 "SessionStoreBackupResource.backup should return null as its ManifestEntry" 160 ); 161 162 /** 163 * We don't expect the actual file sessionstore.jsonlz4 to exist in the profile directory before calling the backup method. 164 * Instead, verify that it is created by the backup method and exists in the staging folder right after. 165 */ 166 await assertFilesExist(stagingPath, [ 167 ...simpleCopyFiles, 168 { path: "sessionstore.jsonlz4" }, 169 ]); 170 171 /** 172 * Do a deep comparison between the recorded session state before backup and the file made in the staging folder 173 * to verify that information about session state was correctly written for backup. 174 */ 175 let sessionStoreStateStaged = await IOUtils.readJSON( 176 PathUtils.join(stagingPath, "sessionstore.jsonlz4"), 177 { decompress: true } 178 ); 179 180 /** 181 * These timestamps might be slightly different from one another, so we'll exclude 182 * them from the comparison. 183 */ 184 delete sessionStoreStateStaged.session.lastUpdate; 185 delete sessionStoreState.session.lastUpdate; 186 187 // Expect that the copy does not include cookies. 188 Assert.equal( 189 sessionStoreStateStaged.cookies.length, 190 0, 191 "expected no cookies in copied session state" 192 ); 193 // Delete the cookies from the original, to allow comparison with deepEqual 194 sessionStoreState.cookies = []; 195 196 Assert.deepEqual( 197 sessionStoreStateStaged, 198 sessionStoreState, 199 "sessionstore.jsonlz4 in the staging folder matches the recorded session state" 200 ); 201 202 await maybeRemovePath(stagingPath); 203 await maybeRemovePath(sourcePath); 204 205 sandbox.restore(); 206 } 207 208 /** 209 * Test that the recover method correctly copies items from the recovery 210 * directory into the destination profile directory. 211 */ 212 add_task(async function test_recover() { 213 let sessionStoreBackupResource = new SessionStoreBackupResource(); 214 let recoveryPath = await IOUtils.createUniqueDirectory( 215 PathUtils.tempDir, 216 "SessionStoreBackupResource-recovery-test" 217 ); 218 let destProfilePath = await IOUtils.createUniqueDirectory( 219 PathUtils.tempDir, 220 "SessionStoreBackupResource-test-profile" 221 ); 222 223 const simpleCopyFiles = [ 224 { path: ["sessionstore-backups", "test-sessionstore-backup.jsonlz4"] }, 225 { path: ["sessionstore-backups", "test-sessionstore-recovery.baklz4"] }, 226 ]; 227 await createTestFiles(recoveryPath, simpleCopyFiles); 228 229 // We backup a copy of sessionstore.jsonlz4, so ensure it exists in the recovery path 230 let sessionStoreState = SessionStore.getCurrentState(true); 231 let sessionStoreBackupPath = PathUtils.join( 232 recoveryPath, 233 "sessionstore.jsonlz4" 234 ); 235 await IOUtils.writeJSON(sessionStoreBackupPath, sessionStoreState, { 236 compress: true, 237 }); 238 239 // The backup method is expected to have returned a null ManifestEntry 240 let postRecoveryEntry = await sessionStoreBackupResource.recover( 241 null /* manifestEntry */, 242 recoveryPath, 243 destProfilePath 244 ); 245 Assert.equal( 246 postRecoveryEntry, 247 null, 248 "SessionStoreBackupResource.recover should return null as its post recovery entry" 249 ); 250 251 await assertFilesExist(destProfilePath, [ 252 ...simpleCopyFiles, 253 { path: "sessionstore.jsonlz4" }, 254 ]); 255 256 let sessionStateCopied = await IOUtils.readJSON( 257 PathUtils.join(destProfilePath, "sessionstore.jsonlz4"), 258 { decompress: true } 259 ); 260 261 /** 262 * These timestamps might be slightly different from one another, so we'll exclude 263 * them from the comparison. 264 */ 265 delete sessionStateCopied.session.lastUpdate; 266 delete sessionStoreState.session.lastUpdate; 267 Assert.deepEqual( 268 sessionStateCopied, 269 sessionStoreState, 270 "sessionstore.jsonlz4 in the destination profile folder matches the backed up session state" 271 ); 272 273 await maybeRemovePath(recoveryPath); 274 await maybeRemovePath(destProfilePath); 275 });