Bug 1261494 - Block Gecko from screwing with the client ID file by resetting the cache at the last minute. r=grisha

MozReview-Commit-ID: EZYrk8IiRiF

--HG--
extra : rebase_source : 420c4e802c6d234a594b9f17670791e0029e237e
This commit is contained in:
Michael Comella 2016-06-07 15:32:04 -07:00
parent a957702d89
commit db493df167

View File

@ -35,46 +35,57 @@ public class testUnifiedTelemetryClientId extends JavascriptBridgeTest {
getFHRClientIdFile(),
getFHRClientIdParentDir(),
};
// In local testing, it's possible to ^C out of the harness and not have tearDown called,
// hence reset. We can't clear the cache because Gecko is not running yet.
resetTest(false);
}
public void tearDown() throws Exception {
// Don't clear cache because who knows what state Gecko is in.
resetTest(false);
deleteClientIDFiles();
super.tearDown();
}
private void resetTest(final boolean resetJSCache) {
Log.d(LOGTAG, "resetTest: begin");
private void deleteClientIDFiles() {
Log.d(LOGTAG, "deleteClientIDFiles: begin");
if (resetJSCache) {
resetJSCache();
}
for (final File file : filesToDeleteOnReset) {
file.delete();
file.delete(); // can't check return value because the file may not exist before deletion.
fAssertFalse("Deleted file in reset does not exist", file.exists()); // sanity check.
}
Log.d(LOGTAG, "resetTest: end");
Log.d(LOGTAG, "deleteClientIDFiles: end");
}
public void testUnifiedTelemetryClientId() throws Exception {
blockForReadyAndLoadJS(TEST_JS);
resetJSCache(); // Must be called after Gecko is loaded.
fAssertTrue("Profile directory exists", profileDir.exists());
// Important note: we cannot stop Gecko from running while we run this test and
// Gecko is capable of creating client ID files while we run this test. However,
// ClientID.jsm will not touch modify the client ID files on disk if its client
// ID cache is filled. As such, we prevent it from touching the disk by intentionally
// priming the cache & deleting the files it added now, and resetting the cache at the
// latest possible moment before we attempt to test the client ID file.
//
// This is fragile because it relies on the ClientID cache's implementation, however,
// some alternatives (e.g. changing file system permissions, file locking) are worse
// because they can fire error handling code, which is not currently under test.
//
// First, we delete the test files - we don't want the cache prime to fail which could happen if
// these files are around & corrupted from a previous test/install. Then we prime the cache,
// and delete the files the cache priming added, so the tests are ready to add their own version
// of these files.
deleteClientIDFiles();
primeJsClientIdCache();
deleteClientIDFiles();
// TODO: If these tests weren't so expensive to run in automation,
// this should be two separate tests to avoid storing state between tests.
testJavaCreatesClientId();
resetTest(true);
testJsCreatesClientId();
resetTest(true);
testJavaMigratesFromHealthReport();
resetTest(true);
testJsMigratesFromHealthReport();
testJavaCreatesClientId(); // leaves cache filled.
deleteClientIDFiles();
testJsCreatesClientId(); // leaves cache filled.
deleteClientIDFiles();
testJavaMigratesFromHealthReport(); // leaves cache filled.
deleteClientIDFiles();
testJsMigratesFromHealthReport(); // leaves cache filled.
getJS().syncCall("endTest");
}
@ -92,6 +103,7 @@ public class testUnifiedTelemetryClientId extends JavascriptBridgeTest {
fAssertFalse("Client id file does not exist yet", getClientIdFile().exists());
final String clientIdFromJava = getClientIdFromJava();
resetJSCache();
final String clientIdFromJS = getClientIdFromJS();
fAssertEquals("Client ID from Java equals ID from JS", clientIdFromJava, clientIdFromJS);
@ -116,6 +128,7 @@ public class testUnifiedTelemetryClientId extends JavascriptBridgeTest {
fAssertFalse("Client id file does not exist yet", getClientIdFile().exists());
resetJSCache();
final String clientIdFromJS = getClientIdFromJS();
final String clientIdFromJava = getClientIdFromJava();
fAssertEquals("Client ID from JS equals ID from Java", clientIdFromJS, clientIdFromJava);
@ -148,6 +161,7 @@ public class testUnifiedTelemetryClientId extends JavascriptBridgeTest {
final String clientIdFromJava = getClientIdFromJava();
fAssertEquals("Health report client ID merged by Java", expectedClientId, clientIdFromJava);
resetJSCache();
final String clientIdFromJS = getClientIdFromJS();
fAssertEquals("Merged client ID read by JS", expectedClientId, clientIdFromJS);
@ -177,6 +191,7 @@ public class testUnifiedTelemetryClientId extends JavascriptBridgeTest {
final String expectedClientId = UUID.randomUUID().toString();
createFHRClientIdFile(expectedClientId);
resetJSCache();
final String clientIdFromJS = getClientIdFromJS();
fAssertEquals("Health report client ID merged by JS", expectedClientId, clientIdFromJS);
final String clientIdFromJava = getClientIdFromJava();
@ -204,9 +219,20 @@ public class testUnifiedTelemetryClientId extends JavascriptBridgeTest {
return getBlockingFromJsString("clientId");
}
/**
* Must be called after Gecko is loaded.
*/
private void primeJsClientIdCache() {
// Not the cleanest way, but it works.
getClientIdFromJS();
}
/**
* Resets the client ID cache in ClientID.jsm. This method *must* be called after
* Gecko is loaded or else this method will hang.
*
* Note: we do this for very specific reasons - see the comment in the test method
* ({@link #testUnifiedTelemetryClientId()}) for more.
*/
private void resetJSCache() {
// HACK: the backing JS method is a promise with no return value. Rather than writing a method