1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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.ide.eclipse.adt.internal.launch.junit; 17 18 import com.android.ddmlib.IDevice; 19 import com.android.ide.eclipse.adt.AdtPlugin; 20 import com.android.ide.eclipse.adt.internal.launch.DelayedLaunchInfo; 21 import com.android.ide.eclipse.adt.internal.launch.IAndroidLaunchAction; 22 import com.android.ide.eclipse.adt.internal.launch.LaunchMessages; 23 import com.android.ide.eclipse.adt.internal.launch.junit.runtime.AndroidJUnitLaunchInfo; 24 import com.android.ide.eclipse.adt.internal.launch.junit.runtime.RemoteAdtTestRunner; 25 import com.google.common.base.Joiner; 26 27 import org.eclipse.core.runtime.CoreException; 28 import org.eclipse.core.runtime.IProgressMonitor; 29 import org.eclipse.core.runtime.IStatus; 30 import org.eclipse.debug.core.ILaunch; 31 import org.eclipse.debug.core.ILaunchConfiguration; 32 import org.eclipse.debug.core.ILaunchManager; 33 import org.eclipse.debug.core.model.IProcess; 34 import org.eclipse.debug.core.model.IStreamsProxy; 35 import org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate; 36 import org.eclipse.jdt.launching.IVMRunner; 37 import org.eclipse.jdt.launching.VMRunnerConfiguration; 38 import org.eclipse.swt.widgets.Display; 39 40 import java.util.Collection; 41 42 /** 43 * A launch action that executes a instrumentation test run on an Android device. 44 */ 45 class AndroidJUnitLaunchAction implements IAndroidLaunchAction { 46 private static final Joiner JOINER = Joiner.on(',').skipNulls(); 47 private final AndroidJUnitLaunchInfo mLaunchInfo; 48 49 /** 50 * Creates a AndroidJUnitLaunchAction. 51 * 52 * @param launchInfo the {@link AndroidJUnitLaunchInfo} for the JUnit run 53 */ AndroidJUnitLaunchAction(AndroidJUnitLaunchInfo launchInfo)54 public AndroidJUnitLaunchAction(AndroidJUnitLaunchInfo launchInfo) { 55 mLaunchInfo = launchInfo; 56 } 57 58 /** 59 * Launch a instrumentation test run on given Android devices. 60 * Reuses JDT JUnit launch delegate so results can be communicated back to JDT JUnit UI. 61 * <p/> 62 * Note: Must be executed on non-UI thread. 63 * 64 * @see IAndroidLaunchAction#doLaunchActions(DelayedLaunchInfo, IDevice) 65 */ 66 @Override doLaunchAction(DelayedLaunchInfo info, Collection<IDevice> devices)67 public boolean doLaunchAction(DelayedLaunchInfo info, Collection<IDevice> devices) { 68 String msg = String.format(LaunchMessages.AndroidJUnitLaunchAction_LaunchInstr_2s, 69 mLaunchInfo.getRunner(), JOINER.join(devices)); 70 AdtPlugin.printToConsole(info.getProject(), msg); 71 72 try { 73 mLaunchInfo.setDebugMode(info.isDebugMode()); 74 mLaunchInfo.setDevices(devices); 75 JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(mLaunchInfo); 76 final String mode = info.isDebugMode() ? ILaunchManager.DEBUG_MODE : 77 ILaunchManager.RUN_MODE; 78 79 junitDelegate.launch(info.getLaunch().getLaunchConfiguration(), mode, info.getLaunch(), 80 info.getMonitor()); 81 82 // TODO: need to add AMReceiver-type functionality somewhere 83 } catch (CoreException e) { 84 AdtPlugin.printErrorToConsole(info.getProject(), 85 LaunchMessages.AndroidJUnitLaunchAction_LaunchFail); 86 } 87 return true; 88 } 89 90 /** 91 * {@inheritDoc} 92 */ 93 @Override getLaunchDescription()94 public String getLaunchDescription() { 95 return String.format(LaunchMessages.AndroidJUnitLaunchAction_LaunchDesc_s, 96 mLaunchInfo.getRunner()); 97 } 98 99 /** 100 * Extends the JDT JUnit launch delegate to allow for JUnit UI reuse. 101 */ 102 private static class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate { 103 104 private AndroidJUnitLaunchInfo mLaunchInfo; 105 JUnitLaunchDelegate(AndroidJUnitLaunchInfo launchInfo)106 public JUnitLaunchDelegate(AndroidJUnitLaunchInfo launchInfo) { 107 mLaunchInfo = launchInfo; 108 } 109 110 /* (non-Javadoc) 111 * @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor) 112 */ 113 @Override launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)114 public synchronized void launch(ILaunchConfiguration configuration, String mode, 115 ILaunch launch, IProgressMonitor monitor) throws CoreException { 116 // TODO: is progress monitor adjustment needed here? 117 super.launch(configuration, mode, launch, monitor); 118 } 119 120 /** 121 * {@inheritDoc} 122 * @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration) 123 */ 124 @Override verifyMainTypeName(ILaunchConfiguration configuration)125 public String verifyMainTypeName(ILaunchConfiguration configuration) { 126 return "com.android.ide.eclipse.adt.junit.internal.runner.RemoteAndroidTestRunner"; //$NON-NLS-1$ 127 } 128 129 /** 130 * Overrides parent to return a VM Runner implementation which launches a thread, rather 131 * than a separate VM process 132 */ 133 @Override getVMRunner(ILaunchConfiguration configuration, String mode)134 public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) { 135 return new VMTestRunner(mLaunchInfo); 136 } 137 138 /** 139 * {@inheritDoc} 140 * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String) 141 */ 142 @Override getLaunch(ILaunchConfiguration configuration, String mode)143 public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) { 144 return mLaunchInfo.getLaunch(); 145 } 146 } 147 148 /** 149 * Provides a VM runner implementation which starts a inline implementation of a launch process 150 */ 151 private static class VMTestRunner implements IVMRunner { 152 153 private final AndroidJUnitLaunchInfo mJUnitInfo; 154 VMTestRunner(AndroidJUnitLaunchInfo info)155 VMTestRunner(AndroidJUnitLaunchInfo info) { 156 mJUnitInfo = info; 157 } 158 159 /** 160 * {@inheritDoc} 161 * @throws CoreException 162 */ 163 @Override run(final VMRunnerConfiguration config, ILaunch launch, IProgressMonitor monitor)164 public void run(final VMRunnerConfiguration config, ILaunch launch, 165 IProgressMonitor monitor) throws CoreException { 166 167 TestRunnerProcess runnerProcess = 168 new TestRunnerProcess(config, mJUnitInfo); 169 launch.addProcess(runnerProcess); 170 runnerProcess.run(); 171 } 172 } 173 174 /** 175 * Launch process that executes the tests. 176 */ 177 private static class TestRunnerProcess implements IProcess { 178 179 private final VMRunnerConfiguration mRunConfig; 180 private final AndroidJUnitLaunchInfo mJUnitInfo; 181 private RemoteAdtTestRunner mTestRunner = null; 182 private boolean mIsTerminated = false; 183 TestRunnerProcess(VMRunnerConfiguration runConfig, AndroidJUnitLaunchInfo info)184 TestRunnerProcess(VMRunnerConfiguration runConfig, AndroidJUnitLaunchInfo info) { 185 mRunConfig = runConfig; 186 mJUnitInfo = info; 187 } 188 189 /* (non-Javadoc) 190 * @see org.eclipse.debug.core.model.IProcess#getAttribute(java.lang.String) 191 */ 192 @Override getAttribute(String key)193 public String getAttribute(String key) { 194 return null; 195 } 196 197 /** 198 * {@inheritDoc} 199 * @see org.eclipse.debug.core.model.IProcess#getExitValue() 200 */ 201 @Override getExitValue()202 public int getExitValue() { 203 return 0; 204 } 205 206 /* (non-Javadoc) 207 * @see org.eclipse.debug.core.model.IProcess#getLabel() 208 */ 209 @Override getLabel()210 public String getLabel() { 211 return mJUnitInfo.getLaunch().getLaunchMode(); 212 } 213 214 /* (non-Javadoc) 215 * @see org.eclipse.debug.core.model.IProcess#getLaunch() 216 */ 217 @Override getLaunch()218 public ILaunch getLaunch() { 219 return mJUnitInfo.getLaunch(); 220 } 221 222 /* (non-Javadoc) 223 * @see org.eclipse.debug.core.model.IProcess#getStreamsProxy() 224 */ 225 @Override getStreamsProxy()226 public IStreamsProxy getStreamsProxy() { 227 return null; 228 } 229 230 /* (non-Javadoc) 231 * @see org.eclipse.debug.core.model.IProcess#setAttribute(java.lang.String, 232 * java.lang.String) 233 */ 234 @Override setAttribute(String key, String value)235 public void setAttribute(String key, String value) { 236 // ignore 237 } 238 239 /* (non-Javadoc) 240 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) 241 */ 242 @Override getAdapter(Class adapter)243 public Object getAdapter(Class adapter) { 244 return null; 245 } 246 247 /* (non-Javadoc) 248 * @see org.eclipse.debug.core.model.ITerminate#canTerminate() 249 */ 250 @Override canTerminate()251 public boolean canTerminate() { 252 return true; 253 } 254 255 /* (non-Javadoc) 256 * @see org.eclipse.debug.core.model.ITerminate#isTerminated() 257 */ 258 @Override isTerminated()259 public boolean isTerminated() { 260 return mIsTerminated; 261 } 262 263 /** 264 * {@inheritDoc} 265 * @see org.eclipse.debug.core.model.ITerminate#terminate() 266 */ 267 @Override terminate()268 public void terminate() { 269 if (mTestRunner != null) { 270 mTestRunner.terminate(); 271 } 272 mIsTerminated = true; 273 } 274 275 /** 276 * Launches a test runner that will communicate results back to JDT JUnit UI. 277 * <p/> 278 * Must be executed on a non-UI thread. 279 */ run()280 public void run() { 281 if (Display.getCurrent() != null) { 282 AdtPlugin.log(IStatus.ERROR, "Adt test runner executed on UI thread"); 283 AdtPlugin.printErrorToConsole(mJUnitInfo.getProject(), 284 "Test launch failed due to internal error: Running tests on UI thread"); 285 terminate(); 286 return; 287 } 288 mTestRunner = new RemoteAdtTestRunner(); 289 mTestRunner.runTests(mRunConfig.getProgramArguments(), mJUnitInfo); 290 } 291 } 292 } 293 294