Bug 1306140 - Add tests for HomeConfig migration r?sebastian draft
authorAndrzej Hunt <ahunt@mozilla.com>
Thu, 29 Sep 2016 11:36:57 -0700
changeset 419606 5bd9815d4ab89e4f42add6e2cdf71d3db6e7fd11
parent 419605 50df585b35ec48401fec697a87cad2e456d9775b
child 532612 d26342083037cb528d546ba5163af674b18dc1a1
push id30970
push userahunt@mozilla.com
push dateFri, 30 Sep 2016 17:15:18 +0000
reviewerssebastian
bugs1306140
milestone52.0a1
Bug 1306140 - Add tests for HomeConfig migration r?sebastian I am open to replacing "constellations" with a different name, but it was the best I could come up with so far. MozReview-Commit-ID: 9pljV8nC6O8
mobile/android/tests/background/junit4/src/org/mozilla/gecko/home/TestHomeConfigPrefsBackendMigration.java
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/home/TestHomeConfigPrefsBackendMigration.java
@@ -0,0 +1,199 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+package org.mozilla.gecko.home;
+
+import android.content.Context;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mozilla.gecko.background.testhelpers.TestRunner;
+import org.mozilla.gecko.home.HomeConfig.PanelConfig;
+import org.mozilla.gecko.home.HomeConfig.PanelType;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+@RunWith(TestRunner.class)
+public class TestHomeConfigPrefsBackendMigration {
+
+    // Each Pair consists of a list of panels that exist going into a given migration, and a list containing
+    // the expected default output panel corresponding to each given input panel in the list of existing panels.
+    // E.g. if a given N->N+1 migration starts with panels Foo and Bar, and removes Bar, the two lists would
+    // be {Foo, Bar} and {Foo, Foo}.
+    // Note: the index where each pair is inserted corresponds to the HomeConfig version before the migration.
+    // The final item in this list denotes the current HomeCOnfig version, and therefore only needs to contain
+    // the list of panel types that are expected by default (but no list for after the non-existent migration).
+    final SparseArray<Pair<PanelType[], PanelType[]>> migrationConstellations = new SparseArray<>();
+    {
+        // 6->7: the recent tabs panel was merged into the combined history panel
+        migrationConstellations.put(6, new Pair<>(
+                /* Panels that are expected to exist before this migration happens */
+                new PanelType[] {
+                        PanelType.TOP_SITES,
+                        PanelType.BOOKMARKS,
+                        PanelType.COMBINED_HISTORY,
+                        PanelType.DEPRECATED_RECENT_TABS
+                },
+                /* The expected default panel that is expected after the migration */
+                new PanelType[] {
+                        PanelType.TOP_SITES, /* TOP_SITES remains the default if it was previously the default */
+                        PanelType.BOOKMARKS, /* same as TOP_SITES */
+                        PanelType.COMBINED_HISTORY, /* same as TOP_SITES */
+                        PanelType.COMBINED_HISTORY /* DEPRECATED_RECENT_TABS is replaced by COMBINED_HISTORY during this migration and is therefore the new default */
+                }
+        ));
+
+        // 7->8: no changes, this was a fixup migration since 6->7 was previously botched
+        migrationConstellations.put(7, new Pair<>(
+                new PanelType[] {
+                        PanelType.TOP_SITES,
+                        PanelType.BOOKMARKS,
+                        PanelType.COMBINED_HISTORY,
+                },
+                new PanelType[] {
+                        PanelType.TOP_SITES,
+                        PanelType.BOOKMARKS,
+                        PanelType.COMBINED_HISTORY,
+                }
+        ));
+
+        migrationConstellations.put(8, new Pair<>(
+                new PanelType[] {
+                        PanelType.TOP_SITES,
+                        PanelType.BOOKMARKS,
+                        PanelType.COMBINED_HISTORY,
+                },
+                new PanelType[] {
+                        // Last version: no migration exists yet, we only need to define a list
+                        // of expected panels.
+                }
+        ));
+    }
+
+    private JSONArray createConfigsForList(Context context, PanelType[] panels, int defaultIndex) throws JSONException {
+        if (defaultIndex < 0 || defaultIndex >= panels.length) {
+            throw new IllegalArgumentException("defaultIndex must point to panel in the array");
+        }
+
+        final JSONArray jsonPanels = new JSONArray();
+
+        for (int i = 0; i < panels.length; i++) {
+            final PanelType panel = panels[i];
+            final PanelConfig config;
+
+            if (i == defaultIndex) {
+                config = HomeConfig.createBuiltinPanelConfig(context, panel,
+                        EnumSet.of(PanelConfig.Flags.DEFAULT_PANEL));
+            } else {
+                config = HomeConfig.createBuiltinPanelConfig(context, panel);
+            }
+
+            jsonPanels.put(config.toJSON());
+        }
+
+        return jsonPanels;
+    }
+
+    private PanelType getDefaultPanel(final JSONArray jsonPanels) throws JSONException {
+        assertTrue("panel list must not be empty", jsonPanels.length() > 0);
+
+        for (int i = 0; i < jsonPanels.length(); i++) {
+            final JSONObject jsonPanelConfig = jsonPanels.getJSONObject(i);
+            final PanelConfig panelConfig = new PanelConfig(jsonPanelConfig);
+
+            if (panelConfig.isDefault()) {
+                return panelConfig.getType();
+            }
+        }
+
+        return null;
+    }
+
+    private void checkListContainsExpectedPanels(JSONArray jsonPanels, PanelType[] expected) throws JSONException {
+        // Given the short lists we have here an ArraySet might be more appropriate, but it requires API >= 23.
+        final Set<PanelType> expectedSet = new HashSet<>();
+        for (PanelType panelType : expected) {
+            expectedSet.add(panelType);
+        }
+
+        for (int i = 0; i < jsonPanels.length(); i++) {
+            final JSONObject jsonPanelConfig = jsonPanels.getJSONObject(i);
+            final PanelType panelType = new PanelConfig(jsonPanelConfig).getType();
+
+            assertTrue("Unexpected panel of type " + panelType.name() + " found in list",
+                    expectedSet.contains(panelType));
+
+            expectedSet.remove(panelType);
+        }
+
+        assertEquals("Expected panels not contained in list",
+                0, expectedSet.size());
+    }
+
+    @Test
+    public void testMigrationRetainsDefaultAfter6() throws JSONException {
+        final Context context = RuntimeEnvironment.application;
+
+        final Pair<PanelType[], PanelType[]> finalConstellation = migrationConstellations.get(HomeConfigPrefsBackend.VERSION);
+        assertNotNull("It looks like you added a HomeConfig migration, please add an appropriate entry to migrationConstellations",
+                finalConstellation);
+
+        // We want to calculate the number of iterations here to make sure we cover all provided constellations.
+        // Iterating over the array and manually checking for each version could result in constellations
+        // being skipped if there are any gaps in the array
+        final  int firstTestedVersion = HomeConfigPrefsBackend.VERSION - (migrationConstellations.size() - 1);
+
+        // The last constellation is only used for the counts / expected outputs, hence we start
+        // with the second-last constellation
+        for (int testVersion = HomeConfigPrefsBackend.VERSION - 1; testVersion >= firstTestedVersion; testVersion--) {
+
+            final Pair<PanelType[], PanelType[]> currentConstellation = migrationConstellations.get(testVersion);
+            assertNotNull("No constellation for version " + testVersion + " - you must provide a constellation for every version upgrade in the list",
+                    currentConstellation);
+
+            final PanelType[] inputList = currentConstellation.first;
+            final PanelType[] expectedDefaults = currentConstellation.second;
+
+            for (int i = 0; i < inputList.length; i++) {
+                JSONArray jsonPanels = createConfigsForList(context, inputList, i);
+
+
+                // Verify that we still have a default panel, and that it is the expected default panel
+
+                // No need to pass in the prefsEditor since that is only used for the 0->1 migration
+                jsonPanels = HomeConfigPrefsBackend.migratePrefsFromVersionToVersion(context, testVersion, testVersion + 1, jsonPanels, null);
+
+                final PanelType oldDefaultPanelType = inputList[i];
+                final PanelType expectedNewDefaultPanelType = expectedDefaults[i];
+                final PanelType newDefaultPanelType = getDefaultPanel(jsonPanels);
+
+                assertNotNull("No default panel set when migrating from " + testVersion + " to " + testVersion + 1 + ", with previous default as " + oldDefaultPanelType.name(),
+                        newDefaultPanelType);
+
+                assertEquals("Migration changed to unexpected default panel - migrating from " + oldDefaultPanelType.name() + ", expected " +  expectedNewDefaultPanelType.name() + " but got " + newDefaultPanelType.name(),
+                        newDefaultPanelType, expectedNewDefaultPanelType);
+
+
+                // Verify that the panels remaining after the migration correspond to the input panels
+                // for the next migration
+                final PanelType[] expectedOutputList = migrationConstellations.get(testVersion + 1).first;
+
+                assertEquals("Number of panels after migration doesn't match expected count",
+                        jsonPanels.length(), expectedOutputList.length);
+
+                checkListContainsExpectedPanels(jsonPanels, expectedOutputList);
+            }
+        }
+    }
+}