Home | History | Annotate | Download | only in testtype
      1 /*
      2  * Copyright (C) 2010 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.cts.tradefed.testtype;
     17 
     18 import com.android.cts.tradefed.build.CtsBuildHelper;
     19 import com.android.ddmlib.Log;
     20 import com.android.ddmlib.testrunner.TestIdentifier;
     21 import com.android.tradefed.build.IBuildInfo;
     22 import com.android.tradefed.config.ConfigurationException;
     23 import com.android.tradefed.device.DeviceNotAvailableException;
     24 import com.android.tradefed.device.ITestDevice;
     25 import com.android.tradefed.result.ITestInvocationListener;
     26 import com.android.tradefed.testtype.DeviceTestResult.RuntimeDeviceNotAvailableException;
     27 import com.android.tradefed.testtype.IAbi;
     28 import com.android.tradefed.testtype.IAbiReceiver;
     29 import com.android.tradefed.testtype.IBuildReceiver;
     30 import com.android.tradefed.testtype.IDeviceTest;
     31 import com.android.tradefed.testtype.IRemoteTest;
     32 import com.android.tradefed.testtype.JUnitRunUtil;
     33 import com.android.tradefed.util.CommandStatus;
     34 import com.android.tradefed.util.IRunUtil.IRunnableResult;
     35 import com.android.tradefed.util.RunUtil;
     36 
     37 import junit.framework.Test;
     38 import junit.framework.TestCase;
     39 import junit.framework.TestResult;
     40 
     41 import java.io.File;
     42 import java.io.FileNotFoundException;
     43 import java.io.IOException;
     44 import java.net.MalformedURLException;
     45 import java.net.URL;
     46 import java.net.URLClassLoader;
     47 import java.util.Collection;
     48 
     49 /**
     50  * A {@link IRemoteTest} that can run a set of JUnit tests from a CTS jar.
     51  */
     52 public class JarHostTest implements IDeviceTest, IRemoteTest, IBuildReceiver, Test {
     53 
     54     private static final String LOG_TAG = "JarHostTest";
     55 
     56     private ITestDevice mDevice;
     57     private String mJarFileName;
     58     private Collection<TestIdentifier> mTests;
     59     private long mTimeoutMs = 10 * 60 * 1000;
     60     private String mRunName;
     61     private CtsBuildHelper mCtsBuild = null;
     62     private IBuildInfo mBuildInfo = null;
     63     private IAbi mAbi;
     64     private ClassLoader mClassLoader;
     65 
     66     /**
     67      * @param abi the ABI to run the test on
     68      */
     69     public void setAbi(IAbi abi) {
     70         mAbi = abi;
     71     }
     72 
     73     /**
     74      * {@inheritDoc}
     75      */
     76     @Override
     77     public void setBuild(IBuildInfo buildInfo) {
     78         mBuildInfo = buildInfo;
     79         mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
     80     }
     81 
     82     /**
     83      * Set the CTS build container.
     84      * <p/>
     85      * Exposed so unit tests can mock the provided build.
     86      *
     87      * @param buildHelper
     88      */
     89     void setBuildHelper(CtsBuildHelper buildHelper) {
     90         mCtsBuild = buildHelper;
     91     }
     92 
     93     /**
     94      * Get the CTS build container.
     95      *
     96      * @return {@link CtsBuildHelper}
     97      */
     98     CtsBuildHelper getBuildHelper() {
     99         return mCtsBuild;
    100     }
    101 
    102     /**
    103      * Set the jar file to load tests from.
    104      *
    105      * @param jarFileName the file name of the CTS host test jar to use
    106      */
    107     void setJarFileName(String jarFileName) {
    108         mJarFileName = jarFileName;
    109     }
    110 
    111     /**
    112      * Gets the jar file to load tests from.
    113      *
    114      * @return jarFileName the file name of the CTS host test jar to use
    115      */
    116     String getJarFileName() {
    117         return mJarFileName;
    118     }
    119 
    120     /**
    121      * Sets the collection of tests to run
    122      *
    123      * @param tests
    124      */
    125     void setTests(Collection<TestIdentifier> tests) {
    126         mTests = tests;
    127     }
    128 
    129     /**
    130      * Gets the collection of tests to run
    131      *
    132      * @return Collection<{@link TestIdentifier}>
    133      */
    134     Collection<TestIdentifier> getTests() {
    135         return mTests;
    136     }
    137 
    138     /**
    139      * Set the maximum time in ms each test should run.
    140      * <p/>
    141      * Tests that take longer than this amount will be failed with a {@link TestTimeoutException}
    142      * as the cause.
    143      *
    144      * @param testTimeoutMs
    145      */
    146     void setTimeout(long testTimeoutMs) {
    147         mTimeoutMs = testTimeoutMs;
    148     }
    149 
    150     /**
    151      * Set the run name to report to {@link ITestInvocationListener#testRunStarted(String, int)}
    152      *
    153      * @param runName
    154      */
    155     void setRunName(String runName) {
    156         mRunName = runName;
    157     }
    158 
    159     /**
    160      * {@inheritDoc}
    161      */
    162     @Override
    163     public ITestDevice getDevice() {
    164         return mDevice;
    165     }
    166 
    167     /**
    168      * {@inheritDoc}
    169      */
    170     @Override
    171     public void setDevice(ITestDevice device) {
    172         mDevice = device;
    173     }
    174 
    175     /**
    176      * {@inheritDoc}
    177      */
    178     @SuppressWarnings("unchecked")
    179     @Override
    180     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    181         checkFields();
    182         Log.i(LOG_TAG, String.format("Running %s test package from jar, contains %d tests.",
    183                 mRunName, mTests.size()));
    184         JUnitRunUtil.runTest(listener, this, mRunName);
    185     }
    186 
    187     /**
    188      * {@inheritDoc}
    189      */
    190     @Override
    191     public void run(TestResult junitResult) {
    192         for (TestIdentifier testId : mTests) {
    193             Test junitTest = loadTest(testId.getClassName(), testId.getTestName());
    194             if (junitTest != null) {
    195                 runTest(testId, junitTest, junitResult);
    196             }
    197         }
    198     }
    199 
    200     /**
    201      * Run test with timeout support.
    202      */
    203     private void runTest(TestIdentifier testId, final Test junitTest, final TestResult junitResult) {
    204         if (junitTest instanceof IDeviceTest) {
    205             ((IDeviceTest)junitTest).setDevice(getDevice());
    206         } else if (junitTest instanceof com.android.hosttest.DeviceTest) {
    207             // legacy check - see if test uses hosttestlib. This check should go away once
    208             // all host tests are converted to use tradefed
    209             com.android.hosttest.DeviceTest deviceTest = (com.android.hosttest.DeviceTest)junitTest;
    210             deviceTest.setDevice(getDevice().getIDevice());
    211             deviceTest.setTestAppPath(mCtsBuild.getTestCasesDir().getAbsolutePath());
    212         }
    213         if (junitTest instanceof IAbiReceiver) {
    214             ((IAbiReceiver)junitTest).setAbi(mAbi);
    215         }
    216         if (junitTest instanceof IBuildReceiver) {
    217             ((IBuildReceiver)junitTest).setBuild(mBuildInfo);
    218         }
    219         TestRunnable testRunnable = new TestRunnable(junitTest, junitResult);
    220 
    221         CommandStatus status = RunUtil.getDefault().runTimed(mTimeoutMs, testRunnable, true);
    222         if (status.equals(CommandStatus.TIMED_OUT)) {
    223             junitResult.addError(junitTest, new TestTimeoutException());
    224             junitResult.endTest(junitTest);
    225         }
    226         if (testRunnable.getException() != null) {
    227             throw testRunnable.getException();
    228         }
    229     }
    230 
    231     private static class TestRunnable implements IRunnableResult {
    232 
    233         private final Test mJunitTest;
    234         private RuntimeDeviceNotAvailableException mException = null;
    235         private TestResult mJunitResult;
    236 
    237         TestRunnable(Test junitTest, TestResult junitResult) {
    238             mJunitTest = junitTest;
    239             mJunitResult = junitResult;
    240         }
    241 
    242         /**
    243          * {@inheritDoc}
    244          */
    245         @Override
    246         public boolean run() throws Exception {
    247             try {
    248                 mJunitTest.run(mJunitResult);
    249             } catch (RuntimeDeviceNotAvailableException e) {
    250                 mException = e;
    251             }
    252             return true;
    253         }
    254 
    255         public RuntimeDeviceNotAvailableException getException() {
    256             return mException;
    257         }
    258 
    259         /**
    260          * {@inheritDoc}
    261          */
    262         @Override
    263         public void cancel() {
    264         }
    265 
    266     }
    267 
    268     /**
    269      * Load the test with given names from the jar.
    270      *
    271      * @param className
    272      * @param testName
    273      * @return the loaded {@link Test} or <code>null</code> if test could not be loaded.
    274      */
    275     private Test loadTest(String className, String testName) {
    276         try {
    277             Class<?> testClass = loadClass(className);
    278             if (testClass == null) {
    279                 return null;
    280             }
    281             if (TestCase.class.isAssignableFrom(testClass)) {
    282                 TestCase testCase = (TestCase)testClass.newInstance();
    283                 testCase.setName(testName);
    284                 return testCase;
    285             } else if (Test.class.isAssignableFrom(testClass)) {
    286                 Test test = (Test)testClass.newInstance();
    287                 return test;
    288             } else {
    289                 Log.e(LOG_TAG, String.format("Class '%s' from jar '%s' is not a Test",
    290                         className, mJarFileName));
    291             }
    292         } catch (IllegalAccessException e) {
    293             reportLoadError(mJarFileName, className, e);
    294         } catch (InstantiationException e) {
    295             reportLoadError(mJarFileName, className, e);
    296         }
    297         return null;
    298     }
    299 
    300     private Class<?> loadClass(String className) {
    301         try {
    302             if (mClassLoader == null) {
    303                 File jarFile = mCtsBuild.getTestApp(mJarFileName);
    304                 URL urls[] = {jarFile.getCanonicalFile().toURI().toURL()};
    305                 mClassLoader = new URLClassLoader(urls);
    306             }
    307             return mClassLoader.loadClass(className);
    308         } catch (FileNotFoundException fnfe) {
    309             reportLoadError(mJarFileName, className, fnfe);
    310         } catch (MalformedURLException mue) {
    311             reportLoadError(mJarFileName, className, mue);
    312         } catch (IOException ioe) {
    313             reportLoadError(mJarFileName, className, ioe);
    314         } catch (ClassNotFoundException cnfe) {
    315             reportLoadError(mJarFileName, className, cnfe);
    316         }
    317         return null;
    318     }
    319 
    320     /**
    321      * Loads a class from given URLs.
    322      * <p/>
    323      * Exposed so unit tests can mock
    324      *
    325      * @param className
    326      * @param urls
    327      * @return
    328      * @throws ClassNotFoundException
    329      */
    330     Class<?> loadClass(String className, URL[] urls) throws ClassNotFoundException {
    331         URLClassLoader cl = new URLClassLoader(urls);
    332         Class<?> testClass = cl.loadClass(className);
    333         return testClass;
    334     }
    335 
    336     private void reportLoadError(String jarFileName, String className, Exception e) {
    337         Log.e(LOG_TAG, String.format("Failed to load test class '%s' from jar '%s'",
    338                 className, jarFileName));
    339         Log.e(LOG_TAG, e);
    340     }
    341 
    342     /**
    343      * Checks that all mandatory member fields has been set.
    344      */
    345     protected void checkFields() {
    346         if (mRunName == null) {
    347             throw new IllegalArgumentException("run name has not been set");
    348         }
    349         if (mDevice == null) {
    350             throw new IllegalArgumentException("Device has not been set");
    351         }
    352         if (mJarFileName == null) {
    353             throw new IllegalArgumentException("jar file name has not been set");
    354         }
    355         if (mTests == null) {
    356             throw new IllegalArgumentException("tests has not been set");
    357         }
    358         if (mCtsBuild == null) {
    359             throw new IllegalArgumentException("build has not been set");
    360         }
    361         try {
    362             mCtsBuild.getTestApp(mJarFileName);
    363         } catch (FileNotFoundException e) {
    364             throw new IllegalArgumentException(String.format(
    365                     "Could not find jar %s in CTS build %s", mJarFileName,
    366                     mCtsBuild.getRootDir().getAbsolutePath()));
    367         }
    368     }
    369 
    370     /**
    371      * {@inheritDoc}
    372      */
    373     @Override
    374     public int countTestCases() {
    375         return mTests.size();
    376     }
    377 }
    378