root/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. createTestXML
  2. createUpdateXML
  3. runSuccessTest
  4. runFailureTest
  5. Feature
  6. testValidAllTypes
  7. Feature
  8. testValidNoInstall
  9. Feature
  10. testValidNoPing
  11. Feature
  12. testValidNoUpdatecheck
  13. Feature
  14. testValidUpdatecheckNoUpdate
  15. Feature
  16. testValidUpdatecheckError
  17. Feature
  18. testValidUpdatecheckUnknown
  19. Feature
  20. testValidAppStatusRestricted
  21. Feature
  22. testFailBogusResponse
  23. Feature
  24. testBadResponseProtocol
  25. Feature
  26. testFailMissingDaystart
  27. Feature
  28. testAppTagMissingUpdatecheck
  29. Feature
  30. testAppTagUnexpectedUpdatecheck
  31. Feature
  32. testAppTagMissingPing
  33. Feature
  34. testAppTagUnexpectedPing
  35. Feature
  36. testAppTagMissingInstall
  37. Feature
  38. testAppTagUnexpectedInstall
  39. Feature
  40. testAppTagStatusError
  41. Feature
  42. testUpdatecheckMissingUrl

// Copyright 2014 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 android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Xml;

import org.chromium.base.test.util.Feature;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.io.StringWriter;

/**
 * Unit tests for the Omaha ResponseParser.
 */
public class ResponseParserTest extends InstrumentationTestCase {
    // Note that the Omaha server appends "/" to the end of the URL codebase.
    private static final String STRIPPED_URL =
            "https://play.google.com/store/apps/details?id=com.google.android.apps.chrome";
    private static final String URL = STRIPPED_URL + "/";
    private static final String NEXT_VERSION = "1.2.3.4";

    private static final String APP_STATUS_OK = "ok";
    private static final String APP_STATUS_RESTRICTED = "restricted";
    private static final String APP_STATUS_ERROR = "error-whatever-else";

    private static final String UPDATE_STATUS_OK = "ok";
    private static final String UPDATE_STATUS_NOUPDATE = "noupdate";
    private static final String UPDATE_STATUS_ERROR = "error-osnotsupported";
    private static final String UPDATE_STATUS_WTF = "omgwtfbbq";

    /**
     * Create XML for testing.
     * @param xmlProtocol Version number of the protocol.  Expected to be "3.0" for valid XML.
     * @param elapsedSeconds Number of seconds since server midnight.
     * @param appStatus Status to use for the <app> element.
     * @param addInstall Whether or not to add an install event.
     * @param addPing Whether or not to add a ping event.
     * @param updateStatus Status to use for the <updatecheck> element.
     * @return The completed XML.
     */
    private static String createTestXML(String xmlProtocol, String elapsedSeconds,
            String appStatus, boolean addInstall, boolean addPing, String updateStatus,
            String updateUrl) {
        StringWriter writer = new StringWriter();
        try {
            XmlSerializer serializer = Xml.newSerializer();
            serializer.setOutput(writer);
            serializer.startDocument("UTF-8", true);

            // Set up <response ...>
            serializer.startTag(null, "response");
            serializer.attribute(null, "server", "prod");
            if (xmlProtocol != null) {
                serializer.attribute(null, "protocol", xmlProtocol);
            }

            // Create <daystart> element.
            if (elapsedSeconds != null) {
                serializer.startTag(null, "daystart");
                serializer.attribute(null, "elapsed_seconds", elapsedSeconds);
                serializer.endTag(null, "daystart");
            }

            // Create <app> element with unused attribute.
            serializer.startTag(null, "app");
            serializer.attribute(null, "appid", "{APP_ID}");
            serializer.attribute(null, "status", appStatus);
            serializer.attribute(null, "unused", "attribute");

            if (addInstall) {
                serializer.startTag(null, "event");
                serializer.attribute(null, "status", "ok");
                serializer.endTag(null, "event");
            }

            if (addPing) {
                serializer.startTag(null, "ping");
                serializer.attribute(null, "status", "ok");
                serializer.endTag(null, "ping");
            }

            if (updateStatus != null) {
                serializer.startTag(null, "updatecheck");
                serializer.attribute(null, "status", updateStatus);
                if (UPDATE_STATUS_OK.equals(updateStatus)) {
                    createUpdateXML(serializer, updateUrl);
                }
                serializer.endTag(null, "updatecheck");
            }
            serializer.endTag(null, "app");

            // Create extraneous tag.
            serializer.startTag(null, "extraneous");
            serializer.attribute(null, "useless", "yes");
            serializer.endTag(null, "extraneous");

            serializer.endTag(null, "response");
            serializer.endDocument();
        } catch (IOException e) {
            fail("Caught an IOException creating the XML: " + e);
        } catch (IllegalArgumentException e) {
            fail("Caught an IllegalArgumentException creating the XML: " + e);
        } catch (IllegalStateException e) {
            fail("Caught an IllegalStateException creating the XML: " + e);
        }

        return writer.toString();
    }

    private static void createUpdateXML(XmlSerializer serializer, String updateUrl)
            throws IOException {
        // End result should look something like:
        // <updatecheck status="ok">
        //  <urls>
        //    <url codebase="URL" />
        //  </urls>
        //  <manifest garbage="attribute" version="NEXT_VERSION">
        //    <packages>
        //      <package hash="0" name="dummy.apk" required="true" size="0" />
        //    </packages>
        //    <actions>
        //      <action event="install" run="dummy.apk" />
        //      <action event="postinstall" />
        //    </actions>
        //  </manifest>
        //  <better be="ignored" />
        //</updatecheck>

        // Create <urls> and its descendants.
        serializer.startTag(null, "urls");
        if (updateUrl != null) {
            serializer.startTag(null, "url");
            serializer.attribute(null, "codebase", updateUrl);
            serializer.endTag(null, "url");
        }
        serializer.endTag(null, "urls");

        // Create <manifest> and its descendants.
        serializer.startTag(null, "manifest");
        serializer.attribute(null, "garbage", "attribute");
        serializer.attribute(null, "version", NEXT_VERSION);

        // Create <packages> and its children.
        serializer.startTag(null, "packages");
        serializer.startTag(null, "package");
        serializer.attribute(null, "hash", "0");
        serializer.attribute(null, "name", "dummy.apk");
        serializer.attribute(null, "required", "true");
        serializer.attribute(null, "size", "0");
        serializer.endTag(null, "package");
        serializer.endTag(null, "packages");

        // Create <actions> and its children.
        serializer.startTag(null, "actions");
        serializer.startTag(null, "action");
        serializer.attribute(null, "event", "install");
        serializer.attribute(null, "run", "dummy.apk");
        serializer.endTag(null, "action");

        serializer.startTag(null, "action");
        serializer.attribute(null, "event", "postinstall");
        serializer.endTag(null, "action");
        serializer.endTag(null, "actions");
        serializer.endTag(null, "manifest");

        // Create a dummy element for testing to make sure it's ignored.
        serializer.startTag(null, "dummy");
        serializer.attribute(null, "hopefully", "ignored");
        serializer.endTag(null, "dummy");
    }

    /**
     * Runs a test that is expected to pass.
     * @param appStatus Status to use for the <app> element.
     * @param addInstall Whether or not to add an install event.
     * @param addPing Whether or not to add a ping.
     * @param updateStatus Status to use for the <updatecheck> element.
     * @throws RequestFailureException Thrown if the test fails.
     */
    private static void runSuccessTest(String appStatus, boolean addInstall, boolean addPing,
            String updateStatus) throws RequestFailureException {
        String xml =
                createTestXML("3.0", "12345", appStatus, addInstall, addPing, updateStatus, URL);
        ResponseParser parser =
                new ResponseParser(true, "{APP_ID}", addInstall, addPing, updateStatus != null);
        parser.parseResponse(xml);

        assertEquals("elapsed_seconds doesn't match.", 12345, parser.getDaystartSeconds());
        assertEquals("<app> status doesn't match.", appStatus, parser.getAppStatus());
        assertEquals("<updatecheck> status doesn't match.", updateStatus, parser.getUpdateStatus());
        if (UPDATE_STATUS_OK.equals(updateStatus)) {
            assertEquals("Version number doesn't match.", "1.2.3.4", parser.getNewVersion());
            assertEquals("Market URL doesn't match.", STRIPPED_URL, parser.getURL());
        } else {
            assertEquals("Version number doesn't match.", null, parser.getNewVersion());
            assertEquals("Market URL doesn't match.", null, parser.getURL());
        }
    }

    /**
     * Runs a test that is expected to fail in a particular way.
     * @param xml XML to parse.
     * @param expectedErrorCode Expected error code.
     * @param expectInstall Whether or not the parser should expect an install event.
     * @param expectPing Whether or not the parser should expect a ping element.
     * @param expectUpdate Whether or not the parser should expect an update check.
     */
    private static void runFailureTest(String xml, int expectedErrorCode,
            boolean expectInstall, boolean expectPing, boolean expectUpdate) {
        ResponseParser parser =
                new ResponseParser(true, "{APP_ID}", expectInstall, expectPing, expectUpdate);

        try {
            parser.parseResponse(xml);
        } catch (RequestFailureException e) {
            assertEquals("Incorrect error code received.", expectedErrorCode, e.errorCode);
            return;
        }

        fail("Failed to throw RequestFailureException for bad XML.");
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidAllTypes() throws RequestFailureException {
        runSuccessTest(APP_STATUS_OK, true, true, UPDATE_STATUS_OK);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidNoInstall() throws RequestFailureException {
        runSuccessTest(APP_STATUS_OK, false, true, UPDATE_STATUS_OK);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidNoPing() throws RequestFailureException {
        runSuccessTest(APP_STATUS_OK, true, false, UPDATE_STATUS_OK);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidNoUpdatecheck() throws RequestFailureException {
        runSuccessTest(APP_STATUS_OK, true, true, null);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidUpdatecheckNoUpdate() throws RequestFailureException {
        runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_NOUPDATE);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidUpdatecheckError() throws RequestFailureException {
        runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_ERROR);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidUpdatecheckUnknown() throws RequestFailureException {
        runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_WTF);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testValidAppStatusRestricted() throws RequestFailureException {
        runSuccessTest(APP_STATUS_RESTRICTED, false, false, null);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testFailBogusResponse() {
        String xml = "Bogus";
        runFailureTest(xml, RequestFailureException.ERROR_MALFORMED_XML, false, false, false);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testBadResponseProtocol() {
        String xml =
                createTestXML("2.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_RESPONSE, false, false, false);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testFailMissingDaystart() {
        String xml =
                createTestXML("3.0", null, APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_DAYSTART, false, false, true);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testAppTagMissingUpdatecheck() {
        String xml =
                createTestXML("3.0", "12345", APP_STATUS_OK, true, false, null, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, true);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testAppTagUnexpectedUpdatecheck() {
        String xml =
                createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, false);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testAppTagMissingPing() {
        String xml =
                createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, true, true);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testAppTagUnexpectedPing() {
        String xml =
                createTestXML("3.0", "12345", APP_STATUS_OK, false, true, UPDATE_STATUS_OK, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, false, true);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testAppTagMissingInstall() {
        String xml =
                createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, true, false, true);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testAppTagUnexpectedInstall() {
        String xml =
                createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, false, false, true);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testAppTagStatusError() {
        String xml =
                createTestXML("3.0", "12345", APP_STATUS_ERROR, false, false, null, URL);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_APP, false, false, false);
    }

    @SmallTest
    @Feature({"Omaha"})
    public void testUpdatecheckMissingUrl() {
        String xml = createTestXML(
                "3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, null);
        runFailureTest(xml, RequestFailureException.ERROR_PARSE_URLS, false, false, true);
    }
}

/* [<][>][^][v][top][bottom][index][help] */