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