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 " "> ]>
+<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> </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(" ", " ");
+ }
+}