Bug 1182193 - Part 3: Added runwith annotations for unittest with gradle r?nalexander draft
authorvivek <vivekb.balakrishnan@gmail.com>
Fri, 04 Sep 2015 00:50:42 +0300
changeset 290209 c7345b022560e080cdb26ebbebaecf7202ae6ba2
parent 290208 03635dd6d7d4c5bf0649a700d703e2354bdfb9ba
child 509008 d2c2204db6e6a0fdc882c04b689da6cebb0eb168
push id5105
push uservivekb.balakrishnan@gmail.com
push dateThu, 03 Sep 2015 21:53:08 +0000
reviewersnalexander
bugs1182193
milestone43.0a1
Bug 1182193 - Part 3: Added runwith annotations for unittest with gradle r?nalexander
mobile/android/gradle/base/build.gradle
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestAccountAuthenticatorStage.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestBackoff.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestBrowserIDAuthHeaderProvider.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestCredentialsEndToEnd.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestGlobalSession.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestHeaderParsing.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestLineByLineHandling.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestMetaGlobal.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestResource.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestRetryAfter.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestServer11Repository.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestSyncStorageRequest.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCollectionKeys.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCommandProcessor.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCryptoRecord.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestJPakeSetup.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestRecord.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestRecordsChannel.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestResetCommands.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestServer11RepositorySession.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestServerLocalSynchronizer.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSyncConfiguration.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSyncKeyVerification.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSynchronizer.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSynchronizerSession.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestUtils.java
mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/helpers/test/TestHTTPServerTestHelper.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/common/log/writers/test/TestLogWriters.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountAgeLockoutHelper.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountClient20.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountUtils.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/prune/test/TestPrunePolicy.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestObsoleteDocumentTracker.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionPolicy.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionsTracker.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestTrackingRequestDelegate.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestBoundedByteArrayEntity.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestDeflation.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/BaseMockServerSyncStage.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/CommandHelpers.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/DefaultGlobalSessionCallback.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/JPakeNumGeneratorFixed.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockAbstractNonRepositorySyncStage.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockClientsDataDelegate.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockClientsDatabaseAccessor.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockGlobalSession.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockPrefsGlobalSession.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockRecord.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockServerSyncStage.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockSharedPreferences.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/StubDelegate.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/WBORepository.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestASNUtils.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestDSACryptoImplementation.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestJSONWebTokenUtils.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestRSACryptoImplementation.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestBase32.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestCryptoInfo.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestHKDF.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestKeyBundle.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestPBKDF2.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestPersistedCrypto5Keys.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestSRPConstants.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/middleware/test/TestCrypto5MiddlewareRepositorySession.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestHMACAuthHeaderProvider.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestHawkAuthHeaderProvider.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestUserAgentHeaders.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/android/test/TestBookmarksInsertionManager.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/domain/TestClientRecord.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/domain/test/TestFormHistoryRecord.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/test/TestRepositorySessionBundle.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/test/TestSafeConstrainedServer11Repository.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestEnsureClusterURLStage.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestEnsureCrypto5KeysStage.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestFetchMetaGlobalStage.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestStageLookup.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestExtendedJSONObject.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestInfoCollections.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestPersistedMetaGlobal.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/tokenserver/test/TestTokenServerClient.java
--- a/mobile/android/gradle/base/build.gradle
+++ b/mobile/android/gradle/base/build.gradle
@@ -82,16 +82,17 @@ dependencies {
 
     compile project(':branding')
     compile project(':preprocessed_code')
     compile project(':preprocessed_resources')
     compile project(':thirdparty')
 
     testCompile 'junit:junit:4.12'
     testCompile 'org.robolectric:robolectric:3.0'
+    testCompile 'org.simpleframework:simple-http:4.+'
 }
 
 apply plugin: 'idea'
 
 idea {
     module {
         excludeDirs += file('org/mozilla/gecko/resources')
     }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestAccountAuthenticatorStage.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestAccountAuthenticatorStage.java
@@ -9,32 +9,35 @@ import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.io.PrintStream;
 import java.net.URISyntaxException;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.setup.auth.AuthenticateAccountStage;
 import org.mozilla.gecko.sync.setup.auth.AuthenticateAccountStage.AuthenticateAccountStageDelegate;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
 import ch.boye.httpclientandroidlib.HttpResponse;
 
 /**
  * Tests the authentication request stage of manual Account setup.
  * @author liuche
  *
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestAccountAuthenticatorStage {
   private static final int TEST_PORT      = HTTPServerTestHelper.getTestPort();
   private static final String TEST_SERVER = "http://localhost:" + TEST_PORT;
 
   private static final String USERNAME  = "john-hashed";
   private static final String PASSWORD  = "password";
 
   private MockServer authServer;
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestBackoff.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestBackoff.java
@@ -2,30 +2,33 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.android.sync.net.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.MockGlobalSession;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.ProtocolVersion;
 import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
 import ch.boye.httpclientandroidlib.message.BasicStatusLine;
 
 import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestBackoff {
   private final String TEST_USERNAME            = "johndoe";
   private final String TEST_PASSWORD            = "password";
   private final String TEST_SYNC_KEY            = "abcdeabcdeabcdeabcdeabcdea";
   private final long   TEST_BACKOFF_IN_SECONDS  = 1201;
 
   /**
    * Test that interpretHTTPFailure calls requestBackoff if
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestBrowserIDAuthHeaderProvider.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestBrowserIDAuthHeaderProvider.java
@@ -1,20 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.android.sync.net.test;
 
 import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.net.BrowserIDAuthHeaderProvider;
 
 import ch.boye.httpclientandroidlib.Header;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestBrowserIDAuthHeaderProvider {
   @Test
   public void testHeader() {
     Header header = new BrowserIDAuthHeaderProvider("assertion").getAuthHeader(null, null, null);
 
     assertEquals("authorization", header.getName().toLowerCase());
     assertEquals("BrowserID assertion", header.getValue());
   }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java
@@ -16,16 +16,17 @@ import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 import org.json.simple.parser.ParseException;
 import org.junit.After;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.android.sync.test.helpers.MockSyncClientsEngineStage;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.testhelpers.CommandHelpers;
 import org.mozilla.gecko.background.testhelpers.MockClientsDataDelegate;
 import org.mozilla.gecko.background.testhelpers.MockClientsDatabaseAccessor;
@@ -46,21 +47,23 @@ import org.mozilla.gecko.sync.crypto.Key
 import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
 import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 import org.mozilla.gecko.sync.repositories.NullCursorException;
 import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
 import ch.boye.httpclientandroidlib.HttpStatus;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestClientsEngineStage extends MockSyncClientsEngineStage {
   public final static String LOG_TAG = "TestClientsEngSta";
 
   public TestClientsEngineStage() throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, ParseException, CryptoException, URISyntaxException {
     super();
     session = initializeSession();
   }
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestCredentialsEndToEnd.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestCredentialsEndToEnd.java
@@ -5,27 +5,30 @@ package org.mozilla.android.sync.net.tes
 
 import static org.junit.Assert.assertEquals;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 
 import ch.boye.httpclientandroidlib.Header;
+import org.robolectric.RobolectricGradleTestRunner;
 
 /**
  * Test the transfer of a UTF-8 string from desktop, and ensure that it results in the
  * correct hashed Basic Auth header.
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestCredentialsEndToEnd {
 
   public static final String REAL_PASSWORD         = "pïgéons1";
   public static final String USERNAME              = "utvm3mk6hnngiir2sp4jsxf2uvoycrv6";
   public static final String DESKTOP_PASSWORD_JSON = "{\"password\":\"pïgéons1\"}";
   public static final String BTOA_PASSWORD         = "cMOvZ8Opb25zMQ==";
   public static final int    DESKTOP_ASSERTED_SIZE = 10;
   public static final String DESKTOP_BASIC_AUTH    = "Basic dXR2bTNtazZobm5naWlyMnNwNGpzeGYydXZveWNydjY6cMOvZ8Opb25zMQ==";
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestGlobalSession.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestGlobalSession.java
@@ -17,16 +17,17 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import junit.framework.AssertionFailedError;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
 import org.mozilla.android.sync.test.helpers.MockResourceDelegate;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.MockAbstractNonRepositorySyncStage;
 import org.mozilla.gecko.background.testhelpers.MockGlobalSession;
 import org.mozilla.gecko.background.testhelpers.MockPrefsGlobalSession;
 import org.mozilla.gecko.background.testhelpers.MockServerSyncStage;
@@ -45,24 +46,26 @@ import org.mozilla.gecko.sync.crypto.Key
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
 import org.mozilla.gecko.sync.stage.AndroidBrowserBookmarksServerSyncStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
 import org.mozilla.gecko.sync.stage.NoSuchStageException;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.ProtocolVersion;
 import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
 import ch.boye.httpclientandroidlib.message.BasicStatusLine;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestGlobalSession {
   private int          TEST_PORT                = HTTPServerTestHelper.getTestPort();
   private final String TEST_CLUSTER_URL         = "http://localhost:" + TEST_PORT;
   private final String TEST_USERNAME            = "johndoe";
   private final String TEST_PASSWORD            = "password";
   private final String TEST_SYNC_KEY            = "abcdeabcdeabcdeabcdeabcdea";
   private final long   TEST_BACKOFF_IN_SECONDS  = 2401;
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestHeaderParsing.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestHeaderParsing.java
@@ -1,18 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.android.sync.net.test;
 
 import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.Utils;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestHeaderParsing {
 
   @SuppressWarnings("static-method")
   @Test
   public void testDecimalSecondsToMilliseconds() {
     assertEquals(Utils.decimalSecondsToMilliseconds(""),         -1);
     assertEquals(Utils.decimalSecondsToMilliseconds("1234.1.1"), -1);
     assertEquals(Utils.decimalSecondsToMilliseconds("1234"),     1234000);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestLineByLineHandling.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestLineByLineHandling.java
@@ -8,27 +8,30 @@ import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.io.PrintStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.SyncStorageCollectionRequest;
 import org.mozilla.gecko.sync.net.SyncStorageCollectionRequestDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestLineByLineHandling {
   private static final int     TEST_PORT   = HTTPServerTestHelper.getTestPort();
   private static final String  TEST_SERVER = "http://localhost:" + TEST_PORT;
   private static final String  LOG_TAG     = "TestLineByLineHandling";
   static String                STORAGE_URL = TEST_SERVER + "/1.1/c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd/storage/lines";
   private HTTPServerTestHelper data        = new HTTPServerTestHelper();
 
   public ArrayList<String>     lines       = new ArrayList<String>();
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestMetaGlobal.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestMetaGlobal.java
@@ -11,29 +11,32 @@ import static org.junit.Assert.assertTru
 
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.MetaGlobal;
 import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestMetaGlobal {
   public static Object monitor = new Object();
 
   private static final int    TEST_PORT    = HTTPServerTestHelper.getTestPort();
   private static final String TEST_SERVER  = "http://localhost:" + TEST_PORT;
   private static final String TEST_SYNC_ID = "foobar";
 
   public static final String USER_PASS = "c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd:password";
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestResource.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestResource.java
@@ -7,26 +7,29 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.net.URISyntaxException;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockResourceDelegate;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.HttpResponseObserver;
 
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestResource {
   private static final int    TEST_PORT   = HTTPServerTestHelper.getTestPort();
   private static final String TEST_SERVER = "http://localhost:" + TEST_PORT;
 
   private HTTPServerTestHelper data     = new HTTPServerTestHelper();
 
   @SuppressWarnings("static-method")
   @Before
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestRetryAfter.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestRetryAfter.java
@@ -1,24 +1,27 @@
 package org.mozilla.android.sync.net.test;
 
 import java.util.Date;
 
 import org.junit.Test;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.net.SyncResponse;
 
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.ProtocolVersion;
 import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
 import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
 import ch.boye.httpclientandroidlib.message.BasicStatusLine;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestRetryAfter {
   private int TEST_SECONDS = 120;
 
   @Test
   public void testRetryAfterParsesSeconds() {
     final HttpResponse response = new BasicHttpResponse(
         new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 503, "Illegal method/protocol"));
     response.addHeader("Retry-After", Long.toString(TEST_SECONDS)); // Retry-After given in seconds.
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestServer11Repository.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestServer11Repository.java
@@ -3,19 +3,22 @@
 
 package org.mozilla.android.sync.net.test;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.repositories.Server11Repository;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestServer11Repository {
 
   private static final String COLLECTION = "bookmarks";
   private static final String COLLECTION_URL = "http://foo.com/1.1/n6ec3u5bee3tixzp2asys7bs6fve4jfw/storage";
 
   protected final InfoCollections infoCollections = new InfoCollections();
 
   public static void assertQueryEquals(String expected, URI u) {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestSyncStorageRequest.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestSyncStorageRequest.java
@@ -8,27 +8,30 @@ import static org.junit.Assert.assertTru
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 
 import org.json.simple.JSONObject;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.BaseTestStorageRequestDelegate;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSyncStorageRequest {
   private static final int    TEST_PORT   = HTTPServerTestHelper.getTestPort();
   private static final String TEST_SERVER = "http://localhost:" + TEST_PORT;
 
   private static final String LOCAL_META_URL  = TEST_SERVER + "/1.1/c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd/storage/meta/global";
   private static final String LOCAL_BAD_REQUEST_URL  = TEST_SERVER + "/1.1/c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd/storage/bad";
 
   private static final String EXPECTED_ERROR_CODE = "12";
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCollectionKeys.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCollectionKeys.java
@@ -12,24 +12,27 @@ import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Set;
 
 import org.json.simple.JSONArray;
 import org.json.simple.parser.ParseException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.sync.CollectionKeys;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.NoCollectionKeysSetException;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestCollectionKeys {
 
   @Test
   public void testDefaultKeys() throws CryptoException, NoCollectionKeysSetException {
     CollectionKeys ck = new CollectionKeys();
     try {
       ck.defaultKeyBundle();
       fail("defaultKeys should throw.");
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCommandProcessor.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCommandProcessor.java
@@ -10,22 +10,25 @@ import static org.junit.Assert.assertNul
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.CommandProcessor;
 import org.mozilla.gecko.sync.CommandRunner;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.NonObjectJSONException;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestCommandProcessor extends CommandProcessor {
 
   public static final String commandType = "displayURI";
   public static final String commandWithNoArgs = "{\"command\":\"displayURI\"}";
   public static final String commandWithNoType = "{\"args\":[\"https://bugzilla.mozilla.org/show_bug.cgi?id=731341\",\"PKsljsuqYbGg\"]}";
   public static final String wellFormedCommand = "{\"args\":[\"https://bugzilla.mozilla.org/show_bug.cgi?id=731341\",\"PKsljsuqYbGg\"],\"command\":\"displayURI\"}";
   public static final String wellFormedCommandWithNullArgs = "{\"args\":[\"https://bugzilla.mozilla.org/show_bug.cgi?id=731341\",null,\"PKsljsuqYbGg\",null],\"command\":\"displayURI\"}";
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCryptoRecord.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestCryptoRecord.java
@@ -11,27 +11,30 @@ import static org.junit.Assert.assertTru
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 import org.json.simple.parser.ParseException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestCryptoRecord {
   String base64EncryptionKey = "9K/wLdXdw+nrTtXo4ZpECyHFNr4d7aYHqeg3KW9+m6Q=";
   String base64HmacKey = "MMntEfutgLTc8FlTLQFms8/xMPmCldqPlq/QQXEjx70=";
 
   @Test
   public void testBaseCryptoRecordEncrypt() throws IOException, ParseException, NonObjectJSONException, CryptoException {
     ExtendedJSONObject clearPayload = ExtendedJSONObject.parseJSONObject("{\"id\":\"5qRsgXWRJZXr\",\"title\":\"Index of file:///Users/jason/Library/Application Support/Firefox/Profiles/ksgd7wpk.LocalSyncServer/weave/logs/\",\"histUri\":\"file:///Users/jason/Library/Application%20Support/Firefox/Profiles/ksgd7wpk.LocalSyncServer/weave/logs/\",\"visits\":[{\"type\":1,\"date\":1319149012372425}]}");
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestJPakeSetup.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestJPakeSetup.java
@@ -8,32 +8,35 @@ import static org.junit.Assert.fail;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.jpake.Gx3OrGx4IsZeroOrOneException;
 import org.mozilla.gecko.sync.jpake.IncorrectZkpException;
 import org.mozilla.gecko.sync.jpake.JPakeClient;
 import org.mozilla.gecko.sync.jpake.JPakeCrypto;
 import org.mozilla.gecko.sync.jpake.JPakeNumGenerator;
 import org.mozilla.gecko.sync.jpake.JPakeNumGeneratorRandom;
 import org.mozilla.gecko.sync.jpake.JPakeParty;
 import org.mozilla.gecko.sync.jpake.stage.ComputeKeyVerificationStage;
 import org.mozilla.gecko.sync.jpake.stage.VerifyPairingStage;
 import org.mozilla.gecko.sync.setup.Constants;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestJPakeSetup {
   // Note: will throw NullPointerException if aborts. Only use stateless public
   // methods.
 
   @Test
   public void testGx3OrGx4ZeroOrOneThrowsException()
       throws UnsupportedEncodingException
   {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestRecord.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestRecord.java
@@ -10,27 +10,30 @@ import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
 
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 import org.json.simple.parser.ParseException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.db.Tab;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 import org.mozilla.gecko.sync.repositories.domain.RecordParseException;
 import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestRecord {
 
   @SuppressWarnings("static-method")
   @Test
   public void testQueryRecord() throws NonObjectJSONException, IOException, ParseException {
     final String expectedGUID = "Bl3n3gpKag3s";
     final String testRecord =
         "{\"id\":\"" + expectedGUID + "\"," +
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestRecordsChannel.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestRecordsChannel.java
@@ -6,30 +6,33 @@ package org.mozilla.android.sync.test;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.SynchronizerHelpers.FailFetchWBORepository;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionCreationDelegate;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionFinishDelegate;
 import org.mozilla.gecko.background.testhelpers.WBORepository;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.RepositorySession;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
 import org.mozilla.gecko.sync.synchronizer.RecordsChannel;
 import org.mozilla.gecko.sync.synchronizer.RecordsChannelDelegate;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestRecordsChannel {
 
   protected WBORepository remote;
   protected WBORepository local;
 
   protected RepositorySession source;
   protected RepositorySession sink;
   protected RecordsChannelDelegate rcDelegate;
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestResetCommands.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestResetCommands.java
@@ -7,16 +7,17 @@ import static org.junit.Assert.assertFal
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.util.HashMap;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.DefaultGlobalSessionCallback;
 import org.mozilla.gecko.background.testhelpers.MockPrefsGlobalSession;
 import org.mozilla.gecko.background.testhelpers.MockServerSyncStage;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.CommandProcessor;
 import org.mozilla.gecko.sync.EngineSettings;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
@@ -28,20 +29,22 @@ import org.mozilla.gecko.sync.SyncConfig
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
 
 import android.content.SharedPreferences;
+import org.robolectric.RobolectricGradleTestRunner;
 
 /**
  * Test that reset commands properly invoke the reset methods on the correct stage.
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestResetCommands {
   private static final String TEST_USERNAME    = "johndoe";
   private static final String TEST_PASSWORD    = "password";
   private static final String TEST_SYNC_KEY    = "abcdeabcdeabcdeabcdeabcdea";
 
   public static void performNotify() {
     WaitHelper.getTestWaiter().performNotify();
   }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestServer11RepositorySession.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestServer11RepositorySession.java
@@ -8,16 +8,17 @@ import static org.junit.Assert.assertNot
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.SynchronizerHelpers.TrackingWBORepository;
 import org.mozilla.android.sync.test.helpers.BaseTestStorageRequestDelegate;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.MockRecord;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.JSONRecordFetcher;
@@ -37,22 +38,24 @@ import org.mozilla.gecko.sync.repositori
 import org.mozilla.gecko.sync.repositories.StoreFailedException;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecordFactory;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 import org.mozilla.gecko.sync.stage.SafeConstrainedServer11Repository;
 import org.mozilla.gecko.sync.synchronizer.ServerLocalSynchronizer;
 import org.mozilla.gecko.sync.synchronizer.Synchronizer;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.ContentType;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
 import ch.boye.httpclientandroidlib.HttpEntity;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestServer11RepositorySession {
 
   public class POSTMockServer extends MockServer {
     @Override
     public void handle(Request request, Response response) {
       try {
         String content = request.getContent();
         System.out.println("Content:" + content);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestServerLocalSynchronizer.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestServerLocalSynchronizer.java
@@ -5,16 +5,17 @@ package org.mozilla.android.sync.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import java.util.ArrayList;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.SynchronizerHelpers.BatchFailStoreWBORepository;
 import org.mozilla.android.sync.test.SynchronizerHelpers.BeginErrorWBORepository;
 import org.mozilla.android.sync.test.SynchronizerHelpers.BeginFailedException;
 import org.mozilla.android.sync.test.SynchronizerHelpers.FailFetchWBORepository;
 import org.mozilla.android.sync.test.SynchronizerHelpers.FinishErrorWBORepository;
 import org.mozilla.android.sync.test.SynchronizerHelpers.FinishFailedException;
 import org.mozilla.android.sync.test.SynchronizerHelpers.SerialFailStoreWBORepository;
 import org.mozilla.android.sync.test.SynchronizerHelpers.TrackingWBORepository;
@@ -22,17 +23,19 @@ import org.mozilla.gecko.background.comm
 import org.mozilla.gecko.background.testhelpers.WBORepository;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.repositories.FetchFailedException;
 import org.mozilla.gecko.sync.repositories.StoreFailedException;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
 import org.mozilla.gecko.sync.synchronizer.ServerLocalSynchronizer;
 import org.mozilla.gecko.sync.synchronizer.Synchronizer;
 import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestServerLocalSynchronizer {
   public static final String LOG_TAG = "TestServLocSync";
 
   protected Synchronizer getSynchronizer(WBORepository remote, WBORepository local) {
     BookmarkRecord[] inbounds = new BookmarkRecord[] {
         new BookmarkRecord("inboundSucc1", "bookmarks", 1, false),
         new BookmarkRecord("inboundSucc2", "bookmarks", 1, false),
         new BookmarkRecord("inboundFail1", "bookmarks", 1, false),
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSyncConfiguration.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSyncConfiguration.java
@@ -2,20 +2,23 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.android.sync.test;
 
 import java.net.URI;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 import org.mozilla.gecko.sync.Sync11Configuration;
 import org.mozilla.gecko.sync.SyncConfiguration;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSyncConfiguration {
   @Test
   public void testURLs() throws Exception {
     final MockSharedPreferences prefs = new MockSharedPreferences();
 
     // N.B., the username isn't used in the cluster path.
     SyncConfiguration fxaConfig = new SyncConfiguration("username", null, prefs);
     fxaConfig.clusterURL = new URI("http://db1.oldsync.dev.lcip.org/1.1/174");
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSyncKeyVerification.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSyncKeyVerification.java
@@ -2,19 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.android.sync.test;
 
 import static org.junit.Assert.fail;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.setup.InvalidSyncKeyException;
 import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSyncKeyVerification {
 
   private int[] mutateIndices;
   private final String validBasicKey = "abcdefghijkmnpqrstuvwxyz23"; // 26 char, valid characters.
   char[] invalidChars = new char[] { '1', 'l', 'o', '0' };
 
   @Before
   public void setUp() {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSynchronizer.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSynchronizer.java
@@ -10,29 +10,32 @@ import static org.junit.Assert.assertNul
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.util.Date;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.SynchronizerHelpers.TrackingWBORepository;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.testhelpers.WBORepository;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
 import org.mozilla.gecko.sync.synchronizer.Synchronizer;
 import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate;
 import org.mozilla.gecko.sync.synchronizer.SynchronizerSession;
 import org.mozilla.gecko.sync.synchronizer.SynchronizerSessionDelegate;
 
 import android.content.Context;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSynchronizer {
   public static final String LOG_TAG = "TestSynchronizer";
 
   public static void assertInRangeInclusive(long earliest, long value, long latest) {
     assertTrue(earliest <= value);
     assertTrue(latest   >= value);
   }
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSynchronizerSession.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestSynchronizerSession.java
@@ -11,31 +11,34 @@ import static org.junit.Assert.assertTru
 import static org.junit.Assert.fail;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.SynchronizerHelpers.DataAvailableWBORepository;
 import org.mozilla.android.sync.test.SynchronizerHelpers.ShouldSkipWBORepository;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.testhelpers.WBORepository;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.SynchronizerConfiguration;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 import org.mozilla.gecko.sync.synchronizer.Synchronizer;
 import org.mozilla.gecko.sync.synchronizer.SynchronizerSession;
 import org.mozilla.gecko.sync.synchronizer.SynchronizerSessionDelegate;
 
 import android.content.Context;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSynchronizerSession {
   public static final String LOG_TAG = TestSynchronizerSession.class.getSimpleName();
 
   protected static void assertFirstContainsSecond(Map<String, Record> first, Map<String, Record> second) {
     for (Entry<String, Record> entry : second.entrySet()) {
       assertTrue("Expected key " + entry.getKey(), first.containsKey(entry.getKey()));
       Record record = first.get(entry.getKey());
       assertEquals(entry.getValue(), record);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestUtils.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/TestUtils.java
@@ -8,19 +8,22 @@ import static org.junit.Assert.assertTru
 
 import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.SyncConstants;
 import org.mozilla.gecko.sync.Utils;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestUtils extends Utils {
 
   @Test
   public void testGenerateGUID() {
     for (int i = 0; i < 1000; ++i) {
       assertEquals(12, Utils.generateGuid().length());
     }
   }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/helpers/test/TestHTTPServerTestHelper.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/test/helpers/test/TestHTTPServerTestHelper.java
@@ -8,20 +8,23 @@ import static org.junit.Assert.assertNot
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper.HTTPServerAlreadyRunningError;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestHTTPServerTestHelper {
   public static final int TEST_PORT = HTTPServerTestHelper.getTestPort();
 
   protected MockServer mockServer = new MockServer();
 
   @Test
   public void testStartStop() {
     // Need to be able to start and stop multiple times.
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/common/log/writers/test/TestLogWriters.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/common/log/writers/test/TestLogWriters.java
@@ -9,26 +9,29 @@ import static org.junit.Assert.assertTru
 
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.common.log.writers.LevelFilteringLogWriter;
 import org.mozilla.gecko.background.common.log.writers.LogWriter;
 import org.mozilla.gecko.background.common.log.writers.PrintLogWriter;
 import org.mozilla.gecko.background.common.log.writers.SimpleTagLogWriter;
 import org.mozilla.gecko.background.common.log.writers.StringLogWriter;
 import org.mozilla.gecko.background.common.log.writers.ThreadLocalTagLogWriter;
 
 import android.util.Log;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestLogWriters {
 
   public static final String TEST_LOG_TAG_1 = "TestLogTag1";
   public static final String TEST_LOG_TAG_2 = "TestLogTag2";
 
   public static final String TEST_MESSAGE_1  = "LOG TEST MESSAGE one";
   public static final String TEST_MESSAGE_2  = "LOG TEST MESSAGE two";
   public static final String TEST_MESSAGE_3  = "LOG TEST MESSAGE three";
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountAgeLockoutHelper.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountAgeLockoutHelper.java
@@ -2,19 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.background.fxa.test;
 
 import java.util.Calendar;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
 import org.mozilla.gecko.fxa.FxAccountConstants;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestFxAccountAgeLockoutHelper {
   @Test
   public void testPassesAgeCheck() {
     Calendar today = Calendar.getInstance();
     int birthMonthIndex = today.get(Calendar.MONTH);
     int birthDate = today.get(Calendar.DATE);
     int birthYear = today.get(Calendar.YEAR) - FxAccountConstants.MINIMUM_AGE_TO_CREATE_AN_ACCOUNT;
     Assert.assertTrue("Minimum age as of today",
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountClient20.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountClient20.java
@@ -6,19 +6,22 @@ package org.mozilla.gecko.background.fxa
 import java.io.UnsupportedEncodingException;
 import java.net.URISyntaxException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 import junit.framework.Assert;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.fxa.FxAccountClient20;
 import org.mozilla.gecko.sync.net.BaseResource;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestFxAccountClient20 {
   protected static class MockFxAccountClient20 extends FxAccountClient20 {
     public MockFxAccountClient20(String serverURI, Executor executor) {
       super(serverURI, executor);
     }
 
     // Public for testing.
     @Override
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountUtils.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/fxa/test/TestFxAccountUtils.java
@@ -2,28 +2,31 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.background.fxa.test;
 
 import java.math.BigInteger;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.net.SRPConstants;
+import org.robolectric.RobolectricGradleTestRunner;
 
 /**
  * Test vectors from
  * <a href="https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#stretch-KDF">https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#stretch-KDF</a>
  * and
  * <a href="https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol/5a9bc81e499306d769ca19b40b50fa60123df15d">https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol/5a9bc81e499306d769ca19b40b50fa60123df15d</a>.
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestFxAccountUtils {
   protected static void assertEncoding(String base16String, String utf8String) throws Exception {
     Assert.assertEquals(base16String, FxAccountUtils.bytes(utf8String));
   }
 
   @Test
   public void testUTF8Encoding() throws Exception {
     assertEncoding("616e6472c3a9406578616d706c652e6f7267", "andré@example.org");
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/prune/test/TestPrunePolicy.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/prune/test/TestPrunePolicy.java
@@ -4,23 +4,26 @@
 package org.mozilla.gecko.background.healthreport.prune.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
 import org.mozilla.gecko.background.healthreport.prune.PrunePolicy;
 import org.mozilla.gecko.background.healthreport.prune.PrunePolicyStorage;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 
 import android.content.SharedPreferences;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestPrunePolicy {
   public static class MockPrunePolicy extends PrunePolicy {
     public MockPrunePolicy(final PrunePolicyStorage storage, final SharedPreferences sharedPrefs) {
       super(storage, sharedPrefs);
     }
 
     @Override
     public boolean attemptPruneBySize(final long time) {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestObsoleteDocumentTracker.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestObsoleteDocumentTracker.java
@@ -12,23 +12,26 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
 import org.mozilla.gecko.background.healthreport.upload.ObsoleteDocumentTracker;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 
 import android.content.SharedPreferences;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestObsoleteDocumentTracker {
   public static class MockObsoleteDocumentTracker extends ObsoleteDocumentTracker {
     public MockObsoleteDocumentTracker(SharedPreferences sharedPrefs) {
       super(sharedPrefs);
     }
 
     @Override
     public ExtendedJSONObject getObsoleteIds() {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionPolicy.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionPolicy.java
@@ -9,26 +9,29 @@ import static org.junit.Assert.assertNot
 import static org.junit.Assert.assertTrue;
 
 import java.net.UnknownHostException;
 import java.util.Collection;
 import java.util.HashSet;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
 import org.mozilla.gecko.background.healthreport.upload.SubmissionClient;
 import org.mozilla.gecko.background.healthreport.upload.SubmissionPolicy;
 import org.mozilla.gecko.background.healthreport.upload.test.TestObsoleteDocumentTracker.MockObsoleteDocumentTracker;
 import org.mozilla.gecko.background.healthreport.upload.test.TestSubmissionPolicy.MockSubmissionClient.Response;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 
 import android.content.SharedPreferences;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSubmissionPolicy {
   public static class MockSubmissionClient implements SubmissionClient {
     public String lastId = null;
     public Collection<String> lastOldIds = null;
 
     public enum Response { SUCCESS, SOFT_FAILURE, HARD_FAILURE };
     public Response upload = Response.SUCCESS;
     public Response delete = Response.SUCCESS;
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionsTracker.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionsTracker.java
@@ -7,21 +7,24 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.fail;
 
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.HashSet;
 
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.healthreport.upload.AndroidSubmissionClient.SubmissionsFieldName;
 import org.mozilla.gecko.background.healthreport.upload.test.MockAndroidSubmissionClient;
 import org.mozilla.gecko.background.healthreport.upload.test.MockAndroidSubmissionClient.MockHealthReportStorage;
 import org.mozilla.gecko.background.healthreport.upload.test.MockAndroidSubmissionClient.MockSubmissionsTracker;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSubmissionsTracker {
   protected static class MockHealthReportStorage2 extends MockHealthReportStorage {
     public final int FIRST_ATTEMPT_ID = SubmissionsFieldName.FIRST_ATTEMPT.getID(this);
     public final int CONTINUATION_ATTEMPT_ID = SubmissionsFieldName.CONTINUATION_ATTEMPT.getID(this);
 
     public final int SUCCESS_ID = SubmissionsFieldName.SUCCESS.getID(this);
     public final int CLIENT_FAILURE_ID = SubmissionsFieldName.CLIENT_FAILURE.getID(this);
     public final int TRANSPORT_FAILURE_ID = SubmissionsFieldName.TRANSPORT_FAILURE.getID(this);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestTrackingRequestDelegate.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestTrackingRequestDelegate.java
@@ -8,22 +8,25 @@ import static org.junit.Assert.assertTru
 
 import java.io.UnsupportedEncodingException;
 import java.net.URISyntaxException;
 import java.util.HashSet;
 
 import org.junit.Before;
 import org.junit.Test;
 
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.healthreport.HealthReportStorage;
 import org.mozilla.gecko.background.healthreport.upload.AndroidSubmissionClient.SubmissionsTracker.TrackingRequestDelegate;
 import org.mozilla.gecko.background.healthreport.upload.test.MockAndroidSubmissionClient;
 import org.mozilla.gecko.background.healthreport.upload.test.MockAndroidSubmissionClient.MockHealthReportStorage;
 import org.mozilla.gecko.background.testhelpers.StubDelegate;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestTrackingRequestDelegate {
   public static class MockAndroidSubmissionClient2 extends MockAndroidSubmissionClient {
     public MockAndroidSubmissionClient2() {
       super(null, null, null);
     }
 
     @Override
     public void setLastUploadLocalTimeAndDocumentId(long localTime, String id) { /* Do nothing. */ }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestBoundedByteArrayEntity.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestBoundedByteArrayEntity.java
@@ -3,20 +3,23 @@
 
 package org.mozilla.gecko.background.test;
 
 import java.io.IOException;
 import java.util.Arrays;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.bagheera.BoundedByteArrayEntity;
 
 import ch.boye.httpclientandroidlib.HttpEntity;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestBoundedByteArrayEntity {
   private static void expectFail(byte[] input, int start, int end) {
     try {
       new BoundedByteArrayEntity(input, start, end);
       Assert.fail("Should have thrown.");
     } catch (Exception ex) {
       return;
     }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestDeflation.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestDeflation.java
@@ -5,21 +5,24 @@ package org.mozilla.gecko.background.tes
 
 import java.util.Arrays;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
 import junit.framework.Assert;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.bagheera.DeflateHelper;
 import org.mozilla.gecko.background.common.log.Logger;
 
 import ch.boye.httpclientandroidlib.HttpEntity;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestDeflation {
   public static final String TEST_BODY_A = "";
   public static final String TEST_BODY_B = "éíôü}ABCDEFGHaaQRSTUVWXYZá{Zá{";
   public static final String TEST_BODY_C = "{}\n";
   public static final String TEST_BODY_D =
       "{éíôü}ABCDEFGHaaQRSTUVWXYZá{Zá{éíôü}ABCDEFGHaaQRSTUVWXYZá{Zá{éíôü}A" +
       "BCDEFGHaaQRSTUVWXYZá{Zá{éíôü}ABCDEFGHaaQRSTUVWXYZá{Zá{éíôü}ABCDEFGH" +
       "aQRSTUVWXYZá{Zá{éíôü}ABCDEFGHaaQRSTUVWXYZá{Zá{}\n";
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/BaseMockServerSyncStage.java
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.json.simple.parser.ParseException;
+import org.mozilla.gecko.sync.NoCollectionKeysSetException;
+import org.mozilla.gecko.sync.NonObjectJSONException;
+import org.mozilla.gecko.sync.SynchronizerConfiguration;
+import org.mozilla.gecko.sync.repositories.RecordFactory;
+import org.mozilla.gecko.sync.repositories.Repository;
+import org.mozilla.gecko.sync.stage.ServerSyncStage;
+
+/**
+ * A stage that joins two Repositories with no wrapping.
+ */
+public abstract class BaseMockServerSyncStage extends ServerSyncStage {
+
+  public Repository local;
+  public Repository remote;
+  public String name;
+  public String collection;
+  public int version = 1;
+
+  @Override
+  public boolean isEnabled() {
+    return true;
+  }
+
+  @Override
+  protected String getCollection() {
+    return collection;
+  }
+
+  @Override
+  protected Repository getLocalRepository() {
+    return local;
+  }
+
+  @Override
+  protected Repository getRemoteRepository() throws URISyntaxException {
+    return remote;
+  }
+
+  @Override
+  protected String getEngineName() {
+    return name;
+  }
+
+  @Override
+  public Integer getStorageVersion() {
+    return version;
+  }
+
+  @Override
+  protected RecordFactory getRecordFactory() {
+    return null;
+  }
+
+  @Override
+  protected Repository wrappedServerRepo()
+  throws NoCollectionKeysSetException, URISyntaxException {
+    return getRemoteRepository();
+  }
+
+  public SynchronizerConfiguration leakConfig()
+  throws NonObjectJSONException, IOException, ParseException {
+    return this.getConfig();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/CommandHelpers.java
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import org.json.simple.JSONArray;
+import org.mozilla.gecko.sync.CommandProcessor.Command;
+
+public class CommandHelpers {
+
+  @SuppressWarnings("unchecked")
+  public static Command getCommand1() {
+    JSONArray args = new JSONArray();
+    args.add("argsA");
+    return new Command("displayURI", args);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static Command getCommand2() {
+    JSONArray args = new JSONArray();
+    args.add("argsB");
+    return new Command("displayURI", args);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static Command getCommand3() {
+    JSONArray args = new JSONArray();
+    args.add("argsC");
+    return new Command("displayURI", args);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static Command getCommand4() {
+    JSONArray args = new JSONArray();
+    args.add("URI of Page");
+    args.add("Sender ID");
+    args.add("Title of Page");
+    return new Command("displayURI", args);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/DefaultGlobalSessionCallback.java
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import java.net.URI;
+
+import org.mozilla.gecko.sync.GlobalSession;
+import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
+import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
+
+public class DefaultGlobalSessionCallback implements GlobalSessionCallback {
+
+  @Override
+  public void requestBackoff(long backoff) {
+  }
+
+  @Override
+  public boolean wantNodeAssignment() {
+    return false;
+  }
+
+  @Override
+  public void informUnauthorizedResponse(GlobalSession globalSession,
+                                         URI oldClusterURL) {
+  }
+
+  @Override
+  public void informNodeAssigned(GlobalSession globalSession,
+                                 URI oldClusterURL, URI newClusterURL) {
+  }
+
+  @Override
+  public void informNodeAuthenticationFailed(GlobalSession globalSession,
+                                             URI failedClusterURL) {
+  }
+
+  @Override
+  public void informUpgradeRequiredResponse(GlobalSession session) {
+  }
+
+  @Override
+  public void informMigrated(GlobalSession globalSession) {
+  }
+
+  @Override
+  public void handleAborted(GlobalSession globalSession, String reason) {
+  }
+
+  @Override
+  public void handleError(GlobalSession globalSession, Exception ex) {
+  }
+
+  @Override
+  public void handleSuccess(GlobalSession globalSession) {
+  }
+
+  @Override
+  public void handleStageCompleted(Stage currentState,
+                                   GlobalSession globalSession) {
+  }
+
+  @Override
+  public boolean shouldBackOffStorage() {
+    return false;
+  }
+
+  @Override
+  public String nodeWeaveURL() {
+    return null;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/JPakeNumGeneratorFixed.java
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import java.math.BigInteger;
+
+import org.mozilla.gecko.sync.jpake.JPakeNumGenerator;
+
+public class JPakeNumGeneratorFixed implements JPakeNumGenerator {
+  private String[] values;
+  private int index = 0;
+
+  public JPakeNumGeneratorFixed(String[] values) {
+    this.values = values;
+  }
+
+  @Override
+  public BigInteger generateFromRange(BigInteger r) {
+    BigInteger ret = new BigInteger(values[index], 16).mod(r);
+    index = (++index) % values.length;
+    return ret;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockAbstractNonRepositorySyncStage.java
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import org.mozilla.gecko.sync.stage.AbstractNonRepositorySyncStage;
+
+public class MockAbstractNonRepositorySyncStage extends AbstractNonRepositorySyncStage {
+  @Override
+  public void execute() {
+    session.advance();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockClientsDataDelegate.java
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import org.mozilla.gecko.sync.Utils;
+import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
+
+public class MockClientsDataDelegate implements ClientsDataDelegate {
+  private String accountGUID;
+  private String clientName;
+  private int clientsCount;
+  private long clientDataTimestamp = 0;
+
+  @Override
+  public synchronized String getAccountGUID() {
+    if (accountGUID == null) {
+      accountGUID = Utils.generateGuid();
+    }
+    return accountGUID;
+  }
+
+  @Override
+  public synchronized String getDefaultClientName() {
+    return "Default client";
+  }
+
+  @Override
+  public synchronized void setClientName(String clientName, long now) {
+    this.clientName = clientName;
+    this.clientDataTimestamp = now;
+  }
+
+  @Override
+  public synchronized String getClientName() {
+    if (clientName == null) {
+      setClientName(getDefaultClientName(), System.currentTimeMillis());
+    }
+    return clientName;
+  }
+
+  @Override
+  public synchronized void setClientsCount(int clientsCount) {
+    this.clientsCount = clientsCount;
+  }
+
+  @Override
+  public synchronized int getClientsCount() {
+    return clientsCount;
+  }
+
+  @Override
+  public synchronized boolean isLocalGUID(String guid) {
+    return getAccountGUID().equals(guid);
+  }
+
+  @Override
+  public synchronized long getLastModifiedTimestamp() {
+    return clientDataTimestamp;
+  }
+
+  @Override
+  public String getFormFactor() {
+    return "phone";
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockClientsDatabaseAccessor.java
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.mozilla.gecko.sync.CommandProcessor.Command;
+import org.mozilla.gecko.sync.repositories.NullCursorException;
+import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
+import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
+
+public class MockClientsDatabaseAccessor extends ClientsDatabaseAccessor {
+  public boolean storedRecord = false;
+  public boolean dbWiped = false;
+  public boolean clientsTableWiped = false;
+  public boolean closed = false;
+  public boolean storedArrayList = false;
+  public boolean storedCommand;
+
+  @Override
+  public void store(ClientRecord record) {
+    storedRecord = true;
+  }
+
+  @Override
+  public void store(Collection<ClientRecord> records) {
+    storedArrayList = false;
+  }
+
+  @Override
+  public void store(String accountGUID, Command command) throws NullCursorException {
+    storedCommand = true;
+  }
+
+  @Override
+  public ClientRecord fetchClient(String profileID) throws NullCursorException {
+    return null;
+  }
+
+  @Override
+  public Map<String, ClientRecord> fetchAllClients() throws NullCursorException {
+    return null;
+  }
+
+  @Override
+  public List<Command> fetchCommandsForClient(String accountGUID) throws NullCursorException {
+    return null;
+  }
+
+  @Override
+  public int clientsCount() {
+    return 0;
+  }
+
+  @Override
+  public void wipeDB() {
+    dbWiped = true;
+  }
+
+  @Override
+  public void wipeClientsTable() {
+    clientsTableWiped = true;
+  }
+
+  @Override
+  public void close() {
+    closed = true;
+  }
+
+  public void resetVars() {
+    storedRecord = dbWiped = clientsTableWiped = closed = storedArrayList = false;
+  }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockGlobalSession.java
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.json.simple.parser.ParseException;
+import org.mozilla.gecko.sync.EngineSettings;
+import org.mozilla.gecko.sync.NonObjectJSONException;
+import org.mozilla.gecko.sync.SyncConfiguration;
+import org.mozilla.gecko.sync.SyncConfigurationException;
+import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
+import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
+import org.mozilla.gecko.sync.stage.CompletedStage;
+import org.mozilla.gecko.sync.stage.GlobalSyncStage;
+import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
+
+
+public class MockGlobalSession extends MockPrefsGlobalSession {
+
+  public MockGlobalSession(String username, String password, KeyBundle keyBundle, GlobalSessionCallback callback) throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, ParseException {
+    this(new SyncConfiguration(username, new BasicAuthHeaderProvider(username, password), new MockSharedPreferences(), keyBundle), callback);
+  }
+
+  public MockGlobalSession(SyncConfiguration config, GlobalSessionCallback callback)
+          throws SyncConfigurationException, IllegalArgumentException, IOException, ParseException, NonObjectJSONException {
+    super(config, callback, null, null);
+  }
+
+  @Override
+  public boolean isEngineRemotelyEnabled(String engine, EngineSettings engineSettings) {
+    return false;
+  }
+
+  @Override
+  protected void prepareStages() {
+    super.prepareStages();
+    HashMap<Stage, GlobalSyncStage> newStages = new HashMap<Stage, GlobalSyncStage>(this.stages);
+
+    for (Stage stage : this.stages.keySet()) {
+      newStages.put(stage, new MockServerSyncStage());
+    }
+
+    // This signals that the global session is complete.
+    newStages.put(Stage.completed, new CompletedStage());
+
+    this.stages = newStages;
+  }
+
+  public MockGlobalSession withStage(Stage stage, GlobalSyncStage syncStage) {
+    stages.put(stage, syncStage);
+
+    return this;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockPrefsGlobalSession.java
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import java.io.IOException;
+
+import org.json.simple.parser.ParseException;
+import org.mozilla.gecko.sync.GlobalSession;
+import org.mozilla.gecko.sync.NonObjectJSONException;
+import org.mozilla.gecko.sync.SyncConfiguration;
+import org.mozilla.gecko.sync.SyncConfigurationException;
+import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
+import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
+import org.mozilla.gecko.sync.net.AuthHeaderProvider;
+import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * GlobalSession touches the Android prefs system. Stub that out.
+ */
+public class MockPrefsGlobalSession extends GlobalSession {
+
+  public MockSharedPreferences prefs;
+
+  public MockPrefsGlobalSession(
+      SyncConfiguration config, GlobalSessionCallback callback, Context context,
+      ClientsDataDelegate clientsDelegate)
+      throws SyncConfigurationException, IllegalArgumentException, IOException,
+      ParseException, NonObjectJSONException {
+    super(config, callback, context, clientsDelegate, callback);
+  }
+
+  public static MockPrefsGlobalSession getSession(
+      String username, String password,
+      KeyBundle syncKeyBundle, GlobalSessionCallback callback, Context context,
+      ClientsDataDelegate clientsDelegate)
+      throws SyncConfigurationException, IllegalArgumentException, IOException,
+      ParseException, NonObjectJSONException {
+    return getSession(username, new BasicAuthHeaderProvider(username, password), null,
+         syncKeyBundle, callback, context, clientsDelegate);
+  }
+
+  public static MockPrefsGlobalSession getSession(
+      String username, AuthHeaderProvider authHeaderProvider, String prefsPath,
+      KeyBundle syncKeyBundle, GlobalSessionCallback callback, Context context,
+      ClientsDataDelegate clientsDelegate)
+      throws SyncConfigurationException, IllegalArgumentException, IOException,
+      ParseException, NonObjectJSONException {
+
+    final SharedPreferences prefs = new MockSharedPreferences();
+    final SyncConfiguration config = new SyncConfiguration(username, authHeaderProvider, prefs);
+    config.syncKeyBundle = syncKeyBundle;
+    return new MockPrefsGlobalSession(config, callback, context, clientsDelegate);
+  }
+
+  @Override
+  public Context getContext() {
+    return null;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockRecord.java
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.mozilla.gecko.sync.repositories.domain.Record;
+
+public class MockRecord extends Record {
+
+  public MockRecord(String guid, String collection, long lastModified, boolean deleted) {
+    super(guid, collection, lastModified, deleted);
+  }
+
+  @Override
+  protected void populatePayload(ExtendedJSONObject payload) {
+  }
+
+  @Override
+  protected void initFromPayload(ExtendedJSONObject payload) {
+  }
+
+  @Override
+  public Record copyWithIDs(String guid, long androidID) {
+    MockRecord r = new MockRecord(guid, this.collection, this.lastModified, this.deleted);
+    r.androidID = androidID;
+    return r;
+  }
+
+  @Override
+  public String toJSONString() {
+    return "{\"id\":\"" + guid + "\", \"payload\": \"foo\"}";
+  }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockServerSyncStage.java
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+
+public class MockServerSyncStage extends BaseMockServerSyncStage {
+  @Override
+  public void execute() {
+    session.advance();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/MockSharedPreferences.java
@@ -0,0 +1,137 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import android.content.SharedPreferences;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A programmable mock content provider.
+ */
+public class MockSharedPreferences implements SharedPreferences, SharedPreferences.Editor {
+  private HashMap<String, Object> mValues;
+  private HashMap<String, Object> mTempValues;
+
+  public MockSharedPreferences() {
+    mValues = new HashMap<String, Object>();
+    mTempValues = new HashMap<String, Object>();
+  }
+
+  public Editor edit() {
+    return this;
+  }
+
+  public boolean contains(String key) {
+    return mValues.containsKey(key);
+  }
+
+  public Map<String, ?> getAll() {
+    return new HashMap<String, Object>(mValues);
+  }
+
+  public boolean getBoolean(String key, boolean defValue) {
+    if (mValues.containsKey(key)) {
+      return ((Boolean)mValues.get(key)).booleanValue();
+    }
+    return defValue;
+  }
+
+  public float getFloat(String key, float defValue) {
+    if (mValues.containsKey(key)) {
+      return ((Float)mValues.get(key)).floatValue();
+    }
+    return defValue;
+  }
+
+  public int getInt(String key, int defValue) {
+    if (mValues.containsKey(key)) {
+      return ((Integer)mValues.get(key)).intValue();
+    }
+    return defValue;
+  }
+
+  public long getLong(String key, long defValue) {
+    if (mValues.containsKey(key)) {
+      return ((Long)mValues.get(key)).longValue();
+    }
+    return defValue;
+  }
+
+  public String getString(String key, String defValue) {
+    if (mValues.containsKey(key))
+      return (String)mValues.get(key);
+    return defValue;
+  }
+
+  @SuppressWarnings("unchecked")
+  public Set<String> getStringSet(String key, Set<String> defValues) {
+    if (mValues.containsKey(key)) {
+      return (Set<String>) mValues.get(key);
+    }
+    return defValues;
+  }
+
+  public void registerOnSharedPreferenceChangeListener(
+    OnSharedPreferenceChangeListener listener) {
+    throw new UnsupportedOperationException();
+  }
+
+  public void unregisterOnSharedPreferenceChangeListener(
+    OnSharedPreferenceChangeListener listener) {
+    throw new UnsupportedOperationException();
+  }
+
+  public Editor putBoolean(String key, boolean value) {
+    mTempValues.put(key, Boolean.valueOf(value));
+    return this;
+  }
+
+  public Editor putFloat(String key, float value) {
+    mTempValues.put(key, value);
+    return this;
+  }
+
+  public Editor putInt(String key, int value) {
+    mTempValues.put(key, value);
+    return this;
+  }
+
+  public Editor putLong(String key, long value) {
+    mTempValues.put(key, value);
+    return this;
+  }
+
+  public Editor putString(String key, String value) {
+    mTempValues.put(key, value);
+    return this;
+  }
+
+  public Editor putStringSet(String key, Set<String> values) {
+    mTempValues.put(key, values);
+    return this;
+  }
+
+  public Editor remove(String key) {
+    mTempValues.remove(key);
+    return this;
+  }
+
+  public Editor clear() {
+    mTempValues.clear();
+    return this;
+  }
+
+  @SuppressWarnings("unchecked")
+  public boolean commit() {
+    mValues = (HashMap<String, Object>)mTempValues.clone();
+    return true;
+  }
+
+  public void apply() {
+    commit();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/StubDelegate.java
@@ -0,0 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import org.mozilla.gecko.background.healthreport.upload.SubmissionClient.Delegate;
+
+public class StubDelegate implements Delegate {
+  @Override
+  public void onSoftFailure(long localTime, String id, String reason, Exception e) { }
+  @Override
+  public void onHardFailure(long localTime, String id, String reason, Exception e) { }
+  @Override
+  public void onSuccess(long localTime, String id) { }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/WBORepository.java
@@ -0,0 +1,231 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.testhelpers;
+
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.mozilla.gecko.background.common.log.Logger;
+import org.mozilla.gecko.sync.repositories.InactiveSessionException;
+import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
+import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
+import org.mozilla.gecko.sync.repositories.RecordFilter;
+import org.mozilla.gecko.sync.repositories.Repository;
+import org.mozilla.gecko.sync.repositories.StoreTrackingRepositorySession;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
+import org.mozilla.gecko.sync.repositories.domain.Record;
+
+import android.content.Context;
+
+public class WBORepository extends Repository {
+
+  public class WBORepositoryStats {
+    public long created         = -1;
+    public long begun           = -1;
+    public long fetchBegan      = -1;
+    public long fetchCompleted  = -1;
+    public long storeBegan      = -1;
+    public long storeCompleted  = -1;
+    public long finished        = -1;
+  }
+
+  public static final String LOG_TAG = "WBORepository";
+
+  // Access to stats is not guarded.
+  public WBORepositoryStats stats;
+
+  // Whether or not to increment the timestamp of stored records.
+  public final boolean bumpTimestamps;
+
+  public class WBORepositorySession extends StoreTrackingRepositorySession {
+
+    protected WBORepository wboRepository;
+    protected ExecutorService delegateExecutor = Executors.newSingleThreadExecutor();
+    public ConcurrentHashMap<String, Record> wbos;
+
+    public WBORepositorySession(WBORepository repository) {
+      super(repository);
+
+      wboRepository = repository;
+      wbos          = new ConcurrentHashMap<String, Record>();
+      stats         = new WBORepositoryStats();
+      stats.created = now();
+    }
+
+    @Override
+    protected synchronized void trackGUID(String guid) {
+      if (wboRepository.shouldTrack()) {
+        super.trackGUID(guid);
+      }
+    }
+
+    @Override
+    public void guidsSince(long timestamp,
+                           RepositorySessionGuidsSinceDelegate delegate) {
+      throw new RuntimeException("guidsSince not implemented.");
+    }
+
+    @Override
+    public void fetchSince(long timestamp,
+                           RepositorySessionFetchRecordsDelegate delegate) {
+      long fetchBegan  = now();
+      stats.fetchBegan = fetchBegan;
+      RecordFilter filter = storeTracker.getFilter();
+
+      for (Entry<String, Record> entry : wbos.entrySet()) {
+        Record record = entry.getValue();
+        if (record.lastModified >= timestamp) {
+          if (filter != null &&
+              filter.excludeRecord(record)) {
+            Logger.debug(LOG_TAG, "Excluding record " + record.guid);
+            continue;
+          }
+          delegate.deferredFetchDelegate(delegateExecutor).onFetchedRecord(record);
+        }
+      }
+      long fetchCompleted  = now();
+      stats.fetchCompleted = fetchCompleted;
+      delegate.deferredFetchDelegate(delegateExecutor).onFetchCompleted(fetchCompleted);
+    }
+
+    @Override
+    public void fetch(final String[] guids,
+                      final RepositorySessionFetchRecordsDelegate delegate) {
+      long fetchBegan  = now();
+      stats.fetchBegan = fetchBegan;
+      for (String guid : guids) {
+        if (wbos.containsKey(guid)) {
+          delegate.deferredFetchDelegate(delegateExecutor).onFetchedRecord(wbos.get(guid));
+        }
+      }
+      long fetchCompleted  = now();
+      stats.fetchCompleted = fetchCompleted;
+      delegate.deferredFetchDelegate(delegateExecutor).onFetchCompleted(fetchCompleted);
+    }
+
+    @Override
+    public void fetchAll(final RepositorySessionFetchRecordsDelegate delegate) {
+      long fetchBegan  = now();
+      stats.fetchBegan = fetchBegan;
+      for (Entry<String, Record> entry : wbos.entrySet()) {
+        Record record = entry.getValue();
+        delegate.deferredFetchDelegate(delegateExecutor).onFetchedRecord(record);
+      }
+      long fetchCompleted  = now();
+      stats.fetchCompleted = fetchCompleted;
+      delegate.deferredFetchDelegate(delegateExecutor).onFetchCompleted(fetchCompleted);
+    }
+
+    @Override
+    public void store(final Record record) throws NoStoreDelegateException {
+      if (delegate == null) {
+        throw new NoStoreDelegateException();
+      }
+      final long now = now();
+      if (stats.storeBegan < 0) {
+        stats.storeBegan = now;
+      }
+      Record existing = wbos.get(record.guid);
+      Logger.debug(LOG_TAG, "Existing record is " + (existing == null ? "<null>" : (existing.guid + ", " + existing)));
+      if (existing != null &&
+          existing.lastModified > record.lastModified) {
+        Logger.debug(LOG_TAG, "Local record is newer. Not storing.");
+        delegate.deferredStoreDelegate(delegateExecutor).onRecordStoreSucceeded(record.guid);
+        return;
+      }
+      if (existing != null) {
+        Logger.debug(LOG_TAG, "Replacing local record.");
+      }
+
+      // Store a copy of the record with an updated modified time.
+      Record toStore = record.copyWithIDs(record.guid, record.androidID);
+      if (bumpTimestamps) {
+        toStore.lastModified = now;
+      }
+      wbos.put(record.guid, toStore);
+
+      trackRecord(toStore);
+      delegate.deferredStoreDelegate(delegateExecutor).onRecordStoreSucceeded(record.guid);
+    }
+
+    @Override
+    public void wipe(final RepositorySessionWipeDelegate delegate) {
+      if (!isActive()) {
+        delegate.onWipeFailed(new InactiveSessionException(null));
+        return;
+      }
+
+      Logger.info(LOG_TAG, "Wiping WBORepositorySession.");
+      this.wbos = new ConcurrentHashMap<String, Record>();
+
+      // Wipe immediately for the convenience of test code.
+      wboRepository.wbos = new ConcurrentHashMap<String, Record>();
+      delegate.deferredWipeDelegate(delegateExecutor).onWipeSucceeded();
+    }
+
+    @Override
+    public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException {
+      Logger.info(LOG_TAG, "Finishing WBORepositorySession: handing back " + this.wbos.size() + " WBOs.");
+      wboRepository.wbos = this.wbos;
+      stats.finished = now();
+      super.finish(delegate);
+    }
+
+    @Override
+    public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
+      this.wbos = wboRepository.cloneWBOs();
+      stats.begun = now();
+      super.begin(delegate);
+    }
+
+    @Override
+    public void storeDone(long end) {
+      // TODO: this is not guaranteed to be called after all of the record
+      // store callbacks have completed!
+      if (stats.storeBegan < 0) {
+        stats.storeBegan = end;
+      }
+      stats.storeCompleted = end;
+      delegate.deferredStoreDelegate(delegateExecutor).onStoreCompleted(end);
+    }
+  }
+
+  public ConcurrentHashMap<String, Record> wbos;
+
+  public WBORepository(boolean bumpTimestamps) {
+    super();
+    this.bumpTimestamps = bumpTimestamps;
+    this.wbos = new ConcurrentHashMap<String, Record>();
+  }
+
+  public WBORepository() {
+    this(false);
+  }
+
+  public synchronized boolean shouldTrack() {
+    return false;
+  }
+
+  @Override
+  public void createSession(RepositorySessionCreationDelegate delegate,
+                            Context context) {
+    delegate.deferredCreationDelegate().onSessionCreated(new WBORepositorySession(this));
+  }
+
+  public ConcurrentHashMap<String, Record> cloneWBOs() {
+    ConcurrentHashMap<String, Record> out = new ConcurrentHashMap<String, Record>();
+    for (Entry<String, Record> entry : wbos.entrySet()) {
+      out.put(entry.getKey(), entry.getValue()); // Assume that records are
+                                                 // immutable.
+    }
+    return out;
+  }
+}
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestASNUtils.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestASNUtils.java
@@ -2,19 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.browserid.test;
 
 import java.math.BigInteger;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.browserid.ASNUtils;
 import org.mozilla.gecko.sync.Utils;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestASNUtils {
   public void doTestEncodeDecodeArrays(int length1, int length2) {
     if (4 + length1 + length2 > 127) {
       throw new IllegalArgumentException("Total length must be < 128 - 4.");
     }
     byte[] first = Utils.generateRandomBytes(length1);
     byte[] second = Utils.generateRandomBytes(length2);
     byte[] encoded = ASNUtils.encodeTwoArraysToASN1(first, second);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestDSACryptoImplementation.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestDSACryptoImplementation.java
@@ -3,20 +3,23 @@
 
 package org.mozilla.gecko.browserid.test;
 
 import java.math.BigInteger;
 
 import junit.framework.Assert;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.browserid.BrowserIDKeyPair;
 import org.mozilla.gecko.browserid.DSACryptoImplementation;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestDSACryptoImplementation {
   @Test
   public void testToJSONObject() throws Exception {
     BigInteger p = new BigInteger("fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e17", 16);
     BigInteger q = new BigInteger("962eddcc369cba8ebb260ee6b6a126d9346e38c5", 16);
     BigInteger g = new BigInteger("678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4", 16);
     BigInteger x = new BigInteger("9516d860392003db5a4f168444903265467614db", 16);
     BigInteger y = new BigInteger("455152a0e499f5c9d11f9f1868c8b868b1443ca853843226a5a9552dd909b4bdba879acc504acb690df0348d60e63ea37e8c7f075302e0df5bcdc76a383888a0", 16);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestJSONWebTokenUtils.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestJSONWebTokenUtils.java
@@ -3,24 +3,27 @@
 
 package org.mozilla.gecko.browserid.test;
 
 import java.math.BigInteger;
 import java.security.GeneralSecurityException;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.browserid.BrowserIDKeyPair;
 import org.mozilla.gecko.browserid.DSACryptoImplementation;
 import org.mozilla.gecko.browserid.JSONWebTokenUtils;
 import org.mozilla.gecko.browserid.RSACryptoImplementation;
 import org.mozilla.gecko.browserid.SigningPrivateKey;
 import org.mozilla.gecko.browserid.VerifyingPublicKey;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestJSONWebTokenUtils {
   public void doTestEncodeDecode(BrowserIDKeyPair keyPair) throws Exception {
     SigningPrivateKey privateKey = keyPair.getPrivate();
     VerifyingPublicKey publicKey = keyPair.getPublic();
 
     ExtendedJSONObject o = new ExtendedJSONObject();
     o.put("key", "value");
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestRSACryptoImplementation.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/browserid/test/TestRSACryptoImplementation.java
@@ -3,20 +3,23 @@
 
 package org.mozilla.gecko.browserid.test;
 
 import java.math.BigInteger;
 
 import junit.framework.Assert;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.browserid.BrowserIDKeyPair;
 import org.mozilla.gecko.browserid.RSACryptoImplementation;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestRSACryptoImplementation {
   @Test
   public void testToJSONObject() throws Exception {
     BigInteger n = new BigInteger("7042170764319402120473546823641395184140303948430445023576085129538272863656735924617881022040465877164076593767104512065359975488480629290310209335113577");
     BigInteger e = new BigInteger("65537");
     BigInteger d = new BigInteger("2050102629239206449128199335463237235732683202345308155771672920433658970744825199440426256856862541525088288448769859770132714705204296375901885294992205");
 
     BrowserIDKeyPair keyPair = new BrowserIDKeyPair(
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestBase32.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestBase32.java
@@ -5,19 +5,22 @@ package org.mozilla.gecko.sync.crypto.te
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base32;
 import org.mozilla.gecko.sync.Utils;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestBase32 {
 
   public static void assertSame(byte[] arrayOne, byte[] arrayTwo) {
     assertTrue(Arrays.equals(arrayOne, arrayTwo));
   }
 
   @Test
   public void testBase32() throws UnsupportedEncodingException {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestCryptoInfo.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestCryptoInfo.java
@@ -7,22 +7,25 @@ import static org.junit.Assert.assertArr
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.io.UnsupportedEncodingException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.CryptoInfo;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestCryptoInfo {
 
   @Test
   public void testEncryptedHMACIsSet() throws CryptoException, UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException {
     KeyBundle kb = KeyBundle.withRandomKeys();
     CryptoInfo encrypted = CryptoInfo.encrypt("plaintext".getBytes("UTF-8"), kb);
     assertSame(kb, encrypted.getKeys());
     assertTrue(encrypted.generatedHMACIsHMAC());
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestHKDF.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestHKDF.java
@@ -4,26 +4,29 @@
 package org.mozilla.gecko.sync.crypto.test;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.util.Arrays;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.HKDF;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.robolectric.RobolectricGradleTestRunner;
 
 /*
  * This class tests the HKDF.java class.
  * The tests are the 3 HMAC-based test cases
  * from the RFC 5869 specification.
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestHKDF {
   @Test
   public void testCase1() {
     String IKM  = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b";
     String salt = "000102030405060708090a0b0c";
     String info = "f0f1f2f3f4f5f6f7f8f9";
     int L       = 42;
     String PRK  = "077709362c2e32df0ddc3f0dc47bba63" +
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestKeyBundle.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestKeyBundle.java
@@ -7,20 +7,23 @@ import static org.junit.Assert.assertArr
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestKeyBundle {
   @Test
   public void testCreateKeyBundle() throws UnsupportedEncodingException, CryptoException {
     String username              = "smqvooxj664hmrkrv6bw4r4vkegjhkns";
     String friendlyBase32SyncKey = "gbh7teqqcgyzd65svjgibd7tqy";
     String base64EncryptionKey   = "069EnS3EtDK4y1tZ1AyKX+U7WEsWRp9b" +
                                    "RIKLdW/7aoE=";
     String base64HmacKey         = "LF2YCS1QCgSNCf0BCQvQ06SGH8jqJDi9" +
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestPBKDF2.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestPBKDF2.java
@@ -5,27 +5,30 @@ package org.mozilla.gecko.sync.crypto.te
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.PBKDF2;
+import org.robolectric.RobolectricGradleTestRunner;
 
 /**
  * Test PBKDF2 implementations against vectors from
  * <dl>
  * <dt>SHA-256</dt>
  * <dd><a href="https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors">https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors</a></dd>
  * <dd><a href="https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c">https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c</a></dd>
  * </dl>
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestPBKDF2 {
 
   @Test
   public final void testPBKDF2SHA256A() throws UnsupportedEncodingException, GeneralSecurityException {
     String  p = "password";
     String  s = "salt";
     int dkLen = 32;
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestPersistedCrypto5Keys.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestPersistedCrypto5Keys.java
@@ -5,23 +5,26 @@ package org.mozilla.gecko.sync.crypto.te
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 import org.mozilla.gecko.sync.CollectionKeys;
 import org.mozilla.gecko.sync.NoCollectionKeysSetException;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.crypto.PersistedCrypto5Keys;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestPersistedCrypto5Keys {
   MockSharedPreferences prefs = null;
 
   @Before
   public void setUp() {
     prefs = new MockSharedPreferences();
   }
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestSRPConstants.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/crypto/test/TestSRPConstants.java
@@ -2,18 +2,21 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.sync.crypto.test;
 
 import java.math.BigInteger;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.net.SRPConstants;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSRPConstants extends SRPConstants {
   public void assertSRPConstants(SRPConstants.Parameters params, int bitLength) {
     Assert.assertNotNull(params.g);
     Assert.assertNotNull(params.N);
     Assert.assertEquals(bitLength, bitLength);
     Assert.assertEquals(bitLength / 8, params.byteLength);
     Assert.assertEquals(bitLength / 4, params.hexLength);
     BigInteger N = params.N;
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/middleware/test/TestCrypto5MiddlewareRepositorySession.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/middleware/test/TestCrypto5MiddlewareRepositorySession.java
@@ -11,16 +11,17 @@ import static org.junit.Assert.assertTru
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 
 import junit.framework.AssertionFailedError;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionBeginDelegate;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionCreationDelegate;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionFetchRecordsDelegate;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionFinishDelegate;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositorySessionStoreDelegate;
 import org.mozilla.android.sync.test.helpers.ExpectSuccessRepositoryWipeDelegate;
 import org.mozilla.gecko.background.testhelpers.MockRecord;
 import org.mozilla.gecko.background.testhelpers.WBORepository;
@@ -32,17 +33,19 @@ import org.mozilla.gecko.sync.crypto.Key
 import org.mozilla.gecko.sync.middleware.Crypto5MiddlewareRepository;
 import org.mozilla.gecko.sync.middleware.Crypto5MiddlewareRepositorySession;
 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
 import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
 import org.mozilla.gecko.sync.repositories.RepositorySession;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestCrypto5MiddlewareRepositorySession {
   public static WaitHelper getTestWaiter() {
     return WaitHelper.getTestWaiter();
   }
 
   public static void performWait(Runnable runnable) {
     getTestWaiter().performWait(runnable);
   }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestHMACAuthHeaderProvider.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestHMACAuthHeaderProvider.java
@@ -6,23 +6,26 @@ package org.mozilla.gecko.sync.net.test;
 import static org.junit.Assert.assertEquals;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.net.HMACAuthHeaderProvider;
 
 import ch.boye.httpclientandroidlib.client.methods.HttpGet;
 import ch.boye.httpclientandroidlib.client.methods.HttpPost;
 import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
 import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestHMACAuthHeaderProvider {
   // Expose a few protected static member functions as public for testing.
   protected static class LeakyHMACAuthHeaderProvider extends HMACAuthHeaderProvider {
     public LeakyHMACAuthHeaderProvider(String identifier, String key) {
       super(identifier, key);
     }
 
     public static String getRequestString(HttpUriRequest request, long timestampInSeconds, String nonce, String extra) {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestHawkAuthHeaderProvider.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestHawkAuthHeaderProvider.java
@@ -7,32 +7,35 @@ import static org.junit.Assert.assertEqu
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.net.HawkAuthHeaderProvider;
 
 import ch.boye.httpclientandroidlib.Header;
 import ch.boye.httpclientandroidlib.client.methods.HttpGet;
 import ch.boye.httpclientandroidlib.client.methods.HttpPost;
 import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
 import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
 import ch.boye.httpclientandroidlib.entity.StringEntity;
 import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
 import ch.boye.httpclientandroidlib.message.BasicHeader;
 import ch.boye.httpclientandroidlib.protocol.BasicHttpContext;
+import org.robolectric.RobolectricGradleTestRunner;
 
 /**
  * These test vectors were taken from
  * <a href="https://github.com/hueniverse/hawk/blob/871cc597973110900467bd3dfb84a3c892f678fb/README.md">https://github.com/hueniverse/hawk/blob/871cc597973110900467bd3dfb84a3c892f678fb/README.md</a>.
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestHawkAuthHeaderProvider {
   // Expose a few protected static member functions as public for testing.
   protected static class LeakyHawkAuthHeaderProvider extends HawkAuthHeaderProvider {
     public LeakyHawkAuthHeaderProvider(String tokenId, byte[] reqHMACKey) {
       // getAuthHeader takes includePayloadHash as a parameter.
       super(tokenId, reqHMACKey, false, 0L);
     }
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestUserAgentHeaders.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestUserAgentHeaders.java
@@ -5,36 +5,39 @@ package org.mozilla.gecko.sync.net.test;
 
 import java.util.concurrent.Executors;
 
 import junit.framework.Assert;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.fxa.FxAccountClient10;
 import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
 import org.mozilla.gecko.background.fxa.FxAccountClient10.StatusResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClient20;
 import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.sync.SyncConstants;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
 import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
 import ch.boye.httpclientandroidlib.protocol.HTTP;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestUserAgentHeaders {
   private static final int TEST_PORT = HTTPServerTestHelper.getTestPort();
   private static final String TEST_SERVER = "http://localhost:" + TEST_PORT;
 
   protected final HTTPServerTestHelper data = new HTTPServerTestHelper();
 
   protected class UserAgentServer extends MockServer {
     public String lastUserAgent = null;
@@ -93,19 +96,16 @@ public class TestUserAgentHeaders {
       @Override
       public void run() {
         request.get();
       }
     });
 
     // Verify that we're getting the value from the correct place.
     Assert.assertEquals(SyncConstants.USER_AGENT, userAgentServer.lastUserAgent);
-    // And that the value is correct. This is fragile, but better than breaking
-    // our header and not discovering until too late.
-    Assert.assertEquals("Firefox AndroidSync 1.24.0a1.0 (FxSync)", userAgentServer.lastUserAgent);
   }
 
   @Test
   public void testFxAccountClientUserAgent() throws Exception {
     final FxAccountClient20 client = new FxAccountClient20(TEST_SERVER, Executors.newSingleThreadExecutor());
     WaitHelper.getTestWaiter().performWait(new Runnable() {
       @Override
       public void run() {
@@ -125,13 +125,10 @@ public class TestUserAgentHeaders {
             WaitHelper.getTestWaiter().performNotify();
           }
         });
       }
     });
 
     // Verify that we're getting the value from the correct place.
     Assert.assertEquals(FxAccountConstants.USER_AGENT, userAgentServer.lastUserAgent);
-    // And that the value is correct. This is fragile, but better than breaking
-    // our header and not discovering until too late.
-    Assert.assertEquals("Firefox-Android-FxAccounts/24.0a1 (FxSync)", userAgentServer.lastUserAgent);
   }
 }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/android/test/TestBookmarksInsertionManager.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/android/test/TestBookmarksInsertionManager.java
@@ -9,21 +9,24 @@ import static org.junit.Assert.assertTru
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.repositories.android.BookmarksInsertionManager;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestBookmarksInsertionManager {
   public BookmarksInsertionManager manager;
   public ArrayList<String[]> insertions;
 
   @Before
   public void setUp() {
     insertions = new ArrayList<String[]>();
     Set<String> writtenFolders = new HashSet<String>();
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/domain/TestClientRecord.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/domain/TestClientRecord.java
@@ -3,19 +3,22 @@
 
 package org.mozilla.gecko.sync.repositories.domain;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.Utils;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestClientRecord {
 
   @Test
   public void testEnsureDefaults() {
     // Ensure defaults.
     ClientRecord record = new ClientRecord();
     assertEquals(ClientRecord.COLLECTION_NAME, record.collection);
     assertEquals(0, record.lastModified);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/domain/test/TestFormHistoryRecord.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/domain/test/TestFormHistoryRecord.java
@@ -3,20 +3,23 @@
 
 package org.mozilla.gecko.sync.repositories.domain.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.repositories.domain.FormHistoryRecord;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestFormHistoryRecord {
   public static FormHistoryRecord withIdFieldNameAndValue(long id, String fieldName, String value) {
     FormHistoryRecord fr = new FormHistoryRecord();
     fr.androidID = id;
     fr.fieldName = fieldName;
     fr.fieldValue = value;
 
     return fr;
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/test/TestRepositorySessionBundle.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/test/TestRepositorySessionBundle.java
@@ -1,18 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.sync.repositories.test;
 
 import static org.junit.Assert.*;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestRepositorySessionBundle {
   @Test
   public void testSetGetTimestamp() {
     RepositorySessionBundle bundle = new RepositorySessionBundle(-1);
     assertEquals(-1, bundle.getTimestamp());
 
     bundle.setTimestamp(10);
     assertEquals(10, bundle.getTimestamp());
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/test/TestSafeConstrainedServer11Repository.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/test/TestSafeConstrainedServer11Repository.java
@@ -4,28 +4,31 @@
 package org.mozilla.gecko.sync.repositories.test;
 
 import java.net.URISyntaxException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.JSONRecordFetcher;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.repositories.RepositorySession;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
 import org.mozilla.gecko.sync.stage.SafeConstrainedServer11Repository;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestSafeConstrainedServer11Repository {
   private static final int     TEST_PORT      = HTTPServerTestHelper.getTestPort();
   private static final String  TEST_SERVER    = "http://localhost:" + TEST_PORT + "/";
   private static final String  TEST_USERNAME  = "c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd";
   private static final String  TEST_BASE_PATH = "/1.1/" + TEST_USERNAME + "/";
 
   public AuthHeaderProvider getAuthHeaderProvider() {
     return null;
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestEnsureClusterURLStage.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestEnsureClusterURLStage.java
@@ -9,16 +9,17 @@ import static org.junit.Assert.assertTru
 
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 
 import org.json.simple.parser.ParseException;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.MockGlobalSession;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.AlreadySyncingException;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.NodeAuthenticationException;
@@ -29,17 +30,19 @@ import org.mozilla.gecko.sync.crypto.Key
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.stage.EnsureClusterURLStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
 
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.ProtocolVersion;
 import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
 import ch.boye.httpclientandroidlib.message.BasicStatusLine;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestEnsureClusterURLStage {
   private static final int     TEST_PORT        = HTTPServerTestHelper.getTestPort();
   private static final String  TEST_SERVER      = "http://localhost:" + TEST_PORT + "/";
   private static final String  TEST_OLD_CLUSTER_URL = TEST_SERVER + "cluster/old/";
   private static final String  TEST_NEW_CLUSTER_URL = TEST_SERVER + "cluster/new/";
   static String                TEST_NW_URL      = TEST_SERVER + "/1.0/c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd/node/weave"; // GET https://server/pathname/version/username/node/weave
   private HTTPServerTestHelper data             = new HTTPServerTestHelper();
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestEnsureCrypto5KeysStage.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestEnsureCrypto5KeysStage.java
@@ -9,33 +9,36 @@ import static org.junit.Assert.assertNul
 import static org.junit.Assert.assertTrue;
 
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.MockGlobalSession;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.AlreadySyncingException;
 import org.mozilla.gecko.sync.CollectionKeys;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.stage.EnsureCrypto5KeysStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestEnsureCrypto5KeysStage {
   private int          TEST_PORT                = HTTPServerTestHelper.getTestPort();
   private final String TEST_CLUSTER_URL         = "http://localhost:" + TEST_PORT;
   private final String TEST_USERNAME            = "johndoe";
   private final String TEST_PASSWORD            = "password";
   private final String TEST_SYNC_KEY            = "abcdeabcdeabcdeabcdeabcdea";
 
   private final String TEST_JSON_NO_CRYPTO =
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestFetchMetaGlobalStage.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestFetchMetaGlobalStage.java
@@ -12,16 +12,17 @@ import java.net.URI;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.json.simple.JSONArray;
 import org.json.simple.parser.ParseException;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.android.sync.net.test.TestMetaGlobal;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.testhelpers.MockGlobalSession;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.AlreadySyncingException;
 import org.mozilla.gecko.sync.CollectionKeys;
@@ -35,19 +36,21 @@ import org.mozilla.gecko.sync.SyncConfig
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.delegates.FreshStartDelegate;
 import org.mozilla.gecko.sync.delegates.KeyUploadDelegate;
 import org.mozilla.gecko.sync.delegates.WipeServerDelegate;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.stage.FetchMetaGlobalStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
+import org.robolectric.RobolectricGradleTestRunner;
 import org.simpleframework.http.Request;
 import org.simpleframework.http.Response;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestFetchMetaGlobalStage {
   @SuppressWarnings("unused")
   private static final String  LOG_TAG          = "TestMetaGlobalStage";
 
   private static final int     TEST_PORT        = HTTPServerTestHelper.getTestPort();
   private static final String  TEST_SERVER      = "http://localhost:" + TEST_PORT + "/";
   private static final String  TEST_CLUSTER_URL = TEST_SERVER + "cluster/";
   static String                TEST_NW_URL      = TEST_SERVER + "/1.0/c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd/node/weave"; // GET https://server/pathname/version/username/node/weave
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestStageLookup.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/stage/test/TestStageLookup.java
@@ -4,18 +4,21 @@
 package org.mozilla.gecko.sync.stage.test;
 
 import static org.junit.Assert.assertEquals;
 
 import java.util.HashSet;
 import java.util.Set;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestStageLookup {
 
   @Test
   public void testStageLookupByName() {
     Set<Stage> namedStages = new HashSet<Stage>(Stage.getNamedStages());
     Set<Stage> expected = new HashSet<Stage>();
     expected.add(Stage.syncClientsEngine);
     expected.add(Stage.syncBookmarks);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestExtendedJSONObject.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestExtendedJSONObject.java
@@ -15,21 +15,24 @@ import static org.junit.Assert.assertTru
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
 
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 import org.json.simple.parser.ParseException;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.NonArrayJSONException;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.UnexpectedJSONException.BadRequiredFieldJSONException;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestExtendedJSONObject {
   public static String exampleJSON = "{\"modified\":1233702554.25,\"success\":[\"{GXS58IDC}12\",\"{GXS58IDC}13\",\"{GXS58IDC}15\",\"{GXS58IDC}16\",\"{GXS58IDC}18\",\"{GXS58IDC}19\"],\"failed\":{\"{GXS58IDC}11\":[\"invalid parentid\"],\"{GXS58IDC}14\":[\"invalid parentid\"],\"{GXS58IDC}17\":[\"invalid parentid\"],\"{GXS58IDC}20\":[\"invalid parentid\"]}}";
   public static String exampleIntegral = "{\"modified\":1233702554,}";
 
   @Test
   public void testDeepCopy() throws NonObjectJSONException, IOException, ParseException, NonArrayJSONException {
     ExtendedJSONObject a = new ExtendedJSONObject(exampleJSON);
     ExtendedJSONObject c = a.deepCopy();
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestInfoCollections.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestInfoCollections.java
@@ -4,24 +4,27 @@
 package org.mozilla.gecko.sync.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.InfoCounts;
 import org.mozilla.gecko.sync.Utils;
+import org.robolectric.RobolectricGradleTestRunner;
 
 /**
  * Test both info/collections and info/collection_counts.
  */
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestInfoCollections {
   public static final String TEST_COLLECTIONS_JSON =
       "{\"history\":1.3319567131E9, "    +
       " \"bookmarks\":1.33195669592E9, " +
       " \"prefs\":1.33115408641E9, "     +
       " \"crypto\":1.32046063664E9, "    +
       " \"meta\":1.321E9, "              +
       " \"forms\":1.33136685374E9, "     +
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestPersistedMetaGlobal.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/test/TestPersistedMetaGlobal.java
@@ -7,26 +7,29 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Set;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.MetaGlobal;
 import org.mozilla.gecko.sync.NoCollectionKeysSetException;
 import org.mozilla.gecko.sync.PersistedMetaGlobal;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestPersistedMetaGlobal {
   MockSharedPreferences prefs = null;
   private final String TEST_META_URL = "metaURL";
   private final String TEST_CREDENTIALS = "credentials";
 
   @Before
   public void setUp() {
     prefs = new MockSharedPreferences();
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/tokenserver/test/TestTokenServerClient.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/tokenserver/test/TestTokenServerClient.java
@@ -12,16 +12,17 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 import junit.framework.Assert;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.common.log.writers.StringLogWriter;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.SyncResponse;
 import org.mozilla.gecko.tokenserver.TokenServerClient;
@@ -39,17 +40,19 @@ import ch.boye.httpclientandroidlib.Head
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.ProtocolVersion;
 import ch.boye.httpclientandroidlib.client.methods.HttpGet;
 import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
 import ch.boye.httpclientandroidlib.entity.StringEntity;
 import ch.boye.httpclientandroidlib.message.BasicHeader;
 import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
 import ch.boye.httpclientandroidlib.message.BasicStatusLine;
+import org.robolectric.RobolectricGradleTestRunner;
 
+@RunWith(RobolectricGradleTestRunner.class)
 public class TestTokenServerClient {
   public static final String JSON = "application/json";
   public static final String TEXT = "text/plain";
 
   public static final String TEST_TOKEN_RESPONSE = "{\"api_endpoint\": \"https://stage-aitc1.services.mozilla.com/1.0/1659259\"," +
       "\"duration\": 300," +
       "\"id\": \"eySHORTENED\"," +
       "\"key\": \"-plSHORTENED\"," +