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.log.LogUtil.CLog;
24 
25 import junit.framework.Test;
26 import junit.framework.TestSuite;
27 
28 import org.junit.internal.runners.JUnit38ClassRunner;
29 import org.junit.runner.Runner;
30 import org.junit.runner.notification.RunNotifier;
31 import org.junit.runners.Suite;
32 import org.junit.runners.model.InitializationError;
33 import org.junit.runners.model.RunnerBuilder;
34 
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.Method;
37 import java.util.Enumeration;
38 import java.util.HashSet;
39 import java.util.Set;
40 
41 /**
42  * Extends the JUnit4 container {@link Suite} in order to provide a {@link ITestDevice} to the tests
43  * that requires it.
44  */
45 public class DeviceSuite extends Suite
46         implements IDeviceTest, IBuildReceiver, IAbiReceiver, ISetOptionReceiver {
47     private ITestDevice mDevice;
48     private IBuildInfo mBuildInfo;
49     private IAbi mAbi;
50 
51     @Option(name = HostTest.SET_OPTION_NAME, description = HostTest.SET_OPTION_DESC)
52     private Set<String> mKeyValueOptions = new HashSet<>();
53 
DeviceSuite(Class<?> klass, RunnerBuilder builder)54     public DeviceSuite(Class<?> klass, RunnerBuilder builder) throws InitializationError {
55         super(klass, builder);
56     }
57 
58     @Override
setDevice(ITestDevice device)59     public void setDevice(ITestDevice device) {
60         mDevice = device;
61         for (Runner r : getChildren()) {
62             // propagate to runner if it needs a device.
63             if (r instanceof IDeviceTest) {
64                 if (mDevice == null) {
65                     throw new IllegalArgumentException("Missing device");
66                 }
67                 ((IDeviceTest)r).setDevice(mDevice);
68             }
69         }
70     }
71 
72     @Override
getDevice()73     public ITestDevice getDevice() {
74         return mDevice;
75     }
76 
77     @Override
setAbi(IAbi abi)78     public void setAbi(IAbi abi) {
79         mAbi = abi;
80         for (Runner r : getChildren()) {
81             // propagate to runner if it needs an abi.
82             if (r instanceof IAbiReceiver) {
83                 ((IAbiReceiver)r).setAbi(mAbi);
84             }
85         }
86     }
87 
88     @Override
getAbi()89     public IAbi getAbi() {
90         return mAbi;
91     }
92 
93     @Override
setBuild(IBuildInfo buildInfo)94     public void setBuild(IBuildInfo buildInfo) {
95         mBuildInfo = buildInfo;
96         for (Runner r : getChildren()) {
97             // propagate to runner if it needs a buildInfo.
98             if (r instanceof IBuildReceiver) {
99                 if (mBuildInfo == null) {
100                     throw new IllegalArgumentException("Missing build information");
101                 }
102                 ((IBuildReceiver)r).setBuild(mBuildInfo);
103             }
104         }
105     }
106 
107     @Override
runChild(Runner runner, RunNotifier notifier)108     protected void runChild(Runner runner, RunNotifier notifier) {
109         // Handle legacy JUnit3 style
110         if (runner instanceof JUnit38ClassRunner) {
111             JUnit38ClassRunner junit3Runner = (JUnit38ClassRunner) runner;
112             try {
113                 // getTest is private so we use reflection to get the test object.
114                 Method getTest = junit3Runner.getClass().getDeclaredMethod("getTest");
115                 getTest.setAccessible(true);
116                 Test test = (Test) getTest.invoke(junit3Runner);
117                 if (test instanceof TestSuite) {
118                     TestSuite testSuite = (TestSuite) test;
119                     Enumeration<Test> testEnum = testSuite.tests();
120                     while (testEnum.hasMoreElements()) {
121                         Test t = testEnum.nextElement();
122                         injectValues(t);
123                     }
124                 } else {
125                     injectValues(test);
126                 }
127             } catch (NoSuchMethodException
128                     | SecurityException
129                     | IllegalAccessException
130                     | IllegalArgumentException
131                     | InvocationTargetException e) {
132                 throw new RuntimeException(
133                         String.format(
134                                 "Failed to invoke junit3 runner: %s",
135                                 junit3Runner.getClass().getName()),
136                         e);
137             }
138         }
139         try {
140             OptionSetter setter = new OptionSetter(runner);
141             for (String kv : mKeyValueOptions) {
142                 setter.setOptionValue(HostTest.SET_OPTION_NAME, kv);
143             }
144         } catch (ConfigurationException e) {
145             CLog.d("Could not set option set-option on '%s', reason: '%s'", runner, e.getMessage());
146         }
147         super.runChild(runner, notifier);
148     }
149 
150     /** Inject the options to the tests. */
injectValues(Object testObj)151     private void injectValues(Object testObj) {
152         if (testObj instanceof IDeviceTest) {
153             if (mDevice == null) {
154                 throw new IllegalArgumentException("Missing device");
155             }
156             ((IDeviceTest) testObj).setDevice(mDevice);
157         }
158         if (testObj instanceof IBuildReceiver) {
159             if (mBuildInfo == null) {
160                 throw new IllegalArgumentException("Missing build information");
161             }
162             ((IBuildReceiver) testObj).setBuild(mBuildInfo);
163         }
164         // We are more flexible about abi information since not always available.
165         if (testObj instanceof IAbiReceiver) {
166             ((IAbiReceiver) testObj).setAbi(mAbi);
167         }
168     }
169 }
170