1 /* 2 * Copyright (C) 2015 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 package com.android.compatibility.common.util; 17 18 import org.xmlpull.v1.XmlPullParser; 19 import org.xmlpull.v1.XmlPullParserException; 20 import org.xmlpull.v1.XmlPullParserFactory; 21 import org.xmlpull.v1.XmlSerializer; 22 23 import java.io.File; 24 import java.io.FileNotFoundException; 25 import java.io.FileOutputStream; 26 import java.io.FileReader; 27 import java.io.IOException; 28 import java.io.OutputStream; 29 import java.net.InetAddress; 30 import java.net.UnknownHostException; 31 import java.text.SimpleDateFormat; 32 import java.util.ArrayList; 33 import java.util.Comparator; 34 import java.util.Collections; 35 import java.util.Date; 36 import java.util.List; 37 import java.util.Map.Entry; 38 import java.util.Set; 39 40 /** 41 * Handles conversion of results to/from files. 42 */ 43 public class ResultHandler { 44 45 private static final String ENCODING = "UTF-8"; 46 private static final String TYPE = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer"; 47 private static final String NS = null; 48 private static final String RESULT_FILE_VERSION = "5.0"; 49 /* package */ static final String TEST_RESULT_FILE_NAME = "test_result.xml"; 50 51 // XML constants 52 private static final String ABI_ATTR = "abi"; 53 private static final String BUGREPORT_TAG = "BugReport"; 54 private static final String BUILD_FINGERPRINT = "build_fingerprint"; 55 private static final String BUILD_ID = "build_id"; 56 private static final String BUILD_PRODUCT = "build_product"; 57 private static final String BUILD_TAG = "Build"; 58 private static final String CASE_TAG = "TestCase"; 59 private static final String COMMAND_LINE_ARGS = "command_line_args"; 60 private static final String DEVICES_ATTR = "devices"; 61 private static final String DONE_ATTR = "done"; 62 private static final String END_DISPLAY_TIME_ATTR = "end_display"; 63 private static final String END_TIME_ATTR = "end"; 64 private static final String FAILED_ATTR = "failed"; 65 private static final String FAILURE_TAG = "Failure"; 66 private static final String HOST_NAME_ATTR = "host_name"; 67 private static final String JAVA_VENDOR_ATTR = "java_vendor"; 68 private static final String JAVA_VERSION_ATTR = "java_version"; 69 private static final String LOGCAT_TAG = "Logcat"; 70 private static final String LOG_URL_ATTR = "log_url"; 71 private static final String MESSAGE_ATTR = "message"; 72 private static final String MODULE_TAG = "Module"; 73 private static final String MODULES_EXECUTED_ATTR = "modules_done"; 74 private static final String MODULES_TOTAL_ATTR = "modules_total"; 75 private static final String NAME_ATTR = "name"; 76 private static final String NOT_EXECUTED_ATTR = "not_executed"; 77 private static final String OS_ARCH_ATTR = "os_arch"; 78 private static final String OS_NAME_ATTR = "os_name"; 79 private static final String OS_VERSION_ATTR = "os_version"; 80 private static final String PASS_ATTR = "pass"; 81 private static final String REPORT_VERSION_ATTR = "report_version"; 82 private static final String REFERENCE_URL_ATTR = "reference_url"; 83 private static final String RESULT_ATTR = "result"; 84 private static final String RESULT_TAG = "Result"; 85 private static final String RUNTIME_ATTR = "runtime"; 86 private static final String SCREENSHOT_TAG = "Screenshot"; 87 private static final String STACK_TAG = "StackTrace"; 88 private static final String START_DISPLAY_TIME_ATTR = "start_display"; 89 private static final String START_TIME_ATTR = "start"; 90 private static final String SUITE_NAME_ATTR = "suite_name"; 91 private static final String SUITE_PLAN_ATTR = "suite_plan"; 92 private static final String SUITE_VERSION_ATTR = "suite_version"; 93 private static final String SUITE_BUILD_ATTR = "suite_build_number"; 94 private static final String SUMMARY_TAG = "Summary"; 95 private static final String TEST_TAG = "Test"; 96 97 /** 98 * @param resultsDir 99 */ getResults(File resultsDir)100 public static List<IInvocationResult> getResults(File resultsDir) { 101 List<IInvocationResult> results = new ArrayList<>(); 102 File[] files = resultsDir.listFiles(); 103 if (files == null || files.length == 0) { 104 // No results, just return the empty list 105 return results; 106 } 107 for (File resultDir : files) { 108 if (!resultDir.isDirectory()) { 109 continue; 110 } 111 try { 112 File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME); 113 if (!resultFile.exists()) { 114 continue; 115 } 116 IInvocationResult invocation = new InvocationResult(); 117 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 118 XmlPullParser parser = factory.newPullParser(); 119 parser.setInput(new FileReader(resultFile)); 120 121 parser.nextTag(); 122 parser.require(XmlPullParser.START_TAG, NS, RESULT_TAG); 123 invocation.setStartTime(Long.valueOf( 124 parser.getAttributeValue(NS, START_TIME_ATTR))); 125 invocation.setTestPlan(parser.getAttributeValue(NS, SUITE_PLAN_ATTR)); 126 invocation.setCommandLineArgs(parser.getAttributeValue(NS, COMMAND_LINE_ARGS)); 127 String deviceList = parser.getAttributeValue(NS, DEVICES_ATTR); 128 for (String device : deviceList.split(",")) { 129 invocation.addDeviceSerial(device); 130 } 131 132 parser.nextTag(); 133 parser.require(XmlPullParser.START_TAG, NS, BUILD_TAG); 134 invocation.addInvocationInfo(BUILD_ID, parser.getAttributeValue(NS, BUILD_ID)); 135 invocation.addInvocationInfo(BUILD_PRODUCT, parser.getAttributeValue(NS, 136 BUILD_PRODUCT)); 137 invocation.setBuildFingerprint(parser.getAttributeValue(NS, BUILD_FINGERPRINT)); 138 139 // TODO(stuartscott): may want to reload these incase the retry was done with 140 // --skip-device-info flag 141 parser.nextTag(); 142 parser.require(XmlPullParser.END_TAG, NS, BUILD_TAG); 143 parser.nextTag(); 144 parser.require(XmlPullParser.START_TAG, NS, SUMMARY_TAG); 145 parser.nextTag(); 146 parser.require(XmlPullParser.END_TAG, NS, SUMMARY_TAG); 147 while (parser.nextTag() == XmlPullParser.START_TAG) { 148 parser.require(XmlPullParser.START_TAG, NS, MODULE_TAG); 149 String name = parser.getAttributeValue(NS, NAME_ATTR); 150 String abi = parser.getAttributeValue(NS, ABI_ATTR); 151 String moduleId = AbiUtils.createId(abi, name); 152 boolean done = Boolean.parseBoolean(parser.getAttributeValue(NS, DONE_ATTR)); 153 IModuleResult module = invocation.getOrCreateModule(moduleId); 154 module.setDone(done); 155 while (parser.nextTag() == XmlPullParser.START_TAG) { 156 parser.require(XmlPullParser.START_TAG, NS, CASE_TAG); 157 String caseName = parser.getAttributeValue(NS, NAME_ATTR); 158 ICaseResult testCase = module.getOrCreateResult(caseName); 159 while (parser.nextTag() == XmlPullParser.START_TAG) { 160 parser.require(XmlPullParser.START_TAG, NS, TEST_TAG); 161 String testName = parser.getAttributeValue(NS, NAME_ATTR); 162 ITestResult test = testCase.getOrCreateResult(testName); 163 String result = parser.getAttributeValue(NS, RESULT_ATTR); 164 test.setResultStatus(TestStatus.getStatus(result)); 165 if (parser.nextTag() == XmlPullParser.START_TAG) { 166 if (parser.getName().equals(FAILURE_TAG)) { 167 test.setMessage(parser.getAttributeValue(NS, MESSAGE_ATTR)); 168 if (parser.nextTag() == XmlPullParser.START_TAG) { 169 parser.require(XmlPullParser.START_TAG, NS, STACK_TAG); 170 test.setStackTrace(parser.nextText()); 171 parser.require(XmlPullParser.END_TAG, NS, STACK_TAG); 172 parser.nextTag(); 173 } 174 parser.require(XmlPullParser.END_TAG, NS, FAILURE_TAG); 175 parser.nextTag(); 176 } else if (parser.getName().equals(BUGREPORT_TAG)) { 177 test.setBugReport(parser.nextText()); 178 parser.nextTag(); 179 } else if (parser.getName().equals(LOGCAT_TAG)) { 180 test.setLog(parser.nextText()); 181 parser.nextTag(); 182 } else if (parser.getName().equals(SCREENSHOT_TAG)) { 183 test.setScreenshot(parser.nextText()); 184 parser.nextTag(); 185 } else { 186 test.setReportLog(ReportLog.parse(parser)); 187 parser.nextTag(); 188 } 189 } 190 parser.require(XmlPullParser.END_TAG, NS, TEST_TAG); 191 } 192 parser.require(XmlPullParser.END_TAG, NS, CASE_TAG); 193 } 194 parser.require(XmlPullParser.END_TAG, NS, MODULE_TAG); 195 } 196 parser.require(XmlPullParser.END_TAG, NS, RESULT_TAG); 197 results.add(invocation); 198 } catch (XmlPullParserException e) { 199 e.printStackTrace(); 200 } catch (FileNotFoundException e) { 201 e.printStackTrace(); 202 } catch (IOException e) { 203 e.printStackTrace(); 204 } 205 } 206 // Sort the table entries on each entry's timestamp. 207 Collections.sort(results, new Comparator<IInvocationResult>() { 208 public int compare(IInvocationResult result1, IInvocationResult result2) { 209 return Long.compare(result1.getStartTime(), result2.getStartTime()); 210 } 211 }); 212 return results; 213 } 214 215 /** 216 * @param result 217 * @param resultDir 218 * @param startTime 219 * @param referenceUrl A nullable string that can contain a URL to a related data 220 * @param logUrl A nullable string that can contain a URL to related log files 221 * @param commandLineArgs A string containing the arguments to the run command 222 * @return The result file created. 223 * @throws IOException 224 * @throws XmlPullParserException 225 */ writeResults(String suiteName, String suiteVersion, String suitePlan, String suiteBuild, IInvocationResult result, File resultDir, long startTime, long endTime, String referenceUrl, String logUrl, String commandLineArgs)226 public static File writeResults(String suiteName, String suiteVersion, String suitePlan, 227 String suiteBuild, IInvocationResult result, File resultDir, 228 long startTime, long endTime, String referenceUrl, String logUrl, 229 String commandLineArgs) 230 throws IOException, XmlPullParserException { 231 int passed = result.countResults(TestStatus.PASS); 232 int failed = result.countResults(TestStatus.FAIL); 233 int notExecuted = result.countResults(TestStatus.NOT_EXECUTED); 234 File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME); 235 OutputStream stream = new FileOutputStream(resultFile); 236 XmlSerializer serializer = XmlPullParserFactory.newInstance(TYPE, null).newSerializer(); 237 serializer.setOutput(stream, ENCODING); 238 serializer.startDocument(ENCODING, false); 239 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 240 serializer.processingInstruction( 241 "xml-stylesheet type=\"text/xsl\" href=\"compatibility_result.xsl\""); 242 serializer.startTag(NS, RESULT_TAG); 243 serializer.attribute(NS, START_TIME_ATTR, String.valueOf(startTime)); 244 serializer.attribute(NS, END_TIME_ATTR, String.valueOf(endTime)); 245 serializer.attribute(NS, START_DISPLAY_TIME_ATTR, toReadableDateString(startTime)); 246 serializer.attribute(NS, END_DISPLAY_TIME_ATTR, toReadableDateString(endTime)); 247 248 serializer.attribute(NS, SUITE_NAME_ATTR, suiteName); 249 serializer.attribute(NS, SUITE_VERSION_ATTR, suiteVersion); 250 serializer.attribute(NS, SUITE_PLAN_ATTR, suitePlan); 251 serializer.attribute(NS, SUITE_BUILD_ATTR, suiteBuild); 252 serializer.attribute(NS, REPORT_VERSION_ATTR, RESULT_FILE_VERSION); 253 serializer.attribute(NS, COMMAND_LINE_ARGS, nullToEmpty(commandLineArgs)); 254 255 if (referenceUrl != null) { 256 serializer.attribute(NS, REFERENCE_URL_ATTR, referenceUrl); 257 } 258 259 if (logUrl != null) { 260 serializer.attribute(NS, LOG_URL_ATTR, logUrl); 261 } 262 263 // Device Info 264 Set<String> devices = result.getDeviceSerials(); 265 StringBuilder deviceList = new StringBuilder(); 266 boolean first = true; 267 for (String device : devices) { 268 if (first) { 269 first = false; 270 } else { 271 deviceList.append(","); 272 } 273 deviceList.append(device); 274 } 275 serializer.attribute(NS, DEVICES_ATTR, deviceList.toString()); 276 277 // Host Info 278 String hostName = ""; 279 try { 280 hostName = InetAddress.getLocalHost().getHostName(); 281 } catch (UnknownHostException ignored) {} 282 serializer.attribute(NS, HOST_NAME_ATTR, hostName); 283 serializer.attribute(NS, OS_NAME_ATTR, System.getProperty("os.name")); 284 serializer.attribute(NS, OS_VERSION_ATTR, System.getProperty("os.version")); 285 serializer.attribute(NS, OS_ARCH_ATTR, System.getProperty("os.arch")); 286 serializer.attribute(NS, JAVA_VENDOR_ATTR, System.getProperty("java.vendor")); 287 serializer.attribute(NS, JAVA_VERSION_ATTR, System.getProperty("java.version")); 288 289 // Build Info 290 serializer.startTag(NS, BUILD_TAG); 291 for (Entry<String, String> entry : result.getInvocationInfo().entrySet()) { 292 serializer.attribute(NS, entry.getKey(), entry.getValue()); 293 } 294 serializer.endTag(NS, BUILD_TAG); 295 296 // Summary 297 serializer.startTag(NS, SUMMARY_TAG); 298 serializer.attribute(NS, PASS_ATTR, Integer.toString(passed)); 299 serializer.attribute(NS, FAILED_ATTR, Integer.toString(failed)); 300 serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(notExecuted)); 301 serializer.attribute(NS, MODULES_EXECUTED_ATTR, 302 Integer.toString(result.getModuleCompleteCount())); 303 serializer.attribute(NS, MODULES_TOTAL_ATTR, 304 Integer.toString(result.getModules().size())); 305 serializer.endTag(NS, SUMMARY_TAG); 306 307 // Results 308 for (IModuleResult module : result.getModules()) { 309 serializer.startTag(NS, MODULE_TAG); 310 serializer.attribute(NS, NAME_ATTR, module.getName()); 311 serializer.attribute(NS, ABI_ATTR, module.getAbi()); 312 serializer.attribute(NS, RUNTIME_ATTR, String.valueOf(module.getRuntime())); 313 serializer.attribute(NS, DONE_ATTR, Boolean.toString(module.isDone())); 314 for (ICaseResult cr : module.getResults()) { 315 serializer.startTag(NS, CASE_TAG); 316 serializer.attribute(NS, NAME_ATTR, cr.getName()); 317 for (ITestResult r : cr.getResults()) { 318 serializer.startTag(NS, TEST_TAG); 319 serializer.attribute(NS, RESULT_ATTR, r.getResultStatus().getValue()); 320 serializer.attribute(NS, NAME_ATTR, r.getName()); 321 String message = r.getMessage(); 322 if (message != null) { 323 serializer.startTag(NS, FAILURE_TAG); 324 serializer.attribute(NS, MESSAGE_ATTR, message); 325 String stackTrace = r.getStackTrace(); 326 if (stackTrace != null) { 327 serializer.startTag(NS, STACK_TAG); 328 serializer.text(stackTrace); 329 serializer.endTag(NS, STACK_TAG); 330 } 331 serializer.endTag(NS, FAILURE_TAG); 332 } 333 String bugreport = r.getBugReport(); 334 if (bugreport != null) { 335 serializer.startTag(NS, BUGREPORT_TAG); 336 serializer.text(bugreport); 337 serializer.endTag(NS, BUGREPORT_TAG); 338 } 339 String logcat = r.getLog(); 340 if (logcat != null) { 341 serializer.startTag(NS, LOGCAT_TAG); 342 serializer.text(logcat); 343 serializer.endTag(NS, LOGCAT_TAG); 344 } 345 String screenshot = r.getScreenshot(); 346 if (screenshot != null) { 347 serializer.startTag(NS, SCREENSHOT_TAG); 348 serializer.text(screenshot); 349 serializer.endTag(NS, SCREENSHOT_TAG); 350 } 351 ReportLog report = r.getReportLog(); 352 if (report != null) { 353 ReportLog.serialize(serializer, report); 354 } 355 serializer.endTag(NS, TEST_TAG); 356 } 357 serializer.endTag(NS, CASE_TAG); 358 } 359 serializer.endTag(NS, MODULE_TAG); 360 } 361 serializer.endDocument(); 362 return resultFile; 363 } 364 365 /** 366 * Find the IInvocationResult for the given sessionId. 367 */ findResult(File resultsDir, Integer sessionId)368 public static IInvocationResult findResult(File resultsDir, Integer sessionId) 369 throws FileNotFoundException { 370 if (sessionId < 0) { 371 throw new IllegalArgumentException( 372 String.format("Invalid session id [%d] ", sessionId)); 373 } 374 375 List<IInvocationResult> results = getResults(resultsDir); 376 if (results == null || sessionId >= results.size()) { 377 throw new RuntimeException(String.format("Could not find session [%d]", sessionId)); 378 } 379 return results.get(sessionId); 380 } 381 382 /** 383 * Return the given time as a {@link String} suitable for displaying. 384 * <p/> 385 * Example: Fri Aug 20 15:13:03 PDT 2010 386 * 387 * @param time the epoch time in ms since midnight Jan 1, 1970 388 */ toReadableDateString(long time)389 static String toReadableDateString(long time) { 390 SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); 391 return dateFormat.format(new Date(time)); 392 } 393 394 /** 395 * When nullable is null, return an empty string. Otherwise, return the value in nullable. 396 */ nullToEmpty(String nullable)397 private static String nullToEmpty(String nullable) { 398 return nullable == null ? "" : nullable; 399 } 400 } 401