Merge "Wrong log filter in TrustedVoiceHostTest#testUnlock" into nougat-cts-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 0c1a884..9c57f6f 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -71,6 +71,8 @@
android:debuggable="true"
android:largeHeap="true">
+ <meta-data android:name="SuiteName" android:value="CTS_VERIFIER" />
+
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIbK6ldcOzoeRtQ1u1dFVJ1A7KetRhit-a1Xa82Q" />
@@ -145,14 +147,15 @@
android:value="android.software.backup" />
</activity>
- <activity android:name=".backup.BackupAccessibilityTestActivity" android:label="@string/backup_accessibility_test">
+ <!-- Further work is required for this test, b/32798562 -->
+ <!-- activity android:name=".backup.BackupAccessibilityTestActivity" android:label="@string/backup_accessibility_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_required_features"
android:value="android.software.backup" />
- </activity>
+ </activity -->
<activity android:name=".bluetooth.BluetoothTestActivity"
android:label="@string/bluetooth_test"
@@ -2091,6 +2094,7 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_audio" />
<meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
</activity>
<service android:name=".tv.MockTvInputService"
diff --git a/apps/CtsVerifier/assets/report/compatibility_result.css b/apps/CtsVerifier/assets/report/compatibility_result.css
new file mode 100644
index 0000000..699f45a
--- /dev/null
+++ b/apps/CtsVerifier/assets/report/compatibility_result.css
@@ -0,0 +1,164 @@
+/* Copyright (C) 2015 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;
+}
+
+/* Report logo and device name */
+table.title {
+ padding:5px;
+ border-width: 0px;
+ margin-left:auto;
+ margin-right:auto;
+ vertical-align:middle;
+}
+
+table.summary {
+ background-color: rgb(212, 233, 169);
+ border-collapse:collapse;
+ border: 0px solid #A5C639;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+table.summary th {
+ background-color: #A5C639;
+ font-size: 1.2em;
+ padding: 0.5em;
+}
+
+table.summary td {
+ border-width: 0px 0px 0px 0px;
+ border-color: gray;
+ border-style: inset;
+ font-size: 1em;
+ padding: 0.5em;
+ vertical-align: top;
+}
+
+table.testsummary {
+ background-color: rgb(212, 233, 169);
+ border-collapse:collapse;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+table.testsummary th {
+ background-color: #A5C639;
+ border: 1px outset gray;
+ padding: 0.5em;
+}
+
+table.testsummary td {
+ border: 1px outset #A5C639;
+ padding: 0.5em;
+ text-align: center;
+}
+
+table.testdetails {
+ background-color: rgb(212, 233, 169);
+ border-collapse:collapse;
+ border-width:1;
+ border-color: #A5C639;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom: 2em;
+ vertical-align: top;
+ width: 95%;
+}
+
+table.testdetails th {
+ background-color: #A5C639;
+ border-width: 1px;
+ border-color: gray;
+ border-style: outset;
+ height: 2em;
+ padding: 0.2em;
+}
+
+table.testdetails td {
+ border-width: 1px;
+ border-color: #A5C639;
+ border-style: outset;
+ text-align: left;
+ vertical-align: top;
+ padding: 0.2em;
+}
+
+table.testdetails td.module {
+ background-color: white;
+ border: 0px;
+ 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.not_executed {
+ 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;
+}
+
+div.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/apps/CtsVerifier/assets/report/compatibility_result.xsd b/apps/CtsVerifier/assets/report/compatibility_result.xsd
new file mode 100644
index 0000000..9b2758c
--- /dev/null
+++ b/apps/CtsVerifier/assets/report/compatibility_result.xsd
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2015 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/compatibility_result/1.15"
+ xmlns="http://compatibility.android.com/compatibility_result/1.15"
+ elementFormDefault="qualified">
+
+ <xs:element name="Result">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Summary" type="summaryType"/>
+ <xs:element name="Module" type="moduleType" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="start" type="xs:string"/>
+ <xs:attribute name="end" type="xs:string"/>
+ <xs:attribute name="plan" type="xs:string"/>
+ <xs:attribute name="suite_name" type="xs:string"/>
+ <xs:attribute name="suite_version" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:complexType name="summaryType">
+ <xs:attribute name="failed" type="xs:integer"/>
+ <xs:attribute name="not_executed" type="xs:integer"/>
+ <xs:attribute name="pass" type="xs:integer"/>
+ </xs:complexType>
+
+ <xs:complexType name="moduleType">
+ <xs:sequence>
+ <xs:element name="Test" type="testType" minOccurs="1" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="abi" type="xs:string"/>
+ </xs:complexType>
+
+ <xs:simpleType name="unitType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="none"/>
+ <xs:enumeration value="ms"/>
+ <xs:enumeration value="fps"/>
+ <xs:enumeration value="ops"/>
+ <xs:enumeration value="kbps"/>
+ <xs:enumeration value="mbps"/>
+ <xs:enumeration value="byte"/>
+ <xs:enumeration value="count"/>
+ <xs:enumeration value="score"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="scoreTypeType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="higher-better"/>
+ <xs:enumeration value="lower-better"/>
+ <xs:enumeration value="neutral"/>
+ <xs:enumeration value="warning"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="testType">
+ <xs:sequence>
+ <xs:element name="Failure" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="StackTrace" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ <xs:attribute name="message" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Summary" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:decimal">
+ <xs:attribute name="message" type="xs:string" use="required" />
+ <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+ <xs:attribute name="unit" type="unitType" use="required" />
+ <xs:attribute name="target" type="xs:decimal" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Details" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="ValueArray" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Value" type="xs:decimal" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:attribute name="source" type="xs:string" use="required" />
+ <xs:attribute name="message" type="xs:string" use="required" />
+ <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+ <xs:attribute name="unit" type="unitType" use="required" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="result" type="resultType" use="required"/>
+ <xs:attribute name="start" type="xs:string"/>
+ <xs:attribute name="end" type="xs:string"/>
+ </xs:complexType>
+
+ <xs:simpleType name="resultType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="pass"/>
+ <xs:enumeration value="fail"/>
+ <xs:enumeration value="not_executed"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
\ No newline at end of file
diff --git a/apps/CtsVerifier/assets/report/compatibility_result.xsl b/apps/CtsVerifier/assets/report/compatibility_result.xsl
new file mode 100644
index 0000000..b8c1245
--- /dev/null
+++ b/apps/CtsVerifier/assets/report/compatibility_result.xsl
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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>
+ <head>
+ <title>Test Report</title>
+ <style type="text/css">
+ @import "compatibility_result.css";
+ </style>
+ </head>
+ <body>
+ <div>
+ <table class="title">
+ <tr>
+ <td align="left"><img src="logo.png"/></td>
+ </tr>
+ </table>
+ </div>
+
+ <div>
+ <table class="summary">
+ <tr>
+ <th colspan="2">Summary</th>
+ </tr>
+ <tr>
+ <td class="rowtitle">Suite / Plan</td>
+ <td>
+ <xsl:value-of select="Result/@suite_name"/> / <xsl:value-of select="Result/@suite_plan"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Suite / Build</td>
+ <td>
+ <xsl:value-of select="Result/@suite_version"/> / <xsl:value-of select="Result/@suite_build_number"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Host Info</td>
+ <td>
+ Result/@start
+ <xsl:value-of select="Result/@host_name"/>
+ (<xsl:value-of select="Result/@os_name"/> - <xsl:value-of select="Result/@os_version"/>)
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Start time / End Time</td>
+ <td>
+ <xsl:value-of select="Result/@start_display"/> /
+ <xsl:value-of select="Result/@end_display"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Tests Passed</td>
+ <td>
+ <xsl:value-of select="Result/Summary/@pass"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Tests Failed</td>
+ <td>
+ <xsl:value-of select="Result/Summary/@failed"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Tests Not Executed</td>
+ <td>
+ <xsl:value-of select="Result/Summary/@not_executed"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Modules Done</td>
+ <td>
+ <xsl:value-of select="Result/Summary/@modules_done"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Modules Total</td>
+ <td>
+ <xsl:value-of select="Result/Summary/@modules_total"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Fingerprint</td>
+ <td>
+ <xsl:value-of select="Result/Build/@build_fingerprint"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Security Patch</td>
+ <td>
+ <xsl:value-of select="Result/Build/@build_version_security_patch"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Release (SDK)</td>
+ <td>
+ <xsl:value-of select="Result/Build/@build_version_release"/> (<xsl:value-of select="Result/Build/@build_version_sdk"/>)
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">ABIs</td>
+ <td>
+ <xsl:value-of select="Result/Build/@build_abis"/>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <!-- High level summary of test execution -->
+ <br/>
+ <div>
+ <table class="testsummary">
+ <tr>
+ <th>Module</th>
+ <th>Passed</th>
+ <th>Failed</th>
+ <th>Not Executed</th>
+ <th>Total Tests</th>
+ </tr>
+ <xsl:for-each select="Result/Module">
+ <tr>
+ <td>
+ <xsl:variable name="href"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></xsl:variable>
+ <a href="#{$href}"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></a>
+ </td>
+ <td>
+ <xsl:value-of select="count(TestCase/Test[@result = 'pass'])"/>
+ </td>
+ <td>
+ <xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
+ </td>
+ <td>
+ <xsl:value-of select="@not_executed"/>
+ </td>
+ <td>
+ <xsl:value-of select="count(TestCase/Test) + @not_executed"/>
+ </td>
+ </tr>
+ </xsl:for-each> <!-- end Module -->
+ </table>
+ </div>
+
+ <xsl:call-template name="filteredResultTestReport">
+ <xsl:with-param name="header" select="'Failed Tests'" />
+ <xsl:with-param name="resultFilter" select="'fail'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="filteredResultTestReport">
+ <xsl:with-param name="header" select="'Not Executed Tests'" />
+ <xsl:with-param name="resultFilter" select="'not_executed'" />
+ </xsl:call-template>
+
+ <br/>
+ <xsl:call-template name="detailedTestReport" />
+
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template name="filteredResultTestReport">
+ <xsl:param name="header" />
+ <xsl:param name="resultFilter" />
+ <xsl:variable name="numMatching" select="count(Result/Module/TestCase/Test[@result=$resultFilter])" />
+ <xsl:if test="$numMatching > 0">
+ <h2 align="center"><xsl:value-of select="$header" /> (<xsl:value-of select="$numMatching"/>)</h2>
+ <xsl:call-template name="detailedTestReport">
+ <xsl:with-param name="resultFilter" select="$resultFilter"/>
+ <xsl:with-param name="fullStackTrace" select="true()"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template name="detailedTestReport">
+ <xsl:param name="resultFilter" />
+ <xsl:param name="fullStackTrace" />
+ <div>
+ <xsl:for-each select="Result/Module">
+ <xsl:if test="$resultFilter=''
+ or count(TestCase/Test[@result=$resultFilter]) > 0">
+
+ <table class="testdetails">
+ <tr>
+ <td class="module" colspan="3">
+ <xsl:variable name="href"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></xsl:variable>
+ <a name="{$href}"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></a>
+ </td>
+ </tr>
+
+ <tr>
+ <th width="30%">Test</th>
+ <th width="5%">Result</th>
+ <th>Details</th>
+ </tr>
+
+ <xsl:for-each select="TestCase">
+ <xsl:variable name="TestCase" select="."/>
+ <!-- test -->
+ <xsl:for-each select="Test">
+ <xsl:if test="$resultFilter='' or @result=$resultFilter">
+ <tr>
+ <td class="testname"> <xsl:value-of select="$TestCase/@name"/>#<xsl:value-of select="@name"/></td>
+
+ <!-- test results -->
+ <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"/>
+ </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 class="details">
+ <xsl:choose>
+ <xsl:when test="$fullStackTrace=true()">
+ <xsl:value-of select="Failure/StackTrace" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="Failure/@message"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </div>
+ </td>
+ </xsl:if>
+
+ <xsl:if test="@result='not_executed'">
+ <td class="not_executed">
+ <div style="text-align: center; margin-left:auto; margin-right:auto;">
+ <xsl:value-of select="@result"/>
+ </div>
+ </td>
+ <td class="failuredetails"></td>
+ </xsl:if>
+ </tr> <!-- finished with a row -->
+ </xsl:if>
+ </xsl:for-each> <!-- end test -->
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each> <!-- end test Module -->
+ </div>
+ </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/apps/CtsVerifier/assets/report/logo.png b/apps/CtsVerifier/assets/report/logo.png
new file mode 100644
index 0000000..61970b3
--- /dev/null
+++ b/apps/CtsVerifier/assets/report/logo.png
Binary files differ
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
index 33c9b62..b6908a9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
@@ -22,10 +22,20 @@
import android.os.Build;
import android.os.Environment;
+import com.android.compatibility.common.util.FileUtil;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.InvocationResult;
+import com.android.compatibility.common.util.ResultHandler;
+import com.android.compatibility.common.util.ZipUtil;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.lang.System;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@@ -38,6 +48,20 @@
* Background task to generate a report and save it to external storage.
*/
class ReportExporter extends AsyncTask<Void, Void, String> {
+
+ private static final String COMMAND_LINE_ARGS = "";
+ private static final String LOG_URL = null;
+ private static final String REFERENCE_URL = null;
+ private static final String SUITE_NAME_METADATA_KEY = "SuiteName";
+ private static final String SUITE_PLAN = "verifier";
+ private static final String SUITE_BUILD = "0";
+
+ private static final long START_MS = System.currentTimeMillis();
+ private static final long END_MS = START_MS;
+
+ private static final String REPORT_DIRECTORY = "verifierReports";
+ private static final String ZIP_EXTENSION = ".zip";
+
protected static final Logger LOG = Logger.getLogger(ReportExporter.class.getName());
private final Context mContext;
@@ -54,50 +78,80 @@
LOG.log(Level.WARNING, "External storage is not writable.");
return mContext.getString(R.string.no_storage);
}
- byte[] contents;
+ IInvocationResult result;
try {
TestResultsReport report = new TestResultsReport(mContext, mAdapter);
- contents = report.getContents().getBytes();
+ result = report.generateResult();
} catch (Exception e) {
LOG.log(Level.WARNING, "Couldn't create test results report", e);
return mContext.getString(R.string.test_results_error);
}
- File reportPath = new File(Environment.getExternalStorageDirectory(), "ctsVerifierReports");
- reportPath.mkdirs();
+ // create a directory for CTS Verifier reports
+ File externalStorageDirectory = Environment.getExternalStorageDirectory();
+ File verifierReportsDir = new File(externalStorageDirectory, REPORT_DIRECTORY);
+ verifierReportsDir.mkdirs();
- String baseName = getReportBaseName();
- File reportFile = new File(reportPath, baseName + ".zip");
- ZipOutputStream out = null;
+ String suiteName = Version.getMetadata(mContext, SUITE_NAME_METADATA_KEY);
+ // create a temporary directory for this particular report
+ File tempDir = new File(verifierReportsDir, getReportName(suiteName));
+ tempDir.mkdirs();
+
+ // create a File object for a report ZIP file
+ File reportZipFile = new File(
+ verifierReportsDir, getReportName(suiteName) + ZIP_EXTENSION);
+
try {
- out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(reportFile)));
- ZipEntry entry = new ZipEntry(baseName + ".xml");
- out.putNextEntry(entry);
- out.write(contents);
- } catch (IOException e) {
+ // Serialize the report
+ String versionName = Version.getVersionName(mContext);
+ ResultHandler.writeResults(suiteName, versionName, SUITE_PLAN, SUITE_BUILD,
+ result, tempDir, START_MS, END_MS, REFERENCE_URL, LOG_URL,
+ COMMAND_LINE_ARGS);
+
+ // copy formatting files to the temporary report directory
+ copyFormattingFiles(tempDir);
+
+ // create a compressed ZIP file containing the temporary report directory
+ ZipUtil.createZip(tempDir, reportZipFile);
+ } catch (IOException | XmlPullParserException e) {
LOG.log(Level.WARNING, "I/O exception writing report to storage.", e);
return mContext.getString(R.string.no_storage);
} finally {
- try {
- if (out != null) {
- out.close();
- }
- } catch (IOException e) {
- LOG.log(Level.WARNING, "I/O exception closing report.", e);
- }
+ // delete the temporary directory and its files made for the report
+ FileUtil.recursiveDelete(tempDir);
}
-
- return mContext.getString(R.string.report_saved, reportFile.getPath());
+ return mContext.getString(R.string.report_saved, reportZipFile.getPath());
}
- private String getReportBaseName() {
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd-HH.mm.ss", Locale.ENGLISH);
+ /**
+ * Copy the XML formatting files stored in the assets directory to the result output.
+ *
+ * @param resultsDir
+ */
+ private void copyFormattingFiles(File resultsDir) {
+ for (String resultFileName : ResultHandler.RESULT_RESOURCES) {
+ InputStream rawStream = null;
+ try {
+ rawStream = mContext.getAssets().open(
+ String.format("report/%s", resultFileName));
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "Failed to load " + resultFileName + " from assets.");
+ }
+ if (rawStream != null) {
+ File resultFile = new File(resultsDir, resultFileName);
+ try {
+ FileUtil.writeToFile(rawStream, resultFile);
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "Failed to write " + resultFileName + " to a file.");
+ }
+ }
+ }
+ }
+
+ private String getReportName(String suiteName) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss", Locale.ENGLISH);
String date = dateFormat.format(new Date());
- return "ctsVerifierReport"
- + "-" + date
- + "-" + Build.MANUFACTURER
- + "-" + Build.PRODUCT
- + "-" + Build.DEVICE
- + "-" + Build.ID;
+ return String.format( "%s-%s-%s-%s-%s-%s",
+ date, suiteName, Build.MANUFACTURER, Build.PRODUCT, Build.DEVICE, Build.ID);
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
index 1e3f312..4dd7777 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -34,8 +34,6 @@
import android.view.Window;
import android.widget.Toast;
-import java.io.IOException;
-
/** Top-level {@link ListActivity} for launching tests and managing results. */
public class TestListActivity extends AbstractTestListActivity implements View.OnClickListener {
private static final int CTS_VERIFIER_PERMISSION_REQUEST = 1;
@@ -146,15 +144,10 @@
}
private void handleViewItemSelected() {
- try {
- TestResultsReport report = new TestResultsReport(this, mAdapter);
- Intent intent = new Intent(this, ReportViewerActivity.class);
- intent.putExtra(ReportViewerActivity.EXTRA_REPORT_CONTENTS, report.getContents());
- startActivity(intent);
- } catch (IOException e) {
- Toast.makeText(this, R.string.test_results_error, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Couldn't copy test results report", e);
- }
+ TestResultsReport report = new TestResultsReport(this, mAdapter);
+ Intent intent = new Intent(this, ReportViewerActivity.class);
+ intent.putExtra(ReportViewerActivity.EXTRA_REPORT_CONTENTS, report.getContents());
+ startActivity(intent);
}
private void handleExportItemSelected() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index 36be7f9..9d9739d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -21,8 +21,15 @@
import android.text.TextUtils;
import android.util.Xml;
+import com.android.compatibility.common.util.DevicePropertyInfo;
+import com.android.compatibility.common.util.ICaseResult;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.IModuleResult;
+import com.android.compatibility.common.util.InvocationResult;
+import com.android.compatibility.common.util.ITestResult;
import com.android.compatibility.common.util.MetricsXmlSerializer;
import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.TestStatus;
import com.android.cts.verifier.TestListAdapter.TestListItem;
import org.xmlpull.v1.XmlSerializer;
@@ -33,26 +40,10 @@
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
+import java.util.Map.Entry;
/**
- * XML text report of the current test results.
- * <p>
- * Sample:
- * <pre>
- * <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
- * <test-results-report report-version="1" creation-time="Tue Jun 28 11:04:10 PDT 2011">
- * <verifier-info version-name="2.3_r4" version-code="2" />
- * <device-info>
- * <build-info fingerprint="google/soju/crespo:2.3.4/GRJ22/121341:user/release-keys" />
- * </device-info>
- * <test-results>
- * <test title="Audio Quality Verifier" class-name="com.android.cts.verifier.audioquality.AudioQualityVerifierActivity" result="not-executed" />
- * <test title="Hardware/Software Feature Summary" class-name="com.android.cts.verifier.features.FeatureSummaryActivity" result="fail" />
- * <test title="Bluetooth Test" class-name="com.android.cts.verifier.bluetooth.BluetoothTestActivity" result="fail" />
- * <test title="Accelerometer Test" class-name="com.android.cts.verifier.sensors.AccelerometerTestActivity" result="pass" />
- * </test-results>
- * </test-results-report>
- * </pre>
+ * Helper class for creating an {@code InvocationResult} for CTS result generation.
*/
class TestResultsReport {
@@ -63,6 +54,7 @@
private static DateFormat DATE_FORMAT =
new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
+ private static final String PREFIX_TAG = "build_";
private static final String TEST_RESULTS_REPORT_TAG = "test-results-report";
private static final String VERIFIER_INFO_TAG = "verifier-info";
private static final String DEVICE_INFO_TAG = "device-info";
@@ -71,6 +63,9 @@
private static final String TEST_TAG = "test";
private static final String TEST_DETAILS_TAG = "details";
+ private static final String MODULE_ID = "noabi CtsVerifier";
+ private static final String TEST_CASE_NAME = "manualTests";
+
private final Context mContext;
private final TestListAdapter mAdapter;
@@ -80,83 +75,83 @@
this.mAdapter = adapter;
}
- String getContents() throws IllegalArgumentException, IllegalStateException, IOException {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ IInvocationResult generateResult() {
+ String abis = null;
+ String abis32 = null;
+ String abis64 = null;
+ String versionBaseOs = null;
+ String versionSecurityPatch = null;
+ IInvocationResult result = new InvocationResult();
+ IModuleResult moduleResult = result.getOrCreateModule(MODULE_ID);
- XmlSerializer xml = Xml.newSerializer();
- xml.setOutput(outputStream, "utf-8");
- xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- xml.startDocument("utf-8", true);
+ // Collect build fields available in API level 21
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ abis = TextUtils.join(",", Build.SUPPORTED_ABIS);
+ abis32 = TextUtils.join(",", Build.SUPPORTED_32_BIT_ABIS);
+ abis64 = TextUtils.join(",", Build.SUPPORTED_64_BIT_ABIS);
+ }
- xml.startTag(null, TEST_RESULTS_REPORT_TAG);
- xml.attribute(null, "report-version", Integer.toString(REPORT_VERSION));
- xml.attribute(null, "creation-time", DATE_FORMAT.format(new Date()));
+ // Collect build fields available in API level 23
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ versionBaseOs = Build.VERSION.BASE_OS;
+ versionSecurityPatch = Build.VERSION.SECURITY_PATCH;
+ }
- xml.startTag(null, VERIFIER_INFO_TAG);
- xml.attribute(null, "version-name", Version.getVersionName(mContext));
- xml.attribute(null, "version-code", Integer.toString(Version.getVersionCode(mContext)));
- xml.endTag(null, VERIFIER_INFO_TAG);
+ // at the time of writing, the build class has no REFERENCE_FINGERPRINT property
+ String referenceFingerprint = null;
- xml.startTag(null, DEVICE_INFO_TAG);
- xml.startTag(null, BUILD_INFO_TAG);
- xml.attribute(null, "board", Build.BOARD);
- xml.attribute(null, "brand", Build.BRAND);
- xml.attribute(null, "device", Build.DEVICE);
- xml.attribute(null, "display", Build.DISPLAY);
- xml.attribute(null, "fingerprint", Build.FINGERPRINT);
- xml.attribute(null, "id", Build.ID);
- xml.attribute(null, "model", Build.MODEL);
- xml.attribute(null, "product", Build.PRODUCT);
- xml.attribute(null, "release", Build.VERSION.RELEASE);
- xml.attribute(null, "sdk", Integer.toString(Build.VERSION.SDK_INT));
- xml.endTag(null, BUILD_INFO_TAG);
- xml.endTag(null, DEVICE_INFO_TAG);
+ DevicePropertyInfo devicePropertyInfo = new DevicePropertyInfo(Build.CPU_ABI,
+ Build.CPU_ABI2, abis, abis32, abis64, Build.BOARD, Build.BRAND, Build.DEVICE,
+ Build.FINGERPRINT, Build.ID, Build.MANUFACTURER, Build.MODEL, Build.PRODUCT,
+ referenceFingerprint, Build.SERIAL, Build.TAGS, Build.TYPE, versionBaseOs,
+ Build.VERSION.RELEASE, Integer.toString(Build.VERSION.SDK_INT),
+ versionSecurityPatch);
- xml.startTag(null, TEST_RESULTS_TAG);
+ // add device properties to the result with a prefix tag for each key
+ for (Entry<String, String> entry :
+ devicePropertyInfo.getPropertytMapWithPrefix(PREFIX_TAG).entrySet()) {
+ String entryValue = entry.getValue();
+ if (entryValue != null) {
+ result.addInvocationInfo(entry.getKey(), entry.getValue());
+ }
+ }
+
+ ICaseResult caseResult = moduleResult.getOrCreateResult(TEST_CASE_NAME);
int count = mAdapter.getCount();
for (int i = 0; i < count; i++) {
TestListItem item = mAdapter.getItem(i);
if (item.isTest()) {
- xml.startTag(null, TEST_TAG);
- xml.attribute(null, "title", item.title);
- xml.attribute(null, "class-name", item.testName);
- xml.attribute(null, "result", getTestResultString(mAdapter.getTestResult(i)));
+ ITestResult currentTestResult = caseResult.getOrCreateResult(item.testName);
+ currentTestResult.setResultStatus(getTestResultStatus(mAdapter.getTestResult(i)));
+ // TODO: report test details with Extended Device Info (EDI) or CTS metrics
+ // String details = mAdapter.getTestDetails(i);
- String details = mAdapter.getTestDetails(i);
- if (!TextUtils.isEmpty(details)) {
- xml.startTag(null, TEST_DETAILS_TAG);
- xml.text(details);
- xml.endTag(null, TEST_DETAILS_TAG);
- }
-
- // TODO(stuartscott): For v2: ReportLog.serialize(xml, mAdapter.getReportLog(i));
ReportLog reportLog = mAdapter.getReportLog(i);
if (reportLog != null) {
- MetricsXmlSerializer metricsXmlSerializer = new MetricsXmlSerializer(xml);
- metricsXmlSerializer.serialize(reportLog);
+ currentTestResult.setReportLog(reportLog);
}
-
- xml.endTag(null, TEST_TAG);
}
}
- xml.endTag(null, TEST_RESULTS_TAG);
+ moduleResult.setDone(true);
- xml.endTag(null, TEST_RESULTS_REPORT_TAG);
- xml.endDocument();
-
- return outputStream.toString("utf-8");
+ return result;
}
- private String getTestResultString(int testResult) {
+ String getContents() {
+ // TODO: remove getContents and everything that depends on it
+ return "Report viewing is deprecated. See contents on the SD Card.";
+ }
+
+ private TestStatus getTestResultStatus(int testResult) {
switch (testResult) {
case TestResult.TEST_RESULT_PASSED:
- return "pass";
+ return TestStatus.PASS;
case TestResult.TEST_RESULT_FAILED:
- return "fail";
+ return TestStatus.FAIL;
case TestResult.TEST_RESULT_NOT_EXECUTED:
- return "not-executed";
+ return null;
default:
throw new IllegalArgumentException("Unknown test result: " + testResult);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/Version.java b/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
index e7b6121..272fbcd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
@@ -17,12 +17,18 @@
package com.android.cts.verifier;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
class Version {
+ private static final String TAG = Version.class.getSimpleName();
+
+ private static final String UNKNOWN = "unknown";
+
static String getVersionName(Context context) {
return getPackageInfo(context).versionName;
}
@@ -40,4 +46,19 @@
+ context.getPackageName());
}
}
+
+ static String getMetadata(Context context, String name) {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo applicationInfo = packageManager.getApplicationInfo(
+ context.getPackageName(), PackageManager.GET_META_DATA);
+ String value = applicationInfo.metaData.getString(name);
+ if (value != null) {
+ return value;
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Version.getMetadata: " + name, e);
+ }
+ return UNKNOWN;
+ }
}
diff --git a/common/device-side/util/Android.mk b/common/device-side/util/Android.mk
index 350c2db..8eb125c 100644
--- a/common/device-side/util/Android.mk
+++ b/common/device-side/util/Android.mk
@@ -24,7 +24,8 @@
LOCAL_MODULE := compatibility-device-util
-LOCAL_SDK_VERSION := current
+# uncomment when b/13282254 is fixed
+#LOCAL_SDK_VERSION := current
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
new file mode 100644
index 0000000..1a1ec19
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import android.os.Build;
+import android.os.SystemProperties;
+
+/**
+ * Device-side utility class for reading properties and gathering information for testing
+ * Android device compatibility.
+ */
+public class PropertyUtil {
+
+ /**
+ * Name of read-only property detailing the first API level for which the product was
+ * shipped. Property should be undefined for factory ROM products.
+ */
+ public static String FIRST_API_LEVEL = "ro.product.first_api_level";
+
+ /** Value to be returned by getPropertyInt() if property is not found */
+ public static int INT_VALUE_IF_UNSET = -1;
+
+ /** Returns whether the device build is the factory ROM */
+ public static boolean isFactoryROM() {
+ // property should be undefined if and only if the product is factory ROM.
+ return getPropertyInt(FIRST_API_LEVEL) == INT_VALUE_IF_UNSET;
+ }
+
+ /**
+ * Return the first API level for this product. If the read-only property is unset,
+ * this means the first API level is the current API level, and the current API level
+ * is returned.
+ */
+ public static int getFirstApiLevel() {
+ int firstApiLevel = getPropertyInt(FIRST_API_LEVEL);
+ return (firstApiLevel == INT_VALUE_IF_UNSET) ? Build.VERSION.SDK_INT : firstApiLevel;
+ }
+
+ /**
+ * Retrieves the desired integer property, returning INT_VALUE_IF_UNSET if not found.
+ */
+ public static int getPropertyInt(String property) {
+ return SystemProperties.getInt(property, INT_VALUE_IF_UNSET);
+ }
+}
diff --git a/common/host-side/tradefed/res/config/metadata-config.xml b/common/host-side/tradefed/res/config/metadata-config.xml
new file mode 100644
index 0000000..37f1a3e
--- /dev/null
+++ b/common/host-side/tradefed/res/config/metadata-config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Metadata result reporter for Compatibility suites">
+ <result_reporter class="com.android.compatibility.common.tradefed.result.MetadataReporter" />
+</configuration>
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index 235d71b..73f8638 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -33,15 +33,16 @@
public static final String MODULE_IDS = "MODULE_IDS";
- private static final String ROOT_DIR = "ROOT_DIR";
+ public static final String ROOT_DIR = "ROOT_DIR";
+ public static final String SUITE_NAME = "SUITE_NAME";
+ public static final String START_TIME_MS = "START_TIME_MS";
+
private static final String ROOT_DIR2 = "ROOT_DIR2";
private static final String SUITE_BUILD = "SUITE_BUILD";
- private static final String SUITE_NAME = "SUITE_NAME";
private static final String SUITE_FULL_NAME = "SUITE_FULL_NAME";
private static final String SUITE_VERSION = "SUITE_VERSION";
private static final String SUITE_PLAN = "SUITE_PLAN";
private static final String RESULT_DIR = "RESULT_DIR";
- private static final String START_TIME_MS = "START_TIME_MS";
private static final String CONFIG_PATH_PREFIX = "DYNAMIC_CONFIG_FILE:";
private static final String DYNAMIC_CONFIG_OVERRIDE_URL = "DYNAMIC_CONFIG_OVERRIDE_URL";
private static final String COMMAND_LINE_ARGS = "command_line_args";
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/MetadataReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/MetadataReporter.java
new file mode 100644
index 0000000..5f94a88
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/MetadataReporter.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.json.stream.JsonWriter;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.IShardableListener;
+import com.android.tradefed.result.StubTestInvocationListener;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * Write test metadata to the result/metadata folder.
+ */
+public class MetadataReporter extends StubTestInvocationListener implements IShardableListener {
+
+ @Option(name = "include-failure-time", description = "Include timing about tests that failed.")
+ private boolean mIncludeFailures = false;
+
+ @Option(name = "min-test-duration", description = "Ignore test durations less than this.",
+ isTimeVal = true)
+ private long mMinTestDuration = 2 * 1000;
+
+ private static final String METADATA_DIR = "metadata";
+ private CompatibilityBuildHelper mBuildHelper;
+ private File mMetadataDir;
+ private long mStartTime;
+ private String mCurrentModule;
+ private boolean mTestFailed;
+ private Collection<TestMetadata> mTestMetadata = new LinkedList<>();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IShardableListener clone() {
+ MetadataReporter clone = new MetadataReporter();
+ OptionCopier.copyOptionsNoThrow(this, clone);
+ return clone;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invocationStarted(IBuildInfo buildInfo) {
+ if (buildInfo == null) {
+ throw new RuntimeException("buildInfo is null");
+ }
+ synchronized(this) {
+ if (mBuildHelper == null) {
+ mBuildHelper = new CompatibilityBuildHelper(buildInfo);
+ try {
+ mMetadataDir = new File(mBuildHelper.getResultDir(), METADATA_DIR);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("Metadata Directory was not created: " +
+ mMetadataDir.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testRunStarted(String id, int numTests) {
+ this.mCurrentModule = id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testStarted(TestIdentifier test) {
+ mStartTime = System.currentTimeMillis();
+ mTestFailed = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testFailed(TestIdentifier test, String trace) {
+ mTestFailed = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testIgnored(TestIdentifier test) {
+ mTestFailed = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testAssumptionFailure(TestIdentifier test, String trace) {
+ mTestFailed = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
+ long duration = System.currentTimeMillis() - mStartTime;
+ if (mTestFailed && !mIncludeFailures) {
+ return;
+ }
+ if (duration < mMinTestDuration) {
+ return;
+ }
+
+ TestMetadata metadata = new TestMetadata();
+ metadata.testId = buildTestId(test);
+ metadata.seconds = duration / 1000; // convert to second for reporting
+ mTestMetadata.add(metadata);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
+ if (!mTestMetadata.isEmpty()) {
+ tryWriteToFile(mBuildHelper, mCurrentModule, mMetadataDir, mTestMetadata);
+ }
+ mTestMetadata.clear();
+ }
+
+ /** Information about a test's execution. */
+ public static class TestMetadata {
+ // The id of the test
+ String testId;
+ // The duration of the test.
+ long seconds;
+ }
+
+ private static String buildTestId(TestIdentifier test) {
+ return String.format("%s.%s", test.getClassName(), test.getTestName());
+ }
+
+ private static void tryWriteToFile(
+ CompatibilityBuildHelper compatibilityBuildHelper,
+ String moduleName,
+ File metadataDir,
+ Collection<TestMetadata> metadatas) {
+
+ metadataDir.mkdirs();
+
+ String moduleFileName = moduleName + "." + System.currentTimeMillis() + ".json";
+ File metadataFile = new File(metadataDir, moduleFileName);
+ Map<String, String> buildAttributes =
+ compatibilityBuildHelper.getBuildInfo().getBuildAttributes();
+ try (JsonWriter writer = new JsonWriter(new PrintWriter(metadataFile))) {
+ writer.beginObject();
+
+ writer.name("fingerprint");
+ writer.value(buildAttributes.get("cts:build_fingerprint"));
+
+ writer.name("product");
+ writer.value(buildAttributes.get("cts:build_product"));
+
+ writer.name("build_id");
+ writer.value(buildAttributes.get("cts:build_id"));
+
+ writer.name("suite_version");
+ writer.value(compatibilityBuildHelper.getSuiteVersion());
+
+ writer.name("suite_name");
+ writer.value(compatibilityBuildHelper.getSuiteName());
+
+ writer.name("suite_build");
+ writer.value(compatibilityBuildHelper.getSuiteBuild());
+
+ writer.name("module_id");
+ writer.value(moduleName);
+
+ writer.name("test");
+ writer.beginArray();
+ for (TestMetadata metadata : metadatas) {
+ writer.beginObject();
+ writer.name("id");
+ writer.value(metadata.testId);
+ writer.name("sec");
+ writer.value(metadata.seconds);
+ writer.endObject();
+ }
+ writer.endArray();
+
+ writer.endObject();
+ } catch (IOException e) {
+ CLog.e("[%s] While saving metadata.", metadataFile.getAbsolutePath());
+ CLog.e(e);
+ }
+ }
+
+ protected Collection<TestMetadata> getTestMetadata() {
+ return Collections.unmodifiableCollection(mTestMetadata);
+ }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 620fa0a..8136f23 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -77,11 +77,6 @@
private static final String RESULT_KEY = "COMPATIBILITY_TEST_RESULT";
private static final String CTS_PREFIX = "cts:";
private static final String BUILD_INFO = CTS_PREFIX + "build_";
- private static final String[] RESULT_RESOURCES = {
- "compatibility_result.css",
- "compatibility_result.xsd",
- "compatibility_result.xsl",
- "logo.png"};
@Option(name = CompatibilityTest.RETRY_OPTION,
shortName = 'r',
@@ -502,6 +497,10 @@
mBuildHelper.getSuiteBuild(), mResult, mResultDir, startTime,
elapsedTime + startTime, mReferenceUrl, getLogUrl(),
mBuildHelper.getCommandLineArgs());
+ if (mRetrySessionId != null) {
+ copyRetryFiles(ResultHandler.getResultDirectory(
+ mBuildHelper.getResultsDir(), mRetrySessionId), mResultDir);
+ }
File zippedResults = zipResults(mResultDir);
// Create failure report after zip file so extra data is not uploaded
@@ -681,7 +680,7 @@
* @param resultsDir
*/
static void copyFormattingFiles(File resultsDir) {
- for (String resultFileName : RESULT_RESOURCES) {
+ for (String resultFileName : ResultHandler.RESULT_RESOURCES) {
InputStream configStream = ResultHandler.class.getResourceAsStream(
String.format("/report/%s", resultFileName));
if (configStream != null) {
@@ -720,6 +719,33 @@
}
/**
+ * Recursively copy any other files found in the previous session's result directory to the
+ * new result directory, so long as they don't already exist. For example, a "screenshots"
+ * directory generated in a previous session by a passing test will not be generated on retry
+ * unless copied from the old result directory.
+ *
+ * @param oldResultsDir
+ * @param newResultsDir
+ */
+ static void copyRetryFiles(File oldResultsDir, File newResultsDir) {
+ File[] oldFiles = oldResultsDir.listFiles();
+ for (File oldFile : oldFiles) {
+ File newFile = new File (newResultsDir, oldFile.getName());
+ if (!newFile.exists()) {
+ try {
+ if (oldFile.isDirectory()) {
+ FileUtil.recursiveCopy(oldFile, newFile);
+ } else {
+ FileUtil.copyFile(oldFile, newFile);
+ }
+ } catch (IOException e) {
+ warn("Failed to copy file \"%s\" from previous session", oldFile.getName());
+ }
+ }
+ }
+ }
+
+ /**
* Zip the contents of the given results directory.
*
* @param resultsDir
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java
index cd1c911..0805b31 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DeviceInfoCollector.java
@@ -19,6 +19,7 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
import com.android.compatibility.common.tradefed.util.CollectorUtil;
+import com.android.compatibility.common.util.DevicePropertyInfo;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -39,30 +40,29 @@
*/
public class DeviceInfoCollector extends ApkInstrumentationPreparer {
- private static final Map<String, String> BUILD_KEYS = new HashMap<>();
- static {
- BUILD_KEYS.put("cts:build_id", "ro.build.id");
- BUILD_KEYS.put("cts:build_product", "ro.product.name");
- BUILD_KEYS.put("cts:build_device", "ro.product.device");
- BUILD_KEYS.put("cts:build_board", "ro.product.board");
- BUILD_KEYS.put("cts:build_manufacturer", "ro.product.manufacturer");
- BUILD_KEYS.put("cts:build_brand", "ro.product.brand");
- BUILD_KEYS.put("cts:build_model", "ro.product.model");
- BUILD_KEYS.put("cts:build_type", "ro.build.type");
- BUILD_KEYS.put("cts:build_tags", "ro.build.tags");
- BUILD_KEYS.put("cts:build_fingerprint", "ro.build.fingerprint");
- BUILD_KEYS.put("cts:build_abi", "ro.product.cpu.abi");
- BUILD_KEYS.put("cts:build_abi2", "ro.product.cpu.abi2");
- BUILD_KEYS.put("cts:build_abis", "ro.product.cpu.abilist");
- BUILD_KEYS.put("cts:build_abis_32", "ro.product.cpu.abilist32");
- BUILD_KEYS.put("cts:build_abis_64", "ro.product.cpu.abilist64");
- BUILD_KEYS.put("cts:build_serial", "ro.serialno");
- BUILD_KEYS.put("cts:build_version_release", "ro.build.version.release");
- BUILD_KEYS.put("cts:build_version_sdk", "ro.build.version.sdk");
- BUILD_KEYS.put("cts:build_version_base_os", "ro.build.version.base_os");
- BUILD_KEYS.put("cts:build_version_security_patch", "ro.build.version.security_patch");
- BUILD_KEYS.put("cts:build_reference_fingerprint", "ro.build.reference.fingerprint");
- }
+ private static final String ABI = "ro.product.cpu.abi";
+ private static final String ABI2 = "ro.product.cpu.abi2";
+ private static final String ABIS = "ro.product.cpu.abilist";
+ private static final String ABIS_32 = "ro.product.cpu.abilist32";
+ private static final String ABIS_64 = "ro.product.cpu.abilist64";
+ private static final String BOARD = "ro.product.board";
+ private static final String BRAND = "ro.product.brand";
+ private static final String DEVICE = "ro.product.device";
+ private static final String FINGERPRINT = "ro.build.fingerprint";
+ private static final String ID = "ro.build.id";
+ private static final String MANUFACTURER = "ro.product.manufacturer";
+ private static final String MODEL = "ro.product.model";
+ private static final String PRODUCT = "ro.product.name";
+ private static final String REFERENCE_FINGERPRINT = "ro.build.reference.fingerprint";
+ private static final String SERIAL = "ro.serialno";
+ private static final String TAGS = "ro.build.tags";
+ private static final String TYPE = "ro.build.type";
+ private static final String VERSION_BASE_OS = "ro.build.version.base_os";
+ private static final String VERSION_RELEASE = "ro.build.version.release";
+ private static final String VERSION_SDK = "ro.build.version.sdk";
+ private static final String VERSION_SECURITY_PATCH = "ro.build.version.security_patch";
+
+ private static final String PREFIX_TAG = "cts:build_";
@Option(name = CompatibilityTest.SKIP_DEVICE_INFO_OPTION,
shortName = 'd',
@@ -91,9 +91,16 @@
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
BuildError, DeviceNotAvailableException {
- for (Entry<String, String> entry : BUILD_KEYS.entrySet()) {
- buildInfo.addBuildAttribute(
- entry.getKey(), nullToEmpty(device.getProperty(entry.getValue())));
+ DevicePropertyInfo devicePropertyInfo = new DevicePropertyInfo(ABI, ABI2, ABIS, ABIS_32,
+ ABIS_64, BOARD, BRAND, DEVICE, FINGERPRINT, ID, MANUFACTURER, MODEL, PRODUCT,
+ REFERENCE_FINGERPRINT, SERIAL, TAGS, TYPE, VERSION_BASE_OS, VERSION_RELEASE,
+ VERSION_SDK, VERSION_SECURITY_PATCH);
+
+ // add device properties to the result with a prefix tag for each key
+ for (Entry<String, String> entry :
+ devicePropertyInfo.getPropertytMapWithPrefix(PREFIX_TAG).entrySet()) {
+ buildInfo.addBuildAttribute(entry.getKey(),
+ nullToEmpty(device.getProperty(entry.getValue())));
}
if (mSkipDeviceInfo) {
return;
@@ -111,7 +118,7 @@
return;
}
if (mHostDir != null && mHostDir.isDirectory() &&
- mResultDir != null && mResultDir.isDirectory()) {
+ mResultDir != null && mResultDir.isDirectory()) {
CollectorUtil.pullFromHost(mHostDir, mResultDir);
}
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 3b1c5e7..f63093c 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -50,7 +50,6 @@
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
-import com.android.tradefed.result.ResultForwarder;
import com.android.tradefed.targetprep.ITargetPreparer;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IBuildReceiver;
@@ -71,7 +70,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -429,14 +427,8 @@
// execute pre module execution checker
runPreModuleCheck(module.getName(), checkers, mDevice, listener);
- // Workaround to b/34202787: Add result forwarder that ensures module is reported
- // with 0 tests if test runner doesn't report anything in this case.
- // Necessary for solution to b/33289177, in which completed modules may sometimes
- // not be marked done until retried with 0 tests.
- ModuleResultForwarder moduleListener = new ModuleResultForwarder(listener);
try {
- module.run(moduleListener);
- moduleListener.finish(module.getId());
+ module.run(listener);
} catch (DeviceUnresponsiveException due) {
// being able to catch a DeviceUnresponsiveException here implies that recovery
// was successful, and test execution should proceed to next module
@@ -753,32 +745,4 @@
return shardQueue;
}
-
- private class ModuleResultForwarder extends ResultForwarder {
-
- private boolean mTestRunStarted = false;
- private ITestInvocationListener mListener;
-
- public ModuleResultForwarder(ITestInvocationListener listener) {
- super(listener);
- mListener = listener;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void testRunStarted(String name, int numTests) {
- mListener.testRunStarted(name, numTests);
- mTestRunStarted = true;
- }
-
- public void finish(String moduleId) {
- if (!mTestRunStarted) {
- mListener.testRunStarted(moduleId, 0);
- mListener.testRunEnded(0, Collections.emptyMap());
- }
- }
- }
-
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
index d8a2adb..c69b3a7 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
@@ -28,6 +28,7 @@
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.ResultForwarder;
import com.android.tradefed.targetprep.BuildError;
import com.android.tradefed.targetprep.ITargetCleaner;
import com.android.tradefed.targetprep.ITargetPreparer;
@@ -219,7 +220,6 @@
*/
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
- IModuleListener moduleListener = new ModuleListener(this, listener);
// Run DynamicConfigPusher setup once more, in case cleaner has previously
// removed dynamic config file from the target (see b/32877809)
for (ITargetPreparer preparer : mDynamicConfigPreparers) {
@@ -241,7 +241,11 @@
((IDeviceTest) mTest).setDevice(mDevice);
}
- mTest.run(moduleListener);
+ IModuleListener moduleListener = new ModuleListener(this, listener);
+ // Guarantee events testRunStarted and testRunEnded in case underlying test runner does not
+ ModuleFinisher moduleFinisher = new ModuleFinisher(moduleListener);
+ mTest.run(moduleFinisher);
+ moduleFinisher.finish();
// Tear down
for (ITargetCleaner cleaner : mCleaners) {
@@ -309,4 +313,37 @@
e.printStackTrace();
}
}
+
+ /**
+ * ResultForwarder that tracks whether method testRunStarted() has been called for its
+ * listener. If not, invoking finish() will call testRunStarted with 0 tests for this module,
+ * as well as testRunEnded with 0 ms elapsed.
+ */
+ private class ModuleFinisher extends ResultForwarder {
+
+ private boolean mFinished;
+ private ITestInvocationListener mListener;
+
+ public ModuleFinisher(ITestInvocationListener listener) {
+ super(listener);
+ mListener = listener;
+ mFinished = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testRunStarted(String name, int numTests) {
+ mListener.testRunStarted(name, numTests);
+ mFinished = true;
+ }
+
+ public void finish() {
+ if (!mFinished) {
+ mListener.testRunStarted(mId, 0);
+ mListener.testRunEnded(0, Collections.emptyMap());
+ }
+ }
+ }
}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index b8b7858..860c830 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -19,6 +19,7 @@
import com.android.compatibility.common.tradefed.command.CompatibilityConsoleTest;
import com.android.compatibility.common.tradefed.result.ChecksumReporterTest;
import com.android.compatibility.common.tradefed.result.ConsoleReporterTest;
+import com.android.compatibility.common.tradefed.result.MetadataReporterTest;
import com.android.compatibility.common.tradefed.result.ResultReporterTest;
import com.android.compatibility.common.tradefed.result.SubPlanCreatorTest;
import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
@@ -51,6 +52,7 @@
addTestSuite(CompatibilityTestTest.class);
addTestSuite(OptionHelperTest.class);
addTestSuite(CollectorUtilTest.class);
+ addTestSuite(MetadataReporterTest.class);
addTestSuite(ModuleDefTest.class);
addTestSuite(ModuleRepoTest.class);
addTestSuite(PropertyCheckTest.class);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/MetadataReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/MetadataReporterTest.java
new file mode 100644
index 0000000..dba3128
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/MetadataReporterTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.AbiUtils;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.RunUtil;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+
+/**
+ * Unit Tests for {@link MetadataReporter}
+ */
+public class MetadataReporterTest extends TestCase {
+
+ private static final String MIN_TEST_DURATION = "10";
+ private static final String BUILD_NUMBER = "2";
+ private static final String SUITE_PLAN = "cts";
+ private static final String DYNAMIC_CONFIG_URL = "";
+ private static final String ROOT_DIR_NAME = "root";
+ private static final String BASE_DIR_NAME = "android-tests";
+ private static final String TESTCASES = "testcases";
+ private static final String NAME = "ModuleName";
+ private static final String ABI = "mips64";
+ private static final String ID = AbiUtils.createId(ABI, NAME);
+ private static final String CLASS = "android.test.FoorBar";
+ private static final String METHOD_1 = "testBlah1";
+ private static final String METHOD_2 = "testBlah2";
+ private static final String METHOD_3 = "testBlah3";
+ private static final String STACK_TRACE = "Something small is not alright\n " +
+ "at four.big.insects.Marley.sing(Marley.java:10)";
+ private static final long START_TIME = 123456L;
+
+ private MetadataReporter mReporter;
+ private IBuildInfo mBuildInfo;
+ private CompatibilityBuildHelper mBuildHelper;
+
+ private File mRoot = null;
+ private File mBase = null;
+ private File mTests = null;
+
+ @Override
+ public void setUp() throws Exception {
+ mReporter = new MetadataReporter();
+ OptionSetter setter = new OptionSetter(mReporter);
+ setter.setOptionValue("min-test-duration", MIN_TEST_DURATION);
+ mRoot = FileUtil.createTempDir(ROOT_DIR_NAME);
+ mBase = new File(mRoot, BASE_DIR_NAME);
+ mBase.mkdirs();
+ mTests = new File(mBase, TESTCASES);
+ mTests.mkdirs();
+ System.setProperty(CompatibilityBuildHelper.ROOT_DIR, mRoot.getAbsolutePath());
+ mBuildInfo = new BuildInfo(BUILD_NUMBER, "", "");
+ mBuildHelper = new CompatibilityBuildHelper(mBuildInfo);
+ mBuildHelper.init(SUITE_PLAN, DYNAMIC_CONFIG_URL, START_TIME);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mReporter = null;
+ FileUtil.recursiveDelete(mRoot);
+ }
+
+ /**
+ * Test that when tests execute faster than the threshold we do not report then.
+ */
+ public void testResultReportingFastTests() throws Exception {
+ mReporter.invocationStarted(mBuildInfo);
+ mReporter.testRunStarted(ID, 3);
+ runTests(0l);
+ Collection<MetadataReporter.TestMetadata> metadata = mReporter.getTestMetadata();
+ assertTrue(metadata.isEmpty());
+ mReporter.testRunEnded(10, new HashMap<String, String>());
+ mReporter.invocationEnded(10);
+ }
+
+ /**
+ * Test that when tests execute slower than the limit we report them if they passed.
+ */
+ public void testResultReportingSlowTests() throws Exception {
+ mReporter.invocationStarted(mBuildInfo);
+ mReporter.testRunStarted(ID, 3);
+ runTests(50l);
+
+ Collection<MetadataReporter.TestMetadata> metadata = mReporter.getTestMetadata();
+ assertEquals(metadata.size(), 2); // Two passing slow tests.
+
+ mReporter.testRunEnded(10, new HashMap<String, String>());
+ mReporter.invocationEnded(10);
+ }
+
+ /** Run 4 test. */
+ private void runTests(long waitTime) {
+ TestIdentifier test1 = new TestIdentifier(CLASS, METHOD_1);
+ mReporter.testStarted(test1);
+ RunUtil.getDefault().sleep(waitTime);
+ mReporter.testEnded(test1, new HashMap<String, String>());
+
+ TestIdentifier test2 = new TestIdentifier(CLASS, METHOD_2);
+ mReporter.testStarted(test2);
+ RunUtil.getDefault().sleep(waitTime);
+ mReporter.testEnded(test1, new HashMap<String, String>());
+
+ TestIdentifier test3 = new TestIdentifier(CLASS, METHOD_3);
+ mReporter.testStarted(test3);
+ RunUtil.getDefault().sleep(waitTime);
+ mReporter.testFailed(test3, STACK_TRACE);
+ mReporter.testEnded(test3, new HashMap<String, String>());
+
+ TestIdentifier test4 = new TestIdentifier(CLASS, METHOD_3);
+ mReporter.testStarted(test4);
+ RunUtil.getDefault().sleep(waitTime);
+ mReporter.testIgnored(test4);
+ mReporter.testEnded(test4, new HashMap<String, String>());
+ }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
index 2ab884e..019557e 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
@@ -28,9 +28,12 @@
import com.android.tradefed.testtype.ITestCollector;
import com.android.tradefed.testtype.ITestFilterReceiver;
+import org.easymock.EasyMock;
+
import junit.framework.TestCase;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -52,6 +55,21 @@
assertEquals("Incorrect Name", NAME, def.getName());
}
+ public void testModuleFinisher() throws Exception {
+ IAbi abi = new Abi(ABI, "");
+ MockRemoteTest mockTest = new MockRemoteTest();
+ IModuleDef def = new ModuleDef(NAME, abi, mockTest, new ArrayList<ITargetPreparer>());
+ ITestInvocationListener mockListener = EasyMock.createMock(ITestInvocationListener.class);
+ // listener should receive testRunStarted/testRunEnded events even for no-op run() method
+ mockListener.testRunStarted(ID, 0);
+ EasyMock.expectLastCall().once();
+ mockListener.testRunEnded(0, Collections.emptyMap());
+ EasyMock.expectLastCall().once();
+ EasyMock.replay(mockListener);
+ def.run(mockListener);
+ EasyMock.verify(mockListener);
+ }
+
private class MockRemoteTest implements IRemoteTest, ITestFilterReceiver, IAbiReceiver,
IRuntimeHintProvider, ITestCollector {
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/PropertyUtil.java b/common/host-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
new file mode 100644
index 0000000..199b826
--- /dev/null
+++ b/common/host-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+/**
+ * Host-side utility class for reading properties and gathering information for testing
+ * Android device compatibility.
+ */
+public class PropertyUtil {
+
+ /**
+ * Name of read-only property detailing the first API level for which the product was
+ * shipped. Property should be undefined for factory ROM products.
+ */
+ public static String FIRST_API_LEVEL = "ro.product.first_api_level";
+
+ /** Returns whether the device build is the factory ROM */
+ public static boolean isFactoryROM(ITestDevice device) throws DeviceNotAvailableException {
+ // first API level property should be undefined if and only if the product is factory ROM.
+ return device.getProperty(FIRST_API_LEVEL) == null;
+ }
+
+ /**
+ * Return the first API level for this product. If the read-only property is unset,
+ * this means the first API level is the current API level, and the current API level
+ * is returned.
+ */
+ public static int getFirstApiLevel(ITestDevice device) throws DeviceNotAvailableException {
+ String propString = device.getProperty(FIRST_API_LEVEL);
+ return (propString == null) ? device.getApiLevel() : Integer.parseInt(propString);
+ }
+}
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
index 169cfdb..094943b 100644
--- a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
+++ b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
@@ -28,10 +28,9 @@
public HostUnitTests() {
super();
addTestSuite(DynamicConfigHandlerTest.class);
- addTestSuite(ResultHandlerTest.class);
}
public static Test suite() {
return new HostUnitTests();
}
-}
\ No newline at end of file
+}
diff --git a/common/util/Android.mk b/common/util/Android.mk
index c95508b..0d3754b 100644
--- a/common/util/Android.mk
+++ b/common/util/Android.mk
@@ -26,6 +26,8 @@
LOCAL_MODULE := compatibility-common-util-devicesidelib
+LOCAL_STATIC_JAVA_LIBRARIES := guava
+
LOCAL_SDK_VERSION := current
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -42,7 +44,10 @@
LOCAL_MODULE := compatibility-common-util-hostsidelib
-LOCAL_STATIC_JAVA_LIBRARIES := junit kxml2-2.3.0 platform-test-annotations-host
+LOCAL_STATIC_JAVA_LIBRARIES := guavalib \
+ junit \
+ kxml2-2.3.0 \
+ platform-test-annotations-host
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java b/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
similarity index 99%
rename from common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java
rename to common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
index faac61f..32fa532 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java
+++ b/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
@@ -16,8 +16,6 @@
package com.android.compatibility.common.util;
-import com.android.annotations.Nullable;
-
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
@@ -316,7 +314,7 @@
}
private static String buildTestId(
- String suiteName, String caseName, String testName, @Nullable String abi) {
+ String suiteName, String caseName, String testName, String abi) {
String name = Joiner.on(NAME_SEPARATOR).skipNulls().join(
Strings.emptyToNull(suiteName),
Strings.emptyToNull(caseName),
diff --git a/common/util/src/com/android/compatibility/common/util/DevicePropertyInfo.java b/common/util/src/com/android/compatibility/common/util/DevicePropertyInfo.java
new file mode 100644
index 0000000..ec24b42
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/DevicePropertyInfo.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 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.compatibility.common.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class for collecting device information. This is used to enforce
+ * consistent property collection host-side and device-side for CTS reports.
+ *
+ * Note that properties across sources can differ, e.g. {@code android.os.Build}
+ * properties sometimes deviate from the read-only properties that they're based
+ * on.
+ */
+public final class DevicePropertyInfo {
+
+ private final String mAbi;
+ private final String mAbi2;
+ private final String mAbis;
+ private final String mAbis32;
+ private final String mAbis64;
+ private final String mBoard;
+ private final String mBrand;
+ private final String mDevice;
+ private final String mFingerprint;
+ private final String mId;
+ private final String mManufacturer;
+ private final String mModel;
+ private final String mProduct;
+ private final String mReferenceFingerprint;
+ private final String mSerial;
+ private final String mTags;
+ private final String mType;
+ private final String mVersionBaseOs;
+ private final String mVersionRelease;
+ private final String mVersionSdk;
+ private final String mVersionSecurityPatch;
+
+ public DevicePropertyInfo(String abi, String abi2, String abis, String abis32, String abis64,
+ String board, String brand, String device, String fingerprint, String id,
+ String manufacturer, String model, String product, String referenceFigerprint,
+ String serial, String tags, String type, String versionBaseOs, String versionRelease,
+ String versionSdk, String versionSecurityPatch) {
+ mAbi = abi;
+ mAbi2 = abi2;
+ mAbis = abis;
+ mAbis32 = abis32;
+ mAbis64 = abis64;
+ mBoard = board;
+ mBrand = brand;
+ mDevice = device;
+ mFingerprint = fingerprint;
+ mId = id;
+ mManufacturer = manufacturer;
+ mModel = model;
+ mProduct = product;
+ mReferenceFingerprint = referenceFigerprint;
+ mSerial = serial;
+ mTags = tags;
+ mType = type;
+ mVersionBaseOs = versionBaseOs;
+ mVersionRelease = versionRelease;
+ mVersionSdk = versionSdk;
+ mVersionSecurityPatch = versionSecurityPatch;
+ }
+
+ /**
+ * Return a {@code Map} with property keys prepended with a given prefix
+ * string. This is intended to be used to generate entries for
+ * {@code} Build tag attributes in CTS test results.
+ */
+ public Map<String, String> getPropertytMapWithPrefix(String prefix) {
+ Map<String, String> propertyMap = new HashMap<>();
+
+ propertyMap.put(prefix + "abi", mAbi);
+ propertyMap.put(prefix + "abi2", mAbi2);
+ propertyMap.put(prefix + "abis", mAbis);
+ propertyMap.put(prefix + "abis_32", mAbis32);
+ propertyMap.put(prefix + "abis_64", mAbis64);
+ propertyMap.put(prefix + "board", mBoard);
+ propertyMap.put(prefix + "brand", mBrand);
+ propertyMap.put(prefix + "device", mDevice);
+ propertyMap.put(prefix + "fingerprint", mFingerprint);
+ propertyMap.put(prefix + "id", mId);
+ propertyMap.put(prefix + "manufacturer", mManufacturer);
+ propertyMap.put(prefix + "model", mModel);
+ propertyMap.put(prefix + "product", mProduct);
+ propertyMap.put(prefix + "reference_fingerprint", mReferenceFingerprint);
+ propertyMap.put(prefix + "serial", mSerial);
+ propertyMap.put(prefix + "tags", mTags);
+ propertyMap.put(prefix + "type", mType);
+ propertyMap.put(prefix + "version_base_os", mVersionBaseOs);
+ propertyMap.put(prefix + "version_release", mVersionRelease);
+ propertyMap.put(prefix + "version_sdk", mVersionSdk);
+ propertyMap.put(prefix + "version_security_patch", mVersionSecurityPatch);
+
+ return propertyMap;
+ }
+
+}
diff --git a/common/util/src/com/android/compatibility/common/util/FileUtil.java b/common/util/src/com/android/compatibility/common/util/FileUtil.java
new file mode 100644
index 0000000..b59912b
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/FileUtil.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 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.compatibility.common.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A helper class for file related operations
+ */
+public class FileUtil {
+
+ /**
+ * Recursively delete given file or directory and all its contents.
+ *
+ * @param rootDir the directory or file to be deleted; can be null
+ */
+ public static void recursiveDelete(File rootDir) {
+ if (rootDir != null) {
+ if (rootDir.isDirectory()) {
+ File[] childFiles = rootDir.listFiles();
+ if (childFiles != null) {
+ for (File child : childFiles) {
+ recursiveDelete(child);
+ }
+ }
+ }
+ rootDir.delete();
+ }
+ }
+
+ /**
+ * A helper method for writing stream data to file
+ *
+ * @param input the unbuffered input stream
+ * @param destFile the dest file to write to
+ */
+ public static void writeToFile(InputStream input, File destFile) throws IOException {
+ InputStream origStream = null;
+ OutputStream destStream = null;
+ try {
+ origStream = new BufferedInputStream(input);
+ destStream = new BufferedOutputStream(new FileOutputStream(destFile));
+ StreamUtil.copyStreams(origStream, destStream);
+ } finally {
+ origStream.close();
+ destStream.flush();
+ destStream.close();
+ }
+ }
+
+}
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
similarity index 93%
rename from common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
rename to common/util/src/com/android/compatibility/common/util/ResultHandler.java
index 89ec2d4..6c2e98a 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -25,6 +25,7 @@
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
@@ -33,9 +34,6 @@
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
@@ -60,10 +58,17 @@
private static final String TYPE = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
private static final String NS = null;
private static final String RESULT_FILE_VERSION = "5.0";
- /* package */ static final String TEST_RESULT_FILE_NAME = "test_result.xml";
+ public static final String TEST_RESULT_FILE_NAME = "test_result.xml";
private static final String FAILURE_REPORT_NAME = "test_result_failures.html";
private static final String FAILURE_XSL_FILE_NAME = "compatibility_failures.xsl";
+ public static final String[] RESULT_RESOURCES = {
+ "compatibility_result.css",
+ "compatibility_result.xsd",
+ "compatibility_result.xsl",
+ "logo.png"
+ };
+
// XML constants
private static final String ABI_ATTR = "abi";
private static final String BUGREPORT_TAG = "BugReport";
@@ -451,18 +456,22 @@
// If the previous run has an invalid checksum file,
// copy it into current results folder for future troubleshooting
File retryDirectory = invocationResult.getRetryDirectory();
- Path retryChecksum = FileSystems.getDefault().getPath(
- retryDirectory.getAbsolutePath(), ChecksumReporter.NAME);
- if (!retryChecksum.toFile().exists()) {
+ File retryChecksum = new File(retryDirectory, ChecksumReporter.NAME);
+ if (!retryChecksum.exists()) {
// if no checksum file, check for a copy from a previous retry
- retryChecksum = FileSystems.getDefault().getPath(
- retryDirectory.getAbsolutePath(), ChecksumReporter.PREV_NAME);
+ retryChecksum = new File(retryDirectory, ChecksumReporter.PREV_NAME);
}
- if (retryChecksum.toFile().exists()) {
+ if (retryChecksum.exists()) {
File checksumCopy = new File(resultDir, ChecksumReporter.PREV_NAME);
- try (FileOutputStream stream = new FileOutputStream(checksumCopy)) {
- Files.copy(retryChecksum, stream);
+ try (OutputStream out = new FileOutputStream(checksumCopy);
+ InputStream in = new FileInputStream(retryChecksum)) {
+ // Copy the bits from input stream to output stream
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
} catch (IOException e) {
// Do not disrupt the process if there is a problem copying checksum
}
@@ -496,6 +505,22 @@
}
/**
+ * Get the result directory for the given sessionId.
+ */
+ public static File getResultDirectory(File resultsDir, Integer sessionId) {
+ if (sessionId < 0) {
+ throw new IllegalArgumentException(
+ String.format("Invalid session id [%d] ", sessionId));
+ }
+ List<File> allResultDirs = getResultDirectories(resultsDir);
+ if (sessionId >= allResultDirs.size()) {
+ throw new IllegalArgumentException(String.format("Invalid session id [%d], results" +
+ "directory contains only %d results", sessionId, allResultDirs.size()));
+ }
+ return allResultDirs.get(sessionId);
+ }
+
+ /**
* Get a list of child directories that contain test invocation results
* @param resultsDir the root test result directory
* @return
diff --git a/common/util/src/com/android/compatibility/common/util/StreamUtil.java b/common/util/src/com/android/compatibility/common/util/StreamUtil.java
new file mode 100644
index 0000000..febd73d
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/StreamUtil.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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.compatibility.common.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class StreamUtil {
+
+ // 16K buffer size
+ private static final int BUFFER_SIZE = 16 * 1024;
+
+ /**
+ * Copies contents of origStream to destStream.
+ * <p/>
+ * Recommended to provide a buffered stream for input and output
+ *
+ * @param inStream the {@link InputStream}
+ * @param outStream the {@link OutputStream}
+ * @throws IOException
+ */
+ public static void copyStreams(InputStream inStream, OutputStream outStream)
+ throws IOException {
+ byte[] buf = new byte[BUFFER_SIZE];
+ int size = -1;
+ while ((size = inStream.read(buf)) != -1) {
+ outStream.write(buf, 0, size);
+ }
+ }
+
+}
diff --git a/common/util/src/com/android/compatibility/common/util/ZipUtil.java b/common/util/src/com/android/compatibility/common/util/ZipUtil.java
new file mode 100644
index 0000000..6cee83a
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/ZipUtil.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 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.compatibility.common.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class ZipUtil {
+
+ /**
+ * Utility method to create a zip file containing the given directory and
+ * all its contents.
+ *
+ * @param dir the directory to zip
+ * @param zipFile the zip file to create - it should not already exist
+ * @throws IOException if failed to create zip file
+ */
+ public static void createZip(File dir, File zipFile) throws IOException {
+ ZipOutputStream out = null;
+ try {
+ FileOutputStream fileStream = new FileOutputStream(zipFile);
+ out = new ZipOutputStream(new BufferedOutputStream(fileStream));
+ addToZip(out, dir, new LinkedList<String>());
+ } catch (IOException e) {
+ zipFile.delete();
+ throw e;
+ } catch (RuntimeException e) {
+ zipFile.delete();
+ throw e;
+ } finally {
+ out.close();
+ }
+ }
+
+ /**
+ * Recursively adds given file and its contents to ZipOutputStream
+ *
+ * @param out the {@link ZipOutputStream}
+ * @param file the {@link File} to add to the stream
+ * @param relativePathSegs the relative path of file, including separators
+ * @throws IOException if failed to add file to zip
+ */
+ public static void addToZip(ZipOutputStream out, File file, List<String> relativePathSegs)
+ throws IOException {
+ relativePathSegs.add(file.getName());
+ if (file.isDirectory()) {
+ // note: it appears even on windows, ZipEntry expects '/' as a path separator
+ relativePathSegs.add("/");
+ }
+ ZipEntry zipEntry = new ZipEntry(buildPath(relativePathSegs));
+ out.putNextEntry(zipEntry);
+ if (file.isFile()) {
+ writeToStream(file, out);
+ }
+ out.closeEntry();
+ if (file.isDirectory()) {
+ // recursively add contents
+ File[] subFiles = file.listFiles();
+ if (subFiles == null) {
+ throw new IOException(String.format("Could not read directory %s",
+ file.getAbsolutePath()));
+ }
+ for (File subFile : subFiles) {
+ addToZip(out, subFile, relativePathSegs);
+ }
+ // remove the path separator
+ relativePathSegs.remove(relativePathSegs.size()-1);
+ }
+ // remove the last segment, added at beginning of method
+ relativePathSegs.remove(relativePathSegs.size()-1);
+ }
+
+ /**
+ * Builds a file system path from a stack of relative path segments
+ *
+ * @param relativePathSegs the list of relative paths
+ * @return a {@link String} containing all relativePathSegs
+ */
+ private static String buildPath(List<String> relativePathSegs) {
+ StringBuilder pathBuilder = new StringBuilder();
+ for (String segment : relativePathSegs) {
+ pathBuilder.append(segment);
+ }
+ return pathBuilder.toString();
+ }
+
+ /**
+ * Helper method to write input file contents to output stream.
+ *
+ * @param file the input {@link File}
+ * @param out the {@link OutputStream}
+ *
+ * @throws IOException
+ */
+ private static void writeToStream(File file, OutputStream out) throws IOException {
+ InputStream inputStream = null;
+ try {
+ inputStream = new BufferedInputStream(new FileInputStream(file));
+ StreamUtil.copyStreams(inputStream, out);
+ } finally {
+ inputStream.close();
+ }
+ }
+
+}
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
similarity index 96%
rename from common/host-side/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
rename to common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
index 0dfe3f3..dcead0e 100644
--- a/common/host-side/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
@@ -70,13 +70,8 @@
private static final String METHOD_3 = "testBlah3";
private static final String METHOD_4 = "testBlah4";
private static final String SUMMARY_SOURCE = String.format("%s#%s:20", CLASS_B, METHOD_4);
- private static final String DETAILS_SOURCE = String.format("%s#%s:18", CLASS_B, METHOD_4);
private static final String SUMMARY_MESSAGE = "Headline";
private static final double SUMMARY_VALUE = 9001;
- private static final String DETAILS_MESSAGE = "Deats";
- private static final double DETAILS_VALUE_1 = 14;
- private static final double DETAILS_VALUE_2 = 18;
- private static final double DETAILS_VALUE_3 = 17;
private static final String MESSAGE = "Something small is not alright";
private static final String STACK_TRACE = "Something small is not alright\n " +
"at four.big.insects.Marley.sing(Marley.java:10)";
@@ -212,9 +207,8 @@
String moduleBTest4 = String.format(XML_TEST_RESULT, METHOD_4,
SUMMARY_SOURCE, SUMMARY_MESSAGE, ResultType.HIGHER_BETTER.toReportString(),
ResultUnit.SCORE.toReportString(), Double.toString(SUMMARY_VALUE),
- DETAILS_SOURCE, DETAILS_MESSAGE, ResultType.LOWER_BETTER.toReportString(),
- ResultUnit.MS.toReportString(), Double.toString(DETAILS_VALUE_1),
- Double.toString(DETAILS_VALUE_2), Double.toString(DETAILS_VALUE_3));
+ ResultType.LOWER_BETTER.toReportString(),
+ ResultUnit.MS.toReportString());
String moduleBTests = String.format(JOIN, moduleBTest3, moduleBTest4);
String moduleBCases = String.format(XML_CASE, CLASS_B, moduleBTests);
String moduleB = String.format(XML_MODULE, NAME_B, ABI, DEVICE_B, RUNTIME_B, DONE_B,
diff --git a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
index e6c6a87..faa4690 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
@@ -34,6 +34,7 @@
addTestSuite(ModuleResultTest.class);
addTestSuite(MultipartFormTest.class);
addTestSuite(ReportLogTest.class);
+ addTestSuite(ResultHandlerTest.class);
addTestSuite(StatTest.class);
addTestSuite(TestFilterTest.class);
addTestSuite(TestResultTest.class);
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
old mode 100644
new mode 100755
index 6a63cea..203c322
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
@@ -103,7 +103,7 @@
getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
Intent webIntent = new Intent(Intent.ACTION_VIEW);
webIntent.setData(Uri.parse("http://com.android.cts.intent.receiver"));
- List<ResolveInfo> ris = pm.queryIntentActivities(webIntent, 0 /* no flags*/);
+ List<ResolveInfo> ris = pm.queryIntentActivities(webIntent, PackageManager.MATCH_ALL /* all browser*/);
for (ResolveInfo ri : ris) {
Log.d(TAG, "Hiding " + ri.activityInfo.packageName);
dpm.setApplicationHidden(ADMIN_RECEIVER_COMPONENT, ri.activityInfo.packageName, true);
diff --git a/hostsidetests/theme/assets/24/360dpi.zip b/hostsidetests/theme/assets/24/360dpi.zip
new file mode 100755
index 0000000..98782d5
--- /dev/null
+++ b/hostsidetests/theme/assets/24/360dpi.zip
Binary files differ
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index a22d539..d901926 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -23,7 +23,15 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctsdeviceutil ctstestrunner services.core
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 \
+ android-support-multidex \
+ ctsdeviceutil \
+ ctstestrunner \
+ services.core
+
+# Use multi-dex as the compatibility-common-util-devicesidelib dependency
+# on ctsdeviceutil pushes us beyond 64k methods.
+LOCAL_JACK_FLAGS := --multi-dex legacy
# Resource unit tests use a private locale and some densities
LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c small -c normal -c large -c xlarge \
diff --git a/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
old mode 100644
new mode 100755
index 33e8204..b22e421
--- a/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
@@ -63,6 +63,10 @@
EglConfigCtsActivity activity = launchActivity("android.graphics.cts",
EglConfigCtsActivity.class, extras);
activity.waitToFinishDrawing();
+
+ // TODO(b/30948621): Remove the sleep below once b/30948621 is fixed.
+ Thread.sleep(500);
+
activity.finish();
mInstrumentation.waitForIdleSync();
}
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java b/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
index 7b74ba7..9b1dc81 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
@@ -19,11 +19,22 @@
import android.annotation.TargetApi;
import android.content.Context;
+import android.cts.util.MediaUtils;
import android.graphics.Bitmap;
+import android.media.MediaFormat;
+import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import android.view.View;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.Timeout;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
@TargetApi(24)
+@RunWith(AndroidJUnit4.class)
public class DecodeAccuracyTest extends DecodeAccuracyTestBase {
private static final String TAG = DecodeAccuracyTest.class.getSimpleName();
@@ -32,12 +43,17 @@
private static final String H264_CROPPED_VIDEO_FILE_NAME = "520x360h264decodertest.mp4";
private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 90;
private static final int OFFSET = 10;
+ private static final int PER_TEST_TIMEOUT_S = 30;
private View videoView;
private VideoViewFactory videoViewFactory;
+ @Rule
+ public Timeout globalTimeout = Timeout.seconds(PER_TEST_TIMEOUT_S);
+
+ @After
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
if (videoView != null) {
getHelper().cleanUpView(videoView);
}
@@ -48,36 +64,42 @@
}
/* <------------- Tests Using H264 -------------> */
+ @Test
public void testH264GLViewVideoDecode() throws Exception {
runH264DecodeAccuracyTest(
new GLSurfaceViewFactory(),
new VideoFormat(H264_VIDEO_FILE_NAME));
}
+ @Test
public void testH264GLViewLargerHeightVideoDecode() throws Exception {
runH264DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
}
+ @Test
public void testH264GLViewLargerWidthVideoDecode() throws Exception {
runH264DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerWidthVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
}
+ @Test
public void testH264SurfaceViewVideoDecode() throws Exception {
runH264DecodeAccuracyTest(
new SurfaceViewFactory(),
new VideoFormat(H264_VIDEO_FILE_NAME));
}
+ @Test
public void testH264SurfaceViewLargerHeightVideoDecode() throws Exception {
runH264DecodeAccuracyTest(
new SurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
}
+ @Test
public void testH264SurfaceViewLargerWidthVideoDecode() throws Exception {
runH264DecodeAccuracyTest(
new SurfaceViewFactory(),
@@ -85,36 +107,42 @@
}
/* <------------- Tests Using VP9 -------------> */
+ @Test
public void testVP9GLViewVideoDecode() throws Exception {
runVP9DecodeAccuracyTest(
new GLSurfaceViewFactory(),
new VideoFormat(VP9_VIDEO_FILE_NAME));
}
+ @Test
public void testVP9GLViewLargerHeightVideoDecode() throws Exception {
runVP9DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
}
+ @Test
public void testVP9GLViewLargerWidthVideoDecode() throws Exception {
runVP9DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerWidthVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
}
+ @Test
public void testVP9SurfaceViewVideoDecode() throws Exception {
runVP9DecodeAccuracyTest(
new SurfaceViewFactory(),
new VideoFormat(VP9_VIDEO_FILE_NAME));
}
+ @Test
public void testVP9SurfaceViewLargerHeightVideoDecode() throws Exception {
runVP9DecodeAccuracyTest(
new SurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
}
+ @Test
public void testVP9SurfaceViewLargerWidthVideoDecode() throws Exception {
runVP9DecodeAccuracyTest(
new SurfaceViewFactory(),
@@ -122,12 +150,14 @@
}
/* <------------- Tests H264 with cropping -------------> */
+ @Test
public void testH264GLViewCroppedVideoDecode() throws Exception {
runH264DecodeCroppedTest(
new GLSurfaceViewFactory(),
new VideoFormat(H264_CROPPED_VIDEO_FILE_NAME));
}
+ @Test
public void testH264SurfaceViewCroppedVideoDecode() throws Exception {
runH264DecodeCroppedTest(
new SurfaceViewFactory(),
@@ -136,17 +166,23 @@
private void runH264DecodeAccuracyTest(
VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
- runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertestgolden);
+ if (MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertestgolden);
+ }
}
private void runVP9DecodeAccuracyTest(
VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
- runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.vp9decodertestgolden);
+ if (MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_VP9)) {
+ runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.vp9decodertestgolden);
+ }
}
private void runH264DecodeCroppedTest(
VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
- runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertest520x360golden);
+ if (MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertest520x360golden);
+ }
}
private void runDecodeAccuracyTest(
@@ -191,7 +227,8 @@
private void validateResult(
VideoFormat videoFormat, VideoViewSnapshot videoViewSnapshot, int goldenResId) {
- final Bitmap result = getHelper().generateBitmapFromVideoViewSnapshot(videoViewSnapshot);
+ final Bitmap result = checkNotNull("The expected bitmap from snapshot is null",
+ getHelper().generateBitmapFromVideoViewSnapshot(videoViewSnapshot));
final Bitmap golden = getHelper().generateBitmapFromImageResourceId(goldenResId);
final BitmapCompare.Difference difference = BitmapCompare.computeMinimumDifference(
result, golden, videoFormat.getOriginalWidth(), videoFormat.getOriginalHeight());
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java b/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
index fae1bb4..1ce732d 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
+++ b/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
@@ -44,6 +44,8 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.util.Pair;
@@ -73,7 +75,12 @@
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
@TargetApi(16)
+@RunWith(AndroidJUnit4.class)
public class DecodeAccuracyTestBase
extends ActivityInstrumentationTestCase2<DecodeAccuracyTestActivity> {
@@ -86,9 +93,12 @@
super(DecodeAccuracyTestActivity.class);
}
+ @Before
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
+ injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+ setActivityInitialTouchMode(false);
mActivity = getActivity();
getInstrumentation().waitForIdleSync();
mContext = getInstrumentation().getTargetContext();
@@ -96,8 +106,9 @@
testHelper = new TestHelper(mContext, mActivity);
}
+ @After
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
mActivity = null;
super.tearDown();
}
@@ -117,6 +128,11 @@
return reference;
}
+ public static <T> T checkNotNull(String msg, T reference) {
+ assertNotNull(msg, reference);
+ return reference;
+ }
+
public static class SimplePlayer {
public static final long DECODE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1) / 2;
@@ -419,6 +435,8 @@
/* Utility class for collecting common test case functionality. */
class TestHelper {
+ private final String TAG = TestHelper.class.getSimpleName();
+
private final Context context;
private final Handler handler;
private final Activity activity;
@@ -473,13 +491,21 @@
}
public synchronized Bitmap generateBitmapFromVideoViewSnapshot(VideoViewSnapshot snapshot) {
+ final long timeOutMs = TimeUnit.SECONDS.toMillis(10);
+ final long start = SystemClock.elapsedRealtime();
handler.post(snapshot);
try {
- while (!snapshot.isBitmapReady()) {
+ while (!snapshot.isBitmapReady()
+ && (SystemClock.elapsedRealtime() - start < timeOutMs)) {
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
+ return null;
+ }
+ if (!snapshot.isBitmapReady()) {
+ Log.e(TAG, "Time out in generateBitmapFromVideoViewSnapshot().");
+ return null;
}
return snapshot.getBitmap();
}
@@ -1165,8 +1191,7 @@
class SurfaceViewSnapshot extends VideoViewSnapshot {
private static final String TAG = SurfaceViewSnapshot.class.getSimpleName();
- private static final int PIXELCOPY_REQUEST_SLEEP_MS = 30;
- private static final int PIXELCOPY_REQUEST_MAX_ATTEMPTS = 20;
+ private static final int PIXELCOPY_REQUEST_SLEEP_MS = 100;
private static final int PIXELCOPY_TIMEOUT_MS = 1000;
private final Thread copyThread;
@@ -1182,15 +1207,13 @@
bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
try {
// Wait for SurfaceView to be available.
- for (int i = 0; i < PIXELCOPY_REQUEST_MAX_ATTEMPTS; i++) {
- copyResult = copyHelper.request(surfaceView, bitmap);
- if (copyResult == PixelCopy.SUCCESS) {
- break;
- }
+ while (copyResult != PixelCopy.SUCCESS) {
Thread.sleep(PIXELCOPY_REQUEST_SLEEP_MS);
+ copyResult = copyHelper.request(surfaceView, bitmap);
}
} catch (InterruptedException e) {
- Log.w(TAG, "Pixel Copy is stopped/interrupted before it finishes.", e);
+ Log.e(TAG, "Pixel Copy is stopped/interrupted before it finishes.", e);
+ bitmap = null;
}
copyHelper.release();
}
@@ -1294,10 +1317,10 @@
try {
waitForByteBuffer();
} catch (InterruptedException e) {
- Log.w(TAG, e.getMessage());
- Log.w(TAG, "ByteBuffer may contain incorrect pixels.");
+ Log.e(TAG, e.getMessage());
+ bitmap = null;
+ return;
}
- // Get ByteBuffer anyway. Let the test fail if ByteBuffer contains incorrect pixels.
ByteBuffer byteBuffer = glSurfaceViewFactory.getByteBuffer();
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
byteBuffer.rewind();
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index b8478d2..185ebfa 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -387,6 +387,10 @@
* Tests reporting of connectivity changed.
*/
public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
ConnectivityReceiver.prepare();
toggleWifi();
@@ -400,6 +404,10 @@
}
public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
ConnectivityReceiver.prepare();
ConnectivityReceiver receiver = new ConnectivityReceiver();
IntentFilter filter = new IntentFilter();
@@ -416,6 +424,10 @@
public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()
throws InterruptedException {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
Intent startIntent = new Intent();
startIntent.setComponent(new ComponentName("android.net.cts.appForApi23",
"android.net.cts.appForApi23.ConnectivityListeningActivity"));
diff --git a/tests/tests/opengl/Android.mk b/tests/tests/opengl/Android.mk
index 69090f5..dff1778 100644
--- a/tests/tests/opengl/Android.mk
+++ b/tests/tests/opengl/Android.mk
@@ -33,7 +33,9 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+# 18 required for GLES 3, but going that low will stop some ABIs from
+# building. Using official Nougat level 24 to avoid missing something.
+LOCAL_SDK_VERSION := 24
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
diff --git a/tests/tests/opengl/AndroidManifest.xml b/tests/tests/opengl/AndroidManifest.xml
index cc0ab8f..bb79490 100644
--- a/tests/tests/opengl/AndroidManifest.xml
+++ b/tests/tests/opengl/AndroidManifest.xml
@@ -19,7 +19,8 @@
android:versionName="1.0" >
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- <uses-sdk android:minSdkVersion="14" />
+ <!-- GLES30 requires 18 -->
+ <uses-sdk android:minSdkVersion="18" />
<uses-feature android:glEsVersion="0x00020000"/>
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityOne.java b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityOne.java
index 5acac32..33f097c 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityOne.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityOne.java
@@ -41,8 +41,11 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
- window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-
+ window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
int viewType = getIntent().getIntExtra(EXTRA_VIEW_TYPE, -1);
int viewIndex = getIntent().getIntExtra(EXTRA_VIEW_INDEX, -1);
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityTwo.java b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityTwo.java
index 8ed0b9c..f0c7881 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityTwo.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20ActivityTwo.java
@@ -20,6 +20,8 @@
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
import java.lang.InterruptedException;
import java.util.concurrent.CountDownLatch;
@@ -48,14 +50,20 @@
}
public void setView(int type, int i, float[] vertexColors ) {
- view = new OpenGLES20View(this,type,i, vertexColors, mLatch);
+ // Note: Flags should be modified before the content view is set
+ Window window = getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ view = new OpenGLES20View(this, type, i, vertexColors, mLatch);
setContentView(view);
}
public void setView(int type, int i) {
float[] f = {};
- view = new OpenGLES20View(this, type, i, f, mLatch) ;
- setContentView(view);
+ setView(type, i, f);
}
public int getNoOfAttachedShaders() {
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityOne.java b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityOne.java
index 4602d4f..61f97b3 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityOne.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityOne.java
@@ -51,8 +51,11 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
- window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-
+ window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
int viewType = getIntent().getIntExtra(EXTRA_VIEW_TYPE, -1);
int viewIndex = getIntent().getIntExtra(EXTRA_VIEW_INDEX, -1);
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityTwo.java b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityTwo.java
index 6bdf95f..90964f8 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityTwo.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGLES20NativeActivityTwo.java
@@ -20,8 +20,11 @@
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
import java.lang.InterruptedException;
+import java.lang.UnsupportedOperationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -48,12 +51,19 @@
}
public void setView(int type, int i, float[] vertexColors ) {
+ // Note: Flags should be modified before the content view is set
+ Window window = getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
view = new OpenGLES20View(this,type,i, vertexColors, mLatch);
setContentView(view);
}
public void setView(int type, int i) {
-
+ throw new UnsupportedOperationException("No views without vertexColors, please!");
}
public int getNoOfAttachedShaders() {
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/TextureTestActivity.java b/tests/tests/openglperf/src/android/openglperf/cts/TextureTestActivity.java
index 4d7d641..6459c31 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/TextureTestActivity.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/TextureTestActivity.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
+import android.view.WindowManager;
public class TextureTestActivity extends Activity {
private GLSurfaceView mGLView;
@@ -27,6 +28,11 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new TextureTestSurfaceView(this);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
setContentView(mGLView);
}
diff --git a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
index 54b4d23..c65118b 100644
--- a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
+++ b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
@@ -378,7 +378,9 @@
}
// Abort printing
- getActivity().finish();
+ getUiDevice().pressBack();
+ getUiDevice().pressBack();
+ getUiDevice().pressBack();
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
}
diff --git a/tests/tests/security/src/android/security/cts/EncryptionTest.java b/tests/tests/security/src/android/security/cts/EncryptionTest.java
index 1d37ec6..85d82e2 100644
--- a/tests/tests/security/src/android/security/cts/EncryptionTest.java
+++ b/tests/tests/security/src/android/security/cts/EncryptionTest.java
@@ -16,12 +16,13 @@
package android.security.cts;
+import com.android.compatibility.common.util.PropertyUtil;
+
import android.test.AndroidTestCase;
import junit.framework.TestCase;
import android.app.ActivityManager;
import android.content.Context;
-import android.os.SystemProperties;
import android.util.Log;
import java.io.BufferedReader;
import java.io.FileReader;
@@ -34,7 +35,7 @@
System.loadLibrary("ctssecurity_jni");
}
- private static final int min_api_level = 23;
+ private static final int MIN_API_LEVEL = 23;
private static final String TAG = "EncryptionTest";
@@ -77,15 +78,8 @@
}
private boolean isRequired() {
- int first_api_level =
- SystemProperties.getInt("ro.product.first_api_level", 0);
-
- // Optional before min_api_level or if the device has low RAM
- if (first_api_level > 0 && first_api_level < min_api_level) {
- return false;
- } else {
- return !hasLowRAM();
- }
+ // Optional before MIN_API_LEVEL or if the device has low RAM
+ return PropertyUtil.getFirstApiLevel() >= MIN_API_LEVEL && !hasLowRAM();
}
public void testConfig() throws Exception {
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 647d35b..ef3dde5 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -319,6 +319,7 @@
assertFalse("Device *IS* vulnerable to " + cve,
mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
t.stopLooper();
+ t.join(); // wait for thread to exit so we're sure the player was released
}
private void doStagefrightTestMediaCodec(final int rid) throws Exception {
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index b50f8c9..b882c0c 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -508,7 +508,10 @@
final int[] lastChildOnScreenXY = new int[2];
lastListChild.getLocationOnScreen(lastChildOnScreenXY);
- assertTrue(lastChildOnScreenXY[1] + lastListChild.getHeight() <= promptViewOnScreenXY[1]);
+ // The child is above the prompt. They may overlap, as in the case
+ // when the list items do not all fit on screen, but this is still
+ // correct.
+ assertTrue(lastChildOnScreenXY[1] <= promptViewOnScreenXY[1]);
}
@Presubmit