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