diff options
author | Brett Chabot <brettchabot@android.com> | 2011-11-01 14:42:34 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-11-01 14:42:34 -0700 |
commit | 266440ef9b7aee437d971afb688a91b6a48e8cca (patch) | |
tree | 2239631dda2700068e755e03053e26094026753d | |
parent | be92a5c09f66660b78a109a650d1494df3939e12 (diff) | |
parent | 770358ebdbafa73653cced292f5f77cdccfbe2aa (diff) | |
download | cts-ics-factoryrom-2-release.tar.gz |
Merge "Support merging device info metrics." into ics-mr0android-4.0.1_r1.2android-4.0.1_r1.1android-4.0.1_r1ics-factoryrom-2-release
10 files changed, 625 insertions, 235 deletions
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java index bfe5aaa5b52..57b0dad2ce7 100644 --- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java +++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java @@ -40,8 +40,7 @@ public interface DeviceInfoConstants { public static final String SCREEN_SIZE = "screen_size"; public static final String SCREEN_DENSITY_BUCKET = "screen_density_bucket"; public static final String SCREEN_DENSITY = "screen_density"; - public static final String SCREEN_HEIGHT = "screen_height"; - public static final String SCREEN_WIDTH = "screen_width"; + public static final String RESOLUTION = "resolution"; public static final String VERSION_SDK = "androidPlatformVersion"; public static final String VERSION_RELEASE = "buildVersion"; public static final String BUILD_ABI = "build_abi"; @@ -57,5 +56,5 @@ public interface DeviceInfoConstants { public static final String BUILD_ID = "buildID"; public static final String BUILD_VERSION = "buildVersion"; public static final String BUILD_TAGS = "build_tags"; - public static final String SERIAL_NUMBER = "serialNumber"; + public static final String SERIAL_NUMBER = "deviceID"; } diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java index 5ab063d0e19..1b705e7cd4a 100644 --- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java +++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java @@ -68,6 +68,7 @@ public class DeviceInfoInstrument extends Instrumentation implements DeviceInfoC addResult(BUILD_FINGERPRINT, Build.FINGERPRINT); addResult(BUILD_ABI, Build.CPU_ABI); addResult(BUILD_ABI2, Build.CPU_ABI2); + addResult(SERIAL_NUMBER, Build.SERIAL); addResult(VERSION_RELEASE, Build.VERSION.RELEASE); addResult(VERSION_SDK, Build.VERSION.SDK); @@ -77,8 +78,7 @@ public class DeviceInfoInstrument extends Instrumentation implements DeviceInfoC Context.WINDOW_SERVICE); Display d = wm.getDefaultDisplay(); d.getMetrics(metrics); - addResult(SCREEN_WIDTH, metrics.widthPixels); - addResult(SCREEN_HEIGHT, metrics.heightPixels); + addResult(RESOLUTION, String.format("%sx%s", metrics.widthPixels, metrics.heightPixels)); addResult(SCREEN_DENSITY, metrics.density); addResult(SCREEN_X_DENSITY, metrics.xdpi); addResult(SCREEN_Y_DENSITY, metrics.ydpi); diff --git a/tools/host/src/com/android/cts/TestDevice.java b/tools/host/src/com/android/cts/TestDevice.java index a90f6f11468..dcedc0282c0 100644 --- a/tools/host/src/com/android/cts/TestDevice.java +++ b/tools/host/src/com/android/cts/TestDevice.java @@ -682,7 +682,7 @@ public class TestDevice implements DeviceObserver { * @return The screen resolution. */ public String getScreenResolution() { - return mInfoMap.get(SCREEN_WIDTH) + "x" + mInfoMap.get(SCREEN_HEIGHT); + return mInfoMap.get(RESOLUTION); } /** diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath index 335f3c36a7f..197543c0012 100644 --- a/tools/tradefed-host/.classpath +++ b/tools/tradefed-host/.classpath @@ -7,6 +7,6 @@ <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/tradefederation"/> <classpathentry combineaccessrules="false" kind="src" path="/hosttestlib"/> - <classpathentry combineaccessrules="false" kind="src" path="/ctsdeviceinfolib"/> + <classpathentry combineaccessrules="false" exported="true" kind="src" path="/ctsdeviceinfolib"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java index 2893523741f..11b4b1cbd95 100644 --- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java +++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java @@ -16,10 +16,7 @@ package com.android.cts.tradefed.result; -import android.tests.getinfo.DeviceInfoConstants; - import com.android.cts.tradefed.build.CtsBuildHelper; -import com.android.cts.tradefed.build.CtsBuildProvider; import com.android.cts.tradefed.device.DeviceInfoCollector; import com.android.cts.tradefed.testtype.CtsTest; import com.android.ddmlib.Log; @@ -46,9 +43,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.HashMap; import java.util.Map; /** @@ -70,12 +64,6 @@ public class CtsXmlResultReporter implements ITestInvocationListener { /** the XML namespace */ static final String ns = null; - // XML constants - static final String SUMMARY_TAG = "Summary"; - static final String PASS_ATTR = "pass"; - static final String TIMEOUT_ATTR = "timeout"; - static final String NOT_EXECUTED_ATTR = "notExecuted"; - static final String FAILED_ATTR = "failed"; static final String RESULT_TAG = "TestResult"; static final String PLAN_ATTR = "testPlan"; @@ -101,6 +89,7 @@ public class CtsXmlResultReporter implements ITestInvocationListener { private String mDeviceSerial; private TestResults mResults = new TestResults(); private TestPackageResult mCurrentPkgResult = null; + private boolean mIsDeviceInfoRun = false; public void setReportDir(File reportDir) { mReportDir = reportDir; @@ -189,15 +178,18 @@ public class CtsXmlResultReporter implements ITestInvocationListener { // display results from previous run logCompleteRun(mCurrentPkgResult); } - if (name.equals(DeviceInfoCollector.APP_PACKAGE_NAME)) { + mIsDeviceInfoRun = name.equals(DeviceInfoCollector.APP_PACKAGE_NAME); + if (mIsDeviceInfoRun) { logResult("Collecting device info"); - } else if (mCurrentPkgResult == null || !name.equals( - mCurrentPkgResult.getAppPackageName())) { - logResult("-----------------------------------------"); - logResult("Test package %s started", name); - logResult("-----------------------------------------"); + } else { + if (mCurrentPkgResult == null || !name.equals(mCurrentPkgResult.getAppPackageName())) { + logResult("-----------------------------------------"); + logResult("Test package %s started", name); + logResult("-----------------------------------------"); + } + mCurrentPkgResult = mResults.getOrCreatePackage(name); } - mCurrentPkgResult = mResults.getOrCreatePackage(name); + } /** @@ -233,7 +225,11 @@ public class CtsXmlResultReporter implements ITestInvocationListener { */ @Override public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) { - mCurrentPkgResult.populateMetrics(runMetrics); + if (mIsDeviceInfoRun) { + mResults.populateDeviceInfoMetrics(runMetrics); + } else { + mCurrentPkgResult.populateMetrics(runMetrics); + } } /** @@ -317,194 +313,12 @@ public class CtsXmlResultReporter implements ITestInvocationListener { serializer.attribute(ns, "endtime", endTime); serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION); - serializeDeviceInfo(serializer); - serializeHostInfo(serializer); - serializeTestSummary(serializer); mResults.serialize(serializer); // TODO: not sure why, but the serializer doesn't like this statement //serializer.endTag(ns, RESULT_TAG); } /** - * Output the device info XML. - * - * @param serializer - */ - private void serializeDeviceInfo(KXmlSerializer serializer) throws IOException { - serializer.startTag(ns, "DeviceInfo"); - - Map<String, String> deviceInfoMetrics = mResults.getDeviceInfoMetrics(); - if (deviceInfoMetrics == null || deviceInfoMetrics.isEmpty()) { - // this might be expected, if device info collection was turned off - CLog.d("Could not find device info"); - return; - } - - // Extract metrics that need extra handling, and then dump the remainder into BuildInfo - Map<String, String> metricsCopy = new HashMap<String, String>( - deviceInfoMetrics); - serializer.startTag(ns, "Screen"); - String screenWidth = metricsCopy.remove(DeviceInfoConstants.SCREEN_WIDTH); - String screenHeight = metricsCopy.remove(DeviceInfoConstants.SCREEN_HEIGHT); - serializer.attribute(ns, "resolution", String.format("%sx%s", screenWidth, screenHeight)); - serializer.attribute(ns, DeviceInfoConstants.SCREEN_DENSITY, - metricsCopy.remove(DeviceInfoConstants.SCREEN_DENSITY)); - serializer.attribute(ns, DeviceInfoConstants.SCREEN_DENSITY_BUCKET, - metricsCopy.remove(DeviceInfoConstants.SCREEN_DENSITY_BUCKET)); - serializer.attribute(ns, DeviceInfoConstants.SCREEN_SIZE, - metricsCopy.remove(DeviceInfoConstants.SCREEN_SIZE)); - serializer.endTag(ns, "Screen"); - - serializer.startTag(ns, "PhoneSubInfo"); - serializer.attribute(ns, "subscriberId", metricsCopy.remove( - DeviceInfoConstants.PHONE_NUMBER)); - serializer.endTag(ns, "PhoneSubInfo"); - - String featureData = metricsCopy.remove(DeviceInfoConstants.FEATURES); - String processData = metricsCopy.remove(DeviceInfoConstants.PROCESSES); - - // dump the remaining metrics without translation - serializer.startTag(ns, "BuildInfo"); - for (Map.Entry<String, String> metricEntry : metricsCopy.entrySet()) { - serializer.attribute(ns, metricEntry.getKey(), metricEntry.getValue()); - } - serializer.attribute(ns, "deviceID", mDeviceSerial); - serializer.endTag(ns, "BuildInfo"); - - serializeFeatureInfo(serializer, featureData); - serializeProcessInfo(serializer, processData); - - serializer.endTag(ns, "DeviceInfo"); - } - - /** - * Prints XML indicating what features are supported by the device. It parses a string from the - * featureData argument that is in the form of "feature1:true;feature2:false;featuer3;true;" - * with a trailing semi-colon. - * - * <pre> - * <FeatureInfo> - * <Feature name="android.name.of.feature" available="true" /> - * ... - * </FeatureInfo> - * </pre> - * - * @param serializer used to create XML - * @param featureData raw unparsed feature data - */ - private void serializeFeatureInfo(KXmlSerializer serializer, String featureData) throws IOException { - serializer.startTag(ns, "FeatureInfo"); - - if (featureData == null) { - featureData = ""; - } - - String[] featurePairs = featureData.split(";"); - for (String featurePair : featurePairs) { - String[] nameTypeAvailability = featurePair.split(":"); - if (nameTypeAvailability.length >= 3) { - serializer.startTag(ns, "Feature"); - serializer.attribute(ns, "name", nameTypeAvailability[0]); - serializer.attribute(ns, "type", nameTypeAvailability[1]); - serializer.attribute(ns, "available", nameTypeAvailability[2]); - serializer.endTag(ns, "Feature"); - } - } - serializer.endTag(ns, "FeatureInfo"); - } - - /** - * Prints XML data indicating what particular processes of interest were running on the device. - * It parses a string from the rootProcesses argument that is in the form of - * "processName1;processName2;..." with a trailing semi-colon. - * - * <pre> - * <ProcessInfo> - * <Process name="long_cat_viewer" uid="0" /> - * ... - * </ProcessInfo> - * </pre> - */ - private void serializeProcessInfo(KXmlSerializer serializer, String rootProcesses) - throws IOException { - serializer.startTag(ns, "ProcessInfo"); - - if (rootProcesses == null) { - rootProcesses = ""; - } - - String[] processNames = rootProcesses.split(";"); - for (String processName : processNames) { - processName = processName.trim(); - if (processName.length() > 0) { - serializer.startTag(ns, "Process"); - serializer.attribute(ns, "name", processName); - serializer.attribute(ns, "uid", "0"); - serializer.endTag(ns, "Process"); - } - } - serializer.endTag(ns, "ProcessInfo"); - } - - /** - * Output the host info XML. - * - * @param serializer - */ - private void serializeHostInfo(KXmlSerializer serializer) throws IOException { - serializer.startTag(ns, "HostInfo"); - - String hostName = ""; - try { - hostName = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException ignored) {} - serializer.attribute(ns, "name", hostName); - - serializer.startTag(ns, "Os"); - serializer.attribute(ns, "name", System.getProperty("os.name")); - serializer.attribute(ns, "version", System.getProperty("os.version")); - serializer.attribute(ns, "arch", System.getProperty("os.arch")); - serializer.endTag(ns, "Os"); - - serializer.startTag(ns, "Java"); - serializer.attribute(ns, "name", System.getProperty("java.vendor")); - serializer.attribute(ns, "version", System.getProperty("java.version")); - serializer.endTag(ns, "Java"); - - serializer.startTag(ns, "Cts"); - serializer.attribute(ns, "version", CtsBuildProvider.CTS_BUILD_VERSION); - // TODO: consider outputting other tradefed options here - serializer.startTag(ns, "IntValue"); - serializer.attribute(ns, "name", "testStatusTimeoutMs"); - // TODO: create a constant variable for testStatusTimeoutMs value. Currently it cannot be - // changed - serializer.attribute(ns, "value", "600000"); - serializer.endTag(ns, "IntValue"); - serializer.endTag(ns, "Cts"); - - serializer.endTag(ns, "HostInfo"); - } - - /** - * Output the test summary XML containing summary totals for all tests. - * - * @param serializer - * @throws IOException - */ - private void serializeTestSummary(KXmlSerializer serializer) throws IOException { - serializer.startTag(ns, SUMMARY_TAG); - serializer.attribute(ns, FAILED_ATTR, Integer.toString(mResults.countTests( - CtsTestStatus.FAIL))); - serializer.attribute(ns, NOT_EXECUTED_ATTR, Integer.toString(mResults.countTests( - CtsTestStatus.NOT_EXECUTED))); - // ignore timeouts - these are reported as errors - serializer.attribute(ns, TIMEOUT_ATTR, "0"); - serializer.attribute(ns, PASS_ATTR, Integer.toString(mResults.countTests( - CtsTestStatus.PASS))); - serializer.endTag(ns, SUMMARY_TAG); - } - - /** * Creates the output stream to use for test results. Exposed for mocking. */ OutputStream createOutputResultStream(File reportDir) throws IOException { diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java new file mode 100644 index 00000000000..102e99886f3 --- /dev/null +++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.cts.tradefed.result; + +import android.tests.getinfo.DeviceInfoConstants; + +import com.android.tradefed.log.LogUtil.CLog; + +import org.kxml2.io.KXmlSerializer; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Data structure for the device info collected by CTS. + * <p/> + * Provides methods to serialize and deserialize from XML, as well as checks for consistency + * when multiple devices are used to generate the report. + */ +class DeviceInfoResult extends AbstractXmlPullParser { + static final String TAG = "DeviceInfo"; + private static final String ns = CtsXmlResultReporter.ns; + private static final String BUILD_TAG = "BuildInfo"; + private static final String PHONE_TAG = "PhoneSubInfo"; + private static final String SCREEN_TAG = "Screen"; + private static final String FEATURE_INFO_TAG = "FeatureInfo"; + private static final String FEATURE_TAG = "Feature"; + private static final String FEATURE_ATTR_DELIM = ":"; + private static final String FEATURE_DELIM = ";"; + private static final String PROCESS_INFO_TAG = "ProcessInfo"; + private static final String PROCESS_TAG = "Process"; + private static final String PROCESS_DELIM = ";"; + + private Map<String, String> mMetrics = new HashMap<String, String>(); + + /** + * Serialize this object and all its contents to XML. + * + * @param serializer + * @throws IOException + */ + public void serialize(KXmlSerializer serializer) throws IOException { + serializer.startTag(ns, TAG); + + if (!mMetrics.isEmpty()) { + + // Extract metrics that need extra handling, and then dump the remainder into BuildInfo + Map<String, String> metricsCopy = new HashMap<String, String>(mMetrics); + serializer.startTag(ns, SCREEN_TAG); + serializer.attribute(ns, DeviceInfoConstants.RESOLUTION, + getMetric(metricsCopy, DeviceInfoConstants.RESOLUTION)); + serializer.attribute(ns, DeviceInfoConstants.SCREEN_DENSITY, + getMetric(metricsCopy, DeviceInfoConstants.SCREEN_DENSITY)); + serializer.attribute(ns, DeviceInfoConstants.SCREEN_DENSITY_BUCKET, + getMetric(metricsCopy, DeviceInfoConstants.SCREEN_DENSITY_BUCKET)); + serializer.attribute(ns, DeviceInfoConstants.SCREEN_SIZE, + getMetric(metricsCopy, DeviceInfoConstants.SCREEN_SIZE)); + serializer.endTag(ns, SCREEN_TAG); + + serializer.startTag(ns, PHONE_TAG); + serializer.attribute(ns, DeviceInfoConstants.PHONE_NUMBER, + getMetric(metricsCopy, DeviceInfoConstants.PHONE_NUMBER)); + serializer.endTag(ns, PHONE_TAG); + + String featureData = getMetric(metricsCopy, DeviceInfoConstants.FEATURES); + String processData = getMetric(metricsCopy, DeviceInfoConstants.PROCESSES); + + // dump the remaining metrics without translation + serializer.startTag(ns, BUILD_TAG); + for (Map.Entry<String, String> metricEntry : metricsCopy.entrySet()) { + serializer.attribute(ns, metricEntry.getKey(), metricEntry.getValue()); + } + serializer.endTag(ns, BUILD_TAG); + + serializeFeatureInfo(serializer, featureData); + serializeProcessInfo(serializer, processData); + } else { + // this might be expected, if device info collection was turned off + CLog.d("Could not find device info"); + } + serializer.endTag(ns, TAG); + } + + /** + * Fetch and remove given metric from hashmap. + * + * @return the metric value or empty string if it was not present in map. + */ + private String getMetric(Map<String, String> metrics, String metricName ) { + String value = metrics.remove(metricName); + if (value == null) { + value = ""; + } + return value; + } + + /** + * Prints XML indicating what features are supported by the device. It parses a string from the + * featureData argument that is in the form of "feature1:true;feature2:false;featuer3;true;" + * with a trailing semi-colon. + * + * <pre> + * <FeatureInfo> + * <Feature name="android.name.of.feature" available="true" /> + * ... + * </FeatureInfo> + * </pre> + * + * @param serializer used to create XML + * @param featureData raw unparsed feature data + */ + private void serializeFeatureInfo(KXmlSerializer serializer, String featureData) + throws IOException { + serializer.startTag(ns, FEATURE_INFO_TAG); + + if (featureData == null) { + featureData = ""; + } + + String[] featurePairs = featureData.split(FEATURE_DELIM); + for (String featurePair : featurePairs) { + String[] nameTypeAvailability = featurePair.split(FEATURE_ATTR_DELIM); + if (nameTypeAvailability.length >= 3) { + serializer.startTag(ns, FEATURE_TAG); + serializer.attribute(ns, "name", nameTypeAvailability[0]); + serializer.attribute(ns, "type", nameTypeAvailability[1]); + serializer.attribute(ns, "available", nameTypeAvailability[2]); + serializer.endTag(ns, FEATURE_TAG); + } + } + serializer.endTag(ns, FEATURE_INFO_TAG); + } + + /** + * Prints XML data indicating what particular processes of interest were running on the device. + * It parses a string from the rootProcesses argument that is in the form of + * "processName1;processName2;..." with a trailing semi-colon. + * + * <pre> + * <ProcessInfo> + * <Process name="long_cat_viewer" uid="0" /> + * ... + * </ProcessInfo> + * </pre> + */ + private void serializeProcessInfo(KXmlSerializer serializer, String rootProcesses) + throws IOException { + serializer.startTag(ns, PROCESS_INFO_TAG); + + if (rootProcesses == null) { + rootProcesses = ""; + } + + String[] processNames = rootProcesses.split(PROCESS_DELIM); + for (String processName : processNames) { + processName = processName.trim(); + if (processName.length() > 0) { + serializer.startTag(ns, PROCESS_TAG); + serializer.attribute(ns, "name", processName); + serializer.attribute(ns, "uid", "0"); + serializer.endTag(ns, PROCESS_TAG); + } + } + serializer.endTag(ns, PROCESS_INFO_TAG); + } + + /** + * Populates this class with package result data parsed from XML. + * + * @param parser the {@link XmlPullParser}. Expected to be pointing at start + * of a {@link #TAG} + */ + @Override + void parse(XmlPullParser parser) throws XmlPullParserException, IOException { + if (!parser.getName().equals(TAG)) { + throw new XmlPullParserException(String.format( + "invalid XML: Expected %s tag but received %s", TAG, parser.getName())); + } + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(SCREEN_TAG) || + parser.getName().equals(PHONE_TAG) || + parser.getName().equals(BUILD_TAG)) { + addMetricsFromAttributes(parser); + } else if (parser.getName().equals(FEATURE_INFO_TAG)) { + // store features into metrics map, in the same format as when collected from + // device + mMetrics.put(DeviceInfoConstants.FEATURES, parseFeatures(parser)); + } else if (parser.getName().equals(PROCESS_INFO_TAG)) { + // store processes into metrics map, in the same format as when collected from + // device + mMetrics.put(DeviceInfoConstants.PROCESSES, parseProcess(parser)); + } + } else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG)) { + return; + } + eventType = parser.next(); + } + } + + /** + * Parse process XML, and return its contents as a delimited String + */ + private String parseProcess(XmlPullParser parser) throws XmlPullParserException, IOException { + if (!parser.getName().equals(PROCESS_INFO_TAG)) { + throw new XmlPullParserException(String.format( + "invalid XML: Expected %s tag but received %s", PROCESS_INFO_TAG, + parser.getName())); + } + StringBuilder processString = new StringBuilder(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && parser.getName().equals(PROCESS_TAG)) { + processString.append(getAttribute(parser, "name")); + processString.append(PROCESS_DELIM); + } else if (eventType == XmlPullParser.END_TAG && parser.getName().equals( + PROCESS_INFO_TAG)) { + return processString.toString(); + } + eventType = parser.next(); + } + return processString.toString(); + } + + /** + * Parse feature XML, and return its contents as a delimited String + */ + private String parseFeatures(XmlPullParser parser) throws XmlPullParserException, IOException { + if (!parser.getName().equals(FEATURE_INFO_TAG)) { + throw new XmlPullParserException(String.format( + "invalid XML: Expected %s tag but received %s", FEATURE_INFO_TAG, + parser.getName())); + } + StringBuilder featureString = new StringBuilder(); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && parser.getName().equals(FEATURE_TAG)) { + featureString.append(getAttribute(parser, "name")); + featureString.append(FEATURE_ATTR_DELIM); + featureString.append(getAttribute(parser, "type")); + featureString.append(FEATURE_ATTR_DELIM); + featureString.append(getAttribute(parser, "available")); + featureString.append(FEATURE_DELIM); + } else if (eventType == XmlPullParser.END_TAG + && parser.getName().equals(FEATURE_INFO_TAG)) { + return featureString.toString(); + } + eventType = parser.next(); + } + return featureString.toString(); + + } + + /** + * Adds all attributes from the current XML tag to metrics as name-value pairs + */ + private void addMetricsFromAttributes(XmlPullParser parser) { + int attrCount = parser.getAttributeCount(); + for (int i = 0; i < attrCount; i++) { + mMetrics.put(parser.getAttributeName(i), parser.getAttributeValue(i)); + } + } + + /** + * Populate the device info metrics with values collected from device. + * <p/> + * Check that the provided device info metrics are consistent with the currently stored metrics. + * If any inconsistencies occur, logs errors and stores error messages in the metrics map + * + * @param runResult + */ + public void populateMetrics(Map<String, String> metrics) { + if (mMetrics.isEmpty()) { + // no special processing needed, no existing metrics + mMetrics.putAll(metrics); + return; + } + Map<String, String> metricsCopy = new HashMap<String, String>( + metrics); + // add values for metrics that might be different across runs + combineMetrics(metricsCopy, DeviceInfoConstants.PHONE_NUMBER, DeviceInfoConstants.IMSI, + DeviceInfoConstants.IMSI, DeviceInfoConstants.SERIAL_NUMBER); + + // ensure all the metrics we expect to be identical actually are + checkMetrics(metricsCopy, DeviceInfoConstants.BUILD_FINGERPRINT, + DeviceInfoConstants.BUILD_MODEL, DeviceInfoConstants.BUILD_BRAND, + DeviceInfoConstants.BUILD_MANUFACTURER, DeviceInfoConstants.BUILD_BOARD, + DeviceInfoConstants.BUILD_DEVICE, DeviceInfoConstants.PRODUCT_NAME, + DeviceInfoConstants.BUILD_ABI, DeviceInfoConstants.BUILD_ABI2, + DeviceInfoConstants.SCREEN_SIZE); + } + + private void combineMetrics(Map<String, String> metrics, String... keysToCombine) { + for (String combineKey : keysToCombine) { + String currentKeyValue = mMetrics.get(combineKey); + String valueToAdd = metrics.remove(combineKey); + if (valueToAdd != null) { + if (currentKeyValue == null) { + // strange - no existing value. Can occur during unit testing + mMetrics.put(combineKey, valueToAdd); + } else if (!currentKeyValue.equals(valueToAdd)) { + // new value! store a comma separated list + valueToAdd = String.format("%s,%s", currentKeyValue, valueToAdd); + mMetrics.put(combineKey, valueToAdd); + } else { + // ignore, current value is same as existing + } + + } else { + CLog.d("Missing metric %s", combineKey); + } + } + } + + private void checkMetrics(Map<String, String> metrics, String... keysToCheck) { + Set<String> keyCheckSet = new HashSet<String>(); + Collections.addAll(keyCheckSet, keysToCheck); + for (Map.Entry<String, String> metricEntry : metrics.entrySet()) { + String currentValue = mMetrics.get(metricEntry.getKey()); + if (keyCheckSet.contains(metricEntry.getKey()) && currentValue != null + && !metricEntry.getValue().equals(currentValue)) { + CLog.e("Inconsistent info collected from devices. " + + "Current result has %s='%s', Received '%s'. Are you sharding or " + + "resuming a test run across different devices and/or builds?", + metricEntry.getKey(), currentValue, metricEntry.getValue()); + mMetrics.put(metricEntry.getKey(), + String.format("ERROR: Inconsistent results: %s, %s", + metricEntry.getValue(), currentValue)); + } else { + mMetrics.put(metricEntry.getKey(), metricEntry.getValue()); + } + } + } + + /** + * Return the currently stored metrics. + * <p/> + * Exposed for unit testing. + */ + Map<String, String> getMetrics() { + return mMetrics; + } +} diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java index a8742273a56..2f9eaddaa35 100644 --- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java +++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java @@ -15,7 +15,7 @@ */ package com.android.cts.tradefed.result; -import com.android.cts.tradefed.device.DeviceInfoCollector; +import com.android.cts.tradefed.build.CtsBuildProvider; import com.android.tradefed.log.LogUtil.CLog; import org.kxml2.io.KXmlSerializer; @@ -23,6 +23,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -38,8 +40,18 @@ import java.util.Map; */ class TestResults extends AbstractXmlPullParser { - private Map<String, TestPackageResult> mPackageMap = new LinkedHashMap<String, TestPackageResult>(); - private TestPackageResult mDeviceInfoPkg = new TestPackageResult(); + private static final String ns = CtsXmlResultReporter.ns; + + // XML constants + static final String SUMMARY_TAG = "Summary"; + static final String PASS_ATTR = "pass"; + static final String TIMEOUT_ATTR = "timeout"; + static final String NOT_EXECUTED_ATTR = "notExecuted"; + static final String FAILED_ATTR = "failed"; + + private Map<String, TestPackageResult> mPackageMap = + new LinkedHashMap<String, TestPackageResult>(); + private DeviceInfoResult mDeviceInfo = new DeviceInfoResult(); /** * {@inheritDoc} @@ -49,6 +61,10 @@ class TestResults extends AbstractXmlPullParser { int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && parser.getName().equals( + DeviceInfoResult.TAG)) { + mDeviceInfo.parse(parser); + } + if (eventType == XmlPullParser.START_TAG && parser.getName().equals( TestPackageResult.TAG)) { TestPackageResult pkg = new TestPackageResult(); pkg.parse(parser); @@ -83,30 +99,77 @@ class TestResults extends AbstractXmlPullParser { } /** - * @return + * Serialize the test results to XML. + * + * @param serializer + * @throws IOException */ - public Map<String, String> getDeviceInfoMetrics() { - return mDeviceInfoPkg.getMetrics(); + public void serialize(KXmlSerializer serializer) throws IOException { + mDeviceInfo.serialize(serializer); + serializeHostInfo(serializer); + serializeTestSummary(serializer); + // sort before serializing + List<TestPackageResult> pkgs = new ArrayList<TestPackageResult>(mPackageMap.values()); + Collections.sort(pkgs, new PkgComparator()); + for (TestPackageResult r : pkgs) { + r.serialize(serializer); + } } /** - * @param mCurrentPkgResult + * Output the host info XML. + * + * @param serializer */ - public void addPackageResult(TestPackageResult pkgResult) { - mPackageMap.put(pkgResult.getName(), pkgResult); + private void serializeHostInfo(KXmlSerializer serializer) throws IOException { + serializer.startTag(ns, "HostInfo"); + + String hostName = ""; + try { + hostName = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException ignored) {} + serializer.attribute(ns, "name", hostName); + + serializer.startTag(ns, "Os"); + serializer.attribute(ns, "name", System.getProperty("os.name")); + serializer.attribute(ns, "version", System.getProperty("os.version")); + serializer.attribute(ns, "arch", System.getProperty("os.arch")); + serializer.endTag(ns, "Os"); + + serializer.startTag(ns, "Java"); + serializer.attribute(ns, "name", System.getProperty("java.vendor")); + serializer.attribute(ns, "version", System.getProperty("java.version")); + serializer.endTag(ns, "Java"); + + serializer.startTag(ns, "Cts"); + serializer.attribute(ns, "version", CtsBuildProvider.CTS_BUILD_VERSION); + // TODO: consider outputting other tradefed options here + serializer.startTag(ns, "IntValue"); + serializer.attribute(ns, "name", "testStatusTimeoutMs"); + // TODO: create a constant variable for testStatusTimeoutMs value. Currently it cannot be + // changed + serializer.attribute(ns, "value", "600000"); + serializer.endTag(ns, "IntValue"); + serializer.endTag(ns, "Cts"); + + serializer.endTag(ns, "HostInfo"); } /** + * Output the test summary XML containing summary totals for all tests. + * * @param serializer * @throws IOException */ - public void serialize(KXmlSerializer serializer) throws IOException { - // sort before serializing - List<TestPackageResult> pkgs = new ArrayList<TestPackageResult>(mPackageMap.values()); - Collections.sort(pkgs, new PkgComparator()); - for (TestPackageResult r : pkgs) { - r.serialize(serializer); - } + private void serializeTestSummary(KXmlSerializer serializer) throws IOException { + serializer.startTag(ns, SUMMARY_TAG); + serializer.attribute(ns, FAILED_ATTR, Integer.toString(countTests(CtsTestStatus.FAIL))); + serializer.attribute(ns, NOT_EXECUTED_ATTR, + Integer.toString(countTests(CtsTestStatus.NOT_EXECUTED))); + // ignore timeouts - these are reported as errors + serializer.attribute(ns, TIMEOUT_ATTR, "0"); + serializer.attribute(ns, PASS_ATTR, Integer.toString(countTests(CtsTestStatus.PASS))); + serializer.endTag(ns, SUMMARY_TAG); } private static class PkgComparator implements Comparator<TestPackageResult> { @@ -127,10 +190,6 @@ class TestResults extends AbstractXmlPullParser { * @return */ public TestPackageResult getOrCreatePackage(String appPackageName) { - if (appPackageName.equals(DeviceInfoCollector.APP_PACKAGE_NAME)) { - mDeviceInfoPkg.setAppPackageName(DeviceInfoCollector.APP_PACKAGE_NAME); - return mDeviceInfoPkg ; - } TestPackageResult pkgResult = mPackageMap.get(appPackageName); if (pkgResult == null) { pkgResult = new TestPackageResult(); @@ -139,4 +198,12 @@ class TestResults extends AbstractXmlPullParser { } return pkgResult; } + + /** + * Populate the results with collected device info metrics. + * @param runMetrics + */ + public void populateDeviceInfoMetrics(Map<String, String> runMetrics) { + mDeviceInfo.populateMetrics(runMetrics); + } } diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestSummaryXml.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestSummaryXml.java index b91a391683a..4f0b59bff8e 100644 --- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestSummaryXml.java +++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestSummaryXml.java @@ -103,11 +103,11 @@ public class TestSummaryXml extends AbstractXmlPullParser implements ITestSummar CtsXmlResultReporter.RESULT_TAG)) { mPlan = getAttribute(parser, CtsXmlResultReporter.PLAN_ATTR); } else if (eventType == XmlPullParser.START_TAG && parser.getName().equals( - CtsXmlResultReporter.SUMMARY_TAG)) { - mNumFailed = parseIntAttr(parser, CtsXmlResultReporter.FAILED_ATTR) + - parseIntAttr(parser, CtsXmlResultReporter.TIMEOUT_ATTR); - mNumNotExecuted = parseIntAttr(parser, CtsXmlResultReporter.NOT_EXECUTED_ATTR); - mNumPassed = parseIntAttr(parser, CtsXmlResultReporter.PASS_ATTR); + TestResults.SUMMARY_TAG)) { + mNumFailed = parseIntAttr(parser, TestResults.FAILED_ATTR) + + parseIntAttr(parser, TestResults.TIMEOUT_ATTR); + mNumNotExecuted = parseIntAttr(parser, TestResults.NOT_EXECUTED_ATTR); + mNumPassed = parseIntAttr(parser, TestResults.PASS_ATTR); return; } eventType = parser.next(); diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java index 7566fce9901..dcf013cb047 100644 --- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java +++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java @@ -145,7 +145,7 @@ public class CtsXmlResultReporterTest extends TestCase { assertTrue(output.contains( "<Summary failed=\"1\" notExecuted=\"0\" timeout=\"0\" pass=\"0\" />")); final String failureTag = - "<FailedScene message=\"this is a trace\"> <StackTrace>this is a tracemore trace"; + "<FailedScene message=\"this is a trace\"> <StackTrace>this is a tracemore trace"; assertTrue(output.contains(failureTag)); } diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/DeviceInfoResultTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/DeviceInfoResultTest.java new file mode 100644 index 00000000000..a0388b2b32a --- /dev/null +++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/DeviceInfoResultTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.cts.tradefed.result; + +import android.tests.getinfo.DeviceInfoConstants; + +import junit.framework.TestCase; + +import org.kxml2.io.KXmlSerializer; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests for {@link DeviceInfoResult} + */ +public class DeviceInfoResultTest extends TestCase { + + private DeviceInfoResult mDeserializingInfo; + + @Override + protected void setUp() throws Exception { + mDeserializingInfo = new DeviceInfoResult() { + // override parent to advance xml parser to correct tag + @Override + void parse(XmlPullParser parser) throws XmlPullParserException, IOException { + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && parser.getName().equals(TAG)) { + super.parse(parser); + return; + } + eventType = parser.next(); + } + throw new XmlPullParserException(String.format("Could not find tag %s", TAG)); + } + }; + } + + /** + * Test the roundtrip setting of device process details, serializing and parsing from/to XML. + */ + public void testProcess() throws Exception { + final String processString = "ueventd;netd;"; + DeviceInfoResult serializedInfo = new DeviceInfoResult(); + addMetric(DeviceInfoConstants.PROCESSES, processString, serializedInfo); + String serializedOutput = serialize(serializedInfo); + mDeserializingInfo.parse(new StringReader(serializedOutput)); + assertEquals(processString, mDeserializingInfo.getMetrics().get( + DeviceInfoConstants.PROCESSES)); + } + + /** + * Test the roundtrip setting of device feature details, serializing and parsing from/to XML. + */ + public void testFeature() throws Exception { + final String featureString = + "android.hardware.audio.low_latency:sdk:false;android.hardware.bluetooth:sdk:true;"; + DeviceInfoResult serializedInfo = new DeviceInfoResult(); + addMetric(DeviceInfoConstants.FEATURES, featureString, serializedInfo); + String serializedOutput = serialize(serializedInfo); + mDeserializingInfo.parse(new StringReader(serializedOutput)); + assertEquals(featureString, mDeserializingInfo.getMetrics().get( + DeviceInfoConstants.FEATURES)); + } + + /** + * Test populating a combined metric like device serial + */ + public void testPopulateMetrics_combinedSerial() throws Exception { + DeviceInfoResult info = new DeviceInfoResult(); + // first add another metric to make hashmap non empty, so combined logic is triggered + addMetric(DeviceInfoConstants.PROCESSES, "proc", info); + addMetric(DeviceInfoConstants.SERIAL_NUMBER, "device1", info); + // ensure the stored serial number equals the value that was just set + assertEquals("device1", info.getMetrics().get( + DeviceInfoConstants.SERIAL_NUMBER)); + // now add it again + addMetric(DeviceInfoConstants.SERIAL_NUMBER, "device1", info); + // should still equal same value + assertEquals("device1", info.getMetrics().get( + DeviceInfoConstants.SERIAL_NUMBER)); + // now store different serial, and expect csv + addMetric(DeviceInfoConstants.SERIAL_NUMBER, "device2", info); + assertEquals("device1,device2", info.getMetrics().get( + DeviceInfoConstants.SERIAL_NUMBER)); + } + + /** + * Test populating a verified-to-be-identical metric like DeviceInfoConstants.BUILD_FINGERPRINT + */ + public void testPopulateMetrics_verify() throws Exception { + DeviceInfoResult info = new DeviceInfoResult(); + addMetric(DeviceInfoConstants.BUILD_FINGERPRINT, "fingerprint1", info); + // ensure the stored fingerprint equals the value that was just set + assertEquals("fingerprint1", info.getMetrics().get( + DeviceInfoConstants.BUILD_FINGERPRINT)); + // now add it again + addMetric(DeviceInfoConstants.BUILD_FINGERPRINT, "fingerprint1", info); + // should still equal same value + assertEquals("fingerprint1", info.getMetrics().get( + DeviceInfoConstants.BUILD_FINGERPRINT)); + // now store different serial, and expect error message + addMetric(DeviceInfoConstants.BUILD_FINGERPRINT, "fingerprint2", info); + assertTrue(info.getMetrics().get( + DeviceInfoConstants.BUILD_FINGERPRINT).contains("ERROR")); + } + + /** + * Helper method to add given metric to the {@link DeviceInfoResult} + */ + private void addMetric(String metricName, String metricValue, DeviceInfoResult serializedInfo) { + Map<String, String> collectedMetrics = new HashMap<String, String>(); + collectedMetrics.put(metricName, metricValue); + serializedInfo.populateMetrics(collectedMetrics); + } + + /** + * Helper method to serialize given object to XML + */ + private String serialize(DeviceInfoResult serializedInfo) + throws IOException { + KXmlSerializer xmlSerializer = new KXmlSerializer(); + StringWriter serializedOutput = new StringWriter(); + xmlSerializer.setOutput(serializedOutput); + serializedInfo.serialize(xmlSerializer); + return serializedOutput.toString(); + } +} |