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