| Index: chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaClientTest.java
|
| diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaClientTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaClientTest.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ab62f69a9a0211f5e36b75c036b1f1d6869ebf1d
|
| --- /dev/null
|
| +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaClientTest.java
|
| @@ -0,0 +1,809 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.chrome.browser.omaha;
|
| +
|
| +import static org.chromium.chrome.test.omaha.MockRequestGenerator.DeviceType;
|
| +
|
| +import android.app.AlarmManager;
|
| +import android.app.PendingIntent;
|
| +import android.content.ComponentName;
|
| +import android.content.Context;
|
| +import android.content.Intent;
|
| +import android.content.SharedPreferences;
|
| +import android.content.pm.ApplicationInfo;
|
| +import android.test.InstrumentationTestCase;
|
| +import android.test.suitebuilder.annotation.SmallTest;
|
| +
|
| +import org.chromium.base.test.util.AdvancedMockContext;
|
| +import org.chromium.base.test.util.Feature;
|
| +import org.chromium.chrome.test.omaha.AttributeFinder;
|
| +import org.chromium.chrome.test.omaha.MockRequestGenerator;
|
| +
|
| +import java.io.ByteArrayInputStream;
|
| +import java.io.ByteArrayOutputStream;
|
| +import java.io.IOException;
|
| +import java.io.InputStream;
|
| +import java.io.OutputStream;
|
| +import java.net.HttpURLConnection;
|
| +import java.net.MalformedURLException;
|
| +import java.net.SocketTimeoutException;
|
| +import java.net.URL;
|
| +import java.util.HashSet;
|
| +import java.util.LinkedList;
|
| +
|
| +/**
|
| + * Tests for the {@link OmahaClient}.
|
| + * Tests often override the original OmahaClient's functions with the HookedOmahaClient, which
|
| + * provides a way to hook into functions to return values that would normally be provided by the
|
| + * system, such as whether Chrome was installed through the system image.
|
| + */
|
| +public class OmahaClientTest extends InstrumentationTestCase {
|
| + private enum ServerResponse {
|
| + SUCCESS, FAILURE
|
| + }
|
| +
|
| + private enum ConnectionStatus {
|
| + RESPONDS, TIMES_OUT
|
| + }
|
| +
|
| + private enum InstallEvent {
|
| + SEND, DONT_SEND
|
| + }
|
| +
|
| + private enum ForceTest {
|
| + FORCED, NOT_FORCED
|
| + }
|
| +
|
| + private enum PostStatus {
|
| + DUE, NOT_DUE
|
| + }
|
| +
|
| + private MockOmahaContext mContext;
|
| + private HookedOmahaClient mOmahaClient;
|
| +
|
| + @Override
|
| + protected void setUp() {
|
| + Context targetContext = getInstrumentation().getTargetContext();
|
| + mContext = new MockOmahaContext(targetContext);
|
| + mOmahaClient = HookedOmahaClient.create(mContext);
|
| + }
|
| +
|
| + /**
|
| + * If a request exists during handleInitialize(), a POST Intent should be fired immediately.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testInitializeWithRequest() {
|
| + mOmahaClient.registerNewRequest(10);
|
| +
|
| + Intent intent = OmahaClient.createInitializeIntent(mContext);
|
| + mOmahaClient.onHandleIntent(intent);
|
| + assertTrue("OmahaClient has no registered request", mOmahaClient.hasRequest());
|
| + assertTrue("Alarm does not have the correct state", mOmahaClient.getRequestAlarmWasSet());
|
| + assertTrue("OmahaClient wasn't restarted.", mContext.mOmahaClientRestarted);
|
| + assertNotNull(mContext.mIntentFired);
|
| + assertEquals("POST intent not fired.",
|
| + OmahaClient.createPostRequestIntent(mContext, false).getAction(),
|
| + mContext.mIntentFired.getAction());
|
| + }
|
| +
|
| + /**
|
| + * If a request doesn't exist during handleInitialize(), no intent should be fired.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testInitializeWithoutRequest() {
|
| + Intent intent = OmahaClient.createInitializeIntent(mContext);
|
| + mOmahaClient.onHandleIntent(intent);
|
| + assertFalse("OmahaClient has a registered request", mOmahaClient.hasRequest());
|
| + assertTrue("Alarm does not have the correct state", mOmahaClient.getRequestAlarmWasSet());
|
| + assertFalse("OmahaClient was restarted.", mContext.mOmahaClientRestarted);
|
| + assertNull(mContext.mIntentFired);
|
| + }
|
| +
|
| + /**
|
| + * Catch situations where the install source isn't set prior to restoring a saved request.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testInstallSourceSetBeforeRestoringRequest() {
|
| + // Plant a failed request.
|
| + Context targetContext = getInstrumentation().getTargetContext();
|
| + MockOmahaContext mockContext = new MockOmahaContext(targetContext);
|
| + SharedPreferences prefs =
|
| + mockContext.getSharedPreferences(OmahaClient.PREF_PACKAGE, Context.MODE_PRIVATE);
|
| + SharedPreferences.Editor editor = prefs.edit();
|
| + editor.putLong(OmahaClient.PREF_TIMESTAMP_OF_REQUEST, 0);
|
| + editor.putString(OmahaClient.PREF_PERSISTED_REQUEST_ID, "persisted_id");
|
| + editor.apply();
|
| +
|
| + // Send it off and don't crash when the state is restored and the XML is generated.
|
| + HookedOmahaClient omahaClient = HookedOmahaClient.create(mockContext);
|
| + omahaClient.mMockScheduler.setCurrentTime(1000);
|
| + Intent postIntent = OmahaClient.createPostRequestIntent(mockContext, false);
|
| + omahaClient.onHandleIntent(postIntent);
|
| +
|
| + // Check that the request was actually generated and tried to be sent.
|
| + MockConnection connection = omahaClient.getLastConnection();
|
| + assertTrue("Didn't try to make a connection.", connection.getSentRequest());
|
| + assertFalse("OmahaClient still has a registered request", omahaClient.hasRequest());
|
| + assertTrue("Failed to send request", omahaClient.getCumulativeFailedAttempts() == 0);
|
| + }
|
| +
|
| + /**
|
| + * Makes sure that we don't generate a request if we don't have to.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha", "Main"})
|
| + public void testOmahaClientDoesNotGenerateRequest() {
|
| + // Change the time so the OmahaClient thinks no request is necessary.
|
| + mOmahaClient.mMockScheduler.setCurrentTime(-1000);
|
| + Intent intent = OmahaClient.createRegisterRequestIntent(mContext, false);
|
| + mOmahaClient.onHandleIntent(intent);
|
| + assertFalse("OmahaClient has a registered request", mOmahaClient.hasRequest());
|
| + assertFalse("OmahaClient was relaunched", mContext.mOmahaClientRestarted);
|
| + }
|
| +
|
| + /**
|
| + * Makes sure that firing a XML request triggers a post intent.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha", "Main"})
|
| + public void testOmahaClientRequestToPost() {
|
| + // Change the time so the OmahaClient thinks a request is overdue.
|
| + mOmahaClient.mMockScheduler.setCurrentTime(1000);
|
| + Intent intent = OmahaClient.createRegisterRequestIntent(mContext, false);
|
| + mOmahaClient.onHandleIntent(intent);
|
| + assertTrue("OmahaClient has no registered request", mOmahaClient.hasRequest());
|
| + assertTrue("OmahaClient wasn't relaunched", mContext.mOmahaClientRestarted);
|
| + }
|
| +
|
| + /**
|
| + * Makes sure that forcing an XML request generates a request and triggers a post intent.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha", "Main"})
|
| + public void testOmahaClientForcedRequestToPost() {
|
| + Intent intent = OmahaClient.createRegisterRequestIntent(mContext, true);
|
| + mOmahaClient.onHandleIntent(intent);
|
| + assertTrue("OmahaClient has no registered request", mOmahaClient.hasRequest());
|
| + assertTrue("OmahaClient wasn't relaunched", mContext.mOmahaClientRestarted);
|
| + }
|
| +
|
| + /**
|
| + * Makes sure that incorrect timestamps are caught.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testIncorrectDelays() {
|
| + // Set the time to be 2 days past epoch, then generate a request.
|
| + final long millisecondsPerDay = 86400000;
|
| + long currentTimestamp = millisecondsPerDay * 2;
|
| + mOmahaClient.mMockScheduler.setCurrentTime(currentTimestamp);
|
| + mOmahaClient.registerNewRequest(currentTimestamp);
|
| +
|
| + // Rewind the clock 2 days.
|
| + currentTimestamp -= millisecondsPerDay * 2;
|
| + mOmahaClient.mMockScheduler.setCurrentTime(currentTimestamp);
|
| + mOmahaClient.restoreState();
|
| +
|
| + // Confirm that the post timestamp was reset, since it's larger than the exponential
|
| + // backoff delay.
|
| + assertEquals("Post timestamp was not cleared.",
|
| + mOmahaClient.getTimestampForNextPostAttempt(), 0);
|
| +
|
| + // Confirm that the request timestamp was reset, since the next timestamp is more than
|
| + // a day away.
|
| + assertEquals("Request timestamp was not cleared.",
|
| + mOmahaClient.getTimestampForNewRequest(), 0);
|
| + }
|
| +
|
| + /**
|
| + * Checks that reading and writing out the preferences works.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientFileIO() {
|
| + // Register a request and save it to disk.
|
| + mOmahaClient.registerNewRequest(0);
|
| +
|
| + // The second OmahaCLient should retrieve the request that the first one saved.
|
| + HookedOmahaClient secondClient = HookedOmahaClient.create(mContext);
|
| + assertTrue("The request from the first OmahaClient wasn't written",
|
| + secondClient.hasRequest());
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostHandsetFailure() {
|
| + postRequestToServer(DeviceType.HANDSET, ServerResponse.FAILURE, ConnectionStatus.RESPONDS,
|
| + InstallEvent.DONT_SEND, ForceTest.NOT_FORCED, PostStatus.DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostHandsetSuccess() {
|
| + postRequestToServer(DeviceType.HANDSET, ServerResponse.SUCCESS, ConnectionStatus.RESPONDS,
|
| + InstallEvent.DONT_SEND, ForceTest.NOT_FORCED, PostStatus.DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostTabletFailure() {
|
| + postRequestToServer(DeviceType.TABLET, ServerResponse.FAILURE, ConnectionStatus.RESPONDS,
|
| + InstallEvent.DONT_SEND, ForceTest.NOT_FORCED, PostStatus.DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostTabletSuccess() {
|
| + postRequestToServer(DeviceType.TABLET, ServerResponse.SUCCESS, ConnectionStatus.RESPONDS,
|
| + InstallEvent.DONT_SEND, ForceTest.NOT_FORCED, PostStatus.DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostHandsetTimeout() {
|
| + postRequestToServer(DeviceType.HANDSET, ServerResponse.FAILURE, ConnectionStatus.TIMES_OUT,
|
| + InstallEvent.DONT_SEND, ForceTest.NOT_FORCED, PostStatus.DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostInstallEventSuccess() {
|
| + postRequestToServer(DeviceType.HANDSET, ServerResponse.SUCCESS, ConnectionStatus.RESPONDS,
|
| + InstallEvent.SEND, ForceTest.NOT_FORCED, PostStatus.DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostInstallEventFailure() {
|
| + postRequestToServer(DeviceType.HANDSET, ServerResponse.FAILURE, ConnectionStatus.RESPONDS,
|
| + InstallEvent.SEND, ForceTest.NOT_FORCED, PostStatus.DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostForcedPostWhenNotDue() {
|
| + postRequestToServer(DeviceType.HANDSET, ServerResponse.SUCCESS, ConnectionStatus.RESPONDS,
|
| + InstallEvent.DONT_SEND, ForceTest.FORCED, PostStatus.NOT_DUE);
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testOmahaClientPostWhenNotDue() {
|
| + postRequestToServer(DeviceType.HANDSET, ServerResponse.FAILURE, ConnectionStatus.RESPONDS,
|
| + InstallEvent.DONT_SEND, ForceTest.NOT_FORCED, PostStatus.NOT_DUE);
|
| + }
|
| +
|
| + /**
|
| + * Pretends to post a request to the Omaha server.
|
| + * @param deviceType Whether or not to use an app-ID indicating a tablet.
|
| + * @param response Whether the server acknowledged the request correctly.
|
| + * @param connectionStatus Whether the connection times out when it tries to contact the
|
| + * Omaha server.
|
| + * @param installType Whether we're sending an install event or not.
|
| + * @param forceType Whether we're testing POSTing a request, regardless of timers.
|
| + * @param postStatus Whether we're due for a POST or not.
|
| + */
|
| + public void postRequestToServer(DeviceType deviceType, ServerResponse response,
|
| + ConnectionStatus connectionStatus, InstallEvent installType, ForceTest forceType,
|
| + PostStatus postStatus) {
|
| + final boolean succeeded = response == ServerResponse.SUCCESS;
|
| + final boolean sentInstallEvent = installType == InstallEvent.SEND;
|
| +
|
| + HookedOmahaClient omahaClient = new HookedOmahaClient(mContext, deviceType, response,
|
| + connectionStatus, false);
|
| + omahaClient.onCreate();
|
| + omahaClient.restoreState();
|
| +
|
| + // Set whether or not we're sending the install event.
|
| + assertTrue("Should default to sending install event.", omahaClient.isSendInstallEvent());
|
| + omahaClient.setSendInstallEvent(installType == InstallEvent.SEND);
|
| + omahaClient.registerNewRequest(0);
|
| +
|
| + // Set up the POST request.
|
| + if (postStatus == PostStatus.NOT_DUE) {
|
| + // Rewind the clock so that we don't send the request yet.
|
| + omahaClient.mMockScheduler.setCurrentTime(-1000);
|
| + }
|
| + Intent postIntent =
|
| + OmahaClient.createPostRequestIntent(mContext, forceType == ForceTest.FORCED);
|
| + omahaClient.onHandleIntent(postIntent);
|
| +
|
| + assertTrue("hasRequest() returned wrong value", succeeded != omahaClient.hasRequest());
|
| + if (postStatus == PostStatus.NOT_DUE && forceType == ForceTest.NOT_FORCED) {
|
| + // No POST attempt was made.
|
| + assertTrue("POST was attempted and failed.",
|
| + omahaClient.getCumulativeFailedAttempts() == 0);
|
| + assertTrue("POST alarm wasn't set for reattempt", omahaClient.getPOSTAlarmWasSet());
|
| + } else {
|
| + // Since we start with no failures, the counter incrementing should indicate whether it
|
| + // succeeded or not.
|
| + assertEquals("Expected different outcome", succeeded,
|
| + omahaClient.getCumulativeFailedAttempts() == 0);
|
| + assertTrue("Alarm state was changed when it shouldn't have been",
|
| + succeeded != omahaClient.getPOSTAlarmWasSet());
|
| +
|
| + // If we're sending an install event, we will immediately attempt to send a ping in a
|
| + // follow-up request.
|
| + int numExpectedRequests = succeeded && sentInstallEvent ? 2 : 1;
|
| + assertEquals("Didn't send the correct number of XML requests.", numExpectedRequests,
|
| + omahaClient.getNumConnectionsMade());
|
| +
|
| + MockConnection connection = omahaClient.getLastConnection();
|
| + assertEquals("Didn't try to make a connection.", true, connection.getSentRequest());
|
| +
|
| + if (connectionStatus == ConnectionStatus.TIMES_OUT) {
|
| + // Several events shouldn't happen if the connection times out.
|
| + assertEquals("Retrieved response code when it should have bailed earlier.",
|
| + 0, connection.getNumTimesResponseCodeRetrieved());
|
| + assertFalse("Grabbed input stream when it should have bailed earlier.",
|
| + connection.getGotInputStream());
|
| + }
|
| + }
|
| +
|
| + // Check that the latest version and market URLs were saved correctly.
|
| + String expectedVersion = succeeded ? MockConnection.UPDATE_VERSION : "";
|
| + String expectedURL = succeeded ? MockConnection.STRIPPED_MARKET_URL : "";
|
| +
|
| + // Make sure we properly parsed out the server's response.
|
| + assertEquals("Latest version numbers didn't match", expectedVersion,
|
| + OmahaClient.getVersionNumberGetter().getLatestKnownVersion(
|
| + mContext, OmahaClient.PREF_PACKAGE, OmahaClient.PREF_LATEST_VERSION));
|
| + assertEquals("Market URL didn't match", expectedURL, OmahaClient.getMarketURL(mContext));
|
| +
|
| + // Check that the install event was sent properly.
|
| + if (sentInstallEvent) {
|
| + assertFalse("OmahaPingService is going to send another install <event>.",
|
| + succeeded == omahaClient.isSendInstallEvent());
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Test whether we're using request and session IDs properly for POSTs.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testRequestAndSessionIDs() {
|
| + assertTrue("Should default to sending install event.", mOmahaClient.isSendInstallEvent());
|
| +
|
| + // Send the POST request.
|
| + mOmahaClient.registerNewRequest(0);
|
| + Intent postIntent = OmahaClient.createPostRequestIntent(mContext, true);
|
| + mOmahaClient.onHandleIntent(postIntent);
|
| +
|
| + // If we're sending an install event, we will immediately attempt to send a ping in a
|
| + // follow-up request. These should have the same session ID, but different request IDs.
|
| + int numRequests = mOmahaClient.getNumConnectionsMade();
|
| +
|
| + HashSet<String> sessionIDs = new HashSet<String>();
|
| + HashSet<String> requestIDs = new HashSet<String>();
|
| + for (int i = 0; i < numRequests; ++i) {
|
| + String request = mOmahaClient.getConnection(i).getOutputStreamContents();
|
| +
|
| + String sessionID =
|
| + new AttributeFinder(request, "request", "sessionid").getValue();
|
| + assertNotNull(sessionID);
|
| + sessionIDs.add(sessionID);
|
| +
|
| + String requestID =
|
| + new AttributeFinder(request, "request", "requestid").getValue();
|
| + assertNotNull(requestID);
|
| + requestIDs.add(requestID);
|
| + }
|
| + assertEquals("Session ID was not the same across all requests", 1,
|
| + sessionIDs.size());
|
| + assertEquals("Request ID was duplicated", numRequests, requestIDs.size());
|
| +
|
| + // Send another XML request and make sure the IDs are all different.
|
| + assertFalse("OmahaPingService is going to send another install <event>.",
|
| + mOmahaClient.isSendInstallEvent());
|
| + mOmahaClient.registerNewRequest(0);
|
| + postIntent = OmahaClient.createPostRequestIntent(mContext, true);
|
| + mOmahaClient.onHandleIntent(postIntent);
|
| +
|
| + assertEquals("Didn't send the correct number of XML requests.", numRequests + 1,
|
| + mOmahaClient.getNumConnectionsMade());
|
| + String newRequest = mOmahaClient.getConnection(numRequests).getOutputStreamContents();
|
| +
|
| + String newSessionID = new AttributeFinder(newRequest, "request", "sessionid").getValue();
|
| + assertNotNull(newSessionID);
|
| + assertFalse("Session ID was reused.", sessionIDs.contains(newSessionID));
|
| +
|
| + String newRequestID = new AttributeFinder(newRequest, "request", "requestid").getValue();
|
| + assertNotNull(newRequestID);
|
| + assertFalse("Request ID was reused.", requestIDs.contains(newRequestID));
|
| + }
|
| +
|
| + /**
|
| + * Checks to see that the header is added only for persisted XML requests.
|
| + */
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testHTTPHeaderForPersistedXMLRequest() {
|
| + final String xml = "<lorem ipsum=\"dolor\" />";
|
| + final long requestTimestamp = 0;
|
| + final long currentTimestamp = 11684;
|
| + final long currentTimestampInSeconds = currentTimestamp / 1000;
|
| +
|
| + mOmahaClient.registerNewRequest(requestTimestamp);
|
| +
|
| + assertTrue("Should default to sending install event.", mOmahaClient.isSendInstallEvent());
|
| + assertEquals("Shouldn't have any failed attempts.", 0,
|
| + mOmahaClient.getCumulativeFailedAttempts());
|
| +
|
| + MockConnection connection = null;
|
| + try {
|
| + mOmahaClient.postRequest(currentTimestamp, xml);
|
| + connection = mOmahaClient.getLastConnection();
|
| + } catch (RequestFailureException e) {
|
| + fail();
|
| + }
|
| + assertEquals("Age property field was unexpectedly added.", null,
|
| + connection.getRequestPropertyField());
|
| + assertEquals("Age property value was unexpectedly set.", null,
|
| + connection.getRequestPropertyValue());
|
| +
|
| + // Fail once, then check that the header is added.
|
| + mOmahaClient.getBackoffScheduler().increaseFailedAttempts();
|
| + try {
|
| + mOmahaClient.postRequest(currentTimestamp, xml);
|
| + connection = mOmahaClient.getLastConnection();
|
| + } catch (RequestFailureException e) {
|
| + fail();
|
| + }
|
| + assertEquals("Age property field was not added.", "X-RequestAge",
|
| + connection.getRequestPropertyField());
|
| + assertEquals("Age property value was incorrectly set.", currentTimestampInSeconds,
|
| + Long.parseLong(connection.getRequestPropertyValue()));
|
| +
|
| + // Make sure the header isn't added if we're not sending an install ping.
|
| + mOmahaClient.setSendInstallEvent(false);
|
| + mOmahaClient.registerNewRequest(requestTimestamp);
|
| + mOmahaClient.getBackoffScheduler().increaseFailedAttempts();
|
| + try {
|
| + mOmahaClient.postRequest(currentTimestamp, xml);
|
| + connection = mOmahaClient.getLastConnection();
|
| + } catch (RequestFailureException e) {
|
| + fail();
|
| + }
|
| + assertEquals("Age property field was unexpectedly added.", null,
|
| + connection.getRequestPropertyField());
|
| + assertEquals("Age property value was unexpectedly set.", null,
|
| + connection.getRequestPropertyValue());
|
| + }
|
| +
|
| + @SmallTest
|
| + @Feature({"Omaha"})
|
| + public void testInstallSource() {
|
| + HookedOmahaClient organicClient = new HookedOmahaClient(mContext, DeviceType.TABLET,
|
| + ServerResponse.SUCCESS, ConnectionStatus.RESPONDS, false);
|
| + String organicInstallSource = organicClient.determineInstallSource(mContext);
|
| + assertEquals("Install source should have been treated as organic.",
|
| + OmahaClient.INSTALL_SOURCE_ORGANIC, organicInstallSource);
|
| +
|
| + HookedOmahaClient systemImageClient = new HookedOmahaClient(mContext, DeviceType.TABLET,
|
| + ServerResponse.SUCCESS, ConnectionStatus.RESPONDS, true);
|
| + String systemImageInstallSource = systemImageClient.determineInstallSource(mContext);
|
| + assertEquals("Install source should have been treated as system image.",
|
| + OmahaClient.INSTALL_SOURCE_SYSTEM, systemImageInstallSource);
|
| + }
|
| +
|
| + /**
|
| + * OmahaClient that overrides simple methods for testing.
|
| + */
|
| + private static class HookedOmahaClient extends OmahaClient {
|
| + private final boolean mIsOnTablet;
|
| + private final boolean mSendValidResponse;
|
| + private final boolean mIsInForeground;
|
| + private final boolean mConnectionTimesOut;
|
| + private final boolean mInstalledOnSystemImage;
|
| +
|
| + private MockExponentialBackoffScheduler mMockScheduler;
|
| + private RequestGenerator mMockGenerator;
|
| + private final LinkedList<MockConnection> mMockConnections;
|
| +
|
| + private boolean mRequestAlarmWasSet;
|
| + private int mNumUUIDsGenerated;
|
| +
|
| + public static HookedOmahaClient create(Context context) {
|
| + HookedOmahaClient omahaClient = new HookedOmahaClient(context, DeviceType.TABLET,
|
| + ServerResponse.SUCCESS, ConnectionStatus.RESPONDS, false);
|
| + omahaClient.onCreate();
|
| + omahaClient.restoreState();
|
| + return omahaClient;
|
| + }
|
| +
|
| + public HookedOmahaClient(Context context, DeviceType deviceType,
|
| + ServerResponse serverResponse, ConnectionStatus connectionStatus,
|
| + boolean installedOnSystemImage) {
|
| + attachBaseContext(context);
|
| + mIsOnTablet = deviceType == DeviceType.TABLET;
|
| + mSendValidResponse = serverResponse == ServerResponse.SUCCESS;
|
| + mIsInForeground = true;
|
| + mConnectionTimesOut = connectionStatus == ConnectionStatus.TIMES_OUT;
|
| + mMockConnections = new LinkedList<MockConnection>();
|
| + mInstalledOnSystemImage = installedOnSystemImage;
|
| + }
|
| +
|
| + @Override
|
| + protected boolean isChromeBeingUsed() {
|
| + return mIsInForeground;
|
| + }
|
| +
|
| + @Override
|
| + public int getApplicationFlags() {
|
| + return mInstalledOnSystemImage ? ApplicationInfo.FLAG_SYSTEM : 0;
|
| + }
|
| +
|
| + /**
|
| + * Checks if an alarm was set by the backoff scheduler.
|
| + */
|
| + public boolean getPOSTAlarmWasSet() {
|
| + return mMockScheduler.getAlarmWasSet();
|
| + }
|
| +
|
| + public boolean getRequestAlarmWasSet() {
|
| + return mRequestAlarmWasSet;
|
| + }
|
| +
|
| + /**
|
| + * Gets the number of MockConnections created.
|
| + */
|
| + public int getNumConnectionsMade() {
|
| + return mMockConnections.size();
|
| + }
|
| +
|
| + /**
|
| + * Returns a particular connection.
|
| + */
|
| + public MockConnection getConnection(int index) {
|
| + return mMockConnections.get(index);
|
| + }
|
| +
|
| + /**
|
| + * Returns the last MockPingConection used to simulate communication with the server.
|
| + */
|
| + public MockConnection getLastConnection() {
|
| + return mMockConnections.getLast();
|
| + }
|
| +
|
| + public boolean isSendInstallEvent() {
|
| + return mSendInstallEvent;
|
| + }
|
| +
|
| + public void setSendInstallEvent(boolean state) {
|
| + mSendInstallEvent = state;
|
| + }
|
| +
|
| + MockExponentialBackoffScheduler getBackoffScheduler() {
|
| + return mMockScheduler;
|
| + }
|
| +
|
| + /**
|
| + * Mocks out the scheduler so that no alarms are really created.
|
| + */
|
| + @Override
|
| + ExponentialBackoffScheduler createBackoffScheduler(String prefPackage, Context context,
|
| + long base, long max) {
|
| + mMockScheduler = new MockExponentialBackoffScheduler(prefPackage, context, base, max);
|
| + return mMockScheduler;
|
| + }
|
| +
|
| + @Override
|
| + RequestGenerator createRequestGenerator(Context context) {
|
| + mMockGenerator = new MockRequestGenerator(
|
| + context, mIsOnTablet ? DeviceType.TABLET : DeviceType.HANDSET);
|
| + return mMockGenerator;
|
| + }
|
| +
|
| + @Override
|
| + protected HttpURLConnection createConnection() throws RequestFailureException {
|
| + MockConnection connection = null;
|
| + try {
|
| + URL url = new URL(mMockGenerator.getServerUrl());
|
| + connection = new MockConnection(url, mIsOnTablet, mSendValidResponse,
|
| + mSendInstallEvent, mConnectionTimesOut);
|
| + mMockConnections.addLast(connection);
|
| + } catch (MalformedURLException e) {
|
| + fail("Caught a malformed URL exception: " + e);
|
| + }
|
| + return connection;
|
| + }
|
| +
|
| + @Override
|
| + protected void setAlarm(AlarmManager am, PendingIntent operation, int alarmType,
|
| + long triggerAtTime) {
|
| + mRequestAlarmWasSet = true;
|
| + }
|
| +
|
| + @Override
|
| + protected String generateRandomUUID() {
|
| + mNumUUIDsGenerated += 1;
|
| + return "UUID" + mNumUUIDsGenerated;
|
| + }
|
| + }
|
| +
|
| +
|
| + /**
|
| + * Simulates communication with the actual Omaha server.
|
| + */
|
| + private static class MockConnection extends HttpURLConnection {
|
| + // Omaha appends a "/" to the URL.
|
| + private static final String STRIPPED_MARKET_URL =
|
| + "https://market.android.com/details?id=com.google.android.apps.chrome";
|
| + private static final String MARKET_URL = STRIPPED_MARKET_URL + "/";
|
| +
|
| + private static final String UPDATE_VERSION = "1.2.3.4";
|
| +
|
| + // Parameters.
|
| + private final boolean mConnectionTimesOut;
|
| + private final ByteArrayInputStream mServerResponse;
|
| + private final ByteArrayOutputStream mOutputStream;
|
| + private final int mHTTPResponseCode;
|
| +
|
| + // Result variables.
|
| + private int mContentLength;
|
| + private int mNumTimesResponseCodeRetrieved;
|
| + private boolean mSentRequest;
|
| + private boolean mGotInputStream;
|
| + private String mRequestPropertyField;
|
| + private String mRequestPropertyValue;
|
| +
|
| + MockConnection(URL url, boolean usingTablet, boolean sendValidResponse,
|
| + boolean sendInstallEvent, boolean connectionTimesOut) {
|
| + super(url);
|
| + assertEquals(MockRequestGenerator.SERVER_URL, url.toString());
|
| +
|
| + String mockResponse = buildServerResponseString(usingTablet, sendInstallEvent);
|
| + mOutputStream = new ByteArrayOutputStream();
|
| + mServerResponse = new ByteArrayInputStream(mockResponse.getBytes());
|
| + mConnectionTimesOut = connectionTimesOut;
|
| +
|
| + if (sendValidResponse) {
|
| + mHTTPResponseCode = 200;
|
| + } else {
|
| + mHTTPResponseCode = 404;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Build a simulated response from the Omaha server indicating an update is available.
|
| + * The response changes based on the device type.
|
| + */
|
| + private String buildServerResponseString(boolean isOnTablet, boolean sendInstallEvent) {
|
| + String response = "";
|
| + response += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
| + response += "<response protocol=\"3.0\" server=\"prod\">";
|
| + response += "<daystart elapsed_seconds=\"12345\"/>";
|
| + response += "<app appid=\"";
|
| + response += (isOnTablet
|
| + ? MockRequestGenerator.UUID_TABLET : MockRequestGenerator.UUID_PHONE);
|
| + response += "\" status=\"ok\">";
|
| + if (sendInstallEvent) {
|
| + response += "<event status=\"ok\"/>";
|
| + } else {
|
| + response += "<updatecheck status=\"ok\">";
|
| + response += "<urls><url codebase=\"" + MARKET_URL + "\"/></urls>";
|
| + response += "<manifest version=\"" + UPDATE_VERSION + "\">";
|
| + response += "<packages>";
|
| + response += "<package hash=\"0\" name=\"dummy.apk\" required=\"true\" size=\"0\"/>";
|
| + response += "</packages>";
|
| + response += "<actions>";
|
| + response += "<action event=\"install\" run=\"dummy.apk\"/>";
|
| + response += "<action event=\"postinstall\"/>";
|
| + response += "</actions>";
|
| + response += "</manifest>";
|
| + response += "</updatecheck>";
|
| + response += "<ping status=\"ok\"/>";
|
| + }
|
| + response += "</app>";
|
| + response += "</response>";
|
| + return response;
|
| + }
|
| +
|
| + @Override
|
| + public boolean usingProxy() {
|
| + return false;
|
| + }
|
| +
|
| + @Override
|
| + public void connect() throws SocketTimeoutException {
|
| + if (mConnectionTimesOut) {
|
| + throw new SocketTimeoutException("Connection timed out.");
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void disconnect() {}
|
| +
|
| + @Override
|
| + public void setDoOutput(boolean value) throws IllegalAccessError {
|
| + assertTrue("Told the HTTPUrlConnection to send no request.", value);
|
| + }
|
| +
|
| + @Override
|
| + public void setFixedLengthStreamingMode(int contentLength) {
|
| + mContentLength = contentLength;
|
| + }
|
| +
|
| + @Override
|
| + public int getResponseCode() {
|
| + if (mNumTimesResponseCodeRetrieved == 0) {
|
| + // The output stream should now have the generated XML for the request.
|
| + // Check if its length is correct.
|
| + assertEquals("Expected OmahaClient to write out certain number of bytes",
|
| + mContentLength, mOutputStream.toByteArray().length);
|
| + }
|
| + assertTrue("Tried to retrieve response code more than twice",
|
| + mNumTimesResponseCodeRetrieved < 2);
|
| + mNumTimesResponseCodeRetrieved++;
|
| + return mHTTPResponseCode;
|
| + }
|
| +
|
| + @Override
|
| + public OutputStream getOutputStream() throws IOException {
|
| + mSentRequest = true;
|
| + connect();
|
| + return mOutputStream;
|
| + }
|
| +
|
| + public String getOutputStreamContents() {
|
| + return mOutputStream.toString();
|
| + }
|
| +
|
| + @Override
|
| + public InputStream getInputStream() {
|
| + assertTrue("Tried to read server response without sending request.", mSentRequest);
|
| + mGotInputStream = true;
|
| + return mServerResponse;
|
| + }
|
| +
|
| + @Override
|
| + public void addRequestProperty(String field, String newValue) {
|
| + mRequestPropertyField = field;
|
| + mRequestPropertyValue = newValue;
|
| + }
|
| +
|
| + public int getNumTimesResponseCodeRetrieved() {
|
| + return mNumTimesResponseCodeRetrieved;
|
| + }
|
| +
|
| + public boolean getGotInputStream() {
|
| + return mGotInputStream;
|
| + }
|
| +
|
| + public boolean getSentRequest() {
|
| + return mSentRequest;
|
| + }
|
| +
|
| + public String getRequestPropertyField() {
|
| + return mRequestPropertyField;
|
| + }
|
| +
|
| + public String getRequestPropertyValue() {
|
| + return mRequestPropertyValue;
|
| + }
|
| + }
|
| +
|
| + private static class MockOmahaContext extends AdvancedMockContext {
|
| + private boolean mOmahaClientRestarted;
|
| + private Intent mIntentFired;
|
| +
|
| + public MockOmahaContext(Context targetContext) {
|
| + super(targetContext);
|
| + }
|
| +
|
| + @Override
|
| + public ComponentName startService(Intent intent) {
|
| + assertEquals(OmahaClient.class.getCanonicalName(),
|
| + intent.getComponent().getClassName());
|
| + mOmahaClientRestarted = true;
|
| + mIntentFired = intent;
|
| + return intent.getComponent();
|
| + }
|
| + }
|
| +}
|
|
|