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 java.io.IOException; 20 import java.util.Collection; 21 22 /** 23 * Represents a runtime session for a test plan, takes charge in running a 24 * plan and the setup&tear-downs. 25 */ 26 public class TestSession { 27 private SessionObserver mSessionObserver; 28 private TestSessionLog mSessionLog; 29 private TestDevice mDevice; 30 31 private int mId; 32 private STATUS mStatus; 33 34 private static int sIdCounter = 0; 35 36 enum STATUS { 37 INIT, STARTED, INSTALLING, RUNNING, PAUSED, RESUMED, STOPPED, FINISHED 38 } 39 40 private int mRequiredDeviceNumber; 41 private boolean mTestStop; 42 private TestSessionThread mTestThread; 43 private boolean mNeedRestartAdbServer; 44 private static boolean mADBServerRestartedMode; 45 46 /** Running count of tests executed since last reboot. */ 47 private static long mTestCount; 48 49 public TestSession(final TestSessionLog sessionLog, 50 final int requiredDeviceNum) { 51 mStatus = STATUS.INIT; 52 53 mNeedRestartAdbServer = false; 54 mADBServerRestartedMode = false; 55 mTestCount = 0; 56 mSessionLog = sessionLog; 57 mDevice = null; 58 mRequiredDeviceNumber = requiredDeviceNum; 59 mTestStop = false; 60 mId = sIdCounter++; 61 } 62 63 /** 64 * Get the last session ID. 65 * 66 * @return The last session ID. 67 */ 68 public static int getLastSessionId() { 69 return sIdCounter-1; 70 } 71 72 /** 73 * Set ADB server restarted mode. 74 */ 75 public static void setADBServerRestartedMode() { 76 mADBServerRestartedMode = true; 77 } 78 79 /** 80 * Reset ADB server restarted mode. 81 */ 82 public static void resetADBServerRestartedMode() { 83 mADBServerRestartedMode = false; 84 } 85 86 /** 87 * Check if it's in ADB server restarted mode. 88 * 89 * @return If in ADB server restarted mode, return true; else, return false. 90 */ 91 public static boolean isADBServerRestartedMode() { 92 return mADBServerRestartedMode; 93 } 94 95 /** 96 * Increase the test count. 97 */ 98 public static void incTestCount() { 99 mTestCount++; 100 } 101 102 /** 103 * Reset the test count. 104 */ 105 public static void resetTestCount() { 106 mTestCount = 0; 107 } 108 109 /** 110 * Get the test count recently has been run. 111 * 112 * @return The test count recently has been run. 113 */ 114 public static long getTestCount() { 115 return mTestCount; 116 } 117 118 /** 119 * Check if the test count exceeds the max test count. If the max test count is disabled 120 * (HostConfig.getMaxTestCount() <= 0), this method always returns false. 121 * 122 * @return true, if the max count is enabled and exceeded. 123 */ 124 public static boolean exceedsMaxCount() { 125 final long maxTestCount = HostConfig.getMaxTestCount(); 126 return (maxTestCount > 0) && (mTestCount >= maxTestCount); 127 } 128 129 /** 130 * Get status. 131 * 132 * @return The status. 133 */ 134 public STATUS getStatus() { 135 return mStatus; 136 } 137 138 /** 139 * Get device ID. 140 * 141 * @return device ID. 142 */ 143 public String getDeviceId() { 144 if (mDevice == null) { 145 return null; 146 } 147 return mDevice.getSerialNumber(); 148 } 149 150 /** 151 * Get the test device. 152 * 153 * @return the test device. 154 */ 155 public TestDevice getDevice() { 156 return mDevice; 157 } 158 159 /** 160 * Get the number of required devices. 161 * 162 * @return The number of required devices. 163 */ 164 public int getNumOfRequiredDevices() { 165 return mRequiredDeviceNumber; 166 } 167 168 /** 169 * Get ID. 170 * 171 * @return ID. 172 */ 173 public int getId() { 174 return mId; 175 } 176 177 /** 178 * Start the single test with full name. 179 * 180 * @param testFullName The test full name. 181 */ 182 public void start(final String testFullName) throws TestNotFoundException, 183 IllegalTestNameException, ADBServerNeedRestartException { 184 185 if ((testFullName == null) || (testFullName.length() == 0)) { 186 throw new IllegalArgumentException(); 187 } 188 189 // The test full name follows the following rule: 190 // java_package_name.class_name#method_name. 191 // Other forms will be treated as illegal. 192 if (!testFullName.matches("(\\w+.)+\\w+")) { 193 throw new IllegalTestNameException(testFullName); 194 } 195 196 Test test = null; 197 TestPackage pkg = null; 198 if (-1 != testFullName.indexOf(Test.METHOD_SEPARATOR)) { 199 test = searchTest(testFullName); 200 if (test == null) { 201 throw new TestNotFoundException( 202 "The specific test does not exist: " + testFullName); 203 } 204 205 mTestThread = new TestSessionThread(this, test); 206 CUIOutputStream.println("start test " + testFullName); 207 } else { 208 pkg = searchTestPackage(testFullName); 209 if (pkg == null) { 210 throw new TestNotFoundException( 211 "The specific test package does not exist: " + testFullName); 212 } 213 214 mTestThread = new TestSessionThread(this, pkg, testFullName); 215 CUIOutputStream.println("start java package " + testFullName); 216 } 217 218 mStatus = STATUS.STARTED; 219 startImpl(); 220 } 221 222 /** 223 * Implement starting/resuming session. 224 */ 225 private void startImpl() throws ADBServerNeedRestartException { 226 String resultPath = mSessionLog.getResultPath(); 227 if ((resultPath == null) || (resultPath.length() == 0)) { 228 mSessionLog.setStartTime(System.currentTimeMillis()); 229 } 230 resetTestCount(); 231 mTestThread.start(); 232 try { 233 mTestThread.join(); 234 } catch (InterruptedException e) { 235 e.printStackTrace(); 236 } 237 if (mNeedRestartAdbServer && HostConfig.getMaxTestCount() > 0) { 238 throw new ADBServerNeedRestartException("Need restart ADB server"); 239 } 240 } 241 242 /** 243 * Resume the test session. 244 */ 245 public void resume() throws ADBServerNeedRestartException { 246 mStatus = STATUS.RESUMED; 247 mTestThread = new TestSessionThread(this); 248 if (!isADBServerRestartedMode()) { 249 CUIOutputStream.println("resume test plan " + getSessionLog().getTestPlanName() 250 + " (session id = " + mId + ")"); 251 } 252 startImpl(); 253 } 254 255 /** 256 * Search the test with the test full name given among the test 257 * packages contained within this session. 258 * 259 * @param testFullName The full name of the test. 260 * @return The test with the full name given. 261 */ 262 private Test searchTest(final String testFullName) { 263 Test test = null; 264 for (TestPackage pkg : mSessionLog.getTestPackages()) { 265 test = pkg.searchTest(testFullName); 266 if (test != null) { 267 break; 268 } 269 } 270 271 return test; 272 } 273 274 /** 275 * Search the test package with the specified java package name. 276 * 277 * @param javaPkgName The java package name. 278 * @return The test package with the specified java package name. 279 */ 280 private TestPackage searchTestPackage(String javaPkgName) { 281 for (TestPackage pkg : mSessionLog.getTestPackages()) { 282 Collection<Test> tests = pkg.getTests(); 283 for (Test test : tests) { 284 String testFullName = test.getFullName(); 285 if (testFullName.startsWith(javaPkgName)) { 286 //adjust the java package name to make it equal to some java package name 287 if (testFullName.charAt(javaPkgName.length()) != '.') { 288 javaPkgName = javaPkgName.substring(0, javaPkgName.lastIndexOf(".")); 289 } 290 return pkg; 291 } 292 } 293 } 294 295 return null; 296 } 297 /** 298 * Start a new test session thread to execute the specific test plan. 299 */ 300 public void start() throws ADBServerNeedRestartException { 301 mStatus = STATUS.STARTED; 302 mSessionLog.setStartTime(System.currentTimeMillis()); 303 mTestThread = new TestSessionThread(this); 304 305 CUIOutputStream.println("start test plan " + getSessionLog().getTestPlanName()); 306 startImpl(); 307 } 308 309 /** 310 * Set observer. 311 * 312 * @param so Session observer. 313 */ 314 public void setObserver(final SessionObserver so) { 315 mSessionObserver = so; 316 } 317 318 /** 319 * Print the message by appending the new line mark. 320 * 321 * @param msg The message to be print. 322 */ 323 private void println(final String msg) { 324 if (!mTestStop) { 325 CUIOutputStream.println(msg); 326 } 327 } 328 329 /** 330 * Set the {@link TestDevice} which will run the test. 331 * 332 * @param device The {@link TestDevice} will run the test. 333 */ 334 public void setTestDevice(final TestDevice device) { 335 mDevice = device; 336 } 337 338 /** 339 * Get the session log of this session. 340 * 341 * @return The session log of this session. 342 */ 343 public TestSessionLog getSessionLog() { 344 return mSessionLog; 345 } 346 347 /** 348 * Get the test packages contained within this session. 349 * 350 * @return The test packages contained within this session. 351 */ 352 public Collection<TestPackage> getTestPackages() { 353 return mSessionLog.getTestPackages(); 354 } 355 356 /** 357 * The Thread to be run the {@link TestSession} 358 */ 359 class TestSessionThread extends Thread { 360 private final int MSEC_PER_SECOND = 1000; 361 362 private TestSession mTestSession; 363 private Test mTest; 364 private TestPackage mTestPackage; 365 private String mJavaPackageName; 366 private ResultObserver mResultObserver; 367 368 public TestSessionThread(final TestSession ts) { 369 mTestSession = ts; 370 mResultObserver = ResultObserver.getInstance(); 371 } 372 373 public TestSessionThread(final TestSession ts, final Test test) { 374 mTestSession = ts; 375 mResultObserver = ResultObserver.getInstance(); 376 mTest = test; 377 } 378 379 public TestSessionThread(final TestSession ts, 380 final TestPackage pkg, final String javaPkgName) { 381 mTestSession = ts; 382 mResultObserver = ResultObserver.getInstance(); 383 mTestPackage = pkg; 384 mJavaPackageName = javaPkgName; 385 } 386 387 /** {@inheritDoc} */ 388 @Override 389 public void run() { 390 Log.d("Start a test session."); 391 mNeedRestartAdbServer = false; 392 mResultObserver.setTestSessionLog(getSessionLog()); 393 mResultObserver.start(); 394 395 try { 396 if (mTest != null) { 397 TestPackage pkg = mTest.getTestPackage(); 398 pkg.setSessionThread(this); 399 pkg.runTest(mDevice, mTest); 400 } else if (mTestPackage != null) { 401 mTestPackage.setSessionThread(this); 402 mTestPackage.run(mDevice, mJavaPackageName, mSessionLog); 403 } else { 404 for (TestPackage pkg : mSessionLog.getTestPackages()) { 405 if (!pkg.isAllTestsRun()) { 406 pkg.setSessionThread(this); 407 pkg.run(mDevice, null, mSessionLog); 408 if (!isAllTestsRun()) { 409 if (HostConfig.getMaxTestCount() > 0) { 410 // ADB server restart enabled 411 markNeedRestartADBServer(); 412 return; 413 } 414 } else { 415 Log.d("All tests have been run."); 416 break; 417 } 418 } 419 } 420 mNeedRestartAdbServer = false; 421 displayTestResultSummary(); 422 } 423 } catch (IOException e) { 424 Log.e("Got exception when running the package", e); 425 } catch (DeviceDisconnectedException e) { 426 Log.e("Device " + e.getMessage() + " disconnected ", null); 427 } catch (ADBServerNeedRestartException e) { 428 Log.d(e.getMessage()); 429 if (mTest == null) { 430 markNeedRestartADBServer(); 431 return; 432 } 433 } catch (InvalidApkPathException e) { 434 Log.e(e.getMessage(), null); 435 } catch (InvalidNameSpaceException e) { 436 Log.e(e.getMessage(), null); 437 } 438 439 long startTime = getSessionLog().getStartTime().getTime(); 440 displayTimeInfo(startTime, System.currentTimeMillis()); 441 442 mStatus = STATUS.FINISHED; 443 mTestSession.getSessionLog().setEndTime(System.currentTimeMillis()); 444 mSessionObserver.notifyFinished(mTestSession); 445 notifyResultObserver(); 446 } 447 448 /** 449 * Mark need restarting ADB server. 450 */ 451 private void markNeedRestartADBServer() { 452 Log.d("mark mNeedRestartAdbServer to true"); 453 mNeedRestartAdbServer = true; 454 mStatus = STATUS.FINISHED; 455 notifyResultObserver(); 456 return; 457 } 458 459 /** 460 * Notify result observer. 461 */ 462 private void notifyResultObserver() { 463 mResultObserver.notifyUpdate(); 464 mResultObserver.finish(); 465 } 466 467 /** 468 * Check if all tests contained in all of the test packages has been run. 469 * 470 * @return If all tests have been run, return true; else, return false. 471 */ 472 private boolean isAllTestsRun() { 473 Collection<TestPackage> pkgs = getTestPackages(); 474 for (TestPackage pkg : pkgs) { 475 if (!pkg.isAllTestsRun()) { 476 return false; 477 } 478 } 479 return true; 480 } 481 482 /** 483 * Display the summary of test result. 484 */ 485 private void displayTestResultSummary() { 486 int passNum = mSessionLog.getTestList(CtsTestResult.CODE_PASS).size(); 487 int failNum = mSessionLog.getTestList(CtsTestResult.CODE_FAIL).size(); 488 int notExecutedNum = 489 mSessionLog.getTestList(CtsTestResult.CODE_NOT_EXECUTED).size(); 490 int timeOutNum = mSessionLog.getTestList(CtsTestResult.CODE_TIMEOUT).size(); 491 int total = passNum + failNum + notExecutedNum + timeOutNum; 492 493 println("Test summary: pass=" + passNum 494 + " fail=" + failNum 495 + " timeOut=" + timeOutNum 496 + " notExecuted=" + notExecutedNum 497 + " Total=" + total); 498 } 499 500 /** 501 * Display the time information of running a test plan. 502 * 503 * @param startTime start time in milliseconds. 504 * @param endTime end time in milliseconds. 505 */ 506 private void displayTimeInfo(final long startTime, final long endTime) { 507 long diff = endTime - startTime; 508 long seconds = diff / MSEC_PER_SECOND; 509 long millisec = diff % MSEC_PER_SECOND; 510 println("Time: " + seconds + "." + millisec + "s\n"); 511 } 512 } 513 514 /** 515 * Update test result after executing each test. 516 * During running test, the process may be interrupted. To avoid 517 * test result losing, it's needed to update the test result into 518 * xml file after executing each test, which is done by this observer. 519 * The possible reasons causing interruption to the process include: 520 * <ul> 521 * <li> Device disconnected 522 * <li> Run time exception 523 * <li> System crash 524 * <li> User action to cause the system exit 525 * </ul> 526 * 527 */ 528 static class ResultObserver { 529 static private boolean mFinished = false; 530 static private boolean mNotified = false; //used for avoiding race condition 531 static private boolean mNeedUpdate = true; 532 static private TestSessionLog mSessionLog; 533 static final ResultObserver sInstance = new ResultObserver(); 534 535 private Observer mObserver; 536 /** 537 * Get the static instance. 538 * 539 * @return The static instance. 540 */ 541 public static final ResultObserver getInstance() { 542 return sInstance; 543 } 544 545 /** 546 * Set TestSessionLog. 547 * 548 * @param log The TestSessionLog. 549 */ 550 public void setTestSessionLog(TestSessionLog log) { 551 mSessionLog = log; 552 } 553 554 /** 555 * Notify this updating thread to update the test result to xml file. 556 */ 557 public void notifyUpdate() { 558 if (mObserver != null) { 559 synchronized (mObserver) { 560 mNotified = true; 561 mObserver.notify(); 562 } 563 } 564 } 565 566 /** 567 * Start the observer. 568 */ 569 public void start() { 570 mFinished = false; 571 mNeedUpdate = true; 572 mObserver = new Observer(); 573 mObserver.start(); 574 } 575 576 /** 577 * Finish updating. 578 */ 579 public void finish() { 580 mFinished = true; 581 mNeedUpdate = false; 582 notifyUpdate(); 583 try { 584 mObserver.join(); 585 mObserver = null; 586 } catch (InterruptedException e) { 587 } 588 } 589 590 /** 591 * Observer which updates the test result to result XML file. 592 * 593 */ 594 class Observer extends Thread { 595 596 /** {@inheritDoc} */ 597 @Override 598 public void run() { 599 while (!mFinished) { 600 try { 601 synchronized (this) { 602 if ((!mNotified) && (!mFinished)) { 603 wait(); 604 } 605 606 mNotified = false; 607 } 608 609 if (mNeedUpdate && (mSessionLog != null)) { 610 mSessionLog.sessionComplete(); 611 } 612 } catch (InterruptedException e) { 613 } 614 } 615 } 616 } 617 } 618 } 619