Bug 1244295 - Add junit4 tests for GeckoProfile.getClient & friends. r=mfinkle

Added testGetDir to sanity check how the profile is set up for the test and
left it in as a bonus.

Additionally, changed access levels on the ensureParentDirs method because it
only needed to be `protected` for testing.

MozReview-Commit-ID: CDVQjyf3yP2
This commit is contained in:
Michael Comella 2016-02-18 17:38:16 -08:00
parent 5f3968b2e9
commit 7588564b44
3 changed files with 249 additions and 4 deletions

View File

@ -611,9 +611,8 @@ public final class GeckoProfile {
* robust way to access it. However, we don't want to rely on Gecko running in order to get * robust way to access it. However, we don't want to rely on Gecko running in order to get
* the client ID so instead we access the file this module accesses directly. However, it's * the client ID so instead we access the file this module accesses directly. However, it's
* possible the format of this file (and the access calls in the jsm) will change, leaving * possible the format of this file (and the access calls in the jsm) will change, leaving
* this code to fail. * this code to fail. There are tests in TestGeckoProfile to verify the file format but be
* * warned: THIS IS NOT FOOLPROOF.
* TODO: Write tests to prevent regressions. Mention them here. Test both file location and file format.
* *
* [1]: https://mxr.mozilla.org/mozilla-central/source/toolkit/modules/ClientID.jsm * [1]: https://mxr.mozilla.org/mozilla-central/source/toolkit/modules/ClientID.jsm
* *
@ -777,7 +776,7 @@ public final class GeckoProfile {
* @return true if the parent directory exists, false otherwise * @return true if the parent directory exists, false otherwise
*/ */
@WorkerThread @WorkerThread
public boolean ensureParentDirs(final String filename) { protected boolean ensureParentDirs(final String filename) {
final File file = new File(getDir(), filename); final File file = new File(getDir(), filename);
final File parentFile = file.getParentFile(); final File parentFile = file.getParentFile();
return parentFile.mkdirs() || parentFile.isDirectory(); return parentFile.mkdirs() || parentFile.isDirectory();

View File

@ -0,0 +1,204 @@
/* -*- 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;
import android.content.Context;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.helpers.FileUtil;
import org.robolectric.RuntimeEnvironment;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import static org.junit.Assert.*;
/**
* Unit test methods of the GeckoProfile class.
*/
@RunWith(TestRunner.class)
public class TestGeckoProfile {
private static final String PROFILE_NAME = "profileName";
private static final String CLIENT_ID_JSON_ATTR = "clientID";
@Rule
public TemporaryFolder dirContainingProfile = new TemporaryFolder();
private File profileDir;
private GeckoProfile profile;
private File clientIdFile;
@Before
public void setUp() throws IOException {
final Context context = RuntimeEnvironment.application;
profileDir = dirContainingProfile.newFolder();
profile = GeckoProfile.get(context, PROFILE_NAME, profileDir);
clientIdFile = new File(profileDir, "datareporting/state.json");
}
public void assertValidClientId(final String clientId) {
// This isn't the method we use in the main GeckoProfile code, but it should be equivalent.
UUID.fromString(clientId); // assert: will throw if null or invalid UUID.
}
@Test
public void testGetDir() {
assertEquals("Profile dir argument during construction and returned value are equal",
profileDir, profile.getDir());
}
@Test
public void testGetClientIdFreshProfile() throws Exception {
assertFalse("client ID file does not exist", clientIdFile.exists());
// No existing client ID file: we're expected to create one.
final String clientId = profile.getClientId();
assertValidClientId(clientId);
assertTrue("client ID file exists", clientIdFile.exists());
assertEquals("Returned client ID is the same as the one previously returned", clientId, profile.getClientId());
assertEquals("clientID file format matches expectations", clientId, readClientIdFromFile(clientIdFile));
}
@Test
public void testGetClientIdMigrateFromFHR() throws Exception {
final File fhrClientIdFile = new File(profileDir, "healthreport/state.json");
final String fhrClientId = "905de1c0-0ea6-4a43-95f9-6170035f5a82";
assertFalse("client ID file does not exist", clientIdFile.exists());
assertTrue("Created FHR data directory", new File(profileDir, "healthreport").mkdirs());
writeClientIdToFile(fhrClientIdFile, fhrClientId);
assertEquals("Migrated Client ID equals FHR client ID", fhrClientId, profile.getClientId());
// Verify migration wrote to contemporary client ID file.
assertTrue("Client ID file created during migration", clientIdFile.exists());
assertEquals("Migrated client ID on disk equals value returned from method",
fhrClientId, readClientIdFromFile(clientIdFile));
assertTrue("Deleted FHR clientID file", fhrClientIdFile.delete());
assertEquals("Ensure method calls read from newly created client ID file & not FHR client ID file",
fhrClientId, profile.getClientId());
}
@Test
public void testGetClientIdInvalidIdOnDisk() throws Exception {
assertTrue("Created the parent dirs of the client ID file", clientIdFile.getParentFile().mkdirs());
writeClientIdToFile(clientIdFile, "");
final String clientIdForEmptyString = profile.getClientId();
assertValidClientId(clientIdForEmptyString);
assertNotEquals("A new client ID was created when the empty String was written to disk", "", clientIdForEmptyString);
writeClientIdToFile(clientIdFile, "invalidClientId");
final String clientIdForInvalidClientId = profile.getClientId();
assertValidClientId(clientIdForInvalidClientId);
assertNotEquals("A new client ID was created when an invalid client ID was written to disk",
"invalidClientId", clientIdForInvalidClientId);
}
@Test
public void testGetClientIdMissingClientIdJSONAttr() throws Exception {
final String validClientId = "905de1c0-0ea6-4a43-95f9-6170035f5a82";
final JSONObject objMissingClientId = new JSONObject();
objMissingClientId.put("irrelevantKey", validClientId);
assertTrue("Created the parent dirs of the client ID file", clientIdFile.getParentFile().mkdirs());
FileUtil.writeJSONObjectToFile(clientIdFile, objMissingClientId);
final String clientIdForMissingAttr = profile.getClientId();
assertValidClientId(clientIdForMissingAttr);
assertNotEquals("Did not use other attr when JSON attr was missing", validClientId, clientIdForMissingAttr);
}
@Test
public void testGetClientIdInvalidIdFileFormat() throws Exception {
final String validClientId = "905de1c0-0ea6-4a43-95f9-6170035f5a82";
assertTrue("Created the parent dirs of the client ID file", clientIdFile.getParentFile().mkdirs());
FileUtil.writeStringToFile(clientIdFile, "clientID: \"" + validClientId + "\"");
final String clientIdForInvalidFormat = profile.getClientId();
assertValidClientId(clientIdForInvalidFormat);
assertNotEquals("Created new ID when file format was invalid", validClientId, clientIdForInvalidFormat);
}
@Test
public void testEnsureParentDirs() {
final File grandParentDir = new File(profileDir, "grandParent");
final File parentDir = new File(grandParentDir, "parent");
final File childFile = new File(parentDir, "child");
// Assert initial state.
assertFalse("Topmost parent dir should not exist yet", grandParentDir.exists());
assertFalse("Bottommost parent dir should not exist yet", parentDir.exists());
assertFalse("Child file should not exist", childFile.exists());
final String fakeFullPath = "grandParent/parent/child";
assertTrue("Parent directories should be created", profile.ensureParentDirs(fakeFullPath));
assertTrue("Topmost parent dir should have been created", grandParentDir.exists());
assertTrue("Bottommost parent dir should have been created", parentDir.exists());
assertFalse("Child file should not have been created", childFile.exists());
// Parents already exist because this is the second time we're calling ensureParentDirs.
assertTrue("Expect true if parent directories already exist", profile.ensureParentDirs(fakeFullPath));
// Assert error condition.
assertTrue("Ensure we can change permissions on profile dir for testing", profileDir.setReadOnly());
assertFalse("Expect false if the parent dir could not be created", profile.ensureParentDirs("unwritableDir/child"));
}
@Test
public void testIsClientIdValid() {
final String[] validClientIds = new String[] {
"905de1c0-0ea6-4a43-95f9-6170035f5a82",
"905de1c0-0ea6-4a43-95f9-6170035f5a83",
"57472f82-453d-4c55-b59c-d3c0e97b76a1",
"895745d1-f31e-46c3-880e-b4dd72963d4f",
};
for (final String validClientId : validClientIds) {
assertTrue("Client ID, " + validClientId + ", is valid", profile.isClientIdValid(validClientId));
}
final String[] invalidClientIds = new String[] {
null,
"",
"a",
"anInvalidClientId",
"905de1c0-0ea6-4a43-95f9-6170035f5a820", // too long (last section)
"905de1c0-0ea6-4a43-95f9-6170035f5a8", // too short (last section)
"05de1c0-0ea6-4a43-95f9-6170035f5a82", // too short (first section)
"905de1c0-0ea6-4a43-95f9-6170035f5a8!", // contains a symbol
};
for (final String invalidClientId : invalidClientIds) {
assertFalse("Client ID, " + invalidClientId + ", is invalid", profile.isClientIdValid(invalidClientId));
}
// We generate client IDs using UUID - better make sure they're valid.
for (int i = 0; i < 30; ++i) {
final String generatedClientId = UUID.randomUUID().toString();
assertTrue("Generated client ID from UUID, " + generatedClientId + ", is valid",
profile.isClientIdValid(generatedClientId));
}
}
private String readClientIdFromFile(final File file) throws Exception {
final JSONObject obj = FileUtil.readJSONObjectFromFile(file);
return obj.getString(CLIENT_ID_JSON_ATTR);
}
private void writeClientIdToFile(final File file, final String clientId) throws Exception {
final JSONObject obj = new JSONObject();
obj.put(CLIENT_ID_JSON_ATTR, clientId);
FileUtil.writeJSONObjectToFile(file, obj);
}
}

View File

@ -0,0 +1,42 @@
/* -*- 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.helpers;
import org.json.JSONObject;
import java.io.File;
import java.io.FileWriter;
import java.util.Scanner;
public class FileUtil {
private FileUtil() { }
public static JSONObject readJSONObjectFromFile(final File file) throws Exception {
final StringBuilder builder = new StringBuilder();
final Scanner scanner = new Scanner(file);
try {
while (scanner.hasNext()) {
builder.append(scanner.next());
}
} finally {
scanner.close();
}
return new JSONObject(builder.toString());
}
public static void writeJSONObjectToFile(final File file, final JSONObject obj) throws Exception {
writeStringToFile(file, obj.toString());
}
public static void writeStringToFile(final File file, final String str) throws Exception {
final FileWriter writer = new FileWriter(file, false);
try {
writer.write(str);
} finally {
writer.close();
}
}
}