• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TestSessionLog(final Collection<TestPackage> packages, final String testPlanName)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      */
getTestPlanName()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      */
getAllResults()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      */
getTestList(int resCode)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      */
getStartTime()178     public Date getStartTime() {
179         return mSessionStartTime;
180     }
181 
182     /**
183      * Get TestSession end time
184      *
185      * @return The end time.
186      */
getEndTime()187     public Date getEndTime() {
188         return mSessionEndTime;
189     }
190 
191     /**
192      * Get test packages.
193      *
194      * @return The test packages.
195      */
getTestPackages()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      */
getResultPath()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      */
getResultDir()213     public String getResultDir() {
214         return mResultDir;
215     }
216 
217     /**
218      * set TestSession start time
219      *
220      * @param time The start time.
221      */
setStartTime(final long time)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      */
setEndTime(final long time)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      */
sessionComplete()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      */
createResultDoc()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      */
addFeatureInfo(Document document, Node parentNode, DeviceParameterCollector deviceInfo)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      */
addProcessInfo(Document document, Node parentNode, DeviceParameterCollector deviceInfo)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.length() > 0) {
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      */
outputTestSuite(final Document doc, final TestPackage testPackage, final Node parentNode, TestSuite testSuite)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      */
sanitizeStackTrace(String trace)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      */
getFailedLineNumber(final String failedResult)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      */
setDeviceInfo(final TestDevice.DeviceParameterCollector dInfo)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