Home | History | Annotate | Download | only in cts
      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