Bug 1045274 - Basic tests for GeckoProfile. r=rnewman
authorWes Johnston <wjohnston@mozilla.com>
Mon, 25 Aug 2014 16:19:16 -0700
changeset 223184 d9ea224d0ebca9aec66d428d62454490db6623fb
parent 223183 b1cfad9bc2dbc22eef183bdf51c890e56b7fdda9
child 223185 341e503cabf91f5fab56451ca4fd90a49c5b5457
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs1045274
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1045274 - Basic tests for GeckoProfile. r=rnewman
mobile/android/base/GeckoProfile.java
mobile/android/base/GeckoProfileDirectories.java
mobile/android/base/tests/robocop.ini
mobile/android/base/tests/testGeckoProfile.java
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -31,17 +31,17 @@ import android.text.TextUtils;
 import android.util.Log;
 
 public final class GeckoProfile {
     private static final String LOGTAG = "GeckoProfile";
 
     // Used to "lock" the guest profile, so that we'll always restart in it
     private static final String LOCK_FILE_NAME = ".active_lock";
     public static final String DEFAULT_PROFILE = "default";
-    private static final String GUEST_PROFILE = "guest";
+    public static final String GUEST_PROFILE = "guest";
 
     private static HashMap<String, GeckoProfile> sProfileCache = new HashMap<String, GeckoProfile>();
     private static String sDefaultProfileName;
 
     // Caches the guest profile dir.
     private static File sGuestDir;
     private static GeckoProfile sGuestProfile;
 
@@ -172,23 +172,21 @@ public final class GeckoProfile {
     }
 
     public static boolean removeProfile(Context context, String profileName) {
         if (profileName == null) {
             Log.w(LOGTAG, "Unable to remove profile: null profile name.");
             return false;
         }
 
-        final boolean success;
-        try {
-            success = new GeckoProfile(context, profileName).remove();
-        } catch (NoMozillaDirectoryException e) {
-            Log.w(LOGTAG, "Unable to remove profile: no Mozilla directory.", e);
-            return true;
+        final GeckoProfile profile = get(context, profileName);
+        if (profile == null) {
+            return false;
         }
+        final boolean success = profile.remove();
 
         if (success) {
             // Clear all shared prefs for the given profile.
             GeckoSharedPrefs.forProfileName(context, profileName)
                             .edit().clear().commit();
         }
 
         return success;
--- a/mobile/android/base/GeckoProfileDirectories.java
+++ b/mobile/android/base/GeckoProfileDirectories.java
@@ -4,16 +4,17 @@
 
 package org.mozilla.gecko;
 
 import java.io.File;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.util.INIParser;
 import org.mozilla.gecko.util.INISection;
 
 import android.content.Context;
 
 /**
  * <code>GeckoProfileDirectories</code> manages access to mappings from profile
  * names to salted profile directory paths, as well as the default profile name.
@@ -70,21 +71,18 @@ public class GeckoProfileDirectories {
     private static INISectionPredicate sectionHasName = new INISectionPredicate() {
         @Override
         public boolean matches(INISection section) {
             final String name = section.getStringProperty("Name");
             return name != null;
         }
     };
 
-    /**
-     * Package-scoped because GeckoProfile needs to dig into this in order to do writes.
-     * This will be fixed in Bug 975212.
-     */
-    static INIParser getProfilesINI(File mozillaDir) {
+    @RobocopTarget
+    public static INIParser getProfilesINI(File mozillaDir) {
         return new INIParser(new File(mozillaDir, "profiles.ini"));
     }
 
     /**
      * Utility method to compute a salted profile name: eight random alphanumeric
      * characters, followed by a period, followed by the profile name.
      */
     public static String saltProfileName(final String name) {
@@ -111,17 +109,18 @@ public class GeckoProfileDirectories {
      *
      * This method is package-scoped so that new {@link GeckoProfile} instances can
      * contextualize themselves.
      *
      * @return a new File object for the Mozilla directory.
      * @throws NoMozillaDirectoryException
      *             if the directory did not exist and could not be created.
      */
-    static File getMozillaDirectory(Context context) throws NoMozillaDirectoryException {
+    @RobocopTarget
+    public static File getMozillaDirectory(Context context) throws NoMozillaDirectoryException {
         final File mozillaDir = new File(context.getFilesDir(), MOZILLA_DIR_NAME);
         if (mozillaDir.exists() || mozillaDir.mkdirs()) {
             return mozillaDir;
         }
 
         // Although this leaks a path to the system log, the path is
         // predictable (unlike a profile directory), so this is fine.
         throw new NoMozillaDirectoryException("Unable to create mozilla directory at " + mozillaDir.getAbsolutePath());
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -1,8 +1,9 @@
+[testGeckoProfile]
 # [test_bug720538] # disabled on fig - bug 897072
 [testAboutPage]
 # disabled on Android 2.3; bug 975187
 skip-if = android_version == "10"
 [testAddonManager]
 # disabled on x86 only; bug 936216
 skip-if = processor == "x86"
 [testAddSearchEngine]
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testGeckoProfile.java
@@ -0,0 +1,282 @@
+/* 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.tests;
+
+import org.mozilla.gecko.GeckoApp;
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoProfileDirectories;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.util.INIParser;
+import org.mozilla.gecko.util.INISection;
+
+import android.view.View;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import android.text.TextUtils;
+
+/**
+ * This patch tests GeckoProfile. It has unit tests for basic getting and removing of profiles, as well as
+ * some guest mode tests. It does not test locking and unlocking profiles yet. It does not test the file management in GeckoProfile.
+ */
+
+public class testGeckoProfile extends PixelTest {
+    private final String TEST_PROFILE_NAME = "testProfile";
+    private File mozDir;
+    public void testGeckoProfile() {
+        blockForGeckoReady();
+
+        try {
+            mozDir = GeckoProfileDirectories.getMozillaDirectory(getActivity());
+        } catch(Exception ex) {
+            // If we can't get the moz dir, something is wrong. Just fail quickly.
+            mAsserter.ok(false, "Couldn't get moz dir", ex.toString());
+            return;
+        }
+
+        testProfileCreationDeletion();
+        testGuestProfile();
+    }
+
+    // This getter just passes an activity. Passing null should throw.
+    private void testDefaultGetter() {
+        mAsserter.info("Test using the default profile", GeckoProfile.DEFAULT_PROFILE);
+        GeckoProfile profile = GeckoProfile.get(getActivity());
+        // This profile has been forced into something strange by the test harness, but its name is still right...
+        verifyProfile(profile, GeckoProfile.DEFAULT_PROFILE, ((GeckoApp) getActivity()).getProfile().getDir(), true);
+
+        try {
+            profile = GeckoProfile.get(null);
+            mAsserter.ok(false, "Passing a null context should throw", profile.toString());
+        } catch(Exception ex) {
+            mAsserter.ok(true, "Passing a null context should throw", ex.toString());
+        }
+    }
+
+    // Test get(Context, String) methods
+    private void testNamedGetter(String name) {
+        mAsserter.info("Test using a named profile", name);
+        GeckoProfile profile = GeckoProfile.get(getActivity(), name);
+        if (!TextUtils.isEmpty(name)) {
+            verifyProfile(profile, name, findDir(name), false);
+            removeProfile(profile, true);
+        } else {
+            // Passing in null for a profile name, should get you the default
+            File defaultProfile = ((GeckoApp) getActivity()).getProfile().getDir();
+            verifyProfile(profile, GeckoProfile.DEFAULT_PROFILE, defaultProfile, true);
+        }
+    }
+
+    // Test get(Context, String, String) methods
+    private void testNameAndPathGetter(String name, boolean createBefore) {
+        if (TextUtils.isEmpty(name)) {
+            testNameAndPathGetter(name, null, createBefore);
+        } else {
+            testNameAndPathGetter(name, name + "_FORCED_DIR", createBefore);
+        }
+    }
+
+    // Test get(Context, String, String) methods
+    private void testNameAndPathGetter(String name, String path, boolean createBefore) {
+        mAsserter.info("Test using a named profile and path", name + ", " + path);
+
+        File f = null;
+        if (!TextUtils.isEmpty(path)) {
+            f = new File(mozDir, path);
+            // GeckoProfile will ignore dirs passed in if they don't exist. For some tests we create explicitly beforehand
+            if (createBefore) {
+                f.mkdir();
+            }
+            path = f.getAbsolutePath();
+        }
+
+        try {
+            GeckoProfile profile = GeckoProfile.get(getActivity(), name, path);
+            if (!TextUtils.isEmpty(name)) {
+                verifyProfile(profile, name, f, createBefore);
+                removeProfile(profile, !createBefore);
+            } else {
+                mAsserter.ok(TextUtils.isEmpty(path), "Passing a null name and non-null path should throw", name + ", " + path);
+                // Passing in null for a profile name and path, should get you the default
+                File defaultProfile = ((GeckoApp) getActivity()).getProfile().getDir();
+                verifyProfile(profile, GeckoProfile.DEFAULT_PROFILE, defaultProfile, true);
+            }
+        } catch(Exception ex) {
+            mAsserter.ok(TextUtils.isEmpty(name) && !TextUtils.isEmpty(path), "Passing a null name and non null path should throw", name + ", " + path);
+        }
+    }
+
+    private void testNameAndFileGetter(String name, boolean createBefore) {
+        if (TextUtils.isEmpty(name)) {
+            testNameAndFileGetter(name, null, createBefore);
+        } else {
+            testNameAndFileGetter(name, new File(mozDir, name + "_FORCED_DIR"), createBefore);
+        }
+    }
+
+    private void testNameAndFileGetter(String name, File f, boolean createBefore) {
+        mAsserter.info("Test using a named profile and path", name + ", " + f);
+        if (f != null && createBefore) {
+            f.mkdir();
+        }
+
+        try {
+            GeckoProfile profile = GeckoProfile.get(getActivity(), name, f);
+            if (!TextUtils.isEmpty(name)) {
+                verifyProfile(profile, name, f, createBefore);
+                removeProfile(profile, !createBefore);
+            } else {
+                mAsserter.ok(f == null, "Passing a null name and non-null file should throw", name + ", " + f);
+                // Passing in null for a profile name and path, should get you the default
+                File defaultProfile = ((GeckoApp) getActivity()).getProfile().getDir();
+                verifyProfile(profile, GeckoProfile.DEFAULT_PROFILE, defaultProfile, true);
+            }
+        } catch(Exception ex) {
+            mAsserter.ok(TextUtils.isEmpty(name) && f != null, "Passing a null name and non null file should throw", name + ", " + f);
+        }
+    }
+
+    private void testProfileCreationDeletion() {
+        // Test
+        testDefaultGetter();
+
+        int index = 0;
+        testNamedGetter(TEST_PROFILE_NAME + (index++)); // 0
+        testNamedGetter("");
+        testNamedGetter(null);
+
+        // name and path
+        testNameAndPathGetter(TEST_PROFILE_NAME + (index++), true); // 1
+        testNameAndPathGetter(TEST_PROFILE_NAME + (index++), false); // 2
+        // null name and path
+        testNameAndPathGetter(null, TEST_PROFILE_NAME + (index++) + "_FORCED_DIR", true); // 3
+        testNameAndPathGetter(null, TEST_PROFILE_NAME + (index++) + "_FORCED_DIR", false); // 4
+        testNameAndPathGetter("", TEST_PROFILE_NAME + (index++) + "_FORCED_DIR", true); // 5
+        testNameAndPathGetter("", TEST_PROFILE_NAME + (index++) + "_FORCED_DIR", false); // 6
+        // name and null path
+        testNameAndPathGetter(TEST_PROFILE_NAME + (index++), null, false); // 7
+        testNameAndPathGetter(TEST_PROFILE_NAME + (index++), "", false); // 8
+        // null name and null path
+        testNameAndPathGetter(null, null, false);
+        testNameAndPathGetter("", null, false);
+        testNameAndPathGetter(null, "", false);
+        testNameAndPathGetter("", "", false);
+
+        // name and path
+        testNameAndFileGetter(TEST_PROFILE_NAME + (index++), true); // 9
+        testNameAndFileGetter(TEST_PROFILE_NAME + (index++), false); // 10
+        // null name and path
+        testNameAndFileGetter(null, new File(mozDir, TEST_PROFILE_NAME + (index++) + "_FORCED_DIR"), true); // 11
+        testNameAndFileGetter(null, new File(mozDir, TEST_PROFILE_NAME + (index++) + "_FORCED_DIR"), false); // 12
+        testNameAndFileGetter("", new File(mozDir, TEST_PROFILE_NAME + (index++) + "_FORCED_DIR"), true); // 13
+        testNameAndFileGetter("", new File(mozDir, TEST_PROFILE_NAME + (index++) + "_FORCED_DIR"), false); // 14
+        // name and null path
+        testNameAndFileGetter(TEST_PROFILE_NAME + (index++), null, false); // 16
+        // null name and null path
+        testNameAndFileGetter(null, null, false);
+    }
+
+    // Tests of Guest profile methods
+    private void testGuestProfile() {
+        mAsserter.info("Test getting a guest profile", "");
+        GeckoProfile profile = GeckoProfile.createGuestProfile(getActivity());
+        verifyProfile(profile, GeckoProfile.GUEST_PROFILE, getActivity().getFileStreamPath("guest"), true);
+        File dir = profile.getDir();
+
+        mAsserter.ok(profile.inGuestMode(), "Profile is in guest mode", profile.getName());
+
+        mAsserter.info("Test deleting a guest profile", "");
+        mAsserter.ok(!GeckoProfile.maybeCleanupGuestProfile(getActivity()), "Can't clean up locked guest profile", profile.getName());
+        GeckoProfile.leaveGuestSession(getActivity());
+        mAsserter.ok(GeckoProfile.maybeCleanupGuestProfile(getActivity()), "Cleaned up unlocked guest profile", profile.getName());
+        mAsserter.ok(!dir.exists(), "Guest dir was deleted", dir.toString());
+    }
+
+    // Runs generic tests on a profile to make sure it looks correct
+    private void verifyProfile(GeckoProfile profile, String name, File requestedDir, boolean shouldHaveFound) {
+        mAsserter.is(profile.getName(), name, "Profile name is correct");
+
+        File dir = null;
+        if (!shouldHaveFound) {
+            mAsserter.is(findDir(name), null, "Dir with name doesn't exist yet");
+
+            dir = profile.getDir();
+            mAsserter.isnot(requestedDir, dir, "Profile should not have used expectedDir");
+
+            // The used dir should be based on the name passed in.
+            requestedDir = findDir(name);
+        } else {
+            dir = profile.getDir();
+        }
+
+        mAsserter.is(dir, requestedDir, "Profile dir is correct");
+        mAsserter.ok(dir.exists(), "Profile dir exists after getting it", dir.toString());
+    }
+
+    // Tries to find a profile in profiles.ini. Makes sure its name and path match what is expected
+    private void findInProfilesIni(GeckoProfile profile, boolean shouldFind) {
+        final File mozDir;
+        try {
+            mozDir = GeckoProfileDirectories.getMozillaDirectory(getActivity());
+        } catch(Exception ex) {
+            mAsserter.ok(false, "Couldn't get moz dir", ex.toString());
+            return;
+        }
+
+        final String name = profile.getName();
+        final File dir = profile.getDir();
+
+        final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozDir);
+        final Hashtable<String, INISection> sections = parser.getSections();
+
+        boolean found = false;
+        for (Enumeration<INISection> e = sections.elements(); e.hasMoreElements();) {
+            final INISection section = e.nextElement();
+            String iniName = section.getStringProperty("Name");
+            if (iniName == null || !iniName.equals(name)) {
+                continue;
+            }
+
+            found = true;
+
+            String iniPath = section.getStringProperty("Path");
+            mAsserter.is(name, iniName, "Section with name found");
+            mAsserter.is(dir.getName(), iniPath, "Section has correct path");
+        }
+
+        mAsserter.is(found, shouldFind, "Found profile where expected");
+    }
+
+    // Tries to remove a profile from Gecko profile. Verifies that it's removed from profiles.ini and its directory is deleted.
+    private void removeProfile(GeckoProfile profile, boolean inProfilesIni) {
+        findInProfilesIni(profile, inProfilesIni);
+        File dir = profile.getDir();
+        mAsserter.ok(dir.exists(), "Profile dir exists before removing", dir.toString());
+        mAsserter.is(inProfilesIni, GeckoProfile.removeProfile(getActivity(), profile.getName()), "Remove was successful");
+        mAsserter.ok(!dir.exists(), "Profile dir was deleted when it was removed", dir.toString());
+        findInProfilesIni(profile, false);
+    }
+
+    // Looks for a dir whose name ends with the passed-in string.
+    private File findDir(String name) {
+        final File root;
+        try {
+            root = GeckoProfileDirectories.getMozillaDirectory(getActivity());
+        } catch(Exception ex) {
+            return null;
+        }
+
+        File[] dirs = root.listFiles();
+        for (File dir : dirs) {
+            if (dir.getName().endsWith(name)) {
+                return dir;
+            }
+        }
+
+        return null;
+    }
+}