Initial cts tradefed xml result reporter.

Change-Id: Ibdb5714d908dc2fd7a1a308576524482089a24fb
diff --git a/tools/tradefed-host/res/result/cts_result.css b/tools/tradefed-host/res/result/cts_result.css
new file mode 100644
index 0000000..d7ce510
--- /dev/null
+++ b/tools/tradefed-host/res/result/cts_result.css
@@ -0,0 +1,234 @@
+/* Copyright (C) 2008 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+body {
+    font-family:arial,sans-serif;
+    color:#000;
+    font-size:13px;
+    color:#333;
+    padding:10;
+    margin:10;
+}
+
+table {
+    font-size:1em;
+    margin:0 0 1em;
+    padding:0;
+    border-collapse:collapse;
+    border-width:0;
+    empty-cells:show;
+    width: 95%;
+}
+
+/* Report logo and device name */
+#title table {
+    padding:5px;
+    border-width: 0px;
+    margin-left:auto;
+    margin-right:auto;
+    vertical-align:middle;
+}
+
+/* Device and test plan summary below the title */
+#summary table {
+    background-color: rgb(212, 233, 169);
+    -moz-border-radius:5px;
+    border-radius:5px;
+    padding:5px;
+    border-color: #A5C639 #A5C639 #A5C639 #A5C639;
+    border-width: 0px 0px 0px 0px;
+    margin-left:auto;
+    margin-right:auto;
+    width:80%;
+}
+
+#summary th {
+    background-color: #A5C639;
+    font-size:1.2em;
+    height: 2em;
+    width: 50%;
+}
+
+#summary td {
+    border-width: 0px 0px 0px 0px;
+    border-color: gray;
+    border-style: inset;
+    font-size:1em;
+    vertical-align: top;
+}
+
+#summaryinfo table {
+    background-color: rgb(212, 233, 169);
+    padding:5px;
+    border-width:0;
+    margin-left:auto;
+    margin-right:auto;
+}
+
+#summaryinfo td {
+    padding:1px;
+    border-width: 0px 0px 0px 0px;
+    vertical-align: top;
+}
+
+/* The test summary */
+#testsummary table {
+    background-color: rgb(212, 233, 169);
+    padding:5px;
+    border-width:1;
+    border-color: #A5C639;
+    margin-left:auto;
+    margin-right:auto;
+    width: 40%;
+}
+
+#testsummary th {
+    background-color: #A5C639;
+    border-width: 1px;
+    border-color: gray;
+    border-style: outset;
+    height: 2em;
+}
+
+#testsummary td {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: center;
+}
+
+/* The test details */
+#testdetail table {
+    background-color: rgb(212, 233, 169);
+    padding:5px;
+    border-width:1;
+    border-color: #A5C639;
+    margin-left:auto;
+    margin-right:auto;
+    width: 95%;
+    table-layout:fixed;
+    vertical-align: top;
+}
+
+#testdetail th {
+    background-color: #A5C639;
+    border-width: 1px;
+    border-color: gray;
+    border-style: outset;
+    height: 2em;
+}
+
+#testdetail td {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+}
+
+/* The test package name */
+#none table {
+    border-width:0;
+    border-color: white;
+    background-color: white;
+    text-align:left;
+    border-collapse:collapse;
+}
+
+#none td {
+    border-width:0;
+    border-color: white;
+    background-color: white;
+    text-align:left;
+    border-collapse:collapse;
+    font-weight:bold;
+}
+
+/* Test cell details */
+td.failed {
+    background-color: #FA5858;
+    font-weight:bold;
+    vertical-align: top;
+    text-align: center;
+}
+
+td.failuredetails {
+    text-align: left;
+}
+
+td.pass {
+    text-align: center;
+    margin-left:auto;
+    margin-right:auto;
+}
+
+td.timeout, td.omitted, td.notExecuted {
+    background-color: #A5C639;
+    vertical-align: top;
+    text-align: center;
+}
+
+td.testname {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+}
+
+td.testcase {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+    font-weight:bold;
+}
+
+td.testcasespacer {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+    font-weight:bold;
+}
+
+td.testsuite {
+    border-width: 1px;
+    border-color: #A5C639;
+    border-style: outset;
+    text-align: left;
+    vertical-align: top;
+    padding:1;
+    overflow:hidden;
+    font-weight:bold;
+}
+
+#details {
+    white-space: pre-wrap;       /* css-3 */
+    white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+    white-space: -pre-wrap;      /* Opera 4-6 */
+    white-space: -o-pre-wrap;    /* Opera 7 */
+    word-wrap: break-word;       /* Internet Explorer 5.5+ */
+    overflow:auto;
+}
diff --git a/tools/tradefed-host/res/result/cts_result.xsd b/tools/tradefed-host/res/result/cts_result.xsd
new file mode 100644
index 0000000..f2fc3a8
--- /dev/null
+++ b/tools/tradefed-host/res/result/cts_result.xsd
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://compatibility.android.com/cts_result/2.0"
+           xmlns="http://compatibility.android.com/cts_result/2.0"
+           elementFormDefault="qualified">
+
+<xs:element name="TestResult">
+  <xs:complexType>
+    <xs:sequence>
+      <xs:element name="DeviceInfo" type="deviceInfoType"/>
+      <xs:element name="HostInfo" type="hostInfoType"/>
+      <xs:element name="Summary" type="summaryType"/>
+      <xs:element name="TestPackage" type="testPackageType" maxOccurs="unbounded" minOccurs="1"/>
+    </xs:sequence>
+    <xs:attribute name="starttime" type="xs:string"/>
+    <xs:attribute name="endtime" type="xs:string"/>
+    <xs:attribute name="testPlan" type="xs:string"/>
+    <xs:attribute name="version" type="xs:string"/>
+    <xs:attribute name="profile" type="xs:string"/>
+  </xs:complexType>
+</xs:element>
+
+<xs:complexType name="deviceInfoType">
+  <xs:sequence>
+    <xs:element name="Screen">
+      <xs:complexType>
+        <xs:attribute name="resolution" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="PhoneSubInfo">
+      <xs:complexType>
+        <xs:attribute name="subscriberId" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="BuildInfo">
+      <xs:complexType>
+        <xs:attribute name="Xdpi" type="xs:decimal"/>
+        <xs:attribute name="Ydpi" type="xs:decimal"/>
+        <xs:attribute name="androidPlatformVersion" type="xs:integer"/>
+        <xs:attribute name="buildID" type="xs:string"/>
+        <xs:attribute name="buildName" type="xs:string"/>
+        <xs:attribute name="buildVersion" type="xs:string"/>
+        <xs:attribute name="build_board" type="xs:string"/>
+        <xs:attribute name="build_brand" type="xs:string"/>
+        <xs:attribute name="build_device" type="xs:string"/>
+        <xs:attribute name="build_fingerprint" type="xs:string"/>
+        <xs:attribute name="build_model" type="xs:string"/>
+        <xs:attribute name="build_type" type="xs:string"/>
+        <xs:attribute name="deviceID" type="xs:string"/>
+        <xs:attribute name="imei" type="xs:integer"/>
+        <xs:attribute name="imsi" type="xs:integer"/>
+        <xs:attribute name="keypad" type="xs:string"/>
+        <xs:attribute name="locales" type="xs:string"/>
+        <xs:attribute name="navigation" type="xs:string"/>
+        <xs:attribute name="network" type="xs:string"/>
+        <xs:attribute name="touch" type="xs:string"/>
+        <xs:attribute name="openGlEsVersion" type="xs:string"/>
+        <xs:attribute name="build_abi" type="xs:string"/>
+        <xs:attribute name="build_abi2" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="FeatureInfo" type="featureInfoType"/>
+    <xs:element name="ProcessInfo" type="processInfoType"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="hostInfoType">
+  <xs:sequence>
+    <xs:element name="Os">
+      <xs:complexType>
+        <xs:attribute name="arch" type="xs:string"/>
+        <xs:attribute name="name" type="xs:string"/>
+        <xs:attribute name="version" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="Java">
+      <xs:complexType>
+        <xs:attribute name="name" type="xs:string"/>
+        <xs:attribute name="version" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="Cts">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="IntValue" minOccurs="0" maxOccurs="unbounded">
+            <xs:complexType>
+              <xs:attribute name="name" type="xs:string"/>
+              <xs:attribute name="value" type="xs:integer"/>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+        <xs:attribute name="version" type="xs:string"/>
+      </xs:complexType>
+    </xs:element>
+  </xs:sequence>
+  <xs:attribute name="name" type="xs:string"/>
+</xs:complexType>
+
+<xs:complexType name="featureInfoType">
+    <xs:sequence>
+        <xs:element name="Feature" minOccurs="0" maxOccurs="unbounded">
+            <xs:complexType>
+                <xs:attribute name="name" type="xs:string" />
+                <xs:attribute name="type" type="xs:string" />
+                <xs:attribute name="available" type="xs:string" />
+            </xs:complexType>
+        </xs:element>
+    </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="processInfoType">
+    <xs:sequence>
+        <xs:element name="Process" minOccurs="0" maxOccurs="unbounded">
+            <xs:complexType>
+                <xs:attribute name="name" type="xs:string" />
+                <xs:attribute name="uid" type="xs:integer" />
+            </xs:complexType>
+        </xs:element>
+    </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="summaryType">
+  <xs:attribute name="failed" type="xs:integer"/>
+  <xs:attribute name="notExecuted" type="xs:integer"/>
+  <xs:attribute name="pass" type="xs:integer"/>
+  <xs:attribute name="timeout" type="xs:integer"/>
+  <xs:attribute name="omitted" type="xs:integer"/>
+  <xs:attribute name="total" type="xs:integer"/>
+</xs:complexType>
+
+<xs:complexType name="testPackageType">
+    <xs:complexContent>
+        <xs:extension base="summaryType">
+            <xs:sequence>
+                <xs:element name="TestCase" type="testCaseType" />
+            </xs:sequence>
+            <xs:attribute name="digest" type="xs:hexBinary" />
+            <xs:attribute name="name" type="xs:string" use="required" />
+            <xs:attribute name="runtime" type="xs:string" />
+        </xs:extension>
+    </xs:complexContent>
+</xs:complexType>
+
+<xs:complexType name="testCaseType">
+  <xs:sequence>
+    <xs:element name="Test" type="testType" minOccurs="0" maxOccurs="unbounded"/>
+  </xs:sequence>
+  <xs:attribute name="name" type="xs:string" use="required"/>
+</xs:complexType>
+
+<xs:complexType name="testType">
+  <xs:sequence>
+    <xs:element name="FailedScene" minOccurs="0" maxOccurs="1">
+      <xs:complexType>
+          <xs:simpleContent>
+              <xs:extension base="xs:string">
+                  <xs:attribute name="message" type="xs:string" />
+              </xs:extension>
+          </xs:simpleContent>
+      </xs:complexType>
+    </xs:element>
+  </xs:sequence>
+  <xs:attribute name="name" type="xs:string" use="required"/>
+  <xs:attribute name="result" type="resultType" use="required"/>
+</xs:complexType>
+
+<xs:simpleType name="resultType">
+  <xs:restriction base="xs:string">
+    <xs:enumeration value="pass"/>
+    <xs:enumeration value="fail"/>
+    <xs:enumeration value="timeout"/>
+    <xs:enumeration value="notExecuted"/>
+    <xs:enumeration value="omitted"/>
+  </xs:restriction>
+</xs:simpleType>
+</xs:schema>
diff --git a/tools/tradefed-host/res/result/cts_result.xsl b/tools/tradefed-host/res/result/cts_result.xsl
new file mode 100644
index 0000000..cb220e1
--- /dev/null
+++ b/tools/tradefed-host/res/result/cts_result.xsl
@@ -0,0 +1,513 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#160;"> ]>
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
+
+    <xsl:template match="/">
+
+        <html>
+            <STYLE type="text/css">
+                @import "cts_result.css";
+            </STYLE>
+
+            <body>
+                <!-- Title of the Report -->
+                <DIV id="title">
+                    <TABLE>
+                        <TR>
+                            <TD width="40%" align="left"><img src="logo.gif"></img></TD>
+                            <TD width="60%" align="left">
+                                <h1>Test Report for <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@build_model"/> -
+                                <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@deviceID"/>
+                            </h1>
+                        </TD>
+                    </TR>
+                </TABLE>
+            </DIV>
+            <img src="newrule-green.png" align="left"></img>
+
+            <br></br>
+            <br></br>
+
+            <!-- Header with phone and plan information -->
+            <DIV id="summary">
+                <TABLE width="90%" frame="none">
+                    <TR>
+                        <TH>Device Information</TH>
+                        <TH>Test Summary</TH>
+                    </TR>
+
+                    <TR>
+                        <TD>
+                            <!-- Device information -->
+                            <div id="summaryinfo">
+                                <TABLE width="75%">
+                                    <TR>
+                                        <TD class="rowtitle">Build Model</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@build_model"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build Name</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@buildName"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Device ID</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@deviceID"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Firmware Version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@buildVersion"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Firmware Build Number</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@buildID"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build Fingerprint</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@build_fingerprint"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build ABI</TD>
+                                        <TD>
+                                            <xsl:value-of
+                                              select="TestResult/DeviceInfo/BuildInfo/@build_abi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Build ABI2</TD>
+                                        <TD>
+                                            <xsl:value-of
+                                              select="TestResult/DeviceInfo/BuildInfo/@build_abi2"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Android Platform Version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@androidPlatformVersion"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Supported Locales</TD>
+                                        <TD>
+                                            <xsl:call-template name="formatDelimitedString">
+                                                <xsl:with-param name="string" select="TestResult/DeviceInfo/BuildInfo/@locales"/>
+                                            </xsl:call-template>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Screen size</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/Screen/@resolution"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Phone number</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/PhoneSubInfo/@subscriberId"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">x dpi</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@Xdpi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">y dpi</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@Ydpi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Touch</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@touch"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Navigation</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@navigation"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Keypad</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@keypad"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Network</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@network"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">IMEI</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@imei"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">IMSI</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@imsi"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Open GL ES Version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/DeviceInfo/BuildInfo/@openGlEsVersion"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Features</TD>
+                                        <TD>
+                                            <xsl:for-each select="TestResult/DeviceInfo/FeatureInfo/Feature[@type='sdk']">
+                                                <xsl:text>[</xsl:text>
+                                                <xsl:choose>
+                                                    <xsl:when test="@available = 'true'">
+                                                        <xsl:text>X</xsl:text>
+                                                    </xsl:when>
+                                                    <xsl:otherwise>
+                                                        <xsl:text>_</xsl:text>
+                                                    </xsl:otherwise>
+                                                </xsl:choose>
+                                                <xsl:text>] </xsl:text>
+
+                                                <xsl:value-of select="@name" />
+                                                <br />
+                                            </xsl:for-each>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Other Features</TD>
+                                        <TD>
+                                            <UL>
+                                                <xsl:for-each select="TestResult/DeviceInfo/FeatureInfo/Feature[@type='other']">
+                                                    <LI><xsl:value-of select="@name" /></LI>
+                                                </xsl:for-each>
+                                            </UL>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Root Processes</TD>
+                                        <TD>
+                                            <UL>
+                                                <xsl:for-each select="TestResult/DeviceInfo/ProcessInfo/Process[@uid='0']">
+                                                    <LI><xsl:value-of select="@name" /></LI>
+                                                </xsl:for-each>
+                                            </UL>
+                                        </TD>
+                                    </TR>
+                                </TABLE>
+                            </div>
+                        </TD>
+
+                        <!-- plan information -->
+                        <TD>
+                            <div id="summaryinfo">
+                                <TABLE width="75%">
+                                    <TR>
+                                        <TD class="rowtitle">CTS version</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/HostInfo/Cts/@version"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Test timeout</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/HostInfo/Cts/IntValue[@name='testStatusTimeoutMs']/@value" /> ms
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Host Info</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/HostInfo/@name"/>
+                                            (<xsl:value-of select="TestResult/HostInfo/Os/@name"/> - 
+                                              <xsl:value-of select="TestResult/HostInfo/Os/@version"/>)
+                                        </TD>
+                                    </TR>
+                                    <TR><TD><BR></BR></TD><TD></TD></TR>
+                                    <TR>
+                                        <TD class="rowtitle">Plan name</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@testPlan"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Profile</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@profile"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Start time</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@starttime"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">End time</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/@endtime"/>
+                                        </TD>
+                                    </TR>
+
+                                    <!-- Test Summary -->
+                                    <TR><TD><BR></BR></TD><TD></TD></TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Passed</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@pass"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Failed</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@failed"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Timed out</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@timeout"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Omitted</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@omitted"/>
+                                        </TD>
+                                    </TR>
+                                    <TR>
+                                        <TD class="rowtitle">Tests Not Executed</TD>
+                                        <TD>
+                                            <xsl:value-of select="TestResult/Summary/@notExecuted"/>
+                                        </TD>
+                                    </TR>
+                                </TABLE>
+                            </div>
+                        </TD>
+                    </TR>
+                </TABLE>
+            </DIV>
+
+            <!-- High level summary of test execution -->
+            <h2 align="center">Test Summary by Package</h2>
+            <DIV id="testsummary">
+                <TABLE>
+                    <TR>
+                        <TH>Test Package</TH>
+                        <TH>Passed</TH>
+                        <TH>Failed</TH>
+                        <TH>Timed Out</TH>
+                        <TH>Total Tests</TH>
+                    </TR>
+                    <xsl:for-each select="TestResult/TestPackage">
+                        <TR>
+                            <TD>
+                                <xsl:variable name="href"><xsl:value-of select="@name"/></xsl:variable>
+                                <a href="#{$href}"><xsl:value-of select="@name"/></a>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@pass"/>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@failed"/>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@timeout"/>
+                            </TD>
+                            <TD>
+                                <xsl:value-of select="@total"/>
+                            </TD>
+                        </TR>
+                    </xsl:for-each> <!-- end package -->
+                </TABLE>
+            </DIV>
+
+            <!-- Details of all the executed tests -->
+            <h2 align="center">Detailed Test Report</h2>
+
+            <!-- test package -->
+            <DIV id="testdetail">
+                <xsl:for-each select="TestResult/TestPackage">
+                    <DIV id="none">
+                        <TABLE>
+                            <TR>
+                                <TD class="none" align="left">
+                                    <xsl:variable name="href"><xsl:value-of select="@name"/></xsl:variable>
+                                    <a name="{$href}">Compatibility Test Package: <xsl:value-of select="@name"/></a>
+                                </TD>
+                            </TR>
+                        </TABLE>
+                    </DIV>
+
+                    <TABLE>
+                        <TR>
+                            <TH width="25%">Test</TH>
+                            <TH width="7%">Result</TH>
+                            <TH width="68%">Failure Details</TH>
+                        </TR>
+
+                        <!-- test case -->
+                        <xsl:for-each select="TestCase">
+
+                            <!-- emit a blank row before every test suite name -->
+                            <xsl:if test="position()!=1">
+                                <TR> <TD class="testcasespacer" colspan="3"></TD> </TR>
+                            </xsl:if>
+
+
+                            <TR>
+                                <TD class="testcase" colspan="3">
+                                    <xsl:value-of select="@name"/>
+                                </TD>
+                            </TR>
+                            
+                            <!-- test -->
+                            <xsl:for-each select="Test">
+                                <TR>
+                                    <TD class="testname"> -- <xsl:value-of select="@name"/></TD>
+
+                                    <!-- test results -->
+                                    <xsl:choose>
+                                        <xsl:when test="string(@KnownFailure)">
+                                            <!-- "pass" indicates the that test actually passed (results have been inverted already) -->
+                                            <xsl:if test="@result='pass'">
+                                                <TD class="pass">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        known problem
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+
+                                            <!-- "fail" indicates that a known failure actually passed (results have been inverted already) -->
+                                            <xsl:if test="@result='fail'">
+                                                <TD class="failed">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                               <TD class="failuredetails">
+                                                    <div id="details">
+                                                        A test that was a known failure actually passed. Please check.
+                                                    </div>
+                                               </TD>
+                                            </xsl:if>
+                                        </xsl:when>
+
+                                        <xsl:otherwise>
+                                            <xsl:if test="@result='pass'">
+                                                <TD class="pass">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='fail'">
+                                                <TD class="failed">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails">
+                                                    <div id="details">
+                                                        <xsl:value-of select="FailedScene/@message"/>
+                                                    </div>
+                                                </TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='timeout'">
+                                                <TD class="timeout">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                <TD class="failuredetails"></TD>
+                                                </TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='omitted'">
+                                                <TD class="omitted">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+
+                                            <xsl:if test="@result='notExecuted'">
+                                                <TD class="notExecuted">
+                                                    <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                        <xsl:value-of select="@result"/>
+                                                    </div>
+                                                </TD>
+                                                <TD class="failuredetails"></TD>
+                                            </xsl:if>
+                                        </xsl:otherwise>
+                                    </xsl:choose>
+                                </TR> <!-- finished with a row -->
+                            </xsl:for-each> <!-- end test -->
+                        </xsl:for-each> <!-- end test case -->
+                    </TABLE>
+                </xsl:for-each> <!-- end test package -->
+            </DIV>
+            </body>
+        </html>
+    </xsl:template>
+
+    <!-- Take a delimited string and insert line breaks after a some number of elements. --> 
+    <xsl:template name="formatDelimitedString">
+        <xsl:param name="string" />
+        <xsl:param name="numTokensPerRow" select="10" />
+        <xsl:param name="tokenIndex" select="1" />
+        <xsl:if test="$string">
+            <!-- Requires the last element to also have a delimiter after it. -->
+            <xsl:variable name="token" select="substring-before($string, ';')" />
+            <xsl:value-of select="$token" />
+            <xsl:text>&#160;</xsl:text>
+          
+            <xsl:if test="$tokenIndex mod $numTokensPerRow = 0">
+                <br />
+            </xsl:if>
+
+            <xsl:call-template name="formatDelimitedString">
+                <xsl:with-param name="string" select="substring-after($string, ';')" />
+                <xsl:with-param name="numTokensPerRow" select="$numTokensPerRow" />
+                <xsl:with-param name="tokenIndex" select="$tokenIndex + 1" />
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/tools/tradefed-host/res/result/logo.gif b/tools/tradefed-host/res/result/logo.gif
new file mode 100644
index 0000000..61970b3
--- /dev/null
+++ b/tools/tradefed-host/res/result/logo.gif
Binary files differ
diff --git a/tools/tradefed-host/res/result/newrule-green.png b/tools/tradefed-host/res/result/newrule-green.png
new file mode 100644
index 0000000..10a4194
--- /dev/null
+++ b/tools/tradefed-host/res/result/newrule-green.png
Binary files differ
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
new file mode 100644
index 0000000..8c43e06
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tradefed.result;
+
+import com.android.cts.tradefed.targetsetup.CtsBuildHelper;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.result.TestResult;
+import com.android.tradefed.result.TestRunResult;
+import com.android.tradefed.result.TestResult.TestStatus;
+import com.android.tradefed.targetsetup.IBuildInfo;
+import com.android.tradefed.targetsetup.IFolderBuildInfo;
+import com.android.tradefed.util.FileUtil;
+
+import org.kxml2.io.KXmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Writes results to an XML files in the CTS format.
+ * <p/>
+ * Collects all test info in memory, then dumps to file when invocation is complete.
+ * <p/>
+ * Outputs xml in format governed by the cts_result.xsd
+ */
+public class CtsXmlResultReporter extends CollectingTestListener {
+
+    private static final String LOG_TAG = "CtsXmlResultReporter";
+
+    private static final String TEST_RESULT_FILE_NAME = "testResult.xml";
+    private static final String CTS_RESULT_FILE_VERSION = "2.0";
+
+    private static final String[] CTS_RESULT_RESOURCES = {"cts_result.xsl", "cts_result.css",
+        "logo.gif", "newrule-green.png"};
+
+    /** the XML namespace */
+    private static final String ns = null;
+
+    private static final String REPORT_DIR_NAME = "output-file-path";
+    @Option(name=REPORT_DIR_NAME, description="root file system path to directory to store xml " +
+            "test results and associated logs. If not specified, results will be stored at " +
+            "<cts root>/repository/results")
+    protected File mReportDir = null;
+
+    protected IBuildInfo mBuildInfo;
+
+    private String mStartTime;
+
+    void setReportDir(File reportDir) {
+        mReportDir = reportDir;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void invocationStarted(IBuildInfo buildInfo) {
+        super.invocationStarted(buildInfo);
+        if (mReportDir == null) {
+            if (!(buildInfo instanceof IFolderBuildInfo)) {
+                throw new IllegalArgumentException("build info is not a IFolderBuildInfo");
+            }
+            IFolderBuildInfo ctsBuild = (IFolderBuildInfo)buildInfo;
+            try {
+                CtsBuildHelper buildHelper = new CtsBuildHelper(ctsBuild.getRootDir());
+                mReportDir = buildHelper.getResultsDir();
+
+            } catch (FileNotFoundException e) {
+                throw new IllegalArgumentException("unrecognized cts structure", e);
+            }
+        }
+        // create a unique directory for saving results, using old cts host convention
+        // TODO: in future, consider using LogFileSaver to create build-specific directories
+        mReportDir = new File(mReportDir, getResultTimestamp());
+        mReportDir.mkdirs();
+        mStartTime = getTimestamp();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testLog(String dataName, LogDataType dataType, InputStream dataStream) {
+        // TODO: implement this
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void invocationEnded(long elapsedTime) {
+        super.invocationEnded(elapsedTime);
+        createXmlResult(mReportDir, mStartTime, elapsedTime);
+        copyFormattingFiles(mReportDir);
+        zipResults(mReportDir);
+    }
+
+    /**
+     * Creates a report file and populates it with the report data from the completed tests.
+     */
+    private void createXmlResult(File reportDir, String startTimestamp, long elapsedTime) {
+        String endTime = getTimestamp();
+
+        OutputStream stream = null;
+        try {
+            stream = createOutputResultStream(reportDir);
+            KXmlSerializer serializer = new KXmlSerializer();
+            serializer.setOutput(stream, "UTF-8");
+            serializer.startDocument("UTF-8", false);
+            serializer.setFeature(
+                    "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.processingInstruction("xml-stylesheet type=\"text/xsl\"  href=\"cts_result.xsl\"");
+            printResultsDoc(serializer, startTimestamp, endTime);
+            serializer.endDocument();
+            // TODO: output not executed timeout omitted counts
+            String msg = String.format("XML test result file generated at %s. Total tests %d, " +
+                    "Failed %d, Error %d", reportDir.getAbsolutePath(), getNumTotalTests(),
+                    getNumFailedTests(), getNumErrorTests());
+            Log.logAndDisplay(LogLevel.INFO, LOG_TAG, msg);
+            Log.logAndDisplay(LogLevel.INFO, LOG_TAG, String.format("Time: %s",
+                    formatElapsedTime(elapsedTime)));
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Failed to generate report data");
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Output the results XML.
+     *
+     * @param serializer the {@link KXmlSerializer} to use
+     * @param startTime the user-friendly starting time of the test invocation
+     * @param endTime the user-friendly ending time of the test invocation
+     * @throws IOException
+     */
+    private void printResultsDoc(KXmlSerializer serializer, String startTime, String endTime)
+            throws IOException {
+        serializer.startTag(ns, "TestResult");
+        // TODO: output test plan and profile values
+        serializer.attribute(ns, "testPlan", "unknown");
+        serializer.attribute(ns, "profile", "unknown");
+        serializer.attribute(ns, "starttime", startTime);
+        serializer.attribute(ns, "endtime", endTime);
+        serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION);
+
+        printDeviceInfo(serializer);
+        printHostInfo(serializer);
+        printTestSummary(serializer);
+        printTestResults(serializer);
+    }
+
+    /**
+     * Output the device info XML.
+     *
+     * @param serializer
+     */
+    private void printDeviceInfo(KXmlSerializer serializer) {
+        // TODO implement this
+    }
+
+    /**
+     * Output the host info XML.
+     *
+     * @param serializer
+     */
+    private void printHostInfo(KXmlSerializer serializer) {
+        // TODO implement this
+    }
+
+    /**
+     * Output the test summary XML containing summary totals for all tests.
+     *
+     * @param serializer
+     * @throws IOException
+     */
+    private void printTestSummary(KXmlSerializer serializer) throws IOException {
+        serializer.startTag(ns, "Summary");
+        serializer.attribute(ns, "failed", Integer.toString(getNumErrorTests() +
+                getNumFailedTests()));
+        // TODO: output notExecuted, timeout, and omitted count
+        serializer.attribute(ns, "notExecuted", "0");
+        serializer.attribute(ns, "timeout", "0");
+        serializer.attribute(ns, "omitted", "0");
+        serializer.attribute(ns, "pass", Integer.toString(getNumPassedTests()));
+        serializer.attribute(ns, "total", Integer.toString(getNumTotalTests()));
+        serializer.endTag(ns, "Summary");
+    }
+
+    /**
+     * Output the detailed test results XML.
+     *
+     * @param serializer
+     * @throws IOException
+     */
+    private void printTestResults(KXmlSerializer serializer) throws IOException {
+        for (TestRunResult runResult : getRunResults()) {
+            printTestRunResult(serializer, runResult);
+        }
+    }
+
+    /**
+     * Output the XML for one test run aka test package.
+     *
+     * @param serializer
+     * @param runResult the {@link TestRunResult}
+     * @throws IOException
+     */
+    private void printTestRunResult(KXmlSerializer serializer, TestRunResult runResult)
+            throws IOException {
+        serializer.startTag(ns, "TestPackage");
+        serializer.attribute(ns, "name", runResult.getName());
+        serializer.attribute(ns, "runTime", formatElapsedTime(runResult.getElapsedTime()));
+        // TODO: generate digest
+        serializer.attribute(ns, "digest", "");
+        serializer.attribute(ns, "failed", Integer.toString(runResult.getNumErrorTests() +
+                runResult.getNumFailedTests()));
+        // TODO: output notExecuted, timeout, and omitted count
+        serializer.attribute(ns, "notExecuted", "0");
+        serializer.attribute(ns, "timeout", "0");
+        serializer.attribute(ns, "omitted", "0");
+        serializer.attribute(ns, "pass", Integer.toString(runResult.getNumPassedTests()));
+        serializer.attribute(ns, "total", Integer.toString(runResult.getNumTests()));
+
+        // the results XML needs to organize test's by class. Build a nested data structure that
+        // group's the results by class name
+        Map<String, Map<TestIdentifier, TestResult>> classResultsMap = buildClassNameMap(
+                runResult.getTestResults());
+
+        for (Map.Entry<String, Map<TestIdentifier, TestResult>> resultsEntry : classResultsMap.entrySet()) {
+            serializer.startTag(ns, "TestCase");
+            serializer.attribute(ns, "name", resultsEntry.getKey());
+            printTests(serializer, resultsEntry.getValue());
+            serializer.endTag(ns, "TestCase");
+        }
+        serializer.endTag(ns, "TestPackage");
+    }
+
+    /**
+     * Organizes the test run results into a format organized by class name.
+     */
+    private Map<String, Map<TestIdentifier, TestResult>> buildClassNameMap(
+            Map<TestIdentifier, TestResult> results) {
+        // use a linked hashmap to have predictable iteration order
+        Map<String, Map<TestIdentifier, TestResult>> classResultMap =
+            new LinkedHashMap<String, Map<TestIdentifier, TestResult>>();
+        for (Map.Entry<TestIdentifier, TestResult> resultEntry : results.entrySet()) {
+            String className = resultEntry.getKey().getClassName();
+            Map<TestIdentifier, TestResult> resultsForClass = classResultMap.get(className);
+            if (resultsForClass == null) {
+                resultsForClass = new LinkedHashMap<TestIdentifier, TestResult>();
+                classResultMap.put(className, resultsForClass);
+            }
+            resultsForClass.put(resultEntry.getKey(), resultEntry.getValue());
+        }
+        return classResultMap;
+    }
+
+    /**
+     * Output XML for given map of tests their results
+     *
+     * @param serializer
+     * @param results
+     * @throws IOException
+     */
+    private void printTests(KXmlSerializer serializer, Map<TestIdentifier, TestResult> results)
+            throws IOException {
+        for (Map.Entry<TestIdentifier, TestResult> resultEntry : results.entrySet()) {
+            printTest(serializer, resultEntry.getKey(), resultEntry.getValue());
+        }
+    }
+
+    /**
+     * Output the XML for given test and result.
+     *
+     * @param serializer
+     * @param testId
+     * @param result
+     * @throws IOException
+     */
+    private void printTest(KXmlSerializer serializer, TestIdentifier testId, TestResult result)
+            throws IOException {
+        serializer.startTag(ns, "Test");
+        serializer.attribute(ns, "name", testId.getTestName());
+        serializer.attribute(ns, "result", convertStatus(result.getStatus()));
+
+        if (result.getStackTrace() != null) {
+            String sanitizedStack = sanitizeStackTrace(result.getStackTrace());
+            serializer.startTag(ns, "FailedScene");
+            serializer.attribute(ns, "message", getFailureMessageFromStackTrace(sanitizedStack));
+            serializer.text(sanitizedStack);
+            serializer.endTag(ns, "FailedScene");
+        }
+        serializer.endTag(ns, "Test");
+    }
+
+    /**
+     * Convert a {@link TestStatus} to the result text to output in XML
+     *
+     * @param status the {@link TestStatus}
+     * @return
+     */
+    private String convertStatus(TestStatus status) {
+        switch (status) {
+            case ERROR:
+                return "fail";
+            case FAILURE:
+                return "fail";
+            case PASSED:
+                return "pass";
+            // TODO add notExecuted, omitted timeout
+        }
+        return "omitted";
+    }
+
+    /**
+     * Strip out any invalid XML characters that might cause the report to be unviewable.
+     * http://www.w3.org/TR/REC-xml/#dt-character
+     */
+    private static String sanitizeStackTrace(String trace) {
+        if (trace != null) {
+            return trace.replaceAll("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD]", "");
+        } else {
+            return null;
+        }
+    }
+
+    private static String getFailureMessageFromStackTrace(String stack) {
+        // This is probably too simplistic to work in all cases, but for now, just return first
+        // line of stack as failure message
+        int firstNewLine = stack.indexOf('\n');
+        if (firstNewLine != -1) {
+            return stack.substring(0, firstNewLine);
+        }
+        return stack;
+    }
+
+    /**
+     * Return the current timestamp as a {@link String} suitable for displaying.
+     * <p/>
+     * Example: Fri Aug 20 15:13:03 PDT 2010
+     */
+    String getTimestamp() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM DD HH:mm:ss x yyyy");
+        return dateFormat.format(new Date());
+    }
+
+    /**
+     * Return the current timestamp in a compressed format, used to uniquely identify results.
+     * <p/>
+     * Example: 2010.08.16_11.42.12
+     */
+    private String getResultTimestamp() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.DD_HH.mm.ss");
+        return dateFormat.format(new Date());
+    }
+
+    /**
+     * Return a prettified version of the given elapsed time
+     * @return
+     */
+    private String formatElapsedTime(long elapsedTimeMs) {
+        long seconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTimeMs) % 60;
+        long minutes = TimeUnit.MILLISECONDS.toMinutes(elapsedTimeMs) % 60;
+        long hours = TimeUnit.MILLISECONDS.toHours(elapsedTimeMs);
+        StringBuilder time = new StringBuilder();
+        if (hours > 0) {
+            time.append(hours);
+            time.append("h ");
+        }
+        if (minutes > 0) {
+            time.append(minutes);
+            time.append("m ");
+        }
+        time.append(seconds);
+        time.append("s");
+
+        return time.toString();
+    }
+
+    /**
+     * Creates the output stream to use for test results. Exposed for mocking.
+     */
+    OutputStream createOutputResultStream(File reportDir) throws IOException {
+        File reportFile = new File(reportDir, TEST_RESULT_FILE_NAME);
+        Log.i(LOG_TAG, String.format("Created xml report file at %s",
+                reportFile.getAbsolutePath()));
+        return new FileOutputStream(reportFile);
+    }
+
+    /**
+     * Copy the xml formatting files stored in this jar to the results directory
+     *
+     * @param resultsDir
+     */
+    private void copyFormattingFiles(File resultsDir) {
+        for (String resultFileName : CTS_RESULT_RESOURCES) {
+            InputStream configStream = getClass().getResourceAsStream(
+                    String.format("/result/%s", resultFileName));
+            if (configStream != null) {
+                File resultFile = new File(resultsDir, resultFileName);
+                try {
+                    FileUtil.writeToFile(configStream, resultFile);
+                } catch (IOException e) {
+                    Log.w(LOG_TAG, String.format("Failed to write %s to file", resultFileName));
+                }
+            } else {
+                Log.w(LOG_TAG, String.format("Failed to load %s from jar", resultFileName));
+            }
+        }
+    }
+
+    /**
+     * Zip the contents of the given results directory.
+     *
+     * @param resultsDir
+     */
+    private void zipResults(File resultsDir) {
+        // TODO: implement this
+    }
+}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
new file mode 100644
index 0000000..113496c
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.tradefed.result;
+
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.ITestRunListener.TestFailure;
+import com.android.tradefed.result.XmlResultReporter;
+import com.android.tradefed.targetsetup.BuildInfo;
+import com.android.tradefed.util.FileUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link XmlResultReporter}.
+ */
+public class CtsXmlResultReporterTest extends TestCase {
+
+    private CtsXmlResultReporter mResultReporter;
+    private ByteArrayOutputStream mOutputStream;
+    private File mReportDir;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mOutputStream = new ByteArrayOutputStream();
+        mResultReporter = new CtsXmlResultReporter() {
+            @Override
+            OutputStream createOutputResultStream(File reportDir) throws IOException {
+                return mOutputStream;
+            }
+
+            @Override
+            String getTimestamp() {
+                return "ignore";
+            }
+        };
+        // TODO: use mock file dir instead
+        mReportDir = FileUtil.createTempDir("foo");
+        mResultReporter.setReportDir(mReportDir);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mReportDir != null) {
+            FileUtil.recursiveDelete(mReportDir);
+        }
+        super.tearDown();
+    }
+
+    /**
+     * A simple test to ensure expected output is generated for test run with no tests.
+     */
+    public void testEmptyGeneration() {
+        final String expectedOutput = "<?xml version='1.0' encoding='UTF-8' standalone='no' ?>" +
+            "<?xml-stylesheet type=\"text/xsl\" href=\"cts_result.xsl\"?>" +
+            "<TestResult testPlan=\"unknown\" profile=\"unknown\" starttime=\"ignore\" endtime=\"ignore\" version=\"2.0\"> " +
+            "<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"0\" total=\"0\" />" +
+            "</TestResult>";
+        mResultReporter.invocationStarted(new BuildInfo(1, "test", "test"));
+        mResultReporter.invocationEnded(1);
+        assertEquals(expectedOutput, getOutput());
+    }
+
+    /**
+     * A simple test to ensure expected output is generated for test run with a single passed test.
+     */
+    public void testSinglePass() {
+        Map<String, String> emptyMap = Collections.emptyMap();
+        final TestIdentifier testId = new TestIdentifier("com.foo.FooTest", "testFoo");
+        mResultReporter.invocationStarted(new BuildInfo());
+        mResultReporter.testRunStarted("run", 1);
+        mResultReporter.testStarted(testId);
+        mResultReporter.testEnded(testId, emptyMap);
+        mResultReporter.testRunEnded(3000, emptyMap);
+        mResultReporter.invocationEnded(1);
+        String output =  getOutput();
+        // TODO: consider doing xml based compare
+        assertTrue(output.contains(
+                "<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"1\" total=\"1\" />"));
+        assertTrue(output.contains("<TestPackage name=\"run\" runTime=\"3s\" digest=\"\" " +
+                "failed=\"0\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"1\" total=\"1\">"));
+        assertTrue(output.contains(String.format("<TestCase name=\"%s\">", testId.getClassName())));
+
+        final String testCaseTag = String.format(
+                "<Test name=\"%s\" result=\"pass\" />", testId.getTestName());
+        assertTrue(output.contains(testCaseTag));
+    }
+
+    /**
+     * A simple test to ensure expected output is generated for test run with a single failed test.
+     */
+    public void testSingleFail() {
+        Map<String, String> emptyMap = Collections.emptyMap();
+        final TestIdentifier testId = new TestIdentifier("FooTest", "testFoo");
+        final String trace = "this is a trace\nmore trace";
+        mResultReporter.invocationStarted(new BuildInfo());
+        mResultReporter.testRunStarted("run", 1);
+        mResultReporter.testStarted(testId);
+        mResultReporter.testFailed(TestFailure.FAILURE, testId, trace);
+        mResultReporter.testEnded(testId, emptyMap);
+        mResultReporter.testRunEnded(3, emptyMap);
+        mResultReporter.invocationEnded(1);
+        String output =  getOutput();
+        System.out.print(getOutput());
+        // TODO: consider doing xml based compare
+        assertTrue(output.contains(
+                "<Summary failed=\"1\" notExecuted=\"0\" timeout=\"0\" omitted=\"0\" pass=\"0\" total=\"1\" />"));
+        final String failureTag =
+                "<FailedScene message=\"this is a trace\">this is a tracemore trace";
+        assertTrue(output.contains(failureTag));
+    }
+
+    /**
+     * Gets the output produced, stripping it of extraneous whitespace characters.
+     */
+    private String getOutput() {
+        String output = mOutputStream.toString();
+        // ignore newlines and tabs whitespace
+        output = output.replaceAll("[\\r\\n\\t]", "");
+        // replace two ws chars with one
+        return output.replaceAll("  ", " ");
+    }
+}