Home | History | Annotate | Download | only in testtype
      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.testtype;
     17 
     18 import com.android.tradefed.build.IBuildInfo;
     19 import com.android.tradefed.config.Option;
     20 import com.android.tradefed.device.ITestDevice;
     21 import com.android.tradefed.invoker.IInvocationContext;
     22 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
     23 import com.android.tradefed.result.InputStreamSource;
     24 import com.android.tradefed.result.LogDataType;
     25 import com.android.tradefed.testtype.MetricTestCase.LogHolder;
     26 import com.android.tradefed.util.proto.TfMetricProtoUtil;
     27 
     28 import org.junit.rules.ExternalResource;
     29 import org.junit.rules.TestRule;
     30 import org.junit.runner.Description;
     31 import org.junit.runners.BlockJUnit4ClassRunner;
     32 import org.junit.runners.model.InitializationError;
     33 import org.junit.runners.model.Statement;
     34 
     35 import java.lang.annotation.Annotation;
     36 import java.util.ArrayList;
     37 import java.util.HashMap;
     38 import java.util.List;
     39 import java.util.Map;
     40 
     41 /**
     42  * JUnit4 test runner that also accommodate {@link IDeviceTest}. Should be specify above JUnit4 Test
     43  * with the RunWith annotation.
     44  */
     45 public class DeviceJUnit4ClassRunner extends BlockJUnit4ClassRunner
     46         implements IDeviceTest,
     47                 IBuildReceiver,
     48                 IAbiReceiver,
     49                 ISetOptionReceiver,
     50                 IMultiDeviceTest,
     51                 IInvocationContextReceiver {
     52     private ITestDevice mDevice;
     53     private IBuildInfo mBuildInfo;
     54     private IAbi mAbi;
     55     private IInvocationContext mContext;
     56     private Map<ITestDevice, IBuildInfo> mDeviceInfos;
     57 
     58     @Option(name = HostTest.SET_OPTION_NAME, description = HostTest.SET_OPTION_DESC)
     59     private List<String> mKeyValueOptions = new ArrayList<>();
     60 
     61     public DeviceJUnit4ClassRunner(Class<?> klass) throws InitializationError {
     62         super(klass);
     63     }
     64 
     65     /**
     66      * We override createTest in order to set the device.
     67      */
     68     @Override
     69     protected Object createTest() throws Exception {
     70         Object testObj = super.createTest();
     71         if (testObj instanceof IDeviceTest) {
     72             if (mDevice == null) {
     73                 throw new IllegalArgumentException("Missing device");
     74             }
     75             ((IDeviceTest) testObj).setDevice(mDevice);
     76         }
     77         if (testObj instanceof IBuildReceiver) {
     78             if (mBuildInfo == null) {
     79                 throw new IllegalArgumentException("Missing build information");
     80             }
     81             ((IBuildReceiver) testObj).setBuild(mBuildInfo);
     82         }
     83         // We are more flexible about abi information since not always available.
     84         if (testObj instanceof IAbiReceiver) {
     85             ((IAbiReceiver) testObj).setAbi(mAbi);
     86         }
     87         if (testObj instanceof IMultiDeviceTest) {
     88             ((IMultiDeviceTest) testObj).setDeviceInfos(mDeviceInfos);
     89         }
     90         if (testObj instanceof IInvocationContextReceiver) {
     91             ((IInvocationContextReceiver) testObj).setInvocationContext(mContext);
     92         }
     93         // Set options of test object
     94         HostTest.setOptionToLoadedObject(testObj, mKeyValueOptions);
     95         return testObj;
     96     }
     97 
     98     @Override
     99     public void setDevice(ITestDevice device) {
    100         mDevice = device;
    101     }
    102 
    103     @Override
    104     public ITestDevice getDevice() {
    105         return mDevice;
    106     }
    107 
    108     @Override
    109     public void setAbi(IAbi abi) {
    110         mAbi = abi;
    111     }
    112 
    113     @Override
    114     public IAbi getAbi() {
    115         return mAbi;
    116     }
    117 
    118     @Override
    119     public void setBuild(IBuildInfo buildInfo) {
    120         mBuildInfo = buildInfo;
    121     }
    122 
    123     @Override
    124     public void setInvocationContext(IInvocationContext invocationContext) {
    125         mContext = invocationContext;
    126     }
    127 
    128     @Override
    129     public void setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos) {
    130         mDeviceInfos = deviceInfos;
    131     }
    132 
    133     /**
    134      * Implementation of {@link ExternalResource} and {@link TestRule}. This rule allows to log
    135      * metrics during a test case (inside @Test). It guarantees that the metrics map is cleaned
    136      * between tests, so the same rule object can be re-used.
    137      *
    138      * <pre>Example:
    139      * &#064;Rule
    140      * public TestMetrics metrics = new TestMetrics();
    141      *
    142      * &#064;Test
    143      * public void testFoo() {
    144      *     metrics.put("key", "value");
    145      *     metrics.put("key2", "value2");
    146      * }
    147      *
    148      * &#064;Test
    149      * public void testFoo2() {
    150      *     metrics.put("key3", "value3");
    151      * }
    152      * </pre>
    153      */
    154     public static class TestMetrics extends ExternalResource {
    155 
    156         Description mDescription;
    157         private Map<String, String> mMetrics = new HashMap<>();
    158         private HashMap<String, Metric> mProtoMetrics = new HashMap<>();
    159 
    160         @Override
    161         public Statement apply(Statement base, Description description) {
    162             mDescription = description;
    163             return super.apply(base, description);
    164         }
    165 
    166         /**
    167          * Log a metric entry for the test case. Each key within a test case must be unique
    168          * otherwise it will override the previous value.
    169          *
    170          * @param key The key of the metric.
    171          * @param value The value associated to the key.
    172          */
    173         public void addTestMetric(String key, String value) {
    174             mMetrics.put(key, value);
    175         }
    176 
    177         /**
    178          * Log a metric entry in proto format for the test case. Each key within a test case must be
    179          * unique otherwise it will override the previous value.
    180          *
    181          * @param key The key of the metric.
    182          * @param metric The value associated to the key.
    183          */
    184         public void addTestMetric(String key, Metric metric) {
    185             mProtoMetrics.put(key, metric);
    186         }
    187 
    188         @Override
    189         protected void before() throws Throwable {
    190             mMetrics = new HashMap<>();
    191             mProtoMetrics = new HashMap<>();
    192         }
    193 
    194         @Override
    195         protected void after() {
    196             // we inject a Description with an annotation carrying metrics.
    197             // We have to go around, since Description cannot be extended and RunNotifier
    198             // does not give us a lot of flexibility to find our metrics back.
    199             mProtoMetrics.putAll(TfMetricProtoUtil.upgradeConvert(mMetrics));
    200             mDescription.addChild(
    201                     Description.createTestDescription(
    202                             "METRICS", "METRICS", new MetricAnnotation(mProtoMetrics)));
    203         }
    204     }
    205 
    206     /** Fake annotation meant to carry metrics to the reporters. */
    207     public static class MetricAnnotation implements Annotation {
    208 
    209         public HashMap<String, Metric> mMetrics = new HashMap<>();
    210 
    211         public MetricAnnotation(HashMap<String, Metric> metrics) {
    212             mMetrics.putAll(metrics);
    213         }
    214 
    215         @Override
    216         public Class<? extends Annotation> annotationType() {
    217             return null;
    218         }
    219     }
    220 
    221     /**
    222      * Implementation of {@link ExternalResource} and {@link TestRule}. This rule allows to log logs
    223      * during a test case (inside @Test). It guarantees that the log list is cleaned between tests,
    224      * so the same rule object can be re-used.
    225      *
    226      * <pre>Example:
    227      * &#064;Rule
    228      * public TestLogData logs = new TestLogData();
    229      *
    230      * &#064;Test
    231      * public void testFoo() {
    232      *     logs.addTestLog("logcat", LogDataType.LOGCAT, new FileInputStreamSource(logcatFile));
    233      * }
    234      *
    235      * &#064;Test
    236      * public void testFoo2() {
    237      *     logs.addTestLog("logcat2", LogDataType.LOGCAT, new FileInputStreamSource(logcatFile2));
    238      * }
    239      * </pre>
    240      */
    241     public static class TestLogData extends ExternalResource {
    242         private Description mDescription;
    243         private List<LogHolder> mLogs = new ArrayList<>();
    244 
    245         @Override
    246         public Statement apply(Statement base, Description description) {
    247             mDescription = description;
    248             return super.apply(base, description);
    249         }
    250 
    251         public final void addTestLog(
    252                 String dataName, LogDataType dataType, InputStreamSource dataStream) {
    253             mLogs.add(new LogHolder(dataName, dataType, dataStream));
    254         }
    255 
    256         @Override
    257         protected void after() {
    258             // we inject a Description with an annotation carrying metrics.
    259             // We have to go around, since Description cannot be extended and RunNotifier
    260             // does not give us a lot of flexibility to find our metrics back.
    261             mDescription.addChild(
    262                     Description.createTestDescription("LOGS", "LOGS", new LogAnnotation(mLogs)));
    263         }
    264     }
    265 
    266     /** Fake annotation meant to carry logs to the reporters. */
    267     public static class LogAnnotation implements Annotation {
    268 
    269         public List<LogHolder> mLogs = new ArrayList<>();
    270 
    271         public LogAnnotation(List<LogHolder> logs) {
    272             mLogs.addAll(logs);
    273         }
    274 
    275         @Override
    276         public Class<? extends Annotation> annotationType() {
    277             return null;
    278         }
    279     }
    280 }
    281