Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts;
     18 
     19 import com.android.cts.TestDevice.DeviceParameterCollector;
     20 
     21 import org.w3c.dom.Document;
     22 import org.w3c.dom.Node;
     23 import org.w3c.dom.ProcessingInstruction;
     24 
     25 import java.io.File;
     26 import java.net.InetAddress;
     27 import java.net.UnknownHostException;
     28 import java.util.ArrayList;
     29 import java.util.Collection;
     30 import java.util.Date;
     31 import java.util.regex.Matcher;
     32 import java.util.regex.Pattern;
     33 
     34 import javax.xml.parsers.DocumentBuilderFactory;
     35 
     36 /**
     37  * Store the information of a test plan.
     38  */
     39 public class TestSessionLog extends XMLResourceHandler {
     40     private static final String EXPR_TEST_FAILED = ".+\\((\\S+):(\\d+)\\)";
     41     private static Pattern mTestFailedPattern = Pattern.compile(EXPR_TEST_FAILED);
     42     private static final String ATTRIBUTE_NAME = "name";
     43     static final String ATTRIBUTE_RESULT = "result";
     44     private static final String ATTRIBUTE_VERSION = "version";
     45     private static final String ATTRIBUTE_DIGEST = "digest";
     46     private static final String ATTRIBUTE_KNOWN_FAILURE = "KnownFailure";
     47 
     48     public static final String CTS_RESULT_FILE_NAME = "testResult.xml";
     49     private static final String CTS_RESULT_FILE_VERSION = "1.11";
     50 
     51     static final String ATTRIBUTE_STARTTIME = "starttime";
     52     static final String ATTRIBUTE_ENDTIME = "endtime";
     53     static final String ATTRIBUTE_TESTPLAN = "testPlan";
     54     static final String ATTRIBUTE_RESOLUTION = "resolution";
     55     static final String ATTRIBUTE_SUBSCRIBER_ID = "subscriberId";
     56     static final String ATTRIBUTE_DEVICE_ID = "deviceID";
     57     static final String ATTRIBUTE_BUILD_ID = "buildID";
     58     static final String ATTRIBUTE_BUILD_VERSION = "buildVersion";
     59     static final String ATTRIBUTE_ANDROID_PLATFORM_VERSION = "androidPlatformVersion";
     60     static final String ATTRIBUTE_LOCALES = "locales";
     61     static final String ATTRIBUTE_XDPI = "Xdpi";
     62     static final String ATTRIBUTE_YDPI = "Ydpi";
     63     static final String ATTRIBUTE_TOUCH = "touch";
     64     static final String ATTRIBUTE_NAVIGATION = "navigation";
     65     static final String ATTRIBUTE_KEYPAD = "keypad";
     66     static final String ATTRIBUTE_NETWORK = "network";
     67     static final String ATTRIBUTE_IMEI = "imei";
     68     static final String ATTRIBUTE_IMSI = "imsi";
     69     static final String ATTRIBUTE_BUILD_NAME = "buildName";
     70     static final String ATTRIBUTE_ARCH = "arch";
     71     static final String ATTRIBUTE_VALUE = "value";
     72     static final String ATTRIBUTE_AVAILABLE = "available";
     73     static final String ATTRIBUTE_TYPE = "type";
     74     static final String ATTRIBUTE_UID = "uid";
     75     static final String ATTRIBUTE_OPEN_GL_ES_VERSION = "openGlEsVersion";
     76     static final String ATTRIBUTE_PARTITIONS = "partitions";
     77 
     78     static final String ATTRIBUTE_PASS = "pass";
     79     static final String ATTRIBUTE_FAILED = "failed";
     80     static final String ATTRIBUTE_TIMEOUT = "timeout";
     81     static final String ATTRIBUTE_NOT_EXECUTED = "notExecuted";
     82 
     83     static final String TAG_DEVICEINFO = "DeviceInfo";
     84     static final String TAG_HOSTINFO = "HostInfo";
     85     static final String TAG_OSINFO = "Os";
     86     static final String TAG_JAVA = "Java";
     87     static final String TAG_CTS = "Cts";
     88     static final String TAG_INTVALUE = "IntValue";
     89     static final String TAG_SUMMARY = "Summary";
     90     static final String TAG_SCREEN = "Screen";
     91     static final String TAG_BUILD_INFO = "BuildInfo";
     92     static final String TAG_FEATURE_INFO = "FeatureInfo";
     93     static final String TAG_FEATURE = "Feature";
     94     static final String TAG_PROCESS_INFO = "ProcessInfo";
     95     static final String TAG_PROCESS = "Process";
     96     static final String TAG_PHONE_SUB_INFO = "PhoneSubInfo";
     97     static final String TAG_TEST_RESULT = "TestResult";
     98     static final String TAG_TESTPACKAGE = "TestPackage";
     99     static final String TAG_TESTSUITE = "TestSuite";
    100     static final String TAG_TESTCASE = "TestCase";
    101     static final String TAG_FAILED_SCENE = "FailedScene";
    102     static final String TAG_STACK_TRACE = "StackTrace";
    103     static final String TAG_FAILED_MESSAGE = "message";
    104 
    105     private Collection<TestPackage> mTestPackages;
    106     private Date mSessionStartTime;
    107     private Date mSessionEndTime;
    108     private String mResultPath;
    109     private String mResultDir;
    110     private String mTestPlanName;
    111 
    112     private ArrayList<DeviceParameterCollector> mDeviceParameterBase;
    113 
    114     public TestSessionLog(final Collection<TestPackage> packages, final String testPlanName) {
    115         mTestPackages = packages;
    116 
    117         mDeviceParameterBase = new ArrayList<TestDevice.DeviceParameterCollector>();
    118         mTestPlanName = testPlanName;
    119 
    120         mSessionStartTime = new Date();
    121         mSessionEndTime = new Date();
    122     }
    123 
    124     /**
    125      * Get the test plan name.
    126      *
    127      * @return The test plan name.
    128      */
    129     public String getTestPlanName() {
    130         return mTestPlanName;
    131     }
    132 
    133     /**
    134      * Get all result of this session.
    135      *
    136      * @return All the tests with a result code of this session.
    137      */
    138     public Collection<Test> getAllResults() {
    139         if (mTestPackages == null || mTestPackages.size() == 0) {
    140             return null;
    141         }
    142 
    143         ArrayList<Test> results = new ArrayList<Test>();
    144         for (TestPackage p : mTestPackages) {
    145             results.addAll(p.getTests());
    146         }
    147 
    148         return results;
    149     }
    150 
    151     /**
    152      * Get test list according to the result type code.
    153      *
    154      * @param resCode The result code.
    155      * @return The list of {@link Test}.
    156      */
    157     public Collection<Test> getTestList(int resCode) {
    158         if ((resCode < CtsTestResult.CODE_FIRST)
    159                 || (resCode > CtsTestResult.CODE_LAST)) {
    160             return null;
    161         }
    162 
    163         ArrayList<Test> tests = new ArrayList<Test>();
    164         for (Test test : getAllResults()) {
    165             if (resCode == test.getResult().getResultCode()) {
    166                 tests.add(test);
    167             }
    168         }
    169 
    170         return tests;
    171     }
    172 
    173     /**
    174      * Get TestSession start time
    175      *
    176      * @return The start time.
    177      */
    178     public Date getStartTime() {
    179         return mSessionStartTime;
    180     }
    181 
    182     /**
    183      * Get TestSession end time
    184      *
    185      * @return The end time.
    186      */
    187     public Date getEndTime() {
    188         return mSessionEndTime;
    189     }
    190 
    191     /**
    192      * Get test packages.
    193      *
    194      * @return The test packages.
    195      */
    196     public Collection<TestPackage> getTestPackages() {
    197         return mTestPackages;
    198     }
    199 
    200     /**
    201      * Get the path to the XML result file.
    202      *
    203      * @return The result path.
    204      */
    205     public String getResultPath() {
    206         return mResultPath;
    207     }
    208 
    209     /**
    210      * Get the result directory.  This is the directory that all result files
    211      * should go into.
    212      */
    213     public String getResultDir() {
    214         return mResultDir;
    215     }
    216 
    217     /**
    218      * set TestSession start time
    219      *
    220      * @param time The start time.
    221      */
    222     public void setStartTime(final long time) {
    223         mSessionStartTime.setTime(time);
    224 
    225         String startTimeStr = HostUtils.getFormattedTimeString(time, "_", ".", ".");
    226         mResultDir = HostConfig.getInstance().getResultRepository().getRoot()
    227             + File.separator + startTimeStr;
    228         mResultPath =  mResultDir + File.separator + CTS_RESULT_FILE_NAME;
    229         // Make sure the result directory exists
    230         new File(mResultDir).mkdirs();
    231     }
    232 
    233     /**
    234      * set TestSession end time
    235      *
    236      * @param time The end time.
    237      */
    238     public void setEndTime(final long time) {
    239         mSessionEndTime.setTime(time);
    240     }
    241 
    242     /**
    243      * Calling this functions indicates that the TestSession is complete.  This
    244      * indicates to the TestSessionLog that it is time to store the results
    245      * to the filesystem.
    246      */
    247     public void sessionComplete() {
    248         try {
    249             writeToFile(new File(mResultPath), createResultDoc());
    250             // Now zip up the results directory so we have something nice
    251             // that people can upload.
    252             HostUtils.zipUpDirectory(mResultDir,
    253                     mResultDir + ".zip",
    254                     new HostUtils.ZipFilenameTransformer() {
    255                         public String transform(String filename) {
    256                             if (filename.startsWith(mResultDir)) {
    257                                 return filename.substring(mResultDir.length() + 1);
    258                             }
    259                             return filename;
    260                         }
    261             });
    262         } catch (Exception e) {
    263             Log.e("Got exception when trying to write to result file", e);
    264         }
    265         HostConfig.getInstance().extractResultResources(mResultDir);
    266     }
    267 
    268     /**
    269      * Create result Doc in XML format.
    270      *
    271      * @return Result document.
    272      */
    273     protected Document createResultDoc() {
    274         try {
    275 
    276             Document doc = DocumentBuilderFactory.newInstance()
    277                     .newDocumentBuilder().newDocument();
    278             ProcessingInstruction pr = doc.createProcessingInstruction(
    279                     "xml-stylesheet", "type=\"text/xsl\"  href=\"cts_result.xsl\"");
    280             doc.appendChild(pr);
    281             Node root = doc.createElement(TAG_TEST_RESULT);
    282             doc.appendChild(root);
    283 
    284             setAttribute(doc, root, ATTRIBUTE_VERSION, CTS_RESULT_FILE_VERSION);
    285             setAttribute(doc, root, ATTRIBUTE_STARTTIME, HostUtils.dateToString(mSessionStartTime));
    286             setAttribute(doc, root, ATTRIBUTE_ENDTIME, HostUtils.dateToString(mSessionEndTime));
    287             setAttribute(doc, root, ATTRIBUTE_TESTPLAN, mTestPlanName);
    288 
    289             // set device information
    290             for (int i = 0; i < mDeviceParameterBase.size(); i ++) {
    291                 DeviceParameterCollector bldInfo = mDeviceParameterBase.get(i);
    292                 // set device setting
    293                 Node deviceSettingNode = doc.createElement(TAG_DEVICEINFO);
    294 
    295                 Node screenNode = doc.createElement(TAG_SCREEN);
    296                 setAttribute(doc, screenNode, ATTRIBUTE_RESOLUTION, bldInfo.getScreenResolution());
    297 
    298                 setAttribute(doc, screenNode, DeviceParameterCollector.SCREEN_SIZE,
    299                         bldInfo.getScreenSize());
    300                 setAttribute(doc, screenNode, DeviceParameterCollector.SCREEN_DENSITY,
    301                         bldInfo.getScreenDensity());
    302                 setAttribute(doc, screenNode, DeviceParameterCollector.SCREEN_DENSITY_BUCKET,
    303                         bldInfo.getScreenDensityBucket());
    304 
    305                 deviceSettingNode.appendChild(screenNode);
    306                 Node simCardNode = doc.createElement(TAG_PHONE_SUB_INFO);
    307                 setAttribute(doc, simCardNode, ATTRIBUTE_SUBSCRIBER_ID, bldInfo.getPhoneNumber());
    308                 deviceSettingNode.appendChild(simCardNode);
    309                 root.appendChild(deviceSettingNode);
    310 
    311                 Node devInfoNode = doc.createElement(TAG_BUILD_INFO);
    312                 setAttribute(doc, devInfoNode, ATTRIBUTE_DEVICE_ID, bldInfo.getSerialNumber());
    313                 setAttribute(doc, devInfoNode, ATTRIBUTE_BUILD_ID, bldInfo.getBuildId());
    314                 setAttribute(doc, devInfoNode, ATTRIBUTE_BUILD_NAME, bldInfo.getProductName());
    315                 setAttribute(doc, devInfoNode, ATTRIBUTE_BUILD_VERSION,
    316                              bldInfo.getBuildVersion());
    317                 setAttribute(doc, devInfoNode, ATTRIBUTE_ANDROID_PLATFORM_VERSION,
    318                              bldInfo.getAndroidPlatformVersion());
    319                 setAttribute(doc, devInfoNode, ATTRIBUTE_LOCALES, bldInfo.getLocales());
    320                 setAttribute(doc, devInfoNode, ATTRIBUTE_XDPI, bldInfo.getXdpi());
    321                 setAttribute(doc, devInfoNode, ATTRIBUTE_YDPI, bldInfo.getYdpi());
    322                 setAttribute(doc, devInfoNode, ATTRIBUTE_TOUCH, bldInfo.getTouchInfo());
    323                 setAttribute(doc, devInfoNode, ATTRIBUTE_NAVIGATION, bldInfo.getNavigation());
    324                 setAttribute(doc, devInfoNode, ATTRIBUTE_KEYPAD, bldInfo.getKeypad());
    325                 setAttribute(doc, devInfoNode, ATTRIBUTE_NETWORK, bldInfo.getNetwork());
    326                 setAttribute(doc, devInfoNode, ATTRIBUTE_IMEI, bldInfo.getIMEI());
    327                 setAttribute(doc, devInfoNode, ATTRIBUTE_IMSI, bldInfo.getIMSI());
    328                 setAttribute(doc, devInfoNode, ATTRIBUTE_OPEN_GL_ES_VERSION,
    329                         bldInfo.getOpenGlEsVersion());
    330                 setAttribute(doc, devInfoNode, ATTRIBUTE_PARTITIONS,
    331                         bldInfo.getPartitions());
    332 
    333                 setAttribute(doc, devInfoNode,
    334                         DeviceParameterCollector.BUILD_FINGERPRINT, bldInfo.getBuildFingerPrint());
    335                 setAttribute(doc, devInfoNode,
    336                         DeviceParameterCollector.BUILD_TYPE, bldInfo.getBuildType());
    337                 setAttribute(doc, devInfoNode,
    338                         DeviceParameterCollector.BUILD_MODEL, bldInfo.getBuildModel());
    339                 setAttribute(doc, devInfoNode,
    340                         DeviceParameterCollector.BUILD_MANUFACTURER,
    341                                 bldInfo.getBuildManufacturer());
    342                 setAttribute(doc, devInfoNode,
    343                         DeviceParameterCollector.BUILD_BRAND, bldInfo.getBuildBrand());
    344                 setAttribute(doc, devInfoNode,
    345                         DeviceParameterCollector.BUILD_BOARD, bldInfo.getBuildBoard());
    346                 setAttribute(doc, devInfoNode,
    347                         DeviceParameterCollector.BUILD_DEVICE, bldInfo.getBuildDevice());
    348                 setAttribute(doc, devInfoNode,
    349                         DeviceParameterCollector.BUILD_ABI, bldInfo.getBuildAbi());
    350                 setAttribute(doc, devInfoNode,
    351                         DeviceParameterCollector.BUILD_ABI2, bldInfo.getBuildAbi2());
    352 
    353                 deviceSettingNode.appendChild(devInfoNode);
    354 
    355                 addFeatureInfo(doc, deviceSettingNode, bldInfo);
    356                 addProcessInfo(doc, deviceSettingNode, bldInfo);
    357             }
    358 
    359             Node hostInfo = doc.createElement(TAG_HOSTINFO);
    360             root.appendChild(hostInfo);
    361             String hostName = "";
    362             try {
    363                 hostName = InetAddress.getLocalHost().getHostName();
    364             } catch (UnknownHostException ignored) {}
    365             setAttribute(doc, hostInfo, ATTRIBUTE_NAME, hostName);
    366             Node osInfo = doc.createElement(TAG_OSINFO);
    367             hostInfo.appendChild(osInfo);
    368             setAttribute(doc, osInfo, ATTRIBUTE_NAME, System.getProperty("os.name"));
    369             setAttribute(doc, osInfo, ATTRIBUTE_VERSION, System.getProperty("os.version"));
    370             setAttribute(doc, osInfo, ATTRIBUTE_ARCH, System.getProperty("os.arch"));
    371             Node javaInfo = doc.createElement(TAG_JAVA);
    372             hostInfo.appendChild(javaInfo);
    373             setAttribute(doc, javaInfo, ATTRIBUTE_NAME, System.getProperty("java.vendor"));
    374             setAttribute(doc, javaInfo, ATTRIBUTE_VERSION, System.getProperty("java.version"));
    375             Node ctsInfo = doc.createElement(TAG_CTS);
    376             hostInfo.appendChild(ctsInfo);
    377             setAttribute(doc, ctsInfo, ATTRIBUTE_VERSION, Version.asString());
    378             for (HostConfig.Ints i : HostConfig.Ints.values()) {
    379                 Node intValue = doc.createElement(TAG_INTVALUE);
    380                 ctsInfo.appendChild(intValue);
    381                 setAttribute(doc, intValue, ATTRIBUTE_NAME, i.name());
    382                 setAttribute(doc, intValue, ATTRIBUTE_VALUE, i.value());
    383             }
    384 
    385             int passNum = getTestList(CtsTestResult.CODE_PASS).size();
    386             int failNum = getTestList(CtsTestResult.CODE_FAIL).size();
    387             int notExecutedNum = getTestList(CtsTestResult.CODE_NOT_EXECUTED).size();
    388             int timeOutNum = getTestList(CtsTestResult.CODE_TIMEOUT).size();
    389             Node summaryNode = doc.createElement(TAG_SUMMARY);
    390             root.appendChild(summaryNode);
    391             setAttribute(doc, summaryNode, ATTRIBUTE_PASS, passNum);
    392             setAttribute(doc, summaryNode, ATTRIBUTE_FAILED, failNum);
    393             setAttribute(doc, summaryNode, ATTRIBUTE_NOT_EXECUTED, notExecutedNum);
    394             setAttribute(doc, summaryNode, ATTRIBUTE_TIMEOUT, timeOutNum);
    395 
    396             for (TestPackage testPackage : mTestPackages) {
    397                 Node testPackageNode = doc.createElement(TAG_TESTPACKAGE);
    398                 setAttribute(doc, testPackageNode, ATTRIBUTE_NAME, testPackage.getAppBinaryName());
    399                 setAttribute(doc, testPackageNode, TestSessionBuilder.ATTRIBUTE_APP_PACKAGE_NAME,
    400                         testPackage.getAppPackageName());
    401                 setAttribute(doc, testPackageNode, ATTRIBUTE_DIGEST,
    402                              testPackage.getMessageDigest());
    403 
    404                 if (testPackage instanceof SignatureCheckPackage) {
    405                     setAttribute(doc, testPackageNode,
    406                               TestSessionBuilder.ATTRIBUTE_SIGNATURE_CHECK, "true");
    407                 }
    408 
    409                 for (TestSuite testSuite : testPackage.getTestSuites()) {
    410                     outputTestSuite(doc, testPackage, testPackageNode, testSuite);
    411                 }
    412                 root.appendChild(testPackageNode);
    413             }
    414 
    415             return doc;
    416         } catch (Exception e) {
    417             Log.e("create result doc failed", e);
    418         }
    419         return null;
    420     }
    421 
    422     /**
    423      * Creates a {@link #TAG_FEATURE_INFO} tag with {@link #TAG_FEATURE} elements indicating
    424      * what features are supported by the device. It parses a string from the deviceInfo argument
    425      * that is in the form of "feature1:true;feature2:false;featuer3;true;" with a trailing
    426      * semi-colon.
    427      *
    428      * <pre>
    429      *  <FeatureInfo>
    430      *     <Feature name="android.name.of.feature" available="true" />
    431      *     ...
    432      *   </FeatureInfo>
    433      * </pre>
    434      * @param document used to create elements
    435      * @param parentNode to attach the FeatureInfo element to
    436      * @param deviceInfo to get the feature data from
    437      */
    438     private void addFeatureInfo(Document document, Node parentNode,
    439             DeviceParameterCollector deviceInfo) {
    440         Node featureInfo = document.createElement(TAG_FEATURE_INFO);
    441         parentNode.appendChild(featureInfo);
    442 
    443         String features = deviceInfo.getFeatures();
    444         if (features == null) {
    445             features = "";
    446         }
    447 
    448         String[] featurePairs = features.split(";");
    449         for (String featurePair : featurePairs) {
    450             String[] nameTypeAvailability = featurePair.split(":");
    451             if (nameTypeAvailability.length >= 3) {
    452                 Node feature = document.createElement(TAG_FEATURE);
    453                 featureInfo.appendChild(feature);
    454 
    455                 setAttribute(document, feature, ATTRIBUTE_NAME, nameTypeAvailability[0]);
    456                 setAttribute(document, feature, ATTRIBUTE_TYPE, nameTypeAvailability[1]);
    457                 setAttribute(document, feature, ATTRIBUTE_AVAILABLE, nameTypeAvailability[2]);
    458             }
    459         }
    460     }
    461 
    462     /**
    463      * Creates a {@link #TAG_PROCESS_INFO} tag with {@link #TAG_PROCESS} elements indicating
    464      * what particular processes of interest were running on the device. It parses a string from
    465      * the deviceInfo argument that is in the form of "processName1;processName2;..." with a
    466      * trailing semi-colon.
    467      *
    468      * <pre>
    469      *   <ProcessInfo>
    470      *     <Process name="long_cat_viewer" uid="0" />
    471      *     ...
    472      *   </ProcessInfo>
    473      * </pre>
    474      *
    475      * @param document
    476      * @param parentNode
    477      * @param deviceInfo
    478      */
    479     private void addProcessInfo(Document document, Node parentNode,
    480             DeviceParameterCollector deviceInfo) {
    481         Node processInfo = document.createElement(TAG_PROCESS_INFO);
    482         parentNode.appendChild(processInfo);
    483 
    484         String rootProcesses = deviceInfo.getProcesses();
    485         if (rootProcesses == null) {
    486             rootProcesses = "";
    487         }
    488 
    489         String[] processNames = rootProcesses.split(";");
    490         for (String processName : processNames) {
    491             processName = processName.trim();
    492             if (!processName.isEmpty()) {
    493                 Node process = document.createElement(TAG_PROCESS);
    494                 processInfo.appendChild(process);
    495 
    496                 setAttribute(document, process, ATTRIBUTE_NAME, processName);
    497                 setAttribute(document, process, ATTRIBUTE_UID, "0");
    498             }
    499         }
    500     }
    501 
    502     /**
    503      * Output TestSuite and result to XML DOM Document.
    504      *
    505      * @param doc The document.
    506      * @param parentNode The parent node.
    507      * @param testSuite The test suite.
    508      */
    509     private void outputTestSuite(final Document doc,
    510             final TestPackage testPackage, final Node parentNode,
    511             TestSuite testSuite) {
    512 
    513         Collection<TestSuite> subSuites = testSuite.getSubSuites();
    514         Collection<TestCase> testCases = testSuite.getTestCases();
    515 
    516         Node testSuiteNode = doc.createElement(TAG_TESTSUITE);
    517         setAttribute(doc, testSuiteNode, ATTRIBUTE_NAME, testSuite.getName());
    518 
    519         for (TestCase testCase : testCases) {
    520             Node testCaseNode = doc.createElement(TAG_TESTCASE);
    521             testSuiteNode.appendChild(testCaseNode);
    522             setAttribute(doc, testCaseNode, ATTRIBUTE_NAME, testCase.getName());
    523             setAttribute(doc, testCaseNode, TestSessionBuilder.ATTRIBUTE_PRIORITY,
    524                     testCase.getPriority());
    525 
    526             Collection<Test> tests = testCase.getTests();
    527             for (Test test : tests) {
    528                 Node testNode = doc.createElement(TestSessionBuilder.TAG_TEST);
    529                 testCaseNode.appendChild(testNode);
    530 
    531                 if (test.isKnownFailure()) {
    532                     setAttribute(doc, testNode, ATTRIBUTE_KNOWN_FAILURE, test.getKnownFailure());
    533                 }
    534 
    535                 CtsTestResult result = test.getResult();
    536                 setAttribute(doc, testNode, ATTRIBUTE_NAME, test.getName());
    537                 setAttribute(doc, testNode, ATTRIBUTE_RESULT, result.getResultString());
    538                 setAttribute(doc, testNode, ATTRIBUTE_STARTTIME,
    539                              new Date(test.getStartTime()).toString());
    540                 setAttribute(doc, testNode, ATTRIBUTE_ENDTIME,
    541                              new Date(test.getEndTime()).toString());
    542 
    543                 String failedMessage = result.getFailedMessage();
    544 
    545                 if (failedMessage != null) {
    546                     // failure message may contain control characters < 0x20 that get translated
    547                     // into illegal XML character entities. Replace them first.
    548                     failedMessage = HostUtils.replaceControlChars(failedMessage);
    549                     Node failedMessageNode = doc.createElement(TAG_FAILED_SCENE);
    550                     testNode.appendChild(failedMessageNode);
    551                     setAttribute(doc, failedMessageNode,TAG_FAILED_MESSAGE, failedMessage);
    552 
    553                     String stackTrace = sanitizeStackTrace(result.getStackTrace());
    554                     if (stackTrace != null) {
    555                         Node stackTraceNode = doc.createElement(TAG_STACK_TRACE);
    556                         failedMessageNode.appendChild(stackTraceNode);
    557                         Node stackTraceTextNode = doc.createTextNode(stackTrace);
    558                         stackTraceNode.appendChild(stackTraceTextNode);
    559                     }
    560                 }
    561             }
    562         }
    563 
    564         for (TestSuite subSuite : subSuites) {
    565             outputTestSuite(doc, testPackage, testSuiteNode, subSuite);
    566             parentNode.appendChild(testSuiteNode);
    567         }
    568         parentNode.appendChild(testSuiteNode);
    569     }
    570 
    571     /**
    572      * Strip out any invalid XML characters that might cause the report to be unviewable.
    573      * http://www.w3.org/TR/REC-xml/#dt-character
    574      */
    575     private static String sanitizeStackTrace(String trace) {
    576         if (trace != null) {
    577             return trace.replaceAll("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD]", "");
    578         } else {
    579             return null;
    580         }
    581     }
    582 
    583     /**
    584      * Fetch failed file name and line number
    585      *
    586      * @param failedResult failed message
    587      * @return failed file name and line number
    588      */
    589     public final static String[] getFailedLineNumber(final String failedResult) {
    590         Matcher m = mTestFailedPattern.matcher(failedResult);
    591         if (m.matches()) {
    592             return new String[]{m.group(1), m.group(2)};
    593         }
    594         return null;
    595     }
    596 
    597     /**
    598      * set the device information of a specific device
    599      *
    600      * @param dInfo The device information.
    601      */
    602     public void setDeviceInfo(final TestDevice.DeviceParameterCollector dInfo) {
    603         for (DeviceParameterCollector collector : mDeviceParameterBase) {
    604             if (collector.getSerialNumber().equals(dInfo.getSerialNumber())) {
    605                 //if there has information for the device with given serial number,
    606                 //replace it with the new information.
    607                 mDeviceParameterBase.remove(collector);
    608                 break;
    609             }
    610         }
    611         mDeviceParameterBase.add(dInfo);
    612     }
    613 }
    614