blob: 39150281174d3558cc85cf8352604a0222291cb4 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tabmodel;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ContextUtils;
import org.chromium.base.FileUtils;
import org.chromium.base.test.util.AdvancedMockContext;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
import org.chromium.chrome.browser.tab.TabStateFileManager;
import org.chromium.chrome.browser.tabpersistence.TabStateDirectory;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.tabmodel.MockTabModelSelector;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.io.File;
import java.io.IOException;
/**
* Test that migrating the old multi-instance tab state folder structure to the new one works.
* Previously each instance had its own subdirectory for storing files. Now there is one
* shared directory.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@Batch(Batch.PER_CLASS)
public class MultiInstanceMigrationTest {
private Context mAppContext;
@Before
public void setUp() {
mAppContext = new AdvancedMockContext(InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext());
ContextUtils.initApplicationContextForTests(mAppContext);
// Set the shared pref stating that the legacy file migration has occurred. The
// multi-instance migration won't happen if the legacy path is taken.
SharedPreferencesManager.getInstance().writeBoolean(
ChromePreferenceKeys.TABMODEL_HAS_RUN_FILE_MIGRATION, true);
}
@After
public void tearDown() {
// Cleanup filesystem changes, shared preference changes and reset sMigrationTask
// between tests - otherwise changes to these in one test will interfere with the other
// tests.
// TODO(crbug.com/1086663) Don't persist changes to SharedPreferences.
SharedPreferencesManager.getInstance().writeBoolean(
ChromePreferenceKeys.TABMODEL_HAS_RUN_MULTI_INSTANCE_FILE_MIGRATION, false);
FileUtils.recursivelyDeleteFile(
TabStateDirectory.getOrCreateBaseStateDirectory(), FileUtils.DELETE_ALL);
TabbedModeTabPersistencePolicy.resetMigrationTaskForTesting();
}
private void buildPersistentStoreAndWaitForMigration() {
TestThreadUtils.runOnUiThreadBlocking(() -> {
MockTabModelSelector selector = new MockTabModelSelector(0, 0, null);
TabbedModeTabPersistencePolicy persistencePolicy =
new TabbedModeTabPersistencePolicy(0, false);
TabPersistentStore store = new TabPersistentStore(persistencePolicy, selector, null);
store.waitForMigrationToFinish();
});
}
/**
* Tests that normal migration of multi-instance state files works.
*/
@Test
@MediumTest
@Feature({"TabPersistentStore"})
public void testMigrateData() throws IOException {
// Write old state files.
File[] stateDirs = createOldStateDirs(TabWindowManager.MAX_SIMULTANEOUS_SELECTORS, true);
File stateFile0 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
File stateFile1 = new File(
stateDirs[1], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
File stateFile2 = new File(
stateDirs[2], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
File customTabsStateFile = new File(
stateDirs[3], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
Assert.assertTrue("Could not create state file 0", stateFile0.createNewFile());
Assert.assertTrue("Could not create state file 1", stateFile1.createNewFile());
Assert.assertTrue("Could not create state file 2", stateFile2.createNewFile());
Assert.assertTrue(
"Could not create custom tabs state file", customTabsStateFile.createNewFile());
// Create a couple of tabs for each tab state subdirectory.
File tab0 = new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "0");
File tab1 = new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "1");
File tab2 = new File(stateDirs[1], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "2");
File tab3 = new File(
stateDirs[1], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "3");
File tab4 = new File(stateDirs[2], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "4");
File tab5 = new File(
stateDirs[2], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "5");
File tab6 = new File(stateDirs[3], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "6");
Assert.assertTrue("Could not create tab 0 file", tab0.createNewFile());
Assert.assertTrue("Could not create tab 1 file", tab1.createNewFile());
Assert.assertTrue("Could not create tab 2 file", tab2.createNewFile());
Assert.assertTrue("Could not create tab 3 file", tab3.createNewFile());
Assert.assertTrue("Could not create tab 4 file", tab4.createNewFile());
Assert.assertTrue("Could not create tab 5 file", tab5.createNewFile());
Assert.assertTrue("Could not create tab 6 file", tab6.createNewFile());
// Build the TabPersistentStore which will try to move the files.
buildPersistentStoreAndWaitForMigration();
// Make sure we don't hit the migration path again.
Assert.assertTrue(SharedPreferencesManager.getInstance().readBoolean(
ChromePreferenceKeys.TABMODEL_HAS_RUN_MULTI_INSTANCE_FILE_MIGRATION, false));
// Check that all metadata files moved.
File newStateFile0 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(0));
File newStateFile1 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(1));
File newStateFile2 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(2));
File newCustomTabsStateFile = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(
TabModelSelectorImpl.CUSTOM_TABS_SELECTOR_INDEX));
Assert.assertTrue("Could not find new state file 0", newStateFile0.exists());
Assert.assertTrue("Could not find new state file 1", newStateFile1.exists());
Assert.assertTrue("Could not find new state file 2", newStateFile2.exists());
Assert.assertTrue(
"Could not find new custom tabs state file", newCustomTabsStateFile.exists());
Assert.assertFalse("Could still find old state file 0", stateFile0.exists());
Assert.assertFalse("Could still find old state file 1", stateFile1.exists());
Assert.assertFalse("Could still find old state file 2", stateFile2.exists());
Assert.assertFalse(
"Could still find old custom tabs state file", customTabsStateFile.exists());
// Check that tab 0 and 1 did not move.
Assert.assertTrue("Could not find tab 0 file", tab0.exists());
Assert.assertTrue("Could not find tab 1 file", tab1.exists());
// Check that tabs 2-5 did move.
File newTab2 =
new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "2");
File newTab3 = new File(
stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "3");
File newTab4 =
new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "4");
File newTab5 = new File(
stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "5");
Assert.assertTrue("Could not find new tab 2 file", newTab2.exists());
Assert.assertTrue("Could not find new tab 3 file", newTab3.exists());
Assert.assertTrue("Could not find new tab 4 file", newTab4.exists());
Assert.assertTrue("Could not find new tab 5 file", newTab5.exists());
Assert.assertFalse("Could still find old tab 2 file", tab2.exists());
Assert.assertFalse("Could still find old tab 3 file", tab3.exists());
Assert.assertFalse("Could still find old tab 4 file", tab4.exists());
Assert.assertFalse("Could still find old tab 5 file", tab5.exists());
// Check that the custom tab (tab 6) was deleted.
File newTab6 =
new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "6");
Assert.assertFalse("Could still find old tab 6 file", tab6.exists());
Assert.assertFalse("Found new tab 6 file. It should have been deleted.", newTab6.exists());
// Check that old directories were deleted.
Assert.assertFalse("Could still find old state dir 1", stateDirs[1].exists());
Assert.assertFalse("Could still find old state dir 2", stateDirs[2].exists());
Assert.assertFalse("Could still find old custom tabs state dir", stateDirs[3].exists());
}
/**
* Tests that the state file migration skips unrelated files. Also tests that migration works
* if the number of tab state subdirectories to migrate is less than
* {@code TabWindowManager.MAX_SIMULTANEOUS_SELECTORS}
*/
@Test
@MediumTest
@Feature({"TabPersistentStore"})
public void testMigrationLeavesOtherFilesAlone() throws IOException {
// Write old state files and an extra file.
File[] stateDirs = createOldStateDirs(2, false);
File stateFile0 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
File stateFile1 = new File(
stateDirs[1], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
File tab0 = new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "0");
File tab1 = new File(stateDirs[1], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "1");
File otherFile = new File(stateDirs[1], "other.file");
Assert.assertTrue("Could not create state file 0", stateFile0.createNewFile());
Assert.assertTrue("Could not create state file 1", stateFile1.createNewFile());
Assert.assertTrue("Could not create tab 0 file", tab0.createNewFile());
Assert.assertTrue("Could not create tab 1 file", tab1.createNewFile());
Assert.assertTrue("Could not create other file", otherFile.createNewFile());
// Build the TabPersistentStore which will try to move the files.
buildPersistentStoreAndWaitForMigration();
// Check that the other file wasn't moved.
File newOtherFile = new File(stateDirs[0], "other.file");
Assert.assertFalse("Could find new other file", newOtherFile.exists());
Assert.assertTrue("Could not find original other file", otherFile.exists());
// Check that the metadata files were renamed and/or moved.
File newStateFile0 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(0));
File newStateFile1 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(1));
Assert.assertTrue("Could not find new state file 0", newStateFile0.exists());
Assert.assertTrue("Could not find new state file 1", newStateFile1.exists());
Assert.assertFalse("Could still find old state file 0", stateFile0.exists());
Assert.assertFalse("Could still find old state file 1", stateFile1.exists());
// Check that tab 0 did not move.
Assert.assertTrue("Could not find tab 0 file", tab0.exists());
// Check that tab 1 did move.
File newTab1 =
new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "1");
Assert.assertTrue("Could not find tab 1 file", newTab1.exists());
Assert.assertFalse("Could still find old tab 1 file", tab1.exists());
}
/**
* Tests that migration of multi-instance state files works when tab files with the same name
* exists in both directories.
*/
@Test
@MediumTest
@Feature({"TabPersistentStore"})
public void testMigrateDataDuplicateTabFiles() throws IOException {
// Write old state files.
File[] stateDirs = createOldStateDirs(2, false);
File stateFile0 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
File stateFile1 = new File(
stateDirs[1], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
Assert.assertTrue("Could not create state file 0", stateFile0.createNewFile());
Assert.assertTrue("Could not create state file 1", stateFile1.createNewFile());
// Create duplicate "tab0" files and ensure tab0Dir1 has been modified more recently so that
// it overwrites tab0Dir0.
File tab0Dir0 =
new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "0");
File tab0Dir1 =
new File(stateDirs[1], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "0");
Assert.assertTrue("Could not create tab 0-0 file", tab0Dir0.createNewFile());
Assert.assertTrue("Could not create tab 0-1 file", tab0Dir1.createNewFile());
long expectedTab0LastModifiedTime = tab0Dir0.lastModified() + 1000;
if (!tab0Dir1.setLastModified(expectedTab0LastModifiedTime)) {
Assert.fail("Failed to set last modified time.");
}
// Create duplicate "tab1" files and ensure tab1Dir0 has been modified more recently so that
// it does not get overwritten.
File tab1Dir0 =
new File(stateDirs[0], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "1");
File tab1Dir1 =
new File(stateDirs[1], TabStateFileManager.SAVED_TAB_STATE_FILE_PREFIX + "1");
Assert.assertTrue("Could not create tab 1-0 file", tab1Dir0.createNewFile());
Assert.assertTrue("Could not create tab 1-1 file", tab1Dir1.createNewFile());
long expectedTab1LastModifiedTime = tab1Dir1.lastModified() + 1000;
if (!tab1Dir0.setLastModified(expectedTab1LastModifiedTime)) {
Assert.fail("Failed to set last modified time.");
}
// Build the TabPersistentStore which will try to move the files.
buildPersistentStoreAndWaitForMigration();
// Check that "tab0" still exists and has the expected last modified time.
Assert.assertTrue("Could not find tab 0 file", tab0Dir0.exists());
Assert.assertFalse("Could still find old tab 0 file", tab0Dir1.exists());
Assert.assertEquals("tab 0 file not overwritten properly", expectedTab0LastModifiedTime,
tab0Dir0.lastModified());
// Check that "tab1" still exists and has the expected last modified time.
Assert.assertTrue("Could not find tab 1 file", tab1Dir0.exists());
Assert.assertFalse("Could still find old tab 1 file", tab1Dir1.exists());
Assert.assertEquals("tab 1 file unexpectedly overwritten", expectedTab1LastModifiedTime,
tab1Dir0.lastModified());
// Check that old directory was deleted.
Assert.assertFalse("Could still find old state dir 1", stateDirs[1].exists());
}
/**
* Tests that tab_state0 is not overwritten if it already exists when migration is attempted.
*/
@Test
@MediumTest
@Feature({"TabPersistentStore"})
public void testNewMetataFileExists() throws IOException {
// Set up two old metadata files.
File[] stateDirs = createOldStateDirs(TabWindowManager.MAX_SIMULTANEOUS_SELECTORS, true);
File stateFile0 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
File stateFile1 = new File(
stateDirs[1], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
Assert.assertTrue("Could not create state file 0", stateFile0.createNewFile());
Assert.assertTrue("Could not create state file 1", stateFile1.createNewFile());
// Create a new metadata file.
File newStateFile0 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(0));
Assert.assertTrue("Could not create new state file 0", newStateFile0.createNewFile());
long expectedLastModifiedTime = newStateFile0.lastModified() - 1000000;
if (!newStateFile0.setLastModified(expectedLastModifiedTime)) {
Assert.fail("Failed to set last modified time.");
}
// Build the TabPersistentStore which will try to move the files.
buildPersistentStoreAndWaitForMigration();
// Check that new metadata file was not overwritten during migration.
Assert.assertEquals("State file 0 unexpectedly overwritten", expectedLastModifiedTime,
newStateFile0.lastModified());
// Check that migration of other files still occurred.
File newStateFile1 = new File(
stateDirs[0], TabbedModeTabPersistencePolicy.getStateFileName(1));
Assert.assertTrue("Could not find new state file 1", newStateFile1.exists());
}
/**
* Creates tab state directories using the old pre-multi-instance migration file paths where
* each tab model had its own state directory.
*
* @param numRegularDirsToCreate The number of regular tab state directories to create.
* @param createCustomTabsDir Whether a custom tabs directory should be created.
* @return An array containing the tab state directories. If createCustomTabsDir is true,
* a directory for custom tabs will be added to the end of the array.
*/
private File[] createOldStateDirs(int numRegularDirsToCreate, boolean createCustomTabsDir) {
int numDirsToCreate =
createCustomTabsDir ? numRegularDirsToCreate + 1 : numRegularDirsToCreate;
File[] stateDirs = new File[numDirsToCreate];
for (int i = 0; i < numRegularDirsToCreate; i++) {
stateDirs[i] = new File(
TabStateDirectory.getOrCreateBaseStateDirectory(), Integer.toString(i));
if (!stateDirs[i].exists()) {
Assert.assertTrue("Could not create state dir " + i, stateDirs[i].mkdirs());
}
}
if (createCustomTabsDir) {
stateDirs[numDirsToCreate - 1] =
new File(TabStateDirectory.getOrCreateBaseStateDirectory(),
Integer.toString(TabModelSelectorImpl.CUSTOM_TABS_SELECTOR_INDEX));
if (!stateDirs[numDirsToCreate - 1].exists()) {
Assert.assertTrue("Could not create custom tab state dir",
stateDirs[numDirsToCreate - 1].mkdirs());
}
}
return stateDirs;
}
}