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.LogDataType; 21 import com.android.tradefed.result.LogFile; 22 import com.android.tradefed.testtype.suite.ModuleDefinition; 23 24 import org.json.JSONException; 25 import org.json.JSONObject; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.PrintWriter; 30 import java.io.StringWriter; 31 import java.util.HashMap; 32 import java.util.Iterator; 33 import java.util.Map; 34 35 /** 36 * Helper to serialize/deserialize the events to be passed to the log. 37 */ 38 public class SubprocessEventHelper { 39 private static final String CLASSNAME_KEY = "className"; 40 private static final String TESTNAME_KEY = "testName"; 41 private static final String TRACE_KEY = "trace"; 42 private static final String CAUSE_KEY = "cause"; 43 private static final String RUNNAME_KEY = "runName"; 44 private static final String TESTCOUNT_KEY = "testCount"; 45 private static final String ATTEMPT_KEY = "runAttempt"; 46 private static final String TIME_KEY = "time"; 47 private static final String REASON_KEY = "reason"; 48 private static final String START_TIME = "start_time"; 49 private static final String END_TIME = "end_time"; 50 51 private static final String DATA_NAME_KEY = "dataName"; 52 private static final String DATA_TYPE_KEY = "dataType"; 53 private static final String DATA_FILE_KEY = "dataFile"; 54 private static final String LOGGED_FILE_KEY = "loggedFile"; 55 56 private static final String TEST_TAG_KEY = "testTag"; 57 58 private static final String MODULE_CONTEXT_KEY = "moduleContextFileName"; 59 private static final String MODULE_NAME = "moduleName"; 60 61 /** 62 * Helper for testRunStarted information 63 */ 64 public static class TestRunStartedEventInfo { 65 public String mRunName = null; 66 public Integer mTestCount = null; 67 public Integer mAttempt = null; 68 69 /** Keep this constructor for legacy compatibility. */ TestRunStartedEventInfo(String runName, int testCount)70 public TestRunStartedEventInfo(String runName, int testCount) { 71 mRunName = runName; 72 mTestCount = testCount; 73 mAttempt = 0; 74 } 75 TestRunStartedEventInfo(String runName, int testCount, int attempt)76 public TestRunStartedEventInfo(String runName, int testCount, int attempt) { 77 mRunName = runName; 78 mTestCount = testCount; 79 mAttempt = attempt; 80 } 81 TestRunStartedEventInfo(JSONObject jsonObject)82 public TestRunStartedEventInfo(JSONObject jsonObject) throws JSONException { 83 mRunName = jsonObject.getString(RUNNAME_KEY); 84 mTestCount = jsonObject.getInt(TESTCOUNT_KEY); 85 mAttempt = jsonObject.optInt(ATTEMPT_KEY, 0); 86 } 87 88 @Override toString()89 public String toString() { 90 JSONObject tags = new JSONObject(); 91 try { 92 if (mRunName != null) { 93 tags.put(RUNNAME_KEY, mRunName); 94 } 95 if (mTestCount != null) { 96 tags.put(TESTCOUNT_KEY, mTestCount.intValue()); 97 } 98 if (mAttempt != null) { 99 tags.put(ATTEMPT_KEY, mAttempt.intValue()); 100 } 101 } catch (JSONException e) { 102 CLog.e(e); 103 } 104 return tags.toString(); 105 } 106 } 107 108 /** 109 * Helper for testRunFailed information 110 */ 111 public static class TestRunFailedEventInfo { 112 public String mReason = null; 113 TestRunFailedEventInfo(String reason)114 public TestRunFailedEventInfo(String reason) { 115 mReason = reason; 116 } 117 TestRunFailedEventInfo(JSONObject jsonObject)118 public TestRunFailedEventInfo(JSONObject jsonObject) throws JSONException { 119 mReason = jsonObject.getString(REASON_KEY); 120 } 121 122 @Override toString()123 public String toString() { 124 JSONObject tags = new JSONObject(); 125 try { 126 if (mReason != null) { 127 tags.put(REASON_KEY, mReason); 128 } 129 } catch (JSONException e) { 130 CLog.e(e); 131 } 132 return tags.toString(); 133 } 134 } 135 136 /** 137 * Helper for testRunEnded Information. 138 */ 139 public static class TestRunEndedEventInfo { 140 public Long mTime = null; 141 public Map<String, String> mRunMetrics = null; 142 TestRunEndedEventInfo(Long time, Map<String, String> runMetrics)143 public TestRunEndedEventInfo(Long time, Map<String, String> runMetrics) { 144 mTime = time; 145 mRunMetrics = runMetrics; 146 } 147 TestRunEndedEventInfo(JSONObject jsonObject)148 public TestRunEndedEventInfo(JSONObject jsonObject) throws JSONException { 149 mTime = jsonObject.getLong(TIME_KEY); 150 jsonObject.remove(TIME_KEY); 151 Iterator<?> i = jsonObject.keys(); 152 mRunMetrics = new HashMap<String, String>(); 153 while(i.hasNext()) { 154 String key = (String) i.next(); 155 mRunMetrics.put(key, jsonObject.get(key).toString()); 156 } 157 } 158 159 @Override toString()160 public String toString() { 161 JSONObject tags = null; 162 try { 163 if (mRunMetrics != null) { 164 tags = new JSONObject(mRunMetrics); 165 } else { 166 tags = new JSONObject(); 167 } 168 if (mTime != null) { 169 tags.put(TIME_KEY, mTime.longValue()); 170 } 171 } catch (JSONException e) { 172 CLog.e(e); 173 } 174 return tags.toString(); 175 } 176 } 177 178 /** 179 * Helper for InvocationFailed information. 180 */ 181 public static class InvocationFailedEventInfo { 182 public Throwable mCause = null; 183 InvocationFailedEventInfo(Throwable cause)184 public InvocationFailedEventInfo(Throwable cause) { 185 mCause = cause; 186 } 187 InvocationFailedEventInfo(JSONObject jsonObject)188 public InvocationFailedEventInfo(JSONObject jsonObject) throws JSONException { 189 String stack = jsonObject.getString("cause"); 190 mCause = new Throwable(stack); 191 } 192 193 @Override toString()194 public String toString() { 195 JSONObject tags = new JSONObject(); 196 try { 197 if (mCause != null) { 198 StringWriter sw = new StringWriter(); 199 PrintWriter pw = new PrintWriter(sw); 200 mCause.printStackTrace(pw); 201 tags.put(CAUSE_KEY, sw.toString()); 202 } 203 } catch (JSONException e) { 204 CLog.e(e); 205 } 206 return tags.toString(); 207 } 208 } 209 210 /** Base Helper for TestIgnored information. */ 211 public static class BaseTestEventInfo { 212 public String mClassName = null; 213 public String mTestName = null; 214 BaseTestEventInfo(String className, String testName)215 public BaseTestEventInfo(String className, String testName) { 216 mClassName = className; 217 mTestName = testName; 218 } 219 BaseTestEventInfo(JSONObject jsonObject)220 public BaseTestEventInfo(JSONObject jsonObject) throws JSONException { 221 mClassName = jsonObject.getString(CLASSNAME_KEY); 222 jsonObject.remove(CLASSNAME_KEY); 223 mTestName = jsonObject.getString(TESTNAME_KEY); 224 jsonObject.remove(TESTNAME_KEY); 225 } 226 getNewJson()227 protected JSONObject getNewJson() { 228 return new JSONObject(); 229 } 230 231 @Override toString()232 public String toString() { 233 JSONObject tags = null; 234 try { 235 tags = getNewJson(); 236 if (mClassName != null) { 237 tags.put(CLASSNAME_KEY, mClassName); 238 } 239 if (mTestName != null) { 240 tags.put(TESTNAME_KEY, mTestName); 241 } 242 } catch (JSONException e) { 243 CLog.e(e); 244 } 245 return tags.toString(); 246 } 247 } 248 249 /** Helper for testStarted information */ 250 public static class TestStartedEventInfo extends BaseTestEventInfo { 251 public Long mStartTime = null; 252 TestStartedEventInfo(String className, String testName, Long startTime)253 public TestStartedEventInfo(String className, String testName, Long startTime) { 254 super(className, testName); 255 mStartTime = startTime; 256 } 257 TestStartedEventInfo(JSONObject jsonObject)258 public TestStartedEventInfo(JSONObject jsonObject) throws JSONException { 259 super(jsonObject); 260 if (jsonObject.has(START_TIME)) { 261 mStartTime = jsonObject.getLong(START_TIME); 262 } 263 jsonObject.remove(START_TIME); 264 } 265 266 @Override getNewJson()267 protected JSONObject getNewJson() { 268 JSONObject json = new JSONObject(); 269 try { 270 json.put(START_TIME, mStartTime); 271 } catch (JSONException e) { 272 CLog.e(e); 273 } 274 return json; 275 } 276 } 277 278 /** Helper for testFailed information. */ 279 public static class FailedTestEventInfo extends BaseTestEventInfo { 280 public String mTrace = null; 281 FailedTestEventInfo(String className, String testName, String trace)282 public FailedTestEventInfo(String className, String testName, String trace) { 283 super(className, testName); 284 mTrace = trace; 285 } 286 FailedTestEventInfo(JSONObject jsonObject)287 public FailedTestEventInfo(JSONObject jsonObject) throws JSONException { 288 super(jsonObject); 289 mTrace = jsonObject.getString(TRACE_KEY); 290 } 291 292 @Override toString()293 public String toString() { 294 JSONObject tags = null; 295 try { 296 tags = new JSONObject(super.toString()); 297 if (mTrace != null) { 298 tags.put(TRACE_KEY, mTrace); 299 } 300 } catch (JSONException e) { 301 CLog.e(e); 302 } 303 return tags.toString(); 304 } 305 } 306 307 /** 308 * Helper for testEnded information. 309 */ 310 public static class TestEndedEventInfo extends BaseTestEventInfo { 311 public Map<String, String> mRunMetrics = null; 312 public Long mEndTime = null; 313 TestEndedEventInfo(String className, String testName, Map<String, String> runMetrics)314 public TestEndedEventInfo(String className, String testName, 315 Map<String, String> runMetrics) { 316 super(className, testName); 317 mRunMetrics = runMetrics; 318 mEndTime = System.currentTimeMillis(); 319 } 320 321 /** 322 * Create an event object to represent the testEnded callback. 323 * 324 * @param className the classname of the tests 325 * @param testName the name of the tests 326 * @param endTime the timestamp at which the test ended (from {@link 327 * System#currentTimeMillis()}) 328 * @param runMetrics the metrics reported by the test. 329 */ TestEndedEventInfo( String className, String testName, Long endTime, Map<String, String> runMetrics)330 public TestEndedEventInfo( 331 String className, String testName, Long endTime, Map<String, String> runMetrics) { 332 super(className, testName); 333 mEndTime = endTime; 334 mRunMetrics = runMetrics; 335 } 336 337 /** Create and populate and event object for testEnded from a JSON. */ TestEndedEventInfo(JSONObject jsonObject)338 public TestEndedEventInfo(JSONObject jsonObject) throws JSONException { 339 super(jsonObject); 340 if (jsonObject.has(END_TIME)) { 341 mEndTime = jsonObject.getLong(END_TIME); 342 } 343 jsonObject.remove(END_TIME); 344 Iterator<?> i = jsonObject.keys(); 345 mRunMetrics = new HashMap<String, String>(); 346 while(i.hasNext()) { 347 String key = (String) i.next(); 348 mRunMetrics.put(key, jsonObject.get(key).toString()); 349 } 350 } 351 352 @Override getNewJson()353 protected JSONObject getNewJson() { 354 JSONObject json; 355 if (mRunMetrics != null) { 356 json = new JSONObject(mRunMetrics); 357 } else { 358 json = new JSONObject(); 359 } 360 try { 361 json.put(END_TIME, mEndTime); 362 } catch (JSONException e) { 363 CLog.e(e); 364 } 365 return json; 366 } 367 } 368 369 /** Helper for testLog information. */ 370 public static class TestLogEventInfo { 371 public String mDataName = null; 372 public LogDataType mLogType = null; 373 public File mDataFile = null; 374 TestLogEventInfo(String dataName, LogDataType dataType, File dataFile)375 public TestLogEventInfo(String dataName, LogDataType dataType, File dataFile) { 376 mDataName = dataName; 377 mLogType = dataType; 378 mDataFile = dataFile; 379 } 380 TestLogEventInfo(JSONObject jsonObject)381 public TestLogEventInfo(JSONObject jsonObject) throws JSONException { 382 mDataName = jsonObject.getString(DATA_NAME_KEY); 383 jsonObject.remove(DATA_NAME_KEY); 384 mLogType = LogDataType.valueOf(jsonObject.getString(DATA_TYPE_KEY)); 385 jsonObject.remove(DATA_TYPE_KEY); 386 mDataFile = new File(jsonObject.getString(DATA_FILE_KEY)); 387 } 388 389 @Override toString()390 public String toString() { 391 JSONObject tags = null; 392 try { 393 tags = new JSONObject(); 394 if (mDataName != null) { 395 tags.put(DATA_NAME_KEY, mDataName); 396 } 397 if (mLogType != null) { 398 tags.put(DATA_TYPE_KEY, mLogType.toString()); 399 } 400 if (mDataFile != null) { 401 tags.put(DATA_FILE_KEY, mDataFile.getAbsolutePath()); 402 } 403 } catch (JSONException e) { 404 CLog.e(e); 405 } 406 return tags.toString(); 407 } 408 } 409 410 /** Helper for logAssociation information. */ 411 public static class LogAssociationEventInfo { 412 public String mDataName = null; 413 public LogFile mLoggedFile = null; 414 LogAssociationEventInfo(String dataName, LogFile loggedFile)415 public LogAssociationEventInfo(String dataName, LogFile loggedFile) { 416 mDataName = dataName; 417 mLoggedFile = loggedFile; 418 } 419 LogAssociationEventInfo(JSONObject jsonObject)420 public LogAssociationEventInfo(JSONObject jsonObject) throws JSONException { 421 mDataName = jsonObject.getString(DATA_NAME_KEY); 422 jsonObject.remove(DATA_NAME_KEY); 423 String file = jsonObject.getString(LOGGED_FILE_KEY); 424 try { 425 mLoggedFile = (LogFile) SerializationUtil.deserialize(new File(file), true); 426 } catch (IOException e) { 427 throw new JSONException(e.getMessage()); 428 } finally { 429 FileUtil.deleteFile(new File(file)); 430 } 431 } 432 433 @Override toString()434 public String toString() { 435 JSONObject tags = null; 436 try { 437 tags = new JSONObject(); 438 if (mDataName != null) { 439 tags.put(DATA_NAME_KEY, mDataName); 440 } 441 if (mLoggedFile != null) { 442 File serializedLoggedFile = SerializationUtil.serialize(mLoggedFile); 443 tags.put(LOGGED_FILE_KEY, serializedLoggedFile.getAbsolutePath()); 444 } 445 } catch (JSONException | IOException e) { 446 CLog.e(e); 447 throw new RuntimeException(e); 448 } 449 return tags.toString(); 450 } 451 } 452 453 /** Helper for invocation started information. */ 454 public static class InvocationStartedEventInfo { 455 public String mTestTag = null; 456 public Long mStartTime = null; 457 InvocationStartedEventInfo(String testTag, Long startTime)458 public InvocationStartedEventInfo(String testTag, Long startTime) { 459 mTestTag = testTag; 460 mStartTime = startTime; 461 } 462 InvocationStartedEventInfo(JSONObject jsonObject)463 public InvocationStartedEventInfo(JSONObject jsonObject) throws JSONException { 464 mTestTag = jsonObject.getString(TEST_TAG_KEY); 465 if (jsonObject.has(START_TIME)) { 466 mStartTime = jsonObject.getLong(START_TIME); 467 } 468 } 469 470 @Override toString()471 public String toString() { 472 JSONObject tags = null; 473 try { 474 tags = new JSONObject(); 475 if (mTestTag != null) { 476 tags.put(TEST_TAG_KEY, mTestTag); 477 } 478 if (mStartTime != null) { 479 tags.put(START_TIME, mStartTime); 480 } 481 } catch (JSONException e) { 482 CLog.e(e); 483 } 484 return tags.toString(); 485 } 486 } 487 488 /** Helper for invocation ended information. */ 489 public static class InvocationEndedEventInfo { 490 public Map<String, String> mBuildAttributes; 491 InvocationEndedEventInfo(Map<String, String> buildAttributes)492 public InvocationEndedEventInfo(Map<String, String> buildAttributes) { 493 mBuildAttributes = new HashMap<String, String>(buildAttributes); 494 } 495 InvocationEndedEventInfo(JSONObject jsonObject)496 public InvocationEndedEventInfo(JSONObject jsonObject) throws JSONException { 497 mBuildAttributes = new HashMap<String, String>(); 498 Iterator<?> i = jsonObject.keys(); 499 while (i.hasNext()) { 500 String key = (String) i.next(); 501 mBuildAttributes.put(key, jsonObject.get(key).toString()); 502 } 503 } 504 505 @Override toString()506 public String toString() { 507 JSONObject jsonObject = new JSONObject(mBuildAttributes); 508 return jsonObject.toString(); 509 } 510 } 511 512 /** Helper for test module started information. */ 513 public static class TestModuleStartedEventInfo { 514 public IInvocationContext mModuleContext; 515 TestModuleStartedEventInfo(IInvocationContext moduleContext)516 public TestModuleStartedEventInfo(IInvocationContext moduleContext) { 517 mModuleContext = moduleContext; 518 } 519 TestModuleStartedEventInfo(JSONObject jsonObject)520 public TestModuleStartedEventInfo(JSONObject jsonObject) throws JSONException { 521 String file = jsonObject.getString(MODULE_CONTEXT_KEY); 522 try { 523 mModuleContext = 524 (IInvocationContext) SerializationUtil.deserialize(new File(file), true); 525 } catch (IOException e) { 526 throw new RuntimeException(e); 527 } 528 } 529 530 @Override toString()531 public String toString() { 532 JSONObject tags = null; 533 try { 534 tags = new JSONObject(); 535 File serializedContext = SerializationUtil.serialize(mModuleContext); 536 tags.put(MODULE_CONTEXT_KEY, serializedContext.getAbsolutePath()); 537 // For easier debugging on the events for modules, add the module name 538 String moduleName = 539 mModuleContext 540 .getAttributes() 541 .getUniqueMap() 542 .get(ModuleDefinition.MODULE_NAME); 543 if (moduleName != null) { 544 tags.put(MODULE_NAME, moduleName); 545 } 546 } catch (IOException | JSONException e) { 547 CLog.e(e); 548 throw new RuntimeException(e); 549 } 550 return tags.toString(); 551 } 552 } 553 } 554