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