15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file. 45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)package org.chromium.chrome.browser.omaha; 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.test.InstrumentationTestCase; 85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.test.suitebuilder.annotation.SmallTest; 95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.util.Xml; 105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import org.chromium.base.test.util.Feature; 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import org.xmlpull.v1.XmlSerializer; 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.io.IOException; 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.io.StringWriter; 165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/** 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Unit tests for the Omaha ResponseParser. 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)public class ResponseParserTest extends InstrumentationTestCase { 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Note that the Omaha server appends "/" to the end of the URL codebase. 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String STRIPPED_URL = 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "https://play.google.com/store/apps/details?id=com.google.android.apps.chrome"; 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String URL = STRIPPED_URL + "/"; 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String NEXT_VERSION = "1.2.3.4"; 265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String APP_STATUS_OK = "ok"; 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String APP_STATUS_RESTRICTED = "restricted"; 295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String APP_STATUS_ERROR = "error-whatever-else"; 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String UPDATE_STATUS_OK = "ok"; 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String UPDATE_STATUS_NOUPDATE = "noupdate"; 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String UPDATE_STATUS_ERROR = "error-osnotsupported"; 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static final String UPDATE_STATUS_WTF = "omgwtfbbq"; 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) /** 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Create XML for testing. 385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param xmlProtocol Version number of the protocol. Expected to be "3.0" for valid XML. 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param elapsedSeconds Number of seconds since server midnight. 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param appStatus Status to use for the <app> element. 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param addInstall Whether or not to add an install event. 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param addPing Whether or not to add a ping event. 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param updateStatus Status to use for the <updatecheck> element. 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return The completed XML. 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static String createTestXML(String xmlProtocol, String elapsedSeconds, 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String appStatus, boolean addInstall, boolean addPing, String updateStatus, 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String updateUrl) { 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) StringWriter writer = new StringWriter(); 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) try { 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) XmlSerializer serializer = Xml.newSerializer(); 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.setOutput(writer); 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startDocument("UTF-8", true); 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Set up <response ...> 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "response"); 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "server", "prod"); 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (xmlProtocol != null) { 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "protocol", xmlProtocol); 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create <daystart> element. 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (elapsedSeconds != null) { 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "daystart"); 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "elapsed_seconds", elapsedSeconds); 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "daystart"); 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create <app> element with unused attribute. 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "app"); 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "appid", "{APP_ID}"); 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "status", appStatus); 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "unused", "attribute"); 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (addInstall) { 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "event"); 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "status", "ok"); 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "event"); 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (addPing) { 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "ping"); 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "status", "ok"); 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "ping"); 855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (updateStatus != null) { 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "updatecheck"); 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "status", updateStatus); 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (UPDATE_STATUS_OK.equals(updateStatus)) { 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createUpdateXML(serializer, updateUrl); 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "updatecheck"); 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "app"); 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create extraneous tag. 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "extraneous"); 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "useless", "yes"); 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "extraneous"); 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "response"); 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endDocument(); 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } catch (IOException e) { 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fail("Caught an IOException creating the XML: " + e); 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } catch (IllegalArgumentException e) { 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fail("Caught an IllegalArgumentException creating the XML: " + e); 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } catch (IllegalStateException e) { 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fail("Caught an IllegalStateException creating the XML: " + e); 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return writer.toString(); 1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static void createUpdateXML(XmlSerializer serializer, String updateUrl) 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) throws IOException { 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // End result should look something like: 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <updatecheck status="ok"> 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <urls> 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <url codebase="URL" /> 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // </urls> 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <manifest garbage="attribute" version="NEXT_VERSION"> 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <packages> 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <package hash="0" name="dummy.apk" required="true" size="0" /> 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // </packages> 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <actions> 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <action event="install" run="dummy.apk" /> 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <action event="postinstall" /> 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // </actions> 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // </manifest> 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // <better be="ignored" /> 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) //</updatecheck> 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create <urls> and its descendants. 1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "urls"); 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (updateUrl != null) { 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "url"); 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "codebase", updateUrl); 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "url"); 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "urls"); 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create <manifest> and its descendants. 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "manifest"); 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "garbage", "attribute"); 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "version", NEXT_VERSION); 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create <packages> and its children. 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "packages"); 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "package"); 1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "hash", "0"); 1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "name", "dummy.apk"); 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "required", "true"); 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "size", "0"); 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "package"); 1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "packages"); 1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create <actions> and its children. 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "actions"); 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "action"); 1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "event", "install"); 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "run", "dummy.apk"); 1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "action"); 1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "action"); 1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "event", "postinstall"); 1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "action"); 1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "actions"); 1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "manifest"); 1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Create a dummy element for testing to make sure it's ignored. 1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.startTag(null, "dummy"); 1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.attribute(null, "hopefully", "ignored"); 1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serializer.endTag(null, "dummy"); 1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) /** 1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Runs a test that is expected to pass. 1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param appStatus Status to use for the <app> element. 1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param addInstall Whether or not to add an install event. 1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param addPing Whether or not to add a ping. 1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param updateStatus Status to use for the <updatecheck> element. 1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @throws RequestFailureException Thrown if the test fails. 1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static void runSuccessTest(String appStatus, boolean addInstall, boolean addPing, 1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String updateStatus) throws RequestFailureException { 1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", appStatus, addInstall, addPing, updateStatus, URL); 1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ResponseParser parser = 1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new ResponseParser(true, "{APP_ID}", addInstall, addPing, updateStatus != null); 1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) parser.parseResponse(xml); 1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("elapsed_seconds doesn't match.", 12345, parser.getDaystartSeconds()); 1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("<app> status doesn't match.", appStatus, parser.getAppStatus()); 1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("<updatecheck> status doesn't match.", updateStatus, parser.getUpdateStatus()); 1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (UPDATE_STATUS_OK.equals(updateStatus)) { 1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("Version number doesn't match.", "1.2.3.4", parser.getNewVersion()); 1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("Market URL doesn't match.", STRIPPED_URL, parser.getURL()); 1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("Version number doesn't match.", null, parser.getNewVersion()); 2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("Market URL doesn't match.", null, parser.getURL()); 2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) /** 2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Runs a test that is expected to fail in a particular way. 2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param xml XML to parse. 2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param expectedErrorCode Expected error code. 2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param expectInstall Whether or not the parser should expect an install event. 2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param expectPing Whether or not the parser should expect a ping element. 2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param expectUpdate Whether or not the parser should expect an update check. 2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private static void runFailureTest(String xml, int expectedErrorCode, 2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) boolean expectInstall, boolean expectPing, boolean expectUpdate) { 2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ResponseParser parser = 2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new ResponseParser(true, "{APP_ID}", expectInstall, expectPing, expectUpdate); 2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) try { 2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) parser.parseResponse(xml); 2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } catch (RequestFailureException e) { 2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) assertEquals("Incorrect error code received.", expectedErrorCode, e.errorCode); 2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fail("Failed to throw RequestFailureException for bad XML."); 2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidAllTypes() throws RequestFailureException { 2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_OK, true, true, UPDATE_STATUS_OK); 2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidNoInstall() throws RequestFailureException { 2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_OK, false, true, UPDATE_STATUS_OK); 2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidNoPing() throws RequestFailureException { 2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_OK, true, false, UPDATE_STATUS_OK); 2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidNoUpdatecheck() throws RequestFailureException { 2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_OK, true, true, null); 2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidUpdatecheckNoUpdate() throws RequestFailureException { 2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_NOUPDATE); 2565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidUpdatecheckError() throws RequestFailureException { 2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_ERROR); 2625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidUpdatecheckUnknown() throws RequestFailureException { 2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_WTF); 2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testValidAppStatusRestricted() throws RequestFailureException { 2735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runSuccessTest(APP_STATUS_RESTRICTED, false, false, null); 2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testFailBogusResponse() { 2795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = "Bogus"; 2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_MALFORMED_XML, false, false, false); 2815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testBadResponseProtocol() { 2865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("2.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); 2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_RESPONSE, false, false, false); 2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testFailMissingDaystart() { 2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 2955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", null, APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); 2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_DAYSTART, false, false, true); 2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testAppTagMissingUpdatecheck() { 3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", APP_STATUS_OK, true, false, null, URL); 3045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, true); 3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testAppTagUnexpectedUpdatecheck() { 3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL); 3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, false); 3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testAppTagMissingPing() { 3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); 3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, true, true); 3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testAppTagUnexpectedPing() { 3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", APP_STATUS_OK, false, true, UPDATE_STATUS_OK, URL); 3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, false, true); 3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testAppTagMissingInstall() { 3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); 3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, true, false, true); 3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testAppTagUnexpectedInstall() { 3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL); 3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, false, false, true); 3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testAppTagStatusError() { 3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = 3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) createTestXML("3.0", "12345", APP_STATUS_ERROR, false, false, null, URL); 3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_APP, false, false, false); 3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @SmallTest 3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @Feature({"Omaha"}) 3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void testUpdatecheckMissingUrl() { 3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) String xml = createTestXML( 3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, null); 3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) runFailureTest(xml, RequestFailureException.ERROR_PARSE_URLS, false, false, true); 3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 363