1 /* 2 * Copyright (C) 2018 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.tradefed.result.proto; 17 18 import com.android.ddmlib.Log.LogLevel; 19 import com.android.tradefed.config.Option; 20 import com.android.tradefed.config.OptionClass; 21 import com.android.tradefed.error.HarnessException; 22 import com.android.tradefed.invoker.IInvocationContext; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 25 import com.android.tradefed.result.FailureDescription; 26 import com.android.tradefed.result.ILogSaverListener; 27 import com.android.tradefed.result.ITestInvocationListener; 28 import com.android.tradefed.result.LogFile; 29 import com.android.tradefed.result.TestDescription; 30 import com.android.tradefed.result.proto.LogFileProto.LogFileInfo; 31 import com.android.tradefed.result.proto.TestRecordProto.ChildReference; 32 import com.android.tradefed.result.proto.TestRecordProto.DebugInfo; 33 import com.android.tradefed.result.proto.TestRecordProto.DebugInfoContext; 34 import com.android.tradefed.result.proto.TestRecordProto.TestRecord; 35 import com.android.tradefed.result.proto.TestRecordProto.TestStatus; 36 import com.android.tradefed.result.retry.ISupportGranularResults; 37 import com.android.tradefed.result.skipped.SkipReason; 38 import com.android.tradefed.testtype.suite.ModuleDefinition; 39 import com.android.tradefed.util.SerializationUtil; 40 import com.android.tradefed.util.StreamUtil; 41 42 import com.google.common.base.Strings; 43 import com.google.protobuf.Any; 44 import com.google.protobuf.Timestamp; 45 46 import java.io.IOException; 47 import java.util.HashMap; 48 import java.util.Map; 49 import java.util.Stack; 50 import java.util.UUID; 51 52 /** 53 * Result reporter build a {@link TestRecord} protobuf with all the results inside. Should be 54 * extended to handle what to do with the final proto in {@link #processFinalProto(TestRecord)}. 55 */ 56 @OptionClass(alias = "proto-reporter") 57 public abstract class ProtoResultReporter 58 implements ITestInvocationListener, ILogSaverListener, ISupportGranularResults { 59 60 @Option( 61 name = "enable-granular-attempts", 62 description = 63 "Whether or not to allow this reporter receiving granular attempts. Feature flag." 64 ) 65 private boolean mReportGranularResults = true; 66 67 private Stack<TestRecord.Builder> mLatestChild; 68 private TestRecord.Builder mInvocationRecordBuilder; 69 private long mInvocationStartTime; 70 private IInvocationContext mContext; 71 72 private FailureDescription mInvocationFailureDescription = null; 73 private SkipReason mInvocationSkipReason = null; 74 /** Whether or not a testModuleStart had currently been called. */ 75 private boolean mModuleInProgress = false; 76 77 private IInvocationContext mModuleContext; 78 /** Track whether or not invocation ended has been reported. */ 79 private boolean mInvocationEnded = false; 80 /** Whether or not to inline test record of child events */ 81 private boolean mInlineRecordOfChildren = true; 82 83 @Override supportGranularResults()84 public boolean supportGranularResults() { 85 return mReportGranularResults; 86 } 87 setGranularResults(boolean granularResults)88 public void setGranularResults(boolean granularResults) { 89 mReportGranularResults = granularResults; 90 } 91 setInlineRecordOfChildren(boolean inline)92 public void setInlineRecordOfChildren(boolean inline) { 93 mInlineRecordOfChildren = inline; 94 } 95 96 /** 97 * Handling of the partial invocation test record proto after {@link 98 * #invocationStarted(IInvocationContext)} occurred. 99 * 100 * @param invocationStartRecord The partial proto populated after the invocationStart. 101 * @param invocationContext The invocation {@link IInvocationContext}. 102 */ processStartInvocation( TestRecord invocationStartRecord, IInvocationContext invocationContext)103 public void processStartInvocation( 104 TestRecord invocationStartRecord, IInvocationContext invocationContext) {} 105 106 /** 107 * Handling of the final proto with all results. 108 * 109 * @param finalRecord The finalized proto with all the invocation results. 110 */ processFinalProto(TestRecord finalRecord)111 public void processFinalProto(TestRecord finalRecord) {} 112 113 /** 114 * Handling of the partial module record proto after {@link 115 * #testModuleStarted(IInvocationContext)} occurred. 116 * 117 * @param moduleStartRecord The partial proto representing the module. 118 */ processTestModuleStarted(TestRecord moduleStartRecord)119 public void processTestModuleStarted(TestRecord moduleStartRecord) {} 120 121 /** 122 * Handling of the finalized module record proto after {@link #testModuleEnded()} occurred. 123 * 124 * @param moduleRecord The finalized proto representing the module. 125 */ processTestModuleEnd(TestRecord moduleRecord)126 public void processTestModuleEnd(TestRecord moduleRecord) {} 127 128 /** 129 * Handling of the partial test run record proto after {@link #testRunStarted(String, int)} 130 * occurred. 131 * 132 * @param runStartedRecord The partial proto representing the run. 133 */ processTestRunStarted(TestRecord runStartedRecord)134 public void processTestRunStarted(TestRecord runStartedRecord) {} 135 136 /** 137 * Handling of the finalized run record proto after {@link #testRunEnded(long, HashMap)} 138 * occurred. 139 * 140 * @param runRecord The finalized proto representing the run. 141 * @param moduleInProgress whether or not a module is in progress. 142 */ processTestRunEnded(TestRecord runRecord, boolean moduleInProgress)143 public void processTestRunEnded(TestRecord runRecord, boolean moduleInProgress) {} 144 145 /** 146 * Handling of the partial test case record proto after {@link #testStarted(TestDescription, 147 * long)} occurred. 148 * 149 * @param testCaseStartedRecord The partial proto representing the test case. 150 */ processTestCaseStarted(TestRecord testCaseStartedRecord)151 public void processTestCaseStarted(TestRecord testCaseStartedRecord) {} 152 153 /** 154 * Handling of the finalized test case record proto after {@link #testEnded(TestDescription, 155 * long, HashMap)} occurred. 156 * 157 * @param testCaseRecord The finalized proto representing a test case. 158 */ processTestCaseEnded(TestRecord testCaseRecord)159 public void processTestCaseEnded(TestRecord testCaseRecord) {} 160 161 /** 162 * Use the invocation record to send one by one all the final logs of the invocation. 163 * 164 * @param invocationLogs The finalized proto representing the invocation. 165 */ processFinalInvocationLogs(TestRecord invocationLogs)166 public void processFinalInvocationLogs(TestRecord invocationLogs) {} 167 168 // Invocation events 169 170 @Override invocationStarted(IInvocationContext context)171 public final void invocationStarted(IInvocationContext context) { 172 mLatestChild = new Stack<>(); 173 mInvocationRecordBuilder = TestRecord.newBuilder(); 174 // Set invocation unique id 175 mInvocationRecordBuilder.setTestRecordId(UUID.randomUUID().toString()); 176 177 // Populate start time of invocation 178 mInvocationStartTime = System.currentTimeMillis(); 179 Timestamp startTime = createTimeStamp(mInvocationStartTime); 180 mInvocationRecordBuilder.setStartTime(startTime); 181 mInvocationRecordBuilder.setDescription(Any.pack(context.toProto())); 182 183 mContext = context; 184 185 // Put the invocation record at the bottom of the stack 186 mLatestChild.add(mInvocationRecordBuilder); 187 188 // Send the invocation proto with the currently set information to indicate the beginning 189 // of the invocation. 190 TestRecord startInvocationProto = mInvocationRecordBuilder.build(); 191 try { 192 processStartInvocation(startInvocationProto, context); 193 } catch (RuntimeException e) { 194 CLog.e("Failed to process invocation started:"); 195 CLog.e(e); 196 } 197 } 198 199 @Override invocationFailed(Throwable cause)200 public void invocationFailed(Throwable cause) { 201 // Translate the exception into a FailureDescription 202 mInvocationFailureDescription = 203 FailureDescription.create(cause.getMessage()).setCause(cause); 204 if (cause instanceof HarnessException) { 205 mInvocationFailureDescription.setErrorIdentifier( 206 ((HarnessException) cause).getErrorId()); 207 mInvocationFailureDescription.setOrigin(((HarnessException) cause).getOrigin()); 208 } 209 } 210 211 @Override invocationFailed(FailureDescription failure)212 public void invocationFailed(FailureDescription failure) { 213 mInvocationFailureDescription = failure; 214 } 215 216 @Override invocationSkipped(SkipReason reason)217 public void invocationSkipped(SkipReason reason) { 218 mInvocationSkipReason = reason; 219 } 220 221 @Override invocationEnded(long elapsedTime)222 public final void invocationEnded(long elapsedTime) { 223 if (mModuleInProgress) { 224 // If we had a module in progress, and a new module start occurs, complete the call 225 testModuleEnded(); 226 } 227 // Populate end time of invocation 228 Timestamp endTime = createTimeStamp(mInvocationStartTime + elapsedTime); 229 mInvocationRecordBuilder.setEndTime(endTime); 230 // Update the context in case it changed 231 mInvocationRecordBuilder.setDescription(Any.pack(mContext.toProto())); 232 233 DebugInfo invocationFailure = handleInvocationFailure(); 234 if (invocationFailure != null) { 235 mInvocationRecordBuilder.setDebugInfo(invocationFailure); 236 mInvocationRecordBuilder.setStatus(TestStatus.FAIL); 237 } else { 238 mInvocationRecordBuilder.setStatus(TestStatus.PASS); 239 } 240 if (mInvocationSkipReason != null) { 241 mInvocationRecordBuilder.setSkipReason(convertSkipReason(mInvocationSkipReason)); 242 } 243 244 // Finalize the protobuf handling: where to put the results. 245 TestRecord record = mInvocationRecordBuilder.build(); 246 try { 247 processFinalProto(record); 248 } catch (RuntimeException e) { 249 CLog.e("Failed to process invocation ended:"); 250 CLog.e(e); 251 } 252 mInvocationEnded = true; 253 } 254 255 // Module events (optional when there is no suite) 256 257 @Override testModuleStarted(IInvocationContext moduleContext)258 public final void testModuleStarted(IInvocationContext moduleContext) { 259 if (mModuleInProgress) { 260 // If we had a module in progress, and a new module start occurs, complete the call 261 testModuleEnded(); 262 } 263 TestRecord.Builder moduleBuilder = TestRecord.newBuilder(); 264 moduleBuilder.setParentTestRecordId(mInvocationRecordBuilder.getTestRecordId()); 265 moduleBuilder.setTestRecordId( 266 moduleContext.getAttributes().get(ModuleDefinition.MODULE_ID).get(0)); 267 moduleBuilder.setStartTime(createTimeStamp(System.currentTimeMillis())); 268 moduleBuilder.setDescription(Any.pack(moduleContext.toProto())); 269 mLatestChild.add(moduleBuilder); 270 mModuleInProgress = true; 271 mModuleContext = moduleContext; 272 try { 273 processTestModuleStarted(moduleBuilder.build()); 274 } catch (RuntimeException e) { 275 CLog.e("Failed to process invocation ended:"); 276 CLog.e(e); 277 } 278 } 279 280 @Override testModuleEnded()281 public final void testModuleEnded() { 282 TestRecord.Builder moduleBuilder = mLatestChild.pop(); 283 mModuleInProgress = false; 284 285 moduleBuilder.setEndTime(createTimeStamp(System.currentTimeMillis())); 286 // Module do not have a fail status 287 moduleBuilder.setStatus(TestStatus.PASS); 288 // Repack module for updated properties 289 moduleBuilder.setDescription(Any.pack(mModuleContext.toProto())); 290 TestRecord.Builder parentBuilder = mLatestChild.peek(); 291 mModuleContext = null; 292 // Finalize the module and track it in the child 293 TestRecord moduleRecord = moduleBuilder.build(); 294 ChildReference moduleReference = createModuleChildReference(moduleRecord); 295 if (moduleReference != null) { 296 parentBuilder.addChildren(moduleReference); 297 } 298 try { 299 processTestModuleEnd(moduleRecord); 300 } catch (RuntimeException e) { 301 CLog.e("Failed to process test module end:"); 302 CLog.e(e); 303 } 304 } 305 306 // Run events 307 308 @Override testRunStarted(String runName, int testCount)309 public final void testRunStarted(String runName, int testCount) { 310 testRunStarted(runName, testCount, 0); 311 } 312 313 @Override testRunStarted(String runName, int testCount, int attemptNumber)314 public void testRunStarted(String runName, int testCount, int attemptNumber) { 315 testRunStarted(runName, testCount, attemptNumber, System.currentTimeMillis()); 316 } 317 318 @Override testRunStarted(String runName, int testCount, int attemptNumber, long startTime)319 public void testRunStarted(String runName, int testCount, int attemptNumber, long startTime) { 320 TestRecord.Builder runBuilder = TestRecord.newBuilder(); 321 TestRecord.Builder parent = mLatestChild.peek(); 322 runBuilder.setParentTestRecordId(parent.getTestRecordId()); 323 runBuilder.setTestRecordId(runName); 324 runBuilder.setNumExpectedChildren(testCount); 325 runBuilder.setStartTime(createTimeStamp(startTime)); 326 runBuilder.setAttemptId(attemptNumber); 327 328 mLatestChild.add(runBuilder); 329 try { 330 processTestRunStarted(runBuilder.build()); 331 } catch (RuntimeException e) { 332 CLog.e("Failed to process invocation ended:"); 333 CLog.e(e); 334 } 335 } 336 337 @Override testRunFailed(String errorMessage)338 public final void testRunFailed(String errorMessage) { 339 TestRecord.Builder current = mLatestChild.peek(); 340 DebugInfo.Builder debugBuilder = DebugInfo.newBuilder(); 341 debugBuilder.setErrorMessage(errorMessage); 342 if (TestStatus.UNKNOWN.equals(current.getStatus())) { 343 current.setDebugInfo(debugBuilder.build()); 344 current.setStatus(TestStatus.FAIL); 345 } else { 346 // We are in a test case and we need the run parent. 347 TestRecord.Builder test = mLatestChild.pop(); 348 TestRecord.Builder run = mLatestChild.peek(); 349 run.setDebugInfo(debugBuilder.build()); 350 run.setStatus(TestStatus.FAIL); 351 // Re-add the test 352 mLatestChild.add(test); 353 } 354 } 355 356 @Override testRunFailed(FailureDescription failure)357 public final void testRunFailed(FailureDescription failure) { 358 TestRecord.Builder current = mLatestChild.peek(); 359 DebugInfo.Builder debugBuilder = DebugInfo.newBuilder(); 360 debugBuilder.setErrorMessage(failure.toString()); 361 if (failure.getFailureStatus() != null) { 362 debugBuilder.setFailureStatus(failure.getFailureStatus()); 363 } 364 DebugInfoContext.Builder debugContext = DebugInfoContext.newBuilder(); 365 if (failure.getActionInProgress() != null) { 366 debugContext.setActionInProgress(failure.getActionInProgress().toString()); 367 } 368 if (!Strings.isNullOrEmpty(failure.getDebugHelpMessage())) { 369 debugContext.setDebugHelpMessage(failure.getDebugHelpMessage()); 370 } 371 if (!Strings.isNullOrEmpty(failure.getOrigin())) { 372 debugContext.setOrigin(failure.getOrigin()); 373 } 374 if (failure.getErrorIdentifier() != null) { 375 debugContext.setErrorName(failure.getErrorIdentifier().name()); 376 debugContext.setErrorCode(failure.getErrorIdentifier().code()); 377 // Error Identifier dictates the status if it exists 378 debugBuilder.setFailureStatus(failure.getErrorIdentifier().status()); 379 } 380 debugBuilder.setDebugInfoContext(debugContext.build()); 381 382 if (TestStatus.UNKNOWN.equals(current.getStatus())) { 383 current.setDebugInfo(debugBuilder.build()); 384 current.setStatus(TestStatus.FAIL); 385 } else { 386 // We are in a test case and we need the run parent. 387 TestRecord.Builder test = mLatestChild.pop(); 388 TestRecord.Builder run = mLatestChild.peek(); 389 run.setDebugInfo(debugBuilder.build()); 390 run.setStatus(TestStatus.FAIL); 391 // Re-add the test 392 mLatestChild.add(test); 393 } 394 } 395 396 @Override testRunEnded(long elapsedTimeMillis, HashMap<String, Metric> runMetrics)397 public final void testRunEnded(long elapsedTimeMillis, HashMap<String, Metric> runMetrics) { 398 TestRecord.Builder runBuilder = mLatestChild.pop(); 399 long startTime = timeStampToMillis(runBuilder.getStartTime()); 400 runBuilder.setEndTime(createTimeStamp(startTime + elapsedTimeMillis)); 401 runBuilder.putAllMetrics(runMetrics); 402 TestRecord.Builder parentBuilder = mLatestChild.peek(); 403 404 if (!runBuilder.hasDebugInfo()) { 405 runBuilder.setStatus(TestStatus.PASS); 406 } 407 408 // Finalize the run and track it in the child 409 TestRecord runRecord = runBuilder.build(); 410 parentBuilder.addChildren(createChildReference(runRecord)); 411 try { 412 processTestRunEnded(runRecord, mModuleInProgress); 413 } catch (RuntimeException e) { 414 CLog.e("Failed to process test run end:"); 415 CLog.e(e); 416 } 417 } 418 419 // test case events 420 421 @Override testStarted(TestDescription test)422 public final void testStarted(TestDescription test) { 423 testStarted(test, System.currentTimeMillis()); 424 } 425 426 @Override testStarted(TestDescription test, long startTime)427 public final void testStarted(TestDescription test, long startTime) { 428 TestRecord.Builder testBuilder = TestRecord.newBuilder(); 429 TestRecord.Builder parent = mLatestChild.peek(); 430 testBuilder.setParentTestRecordId(parent.getTestRecordId()); 431 testBuilder.setTestRecordId(test.toString()); 432 testBuilder.setStartTime(createTimeStamp(startTime)); 433 testBuilder.setStatus(TestStatus.PASS); 434 435 mLatestChild.add(testBuilder); 436 try { 437 processTestCaseStarted(testBuilder.build()); 438 } catch (RuntimeException e) { 439 CLog.e("Failed to process invocation ended:"); 440 CLog.e(e); 441 } 442 } 443 444 @Override testEnded(TestDescription test, HashMap<String, Metric> testMetrics)445 public void testEnded(TestDescription test, HashMap<String, Metric> testMetrics) { 446 testEnded(test, System.currentTimeMillis(), testMetrics); 447 } 448 449 @Override testEnded( TestDescription test, long endTime, HashMap<String, Metric> testMetrics)450 public final void testEnded( 451 TestDescription test, long endTime, HashMap<String, Metric> testMetrics) { 452 TestRecord.Builder testBuilder = mLatestChild.pop(); 453 testBuilder.setEndTime(createTimeStamp(endTime)); 454 testBuilder.putAllMetrics(testMetrics); 455 TestRecord.Builder parentBuilder = mLatestChild.peek(); 456 457 // Finalize the run and track it in the child 458 TestRecord testCaseRecord = testBuilder.build(); 459 parentBuilder.addChildren(createChildReference(testCaseRecord)); 460 try { 461 processTestCaseEnded(testCaseRecord); 462 } catch (RuntimeException e) { 463 CLog.e("Failed to process test case end:"); 464 CLog.e(e); 465 } 466 } 467 468 @Override testSkipped(TestDescription test, SkipReason reason)469 public final void testSkipped(TestDescription test, SkipReason reason) { 470 TestRecord.Builder testBuilder = mLatestChild.peek(); 471 472 testBuilder.setSkipReason(convertSkipReason(reason)); 473 } 474 475 @Override testFailed(TestDescription test, String trace)476 public final void testFailed(TestDescription test, String trace) { 477 TestRecord.Builder testBuilder = mLatestChild.peek(); 478 479 testBuilder.setStatus(TestStatus.FAIL); 480 DebugInfo.Builder debugBuilder = DebugInfo.newBuilder(); 481 // FIXME: extract the error message from the trace 482 debugBuilder.setErrorMessage(trace); 483 debugBuilder.setTrace(trace); 484 testBuilder.setDebugInfo(debugBuilder.build()); 485 } 486 487 @Override testFailed(TestDescription test, FailureDescription failure)488 public final void testFailed(TestDescription test, FailureDescription failure) { 489 TestRecord.Builder testBuilder = mLatestChild.peek(); 490 491 testBuilder.setStatus(TestStatus.FAIL); 492 DebugInfo.Builder debugBuilder = DebugInfo.newBuilder(); 493 // FIXME: extract the error message from the trace 494 debugBuilder.setErrorMessage(failure.toString()); 495 debugBuilder.setTrace(failure.toString()); 496 if (failure.getFailureStatus() != null) { 497 debugBuilder.setFailureStatus(failure.getFailureStatus()); 498 } 499 DebugInfoContext.Builder debugContext = DebugInfoContext.newBuilder(); 500 if (failure.getActionInProgress() != null) { 501 debugContext.setActionInProgress(failure.getActionInProgress().toString()); 502 } 503 if (!Strings.isNullOrEmpty(failure.getDebugHelpMessage())) { 504 debugContext.setDebugHelpMessage(failure.getDebugHelpMessage()); 505 } 506 debugBuilder.setDebugInfoContext(debugContext.build()); 507 508 testBuilder.setDebugInfo(debugBuilder.build()); 509 } 510 511 @Override testIgnored(TestDescription test)512 public final void testIgnored(TestDescription test) { 513 TestRecord.Builder testBuilder = mLatestChild.peek(); 514 testBuilder.setStatus(TestStatus.IGNORED); 515 } 516 517 @Override testAssumptionFailure(TestDescription test, String trace)518 public final void testAssumptionFailure(TestDescription test, String trace) { 519 TestRecord.Builder testBuilder = mLatestChild.peek(); 520 521 testBuilder.setStatus(TestStatus.ASSUMPTION_FAILURE); 522 DebugInfo.Builder debugBuilder = DebugInfo.newBuilder(); 523 // FIXME: extract the error message from the trace 524 debugBuilder.setErrorMessage(trace); 525 debugBuilder.setTrace(trace); 526 testBuilder.setDebugInfo(debugBuilder.build()); 527 } 528 529 @Override testAssumptionFailure(TestDescription test, FailureDescription failure)530 public final void testAssumptionFailure(TestDescription test, FailureDescription failure) { 531 TestRecord.Builder testBuilder = mLatestChild.peek(); 532 533 testBuilder.setStatus(TestStatus.ASSUMPTION_FAILURE); 534 DebugInfo.Builder debugBuilder = DebugInfo.newBuilder(); 535 // FIXME: extract the error message from the trace 536 debugBuilder.setErrorMessage(failure.toString()); 537 debugBuilder.setTrace(failure.toString()); 538 if (failure.getFailureStatus() != null) { 539 debugBuilder.setFailureStatus(failure.getFailureStatus()); 540 } 541 testBuilder.setDebugInfo(debugBuilder.build()); 542 } 543 544 // log events 545 546 @Override logAssociation(String dataName, LogFile logFile)547 public final void logAssociation(String dataName, LogFile logFile) { 548 if (mLatestChild == null || mLatestChild.isEmpty()) { 549 CLog.w("Skip logging '%s' logAssociation called out of sequence.", dataName); 550 return; 551 } 552 TestRecord.Builder current = mLatestChild.peek(); 553 if (mInvocationEnded) { 554 // For after invocation ended events, report artifacts one by one. 555 current.clearArtifacts(); 556 current.clearChildren(); 557 } 558 Map<String, Any> fullmap = new HashMap<>(); 559 fullmap.putAll(current.getArtifactsMap()); 560 Any any = Any.pack(createFileProto(logFile)); 561 // Ensure keys are made unique to avoid colliding in the proto representation. 562 int count = 0; 563 String key; 564 do { 565 key = String.format("%s%s", dataName, count == 0 ? "" : count); 566 count++; 567 } while (fullmap.containsKey(key)); 568 fullmap.put(key, any); 569 current.putAllArtifacts(fullmap); 570 if (mInvocationEnded) { 571 CLog.logAndDisplay(LogLevel.DEBUG, "process final logs: %s", logFile.getPath()); 572 processFinalInvocationLogs(current.build()); 573 } 574 } 575 576 /** 577 * Creates a child reference for a module. 578 */ createModuleChildReference(TestRecord record)579 protected ChildReference createModuleChildReference(TestRecord record) { 580 return createChildReference(record); 581 } 582 createChildReference(TestRecord record)583 private ChildReference createChildReference(TestRecord record) { 584 ChildReference.Builder child = ChildReference.newBuilder(); 585 child.setTestRecordId(record.getTestRecordId()); 586 if (mInlineRecordOfChildren) { 587 child.setInlineTestRecord(record); 588 } 589 return child.build(); 590 } 591 592 /** Create and populate Timestamp as recommended in the javadoc of the Timestamp proto. */ createTimeStamp(long currentTimeMs)593 private Timestamp createTimeStamp(long currentTimeMs) { 594 return Timestamp.newBuilder() 595 .setSeconds(currentTimeMs / 1000) 596 .setNanos((int) ((currentTimeMs % 1000) * 1000000)) 597 .build(); 598 } 599 timeStampToMillis(Timestamp stamp)600 private long timeStampToMillis(Timestamp stamp) { 601 return stamp.getSeconds() * 1000L + (stamp.getNanos() / 1000000L); 602 } 603 createFileProto(LogFile logFile)604 private LogFileInfo createFileProto(LogFile logFile) { 605 LogFileInfo.Builder logFileBuilder = LogFileInfo.newBuilder(); 606 logFileBuilder 607 .setPath(logFile.getPath()) 608 .setIsText(logFile.isText()) 609 .setLogType(logFile.getType().toString()) 610 .setIsCompressed(logFile.isCompressed()) 611 .setSize(logFile.getSize()); 612 // Url can be null so avoid NPE by checking it before setting the proto 613 if (logFile.getUrl() != null) { 614 logFileBuilder.setUrl(logFile.getUrl()); 615 } 616 return logFileBuilder.build(); 617 } 618 handleInvocationFailure()619 private DebugInfo handleInvocationFailure() { 620 DebugInfo.Builder debugBuilder = DebugInfo.newBuilder(); 621 if (mInvocationFailureDescription == null) { 622 return null; 623 } 624 625 Throwable baseException = mInvocationFailureDescription.getCause(); 626 if (mInvocationFailureDescription.getErrorMessage() != null) { 627 debugBuilder.setErrorMessage(mInvocationFailureDescription.getErrorMessage()); 628 } 629 debugBuilder.setTrace(StreamUtil.getStackTrace(baseException)); 630 if (mInvocationFailureDescription != null 631 && mInvocationFailureDescription.getFailureStatus() != null) { 632 debugBuilder.setFailureStatus(mInvocationFailureDescription.getFailureStatus()); 633 } 634 DebugInfoContext.Builder debugContext = DebugInfoContext.newBuilder(); 635 if (mInvocationFailureDescription != null) { 636 if (mInvocationFailureDescription.getActionInProgress() != null) { 637 debugContext.setActionInProgress( 638 mInvocationFailureDescription.getActionInProgress().toString()); 639 } 640 if (!Strings.isNullOrEmpty(mInvocationFailureDescription.getDebugHelpMessage())) { 641 debugContext.setDebugHelpMessage( 642 mInvocationFailureDescription.getDebugHelpMessage()); 643 } 644 if (!Strings.isNullOrEmpty(mInvocationFailureDescription.getOrigin())) { 645 debugContext.setOrigin(mInvocationFailureDescription.getOrigin()); 646 } 647 if (mInvocationFailureDescription.getErrorIdentifier() != null) { 648 debugContext.setErrorName( 649 mInvocationFailureDescription.getErrorIdentifier().name()); 650 debugContext.setErrorCode( 651 mInvocationFailureDescription.getErrorIdentifier().code()); 652 } 653 } 654 try { 655 debugContext.setErrorType(SerializationUtil.serializeToString(baseException)); 656 } catch (IOException e) { 657 CLog.e("Failed to serialize the invocation failure:"); 658 CLog.e(e); 659 } 660 debugBuilder.setDebugInfoContext(debugContext); 661 662 return debugBuilder.build(); 663 } 664 convertSkipReason( SkipReason skip)665 private com.android.tradefed.result.proto.TestRecordProto.SkipReason convertSkipReason( 666 SkipReason skip) { 667 com.android.tradefed.result.proto.TestRecordProto.SkipReason.Builder reason = 668 com.android.tradefed.result.proto.TestRecordProto.SkipReason.newBuilder(); 669 reason.setReason(skip.getReason()).setTrigger(skip.getTrigger()); 670 return reason.build(); 671 } 672 } 673