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