1 /* 2 * Copyright (C) 2016 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.util; 17 18 import com.android.tradefed.invoker.IInvocationContext; 19 import com.android.tradefed.log.LogUtil.CLog; 20 import com.android.tradefed.result.ActionInProgress; 21 import com.android.tradefed.result.FailureDescription; 22 import com.android.tradefed.result.LogDataType; 23 import com.android.tradefed.result.LogFile; 24 import com.android.tradefed.result.error.ErrorIdentifier; 25 import com.android.tradefed.result.proto.TestRecordProto.FailureStatus; 26 import com.android.tradefed.result.skipped.SkipReason; 27 import com.android.tradefed.testtype.suite.ModuleDefinition; 28 29 import com.google.common.base.Strings; 30 31 import org.json.JSONException; 32 import org.json.JSONObject; 33 34 import java.io.File; 35 import java.io.IOException; 36 import java.io.PrintWriter; 37 import java.io.StringWriter; 38 import java.util.HashMap; 39 import java.util.Iterator; 40 import java.util.Map; 41 42 import javax.annotation.Nonnull; 43 44 /** 45 * Helper to serialize/deserialize the events to be passed to the log. 46 */ 47 public class SubprocessEventHelper { 48 private static final String CLASSNAME_KEY = "className"; 49 private static final String TESTNAME_KEY = "testName"; 50 private static final String TRACE_KEY = "trace"; 51 private static final String CAUSE_KEY = "cause"; 52 private static final String RUNNAME_KEY = "runName"; 53 private static final String TESTCOUNT_KEY = "testCount"; 54 private static final String ATTEMPT_KEY = "runAttempt"; 55 private static final String TIME_KEY = "time"; 56 private static final String REASON_KEY = "reason"; 57 private static final String START_TIME = "start_time"; 58 private static final String END_TIME = "end_time"; 59 60 private static final String DATA_NAME_KEY = "dataName"; 61 private static final String DATA_TYPE_KEY = "dataType"; 62 private static final String DATA_FILE_KEY = "dataFile"; 63 private static final String LOGGED_FILE_KEY = "loggedFile"; 64 65 private static final String TEST_TAG_KEY = "testTag"; 66 67 private static final String MODULE_CONTEXT_KEY = "moduleContextFileName"; 68 private static final String MODULE_NAME = "moduleName"; 69 70 // Keys for skip reason 71 private static final String SKIP_REASON_MESSAGE = "skipMessage"; 72 private static final String SKIP_REASON_TRIGGER = "trigger"; 73 74 // keys for Error Classification 75 private static final String FAILURE_STATUS_KEY = "failure_status"; 76 private static final String ACTION_IN_PROGRESS_KEY = "action_in_progress"; 77 private static final String ERROR_NAME_KEY = "error_name"; 78 private static final String ERROR_CODE_KEY = "error_code"; 79 private static final String ERROR_ORIGIN_KEY = "origin"; 80 81 /** 82 * Helper for testRunStarted information 83 */ 84 public static class TestRunStartedEventInfo { 85 public String mRunName = null; 86 public Integer mTestCount = null; 87 public Integer mAttempt = null; 88 public Long mStartTime = null; 89 90 /** Keep this constructor for legacy compatibility. */ TestRunStartedEventInfo(String runName, int testCount)91 public TestRunStartedEventInfo(String runName, int testCount) { 92 mRunName = runName; 93 mTestCount = testCount; 94 mAttempt = 0; 95 mStartTime = System.currentTimeMillis(); 96 } 97 TestRunStartedEventInfo(String runName, int testCount, int attempt, long startTime)98 public TestRunStartedEventInfo(String runName, int testCount, int attempt, long startTime) { 99 mRunName = runName; 100 mTestCount = testCount; 101 mAttempt = attempt; 102 mStartTime = startTime; 103 } 104 TestRunStartedEventInfo(JSONObject jsonObject)105 public TestRunStartedEventInfo(JSONObject jsonObject) throws JSONException { 106 mRunName = jsonObject.getString(RUNNAME_KEY); 107 mTestCount = jsonObject.getInt(TESTCOUNT_KEY); 108 mAttempt = jsonObject.optInt(ATTEMPT_KEY, 0); 109 mStartTime = jsonObject.optLong(START_TIME, System.currentTimeMillis()); 110 } 111 112 @Override toString()113 public String toString() { 114 JSONObject tags = new JSONObject(); 115 try { 116 if (mRunName != null) { 117 tags.put(RUNNAME_KEY, mRunName); 118 } 119 if (mTestCount != null) { 120 tags.put(TESTCOUNT_KEY, mTestCount.intValue()); 121 } 122 if (mAttempt != null) { 123 tags.put(ATTEMPT_KEY, mAttempt.intValue()); 124 } 125 if (mStartTime != null) { 126 tags.put(START_TIME, mStartTime.longValue()); 127 } 128 } catch (JSONException e) { 129 CLog.e(e); 130 } 131 return tags.toString(); 132 } 133 } 134 135 /** 136 * Helper for testRunFailed information 137 */ 138 public static class TestRunFailedEventInfo { 139 public String mReason = null; 140 public FailureDescription mFailure = null; 141 TestRunFailedEventInfo(String reason)142 public TestRunFailedEventInfo(String reason) { 143 mReason = reason; 144 } 145 TestRunFailedEventInfo(FailureDescription failure)146 public TestRunFailedEventInfo(FailureDescription failure) { 147 mFailure = failure; 148 } 149 TestRunFailedEventInfo(JSONObject jsonObject)150 public TestRunFailedEventInfo(JSONObject jsonObject) throws JSONException { 151 mReason = jsonObject.getString(REASON_KEY); 152 mFailure = FailureDescription.create(mReason); 153 updateFailureFromJsonObject(mFailure, jsonObject); 154 } 155 156 @Override toString()157 public String toString() { 158 JSONObject tags = new JSONObject(); 159 try { 160 if (mFailure != null) { 161 tags.put(REASON_KEY, mFailure.getErrorMessage()); 162 tags.putOpt(FAILURE_STATUS_KEY, mFailure.getFailureStatus()); 163 tags.putOpt(ACTION_IN_PROGRESS_KEY, mFailure.getActionInProgress()); 164 tags.putOpt(ERROR_ORIGIN_KEY, mFailure.getOrigin()); 165 if (mFailure.getErrorIdentifier() != null) { 166 tags.putOpt(ERROR_NAME_KEY, mFailure.getErrorIdentifier().name()); 167 tags.putOpt(ERROR_CODE_KEY, mFailure.getErrorIdentifier().code()); 168 tags.putOpt(FAILURE_STATUS_KEY, mFailure.getErrorIdentifier().status()); 169 } 170 } 171 if (mReason != null) { 172 tags.put(REASON_KEY, mReason); 173 } 174 } catch (JSONException e) { 175 CLog.e(e); 176 } 177 return tags.toString(); 178 } 179 } 180 181 /** 182 * Helper for testRunEnded Information. 183 */ 184 public static class TestRunEndedEventInfo { 185 public Long mTime = null; 186 public Map<String, String> mRunMetrics = null; 187 TestRunEndedEventInfo(Long time, Map<String, String> runMetrics)188 public TestRunEndedEventInfo(Long time, Map<String, String> runMetrics) { 189 mTime = time; 190 mRunMetrics = runMetrics; 191 } 192 TestRunEndedEventInfo(JSONObject jsonObject)193 public TestRunEndedEventInfo(JSONObject jsonObject) throws JSONException { 194 mTime = jsonObject.getLong(TIME_KEY); 195 jsonObject.remove(TIME_KEY); 196 Iterator<?> i = jsonObject.keys(); 197 mRunMetrics = new HashMap<String, String>(); 198 while(i.hasNext()) { 199 String key = (String) i.next(); 200 mRunMetrics.put(key, jsonObject.get(key).toString()); 201 } 202 } 203 204 @Override toString()205 public String toString() { 206 JSONObject tags = null; 207 try { 208 if (mRunMetrics != null) { 209 tags = new JSONObject(mRunMetrics); 210 } else { 211 tags = new JSONObject(); 212 } 213 if (mTime != null) { 214 tags.put(TIME_KEY, mTime.longValue()); 215 } 216 } catch (JSONException e) { 217 CLog.e(e); 218 } 219 return tags.toString(); 220 } 221 } 222 223 /** 224 * Helper for InvocationFailed information. 225 */ 226 public static class InvocationFailedEventInfo { 227 public Throwable mCause = null; 228 public FailureDescription mFailure = null; 229 InvocationFailedEventInfo(Throwable cause)230 public InvocationFailedEventInfo(Throwable cause) { 231 mCause = cause; 232 } 233 InvocationFailedEventInfo(FailureDescription failure)234 public InvocationFailedEventInfo(FailureDescription failure) { 235 if (failure.getCause() != null) { 236 mCause = failure.getCause(); 237 } else { 238 mCause = new Throwable(failure.getErrorMessage()); 239 } 240 mFailure = failure; 241 } 242 InvocationFailedEventInfo(JSONObject jsonObject)243 public InvocationFailedEventInfo(JSONObject jsonObject) throws JSONException { 244 String stack = jsonObject.getString(CAUSE_KEY); 245 mCause = new Throwable(stack); 246 247 if (!Strings.isNullOrEmpty(jsonObject.optString(REASON_KEY))) { 248 mFailure = 249 FailureDescription.create(jsonObject.optString(REASON_KEY)) 250 .setOrigin(jsonObject.optString(ERROR_ORIGIN_KEY)) 251 .setCause(mCause); 252 // FailureStatus 253 FailureStatus status = FailureStatus.UNSET; 254 if (!Strings.isNullOrEmpty(jsonObject.optString(FAILURE_STATUS_KEY))) { 255 try { 256 status = FailureStatus.valueOf(jsonObject.optString(FAILURE_STATUS_KEY)); 257 } catch (NullPointerException | IllegalArgumentException e) { 258 CLog.e(e); 259 } 260 } 261 mFailure.setFailureStatus(status); 262 // ActionInProgress 263 ActionInProgress action = ActionInProgress.UNSET; 264 if (!Strings.isNullOrEmpty(jsonObject.optString(ACTION_IN_PROGRESS_KEY))) { 265 try { 266 action = 267 ActionInProgress.valueOf( 268 jsonObject.optString(ACTION_IN_PROGRESS_KEY)); 269 } catch (NullPointerException | IllegalArgumentException e) { 270 CLog.e(e); 271 } 272 } 273 mFailure.setActionInProgress(action); 274 // ErrorIdentifier 275 String errorName = jsonObject.optString(ERROR_NAME_KEY); 276 long errorCode = jsonObject.optLong(ERROR_CODE_KEY); 277 if (errorName != null) { 278 ErrorIdentifier errorId = 279 new ErrorIdentifier() { 280 @Override 281 public String name() { 282 return errorName; 283 } 284 285 @Override 286 public long code() { 287 return errorCode; 288 } 289 290 @Override 291 public @Nonnull FailureStatus status() { 292 FailureStatus status = mFailure.getFailureStatus(); 293 return (status == null ? FailureStatus.UNSET : status); 294 } 295 }; 296 mFailure.setErrorIdentifier(errorId); 297 } 298 } 299 } 300 301 @Override toString()302 public String toString() { 303 JSONObject tags = new JSONObject(); 304 try { 305 if (mFailure != null) { 306 tags.put(REASON_KEY, mFailure.getErrorMessage()); 307 tags.putOpt(ACTION_IN_PROGRESS_KEY, mFailure.getActionInProgress()); 308 tags.putOpt(ERROR_ORIGIN_KEY, mFailure.getOrigin()); 309 if (mFailure.getErrorIdentifier() != null) { 310 tags.putOpt(ERROR_NAME_KEY, mFailure.getErrorIdentifier().name()); 311 tags.putOpt(ERROR_CODE_KEY, mFailure.getErrorIdentifier().code()); 312 tags.putOpt(FAILURE_STATUS_KEY, mFailure.getErrorIdentifier().status()); 313 } else { 314 tags.putOpt(FAILURE_STATUS_KEY, mFailure.getFailureStatus()); 315 } 316 } 317 if (mCause != null) { 318 StringWriter sw = new StringWriter(); 319 PrintWriter pw = new PrintWriter(sw); 320 mCause.printStackTrace(pw); 321 tags.put(CAUSE_KEY, sw.toString()); 322 } 323 } catch (JSONException e) { 324 CLog.e(e); 325 } 326 return tags.toString(); 327 } 328 } 329 330 /** Base Helper for TestIgnored information. */ 331 public static class BaseTestEventInfo { 332 public String mClassName = null; 333 public String mTestName = null; 334 BaseTestEventInfo(String className, String testName)335 public BaseTestEventInfo(String className, String testName) { 336 mClassName = className; 337 mTestName = testName; 338 } 339 BaseTestEventInfo(JSONObject jsonObject)340 public BaseTestEventInfo(JSONObject jsonObject) throws JSONException { 341 mClassName = jsonObject.getString(CLASSNAME_KEY); 342 jsonObject.remove(CLASSNAME_KEY); 343 mTestName = jsonObject.getString(TESTNAME_KEY); 344 jsonObject.remove(TESTNAME_KEY); 345 } 346 getNewJson()347 protected JSONObject getNewJson() { 348 return new JSONObject(); 349 } 350 351 @Override toString()352 public String toString() { 353 JSONObject tags = null; 354 try { 355 tags = getNewJson(); 356 if (mClassName != null) { 357 tags.put(CLASSNAME_KEY, mClassName); 358 } 359 if (mTestName != null) { 360 tags.put(TESTNAME_KEY, mTestName); 361 } 362 } catch (JSONException e) { 363 CLog.e(e); 364 } 365 return tags.toString(); 366 } 367 } 368 369 /** Helper for testStarted information */ 370 public static class TestStartedEventInfo extends BaseTestEventInfo { 371 public Long mStartTime = null; 372 TestStartedEventInfo(String className, String testName, Long startTime)373 public TestStartedEventInfo(String className, String testName, Long startTime) { 374 super(className, testName); 375 mStartTime = startTime; 376 } 377 TestStartedEventInfo(JSONObject jsonObject)378 public TestStartedEventInfo(JSONObject jsonObject) throws JSONException { 379 super(jsonObject); 380 if (jsonObject.has(START_TIME)) { 381 mStartTime = jsonObject.getLong(START_TIME); 382 } 383 jsonObject.remove(START_TIME); 384 } 385 386 @Override getNewJson()387 protected JSONObject getNewJson() { 388 JSONObject json = new JSONObject(); 389 try { 390 json.put(START_TIME, mStartTime); 391 } catch (JSONException e) { 392 CLog.e(e); 393 } 394 return json; 395 } 396 } 397 398 public static class SkippedTestEventInfo extends BaseTestEventInfo { 399 public SkipReason skipReason = null; 400 SkippedTestEventInfo(String className, String testName, SkipReason reason)401 public SkippedTestEventInfo(String className, String testName, SkipReason reason) { 402 super(className, testName); 403 skipReason = reason; 404 } 405 SkippedTestEventInfo(JSONObject jsonObject)406 public SkippedTestEventInfo(JSONObject jsonObject) throws JSONException { 407 super(jsonObject); 408 skipReason = 409 new SkipReason( 410 jsonObject.getString(SKIP_REASON_MESSAGE), 411 jsonObject.getString(SKIP_REASON_TRIGGER)); 412 } 413 414 @Override toString()415 public String toString() { 416 JSONObject tags = null; 417 try { 418 tags = new JSONObject(super.toString()); 419 tags.put(SKIP_REASON_MESSAGE, skipReason.getReason()); 420 tags.put(SKIP_REASON_TRIGGER, skipReason.getTrigger()); 421 } catch (JSONException e) { 422 CLog.e(e); 423 } 424 return tags.toString(); 425 } 426 } 427 428 /** Helper for testFailed information. */ 429 public static class FailedTestEventInfo extends BaseTestEventInfo { 430 public String mTrace = null; 431 public FailureDescription mFailure = null; 432 FailedTestEventInfo(String className, String testName, String trace)433 public FailedTestEventInfo(String className, String testName, String trace) { 434 super(className, testName); 435 mTrace = trace; 436 } 437 FailedTestEventInfo(String className, String testName, FailureDescription failure)438 public FailedTestEventInfo(String className, String testName, FailureDescription failure) { 439 super(className, testName); 440 mFailure = failure; 441 } 442 FailedTestEventInfo(JSONObject jsonObject)443 public FailedTestEventInfo(JSONObject jsonObject) throws JSONException { 444 super(jsonObject); 445 mTrace = jsonObject.getString(TRACE_KEY); 446 mFailure = FailureDescription.create(mTrace); 447 updateFailureFromJsonObject(mFailure, jsonObject); 448 } 449 450 @Override toString()451 public String toString() { 452 JSONObject tags = null; 453 try { 454 tags = new JSONObject(super.toString()); 455 if (mFailure != null) { 456 tags.put(TRACE_KEY, mFailure.getErrorMessage()); 457 tags.putOpt(FAILURE_STATUS_KEY, mFailure.getFailureStatus()); 458 tags.putOpt(ACTION_IN_PROGRESS_KEY, mFailure.getActionInProgress()); 459 tags.putOpt(ERROR_ORIGIN_KEY, mFailure.getOrigin()); 460 if (mFailure.getErrorIdentifier() != null) { 461 tags.putOpt(ERROR_NAME_KEY, mFailure.getErrorIdentifier().name()); 462 tags.putOpt(ERROR_CODE_KEY, mFailure.getErrorIdentifier().code()); 463 tags.putOpt(FAILURE_STATUS_KEY, mFailure.getErrorIdentifier().status()); 464 } 465 } 466 if (mTrace != null) { 467 tags.put(TRACE_KEY, mTrace); 468 } 469 } catch (JSONException e) { 470 CLog.e(e); 471 } 472 return tags.toString(); 473 } 474 } 475 476 /** 477 * Helper for testEnded information. 478 */ 479 public static class TestEndedEventInfo extends BaseTestEventInfo { 480 public Map<String, String> mRunMetrics = null; 481 public Long mEndTime = null; 482 TestEndedEventInfo(String className, String testName, Map<String, String> runMetrics)483 public TestEndedEventInfo(String className, String testName, 484 Map<String, String> runMetrics) { 485 super(className, testName); 486 mRunMetrics = runMetrics; 487 mEndTime = System.currentTimeMillis(); 488 } 489 490 /** 491 * Create an event object to represent the testEnded callback. 492 * 493 * @param className the classname of the tests 494 * @param testName the name of the tests 495 * @param endTime the timestamp at which the test ended (from {@link 496 * System#currentTimeMillis()}) 497 * @param runMetrics the metrics reported by the test. 498 */ TestEndedEventInfo( String className, String testName, Long endTime, Map<String, String> runMetrics)499 public TestEndedEventInfo( 500 String className, String testName, Long endTime, Map<String, String> runMetrics) { 501 super(className, testName); 502 mEndTime = endTime; 503 mRunMetrics = runMetrics; 504 } 505 506 /** Create and populate and event object for testEnded from a JSON. */ TestEndedEventInfo(JSONObject jsonObject)507 public TestEndedEventInfo(JSONObject jsonObject) throws JSONException { 508 super(jsonObject); 509 if (jsonObject.has(END_TIME)) { 510 mEndTime = jsonObject.getLong(END_TIME); 511 } 512 jsonObject.remove(END_TIME); 513 Iterator<?> i = jsonObject.keys(); 514 mRunMetrics = new HashMap<String, String>(); 515 while(i.hasNext()) { 516 String key = (String) i.next(); 517 mRunMetrics.put(key, jsonObject.get(key).toString()); 518 } 519 } 520 521 @Override getNewJson()522 protected JSONObject getNewJson() { 523 JSONObject json; 524 if (mRunMetrics != null) { 525 json = new JSONObject(mRunMetrics); 526 } else { 527 json = new JSONObject(); 528 } 529 try { 530 json.put(END_TIME, mEndTime); 531 } catch (JSONException e) { 532 CLog.e(e); 533 } 534 return json; 535 } 536 } 537 538 /** Helper for testLog information. */ 539 public static class TestLogEventInfo { 540 public String mDataName = null; 541 public LogDataType mLogType = null; 542 public File mDataFile = null; 543 TestLogEventInfo(String dataName, LogDataType dataType, File dataFile)544 public TestLogEventInfo(String dataName, LogDataType dataType, File dataFile) { 545 mDataName = dataName; 546 mLogType = dataType; 547 mDataFile = dataFile; 548 } 549 TestLogEventInfo(JSONObject jsonObject)550 public TestLogEventInfo(JSONObject jsonObject) throws JSONException { 551 mDataName = jsonObject.getString(DATA_NAME_KEY); 552 jsonObject.remove(DATA_NAME_KEY); 553 try { 554 mLogType = LogDataType.valueOf(jsonObject.getString(DATA_TYPE_KEY)); 555 } catch (IllegalArgumentException e) { 556 CLog.e("Failed to parse type: %s", jsonObject.getString(DATA_TYPE_KEY)); 557 mLogType = LogDataType.TEXT; 558 } 559 jsonObject.remove(DATA_TYPE_KEY); 560 mDataFile = new File(jsonObject.getString(DATA_FILE_KEY)); 561 } 562 563 @Override toString()564 public String toString() { 565 JSONObject tags = null; 566 try { 567 tags = new JSONObject(); 568 if (mDataName != null) { 569 tags.put(DATA_NAME_KEY, mDataName); 570 } 571 if (mLogType != null) { 572 tags.put(DATA_TYPE_KEY, mLogType.toString()); 573 } 574 if (mDataFile != null) { 575 tags.put(DATA_FILE_KEY, mDataFile.getAbsolutePath()); 576 } 577 } catch (JSONException e) { 578 CLog.e(e); 579 } 580 return tags.toString(); 581 } 582 } 583 584 /** Helper for logAssociation information. */ 585 public static class LogAssociationEventInfo { 586 public String mDataName = null; 587 public LogFile mLoggedFile = null; 588 LogAssociationEventInfo(String dataName, LogFile loggedFile)589 public LogAssociationEventInfo(String dataName, LogFile loggedFile) { 590 mDataName = dataName; 591 mLoggedFile = loggedFile; 592 } 593 LogAssociationEventInfo(JSONObject jsonObject)594 public LogAssociationEventInfo(JSONObject jsonObject) throws JSONException { 595 mDataName = jsonObject.getString(DATA_NAME_KEY); 596 jsonObject.remove(DATA_NAME_KEY); 597 String file = jsonObject.getString(LOGGED_FILE_KEY); 598 try { 599 mLoggedFile = (LogFile) SerializationUtil.deserialize(new File(file), true); 600 } catch (IOException e) { 601 throw new JSONException(e.getMessage()); 602 } finally { 603 FileUtil.deleteFile(new File(file)); 604 } 605 } 606 607 @Override toString()608 public String toString() { 609 JSONObject tags = null; 610 try { 611 tags = new JSONObject(); 612 if (mDataName != null) { 613 tags.put(DATA_NAME_KEY, mDataName); 614 } 615 if (mLoggedFile != null) { 616 File serializedLoggedFile = SerializationUtil.serialize(mLoggedFile); 617 tags.put(LOGGED_FILE_KEY, serializedLoggedFile.getAbsolutePath()); 618 } 619 } catch (JSONException | IOException e) { 620 CLog.e(e); 621 throw new RuntimeException(e); 622 } 623 return tags.toString(); 624 } 625 } 626 627 /** Helper for invocation started information. */ 628 public static class InvocationStartedEventInfo { 629 public String mTestTag = null; 630 public Long mStartTime = null; 631 InvocationStartedEventInfo(String testTag, Long startTime)632 public InvocationStartedEventInfo(String testTag, Long startTime) { 633 mTestTag = testTag; 634 mStartTime = startTime; 635 } 636 InvocationStartedEventInfo(JSONObject jsonObject)637 public InvocationStartedEventInfo(JSONObject jsonObject) throws JSONException { 638 mTestTag = jsonObject.getString(TEST_TAG_KEY); 639 if (jsonObject.has(START_TIME)) { 640 mStartTime = jsonObject.getLong(START_TIME); 641 } 642 } 643 644 @Override toString()645 public String toString() { 646 JSONObject tags = null; 647 try { 648 tags = new JSONObject(); 649 if (mTestTag != null) { 650 tags.put(TEST_TAG_KEY, mTestTag); 651 } 652 if (mStartTime != null) { 653 tags.put(START_TIME, mStartTime); 654 } 655 } catch (JSONException e) { 656 CLog.e(e); 657 } 658 return tags.toString(); 659 } 660 } 661 662 /** Helper for invocation ended information. */ 663 public static class InvocationEndedEventInfo { 664 public Map<String, String> mBuildAttributes; 665 InvocationEndedEventInfo(Map<String, String> buildAttributes)666 public InvocationEndedEventInfo(Map<String, String> buildAttributes) { 667 mBuildAttributes = new HashMap<String, String>(buildAttributes); 668 } 669 InvocationEndedEventInfo(JSONObject jsonObject)670 public InvocationEndedEventInfo(JSONObject jsonObject) throws JSONException { 671 mBuildAttributes = new HashMap<String, String>(); 672 Iterator<?> i = jsonObject.keys(); 673 while (i.hasNext()) { 674 String key = (String) i.next(); 675 mBuildAttributes.put(key, jsonObject.get(key).toString()); 676 } 677 } 678 679 @Override toString()680 public String toString() { 681 JSONObject jsonObject = new JSONObject(mBuildAttributes); 682 return jsonObject.toString(); 683 } 684 } 685 686 /** Helper for test module started information. */ 687 public static class TestModuleStartedEventInfo { 688 public IInvocationContext mModuleContext; 689 TestModuleStartedEventInfo(IInvocationContext moduleContext)690 public TestModuleStartedEventInfo(IInvocationContext moduleContext) { 691 mModuleContext = moduleContext; 692 } 693 TestModuleStartedEventInfo(JSONObject jsonObject)694 public TestModuleStartedEventInfo(JSONObject jsonObject) throws JSONException { 695 String file = jsonObject.getString(MODULE_CONTEXT_KEY); 696 try { 697 mModuleContext = 698 (IInvocationContext) SerializationUtil.deserialize(new File(file), true); 699 } catch (IOException e) { 700 throw new RuntimeException(e); 701 } 702 } 703 704 @Override toString()705 public String toString() { 706 JSONObject tags = null; 707 try { 708 tags = new JSONObject(); 709 File serializedContext = SerializationUtil.serialize(mModuleContext); 710 tags.put(MODULE_CONTEXT_KEY, serializedContext.getAbsolutePath()); 711 // For easier debugging on the events for modules, add the module name 712 String moduleName = 713 mModuleContext 714 .getAttributes() 715 .getUniqueMap() 716 .get(ModuleDefinition.MODULE_ID); 717 if (moduleName != null) { 718 tags.put(MODULE_NAME, moduleName); 719 } 720 } catch (IOException | JSONException e) { 721 CLog.e(e); 722 throw new RuntimeException(e); 723 } 724 return tags.toString(); 725 } 726 } 727 728 /** 729 * Updates failure with origin, failureStatus, actionInProgress, errorIdentifier from 730 * jsonObejct. 731 */ updateFailureFromJsonObject( FailureDescription failure, JSONObject jsonObject)732 private static void updateFailureFromJsonObject( 733 FailureDescription failure, JSONObject jsonObject) { 734 // Origin 735 failure.setOrigin(jsonObject.optString(ERROR_ORIGIN_KEY)); 736 // FailureStatus 737 FailureStatus status = FailureStatus.UNSET; 738 if (!Strings.isNullOrEmpty(jsonObject.optString(FAILURE_STATUS_KEY))) { 739 try { 740 status = FailureStatus.valueOf(jsonObject.optString(FAILURE_STATUS_KEY)); 741 } catch (NullPointerException | IllegalArgumentException e) { 742 CLog.e(e); 743 } 744 } 745 failure.setFailureStatus(status); 746 // ActionInProgress 747 ActionInProgress action = ActionInProgress.UNSET; 748 if (!Strings.isNullOrEmpty(jsonObject.optString(ACTION_IN_PROGRESS_KEY))) { 749 try { 750 action = ActionInProgress.valueOf(jsonObject.optString(ACTION_IN_PROGRESS_KEY)); 751 } catch (NullPointerException | IllegalArgumentException e) { 752 CLog.e(e); 753 } 754 } 755 failure.setActionInProgress(action); 756 // ErrorIdentifier 757 String errorName = jsonObject.optString(ERROR_NAME_KEY); 758 long errorCode = jsonObject.optLong(ERROR_CODE_KEY); 759 if (errorName != null) { 760 ErrorIdentifier errorId = 761 new ErrorIdentifier() { 762 @Override 763 public String name() { 764 return errorName; 765 } 766 767 @Override 768 public long code() { 769 return errorCode; 770 } 771 772 @Override 773 public @Nonnull FailureStatus status() { 774 FailureStatus status = failure.getFailureStatus(); 775 return (status == null ? FailureStatus.UNSET : status); 776 } 777 }; 778 failure.setErrorIdentifier(errorId); 779 } 780 } 781 } 782