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.SdkConstants;
     19 import com.android.ide.eclipse.adt.AdtConstants;
     20 import com.android.ide.eclipse.adt.AdtPlugin;
     21 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     22 import com.android.ide.eclipse.adt.internal.launch.LaunchMessages;
     23 import com.android.ide.eclipse.adt.internal.launch.MainLaunchConfigTab;
     24 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     25 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
     26 
     27 import org.eclipse.core.resources.IProject;
     28 import org.eclipse.core.resources.IResource;
     29 import org.eclipse.core.resources.IWorkspaceRoot;
     30 import org.eclipse.core.resources.ResourcesPlugin;
     31 import org.eclipse.core.runtime.CoreException;
     32 import org.eclipse.core.runtime.IPath;
     33 import org.eclipse.core.runtime.IStatus;
     34 import org.eclipse.core.runtime.Path;
     35 import org.eclipse.debug.core.ILaunchConfiguration;
     36 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
     37 import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
     38 import org.eclipse.jdt.core.IJavaElement;
     39 import org.eclipse.jdt.core.IJavaModel;
     40 import org.eclipse.jdt.core.IJavaProject;
     41 import org.eclipse.jdt.core.IPackageFragment;
     42 import org.eclipse.jdt.core.IPackageFragmentRoot;
     43 import org.eclipse.jdt.core.ISourceReference;
     44 import org.eclipse.jdt.core.IType;
     45 import org.eclipse.jdt.core.JavaCore;
     46 import org.eclipse.jdt.core.JavaModelException;
     47 import org.eclipse.jdt.internal.junit.Messages;
     48 import org.eclipse.jdt.internal.junit.launcher.ITestKind;
     49 import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
     50 import org.eclipse.jdt.internal.junit.launcher.JUnitMigrationDelegate;
     51 import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
     52 import org.eclipse.jdt.internal.junit.launcher.TestSelectionDialog;
     53 import org.eclipse.jdt.internal.junit.ui.JUnitMessages;
     54 import org.eclipse.jdt.internal.junit.util.LayoutUtil;
     55 import org.eclipse.jdt.internal.junit.util.TestSearchEngine;
     56 import org.eclipse.jdt.internal.ui.wizards.TypedElementSelectionValidator;
     57 import org.eclipse.jdt.internal.ui.wizards.TypedViewerFilter;
     58 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
     59 import org.eclipse.jdt.ui.JavaElementComparator;
     60 import org.eclipse.jdt.ui.JavaElementLabelProvider;
     61 import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
     62 import org.eclipse.jface.dialogs.Dialog;
     63 import org.eclipse.jface.layout.GridDataFactory;
     64 import org.eclipse.jface.viewers.ILabelProvider;
     65 import org.eclipse.jface.viewers.ISelection;
     66 import org.eclipse.jface.viewers.IStructuredSelection;
     67 import org.eclipse.jface.viewers.Viewer;
     68 import org.eclipse.jface.viewers.ViewerFilter;
     69 import org.eclipse.jface.window.Window;
     70 import org.eclipse.swt.SWT;
     71 import org.eclipse.swt.events.ModifyEvent;
     72 import org.eclipse.swt.events.ModifyListener;
     73 import org.eclipse.swt.events.SelectionAdapter;
     74 import org.eclipse.swt.events.SelectionEvent;
     75 import org.eclipse.swt.graphics.Image;
     76 import org.eclipse.swt.layout.GridData;
     77 import org.eclipse.swt.layout.GridLayout;
     78 import org.eclipse.swt.widgets.Button;
     79 import org.eclipse.swt.widgets.Combo;
     80 import org.eclipse.swt.widgets.Composite;
     81 import org.eclipse.swt.widgets.Label;
     82 import org.eclipse.swt.widgets.Shell;
     83 import org.eclipse.swt.widgets.Text;
     84 import org.eclipse.ui.IEditorInput;
     85 import org.eclipse.ui.IEditorPart;
     86 import org.eclipse.ui.IWorkbenchPage;
     87 import org.eclipse.ui.IWorkbenchWindow;
     88 import org.eclipse.ui.PlatformUI;
     89 import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
     90 import org.eclipse.ui.dialogs.SelectionDialog;
     91 
     92 import java.lang.reflect.InvocationTargetException;
     93 import java.util.Arrays;
     94 import java.util.List;
     95 
     96 /**
     97  * The launch config UI tab for Android JUnit
     98  * <p/>
     99  * Based on org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationTab
    100  */
    101 @SuppressWarnings("restriction")
    102 public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurationTab {
    103 
    104     // Project UI widgets
    105     private Label mProjLabel;
    106     private Text mProjText;
    107     private Button mProjButton;
    108 
    109     // Test class UI widgets
    110     private Text mTestText;
    111     private Button mSearchButton;
    112     private String mOriginalTestMethodName;
    113     private Label mTestMethodLabel;
    114     private Text mContainerText;
    115     private IJavaElement mContainerElement;
    116     private final ILabelProvider mJavaElementLabelProvider = new JavaElementLabelProvider();
    117 
    118     private Button mContainerSearchButton;
    119     private Button mTestContainerRadioButton;
    120     private Button mTestRadioButton;
    121     private Label mTestLabel;
    122 
    123     // Android specific members
    124     private Image mTabIcon = null;
    125     private Combo mInstrumentationCombo;
    126     private Combo mTestSizeCombo;
    127     private static final String EMPTY_STRING = ""; //$NON-NLS-1$
    128     private static final String TAG = "AndroidJUnitLaunchConfigurationTab"; //$NON-NLS-1$
    129     private String[] mInstrumentations = null;
    130     private InstrumentationRunnerValidator mInstrValidator = null;
    131     private ProjectChooserHelper mProjectChooserHelper;
    132 
    133     public static final String SMALL_TEST_ANNOTATION = "@SmallTest";        //$NON-NLS-1$
    134     public static final String MEDIUM_TEST_ANNOTATION = "@MediumTest";      //$NON-NLS-1$
    135     public static final String LARGE_TEST_ANNOTATION = "@LargeTest";        //$NON-NLS-1$
    136     private static final List<String> TEST_SIZE_OPTIONS = Arrays.asList(
    137             "All Tests",
    138             SMALL_TEST_ANNOTATION,
    139             MEDIUM_TEST_ANNOTATION,
    140             LARGE_TEST_ANNOTATION
    141     );
    142 
    143     /* (non-Javadoc)
    144      * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite)
    145      */
    146     @Override
    147     public void createControl(Composite parent) {
    148         mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(), null /*filter*/);
    149 
    150         Composite comp = new Composite(parent, SWT.NONE);
    151         setControl(comp);
    152 
    153         GridLayout topLayout = new GridLayout();
    154         topLayout.numColumns = 3;
    155         comp.setLayout(topLayout);
    156 
    157         createSingleTestSection(comp);
    158         createTestContainerSelectionGroup(comp);
    159 
    160         createSpacer(comp);
    161 
    162         createInstrumentationGroup(comp);
    163         createSizeSelector(comp);
    164 
    165         Dialog.applyDialogFont(comp);
    166         // TODO: add help link here when available
    167         //PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(),
    168         //      IJUnitHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_JUNIT_MAIN_TAB);
    169         validatePage();
    170     }
    171 
    172 
    173     private void createSpacer(Composite comp) {
    174         Label label = new Label(comp, SWT.NONE);
    175         GridData gd = new GridData();
    176         gd.horizontalSpan = 3;
    177         label.setLayoutData(gd);
    178     }
    179 
    180     private void createSingleTestSection(Composite comp) {
    181         mTestRadioButton = new Button(comp, SWT.RADIO);
    182         mTestRadioButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_oneTest);
    183         GridData gd = new GridData();
    184         gd.horizontalSpan = 3;
    185         mTestRadioButton.setLayoutData(gd);
    186         mTestRadioButton.addSelectionListener(new SelectionAdapter() {
    187             @Override
    188             public void widgetSelected(SelectionEvent e) {
    189                 if (mTestRadioButton.getSelection()) {
    190                     testModeChanged();
    191                 }
    192             }
    193         });
    194 
    195         mProjLabel = new Label(comp, SWT.NONE);
    196         mProjLabel.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_project);
    197         gd = new GridData();
    198         gd.horizontalIndent = 25;
    199         mProjLabel.setLayoutData(gd);
    200 
    201         mProjText = new Text(comp, SWT.SINGLE | SWT.BORDER);
    202         mProjText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    203         mProjText.addModifyListener(new ModifyListener() {
    204             @Override
    205             public void modifyText(ModifyEvent evt) {
    206                 validatePage();
    207                 updateLaunchConfigurationDialog();
    208                 mSearchButton.setEnabled(mTestRadioButton.getSelection() &&
    209                         mProjText.getText().length() > 0);
    210             }
    211         });
    212 
    213         mProjButton = new Button(comp, SWT.PUSH);
    214         mProjButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_browse);
    215         mProjButton.addSelectionListener(new SelectionAdapter() {
    216             @Override
    217             public void widgetSelected(SelectionEvent evt) {
    218                 handleProjectButtonSelected();
    219             }
    220         });
    221         setButtonGridData(mProjButton);
    222 
    223         mTestLabel = new Label(comp, SWT.NONE);
    224         gd = new GridData();
    225         gd.horizontalIndent = 25;
    226         mTestLabel.setLayoutData(gd);
    227         mTestLabel.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_test);
    228 
    229 
    230         mTestText = new Text(comp, SWT.SINGLE | SWT.BORDER);
    231         mTestText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    232         mTestText.addModifyListener(new ModifyListener() {
    233             @Override
    234             public void modifyText(ModifyEvent evt) {
    235                 validatePage();
    236                 updateLaunchConfigurationDialog();
    237             }
    238         });
    239 
    240         mSearchButton = new Button(comp, SWT.PUSH);
    241         mSearchButton.setEnabled(mProjText.getText().length() > 0);
    242         mSearchButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_search);
    243         mSearchButton.addSelectionListener(new SelectionAdapter() {
    244             @Override
    245             public void widgetSelected(SelectionEvent evt) {
    246                 handleSearchButtonSelected();
    247             }
    248         });
    249         setButtonGridData(mSearchButton);
    250 
    251         new Label(comp, SWT.NONE);
    252 
    253         mTestMethodLabel = new Label(comp, SWT.NONE);
    254         mTestMethodLabel.setText("");  //$NON-NLS-1$
    255         gd = new GridData();
    256         gd.horizontalSpan = 2;
    257         mTestMethodLabel.setLayoutData(gd);
    258     }
    259 
    260     private void createTestContainerSelectionGroup(Composite comp) {
    261         mTestContainerRadioButton = new Button(comp, SWT.RADIO);
    262         mTestContainerRadioButton.setText(
    263                 LaunchMessages.AndroidJUnitTab_TestContainerText);
    264         GridData gd = new GridData();
    265         gd.horizontalSpan = 3;
    266         mTestContainerRadioButton.setLayoutData(gd);
    267         mTestContainerRadioButton.addSelectionListener(new SelectionAdapter() {
    268             @Override
    269             public void widgetSelected(SelectionEvent e) {
    270                 if (mTestContainerRadioButton.getSelection()) {
    271                     testModeChanged();
    272                 }
    273             }
    274         });
    275 
    276         mContainerText = new Text(comp, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY);
    277         gd = new GridData(GridData.FILL_HORIZONTAL);
    278         gd.horizontalIndent = 25;
    279         gd.horizontalSpan = 2;
    280         mContainerText.setLayoutData(gd);
    281         mContainerText.addModifyListener(new ModifyListener() {
    282             @Override
    283             public void modifyText(ModifyEvent evt) {
    284                 updateLaunchConfigurationDialog();
    285             }
    286         });
    287 
    288         mContainerSearchButton = new Button(comp, SWT.PUSH);
    289         mContainerSearchButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_search);
    290         mContainerSearchButton.addSelectionListener(new SelectionAdapter() {
    291             @Override
    292             public void widgetSelected(SelectionEvent evt) {
    293                 handleContainerSearchButtonSelected();
    294             }
    295         });
    296         setButtonGridData(mContainerSearchButton);
    297     }
    298 
    299     private void createInstrumentationGroup(Composite comp) {
    300         Label loaderLabel = new Label(comp, SWT.NONE);
    301         loaderLabel.setText(LaunchMessages.AndroidJUnitTab_LoaderLabel);
    302         GridData gd = new GridData();
    303         gd.horizontalIndent = 0;
    304         loaderLabel.setLayoutData(gd);
    305 
    306         mInstrumentationCombo = new Combo(comp, SWT.DROP_DOWN | SWT.READ_ONLY);
    307         GridDataFactory.defaultsFor(mInstrumentationCombo)
    308             .span(2, 1)
    309             .applyTo(mInstrumentationCombo);
    310         mInstrumentationCombo.clearSelection();
    311         mInstrumentationCombo.addSelectionListener(new SelectionAdapter() {
    312             @Override
    313             public void widgetSelected(SelectionEvent e) {
    314                 validatePage();
    315                 updateLaunchConfigurationDialog();
    316             }
    317         });
    318     }
    319 
    320     private void createSizeSelector(Composite comp) {
    321         Label l = new Label(comp, SWT.NONE);
    322         l.setText(LaunchMessages.AndroidJUnitTab_SizeLabel);
    323         GridData gd = new GridData();
    324         gd.horizontalIndent = 0;
    325         l.setLayoutData(gd);
    326 
    327         mTestSizeCombo = new Combo(comp, SWT.DROP_DOWN | SWT.READ_ONLY);
    328         mTestSizeCombo.setItems(TEST_SIZE_OPTIONS.toArray(new String[TEST_SIZE_OPTIONS.size()]));
    329         mTestSizeCombo.select(0);
    330         mTestSizeCombo.addSelectionListener(new SelectionAdapter() {
    331             @Override
    332             public void widgetSelected(SelectionEvent e) {
    333                 updateLaunchConfigurationDialog();
    334             }
    335         });
    336     }
    337 
    338     private void handleContainerSearchButtonSelected() {
    339         IJavaElement javaElement = chooseContainer(mContainerElement);
    340         if (javaElement != null) {
    341             setContainerElement(javaElement);
    342         }
    343     }
    344 
    345     private void setContainerElement(IJavaElement javaElement) {
    346         mContainerElement = javaElement;
    347         mContainerText.setText(getPresentationName(javaElement));
    348         validatePage();
    349         updateLaunchConfigurationDialog();
    350     }
    351 
    352     /* (non-Javadoc)
    353      * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
    354      */
    355     @Override
    356     public void initializeFrom(ILaunchConfiguration config) {
    357         String projectName = updateProjectFromConfig(config);
    358         String containerHandle = EMPTY_STRING;
    359         try {
    360             containerHandle = config.getAttribute(
    361                     JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, EMPTY_STRING);
    362         } catch (CoreException ce) {
    363             // ignore
    364         }
    365 
    366         if (containerHandle.length() > 0) {
    367             updateTestContainerFromConfig(config);
    368         } else {
    369             updateTestTypeFromConfig(config);
    370         }
    371 
    372         IProject proj = mProjectChooserHelper.getAndroidProject(projectName);
    373         loadInstrumentations(proj);
    374         updateInstrumentationFromConfig(config);
    375         updateTestSizeFromConfig(config);
    376 
    377         validatePage();
    378     }
    379 
    380     private void updateInstrumentationFromConfig(ILaunchConfiguration config) {
    381         boolean found = false;
    382         try {
    383             String currentInstrumentation = config.getAttribute(
    384                     AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME, EMPTY_STRING);
    385             if (mInstrumentations != null) {
    386                 // look for the name of the instrumentation in the combo.
    387                 for (int i = 0; i < mInstrumentations.length; i++) {
    388                    if (currentInstrumentation.equals(mInstrumentations[i])) {
    389                        found = true;
    390                        mInstrumentationCombo.select(i);
    391                        break;
    392                     }
    393                 }
    394             }
    395         } catch (CoreException ce) {
    396             // ignore
    397         }
    398         if (!found) {
    399             mInstrumentationCombo.clearSelection();
    400         }
    401     }
    402 
    403     private void updateTestSizeFromConfig(ILaunchConfiguration config) {
    404         try {
    405             String testSize = config.getAttribute(
    406                     AndroidJUnitLaunchConfigDelegate.ATTR_TEST_SIZE, EMPTY_STRING);
    407             int index = TEST_SIZE_OPTIONS.indexOf(testSize);
    408             if (index >= 0 && mTestSizeCombo != null) {
    409                 mTestSizeCombo.select(index);
    410             }
    411         } catch (CoreException e) {
    412             // ignore
    413         }
    414     }
    415 
    416     private String updateProjectFromConfig(ILaunchConfiguration config) {
    417         String projectName = EMPTY_STRING;
    418         try {
    419             projectName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
    420                     EMPTY_STRING);
    421         } catch (CoreException ce) {
    422             // ignore
    423         }
    424         mProjText.setText(projectName);
    425         return projectName;
    426     }
    427 
    428     private void updateTestTypeFromConfig(ILaunchConfiguration config) {
    429         String testTypeName = EMPTY_STRING;
    430         mOriginalTestMethodName = EMPTY_STRING;
    431         try {
    432             testTypeName = config.getAttribute(
    433                     IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, ""); //$NON-NLS-1$
    434             mOriginalTestMethodName = config.getAttribute(
    435                     JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME, ""); //$NON-NLS-1$
    436         } catch (CoreException ce) {
    437             // ignore
    438         }
    439         mTestRadioButton.setSelection(true);
    440         setEnableSingleTestGroup(true);
    441         setEnableContainerTestGroup(false);
    442         mTestContainerRadioButton.setSelection(false);
    443         mTestText.setText(testTypeName);
    444         mContainerText.setText(EMPTY_STRING);
    445         setTestMethodLabel(mOriginalTestMethodName);
    446     }
    447 
    448     private void setTestMethodLabel(String testMethodName) {
    449         if (!EMPTY_STRING.equals(testMethodName)) {
    450             mTestMethodLabel.setText(
    451                     JUnitMessages.JUnitLaunchConfigurationTab_label_method +
    452                     mOriginalTestMethodName);
    453         } else {
    454             mTestMethodLabel.setText(EMPTY_STRING);
    455         }
    456     }
    457 
    458     private void updateTestContainerFromConfig(ILaunchConfiguration config) {
    459         String containerHandle = EMPTY_STRING;
    460         IJavaElement containerElement = null;
    461         try {
    462             containerHandle = config.getAttribute(
    463                     JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, EMPTY_STRING);
    464             if (containerHandle.length() > 0) {
    465                 containerElement = JavaCore.create(containerHandle);
    466             }
    467         } catch (CoreException ce) {
    468             // ignore
    469         }
    470         if (containerElement != null) {
    471             mContainerElement = containerElement;
    472         }
    473         mTestContainerRadioButton.setSelection(true);
    474         setEnableSingleTestGroup(false);
    475         setEnableContainerTestGroup(true);
    476         mTestRadioButton.setSelection(false);
    477         if (mContainerElement != null) {
    478             mContainerText.setText(getPresentationName(mContainerElement));
    479         }
    480         mTestText.setText(EMPTY_STRING);
    481     }
    482 
    483     /*
    484      * (non-Javadoc)
    485      * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
    486      */
    487     @Override
    488     public void performApply(ILaunchConfigurationWorkingCopy config) {
    489         if (mTestContainerRadioButton.getSelection() && mContainerElement != null) {
    490             config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
    491                     mContainerElement.getJavaProject().getElementName());
    492             config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER,
    493                     mContainerElement.getHandleIdentifier());
    494             config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
    495                     EMPTY_STRING);
    496              //workaround for Eclipse bug 65399
    497             config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
    498                     EMPTY_STRING);
    499         } else {
    500             config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
    501                     mProjText.getText());
    502             config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
    503                     mTestText.getText());
    504             config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER,
    505                     EMPTY_STRING);
    506             config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
    507                     mOriginalTestMethodName);
    508         }
    509         try {
    510             mapResources(config);
    511         } catch (CoreException e) {
    512             // TODO: does the real error need to be extracted out of CoreException
    513             AdtPlugin.log(e, "Error occurred saving configuration"); //$NON-NLS-1$
    514         }
    515         AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config);
    516 
    517         config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME,
    518                 getSelectedInstrumentation());
    519         config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_TEST_SIZE,
    520                 getSelectedTestSize());
    521     }
    522 
    523     private void mapResources(ILaunchConfigurationWorkingCopy config)  throws CoreException {
    524         JUnitMigrationDelegate.mapResources(config);
    525     }
    526 
    527     /* (non-Javadoc)
    528      * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#dispose()
    529      */
    530     @Override
    531     public void dispose() {
    532         super.dispose();
    533         mTabIcon = null;
    534         mJavaElementLabelProvider.dispose();
    535     }
    536 
    537     /* (non-Javadoc)
    538      * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#getImage()
    539      */
    540     @Override
    541     public Image getImage() {
    542         // reuse icon from the Android App Launch config tab
    543         if (mTabIcon == null) {
    544             mTabIcon = IconFactory.getInstance().getIcon(MainLaunchConfigTab.LAUNCH_TAB_IMAGE);
    545         }
    546         return mTabIcon;
    547     }
    548 
    549     /**
    550      * Show a dialog that lists all main types
    551      */
    552     private void handleSearchButtonSelected() {
    553         Shell shell = getShell();
    554 
    555         IJavaProject javaProject = getJavaProject();
    556 
    557         IType[] types = new IType[0];
    558         boolean[] radioSetting = new boolean[2];
    559         try {
    560             // fix for Eclipse bug 66922 Wrong radio behaviour when switching
    561             // remember the selected radio button
    562             radioSetting[0] = mTestRadioButton.getSelection();
    563             radioSetting[1] = mTestContainerRadioButton.getSelection();
    564 
    565             types = TestSearchEngine.findTests(getLaunchConfigurationDialog(), javaProject,
    566                     getTestKind());
    567         } catch (InterruptedException e) {
    568             setErrorMessage(e.getMessage());
    569             return;
    570         } catch (InvocationTargetException e) {
    571             AdtPlugin.log(e.getTargetException(), "Error finding test types"); //$NON-NLS-1$
    572             return;
    573         } finally {
    574             mTestRadioButton.setSelection(radioSetting[0]);
    575             mTestContainerRadioButton.setSelection(radioSetting[1]);
    576         }
    577 
    578         SelectionDialog dialog = new TestSelectionDialog(shell, types);
    579         dialog.setTitle(JUnitMessages.JUnitLaunchConfigurationTab_testdialog_title);
    580         dialog.setMessage(JUnitMessages.JUnitLaunchConfigurationTab_testdialog_message);
    581         if (dialog.open() == Window.CANCEL) {
    582             return;
    583         }
    584 
    585         Object[] results = dialog.getResult();
    586         if ((results == null) || (results.length < 1)) {
    587             return;
    588         }
    589         IType type = (IType) results[0];
    590 
    591         if (type != null) {
    592             mTestText.setText(type.getFullyQualifiedName('.'));
    593             javaProject = type.getJavaProject();
    594             mProjText.setText(javaProject.getElementName());
    595         }
    596     }
    597 
    598     private ITestKind getTestKind() {
    599         // harddcode this to JUnit 3
    600         return TestKindRegistry.getDefault().getKind(TestKindRegistry.JUNIT3_TEST_KIND_ID);
    601     }
    602 
    603     /**
    604      * Show a dialog that lets the user select a Android project.  This in turn provides
    605      * context for the main type, allowing the user to key a main type name, or
    606      * constraining the search for main types to the specified project.
    607      */
    608     private void handleProjectButtonSelected() {
    609         IJavaProject project = mProjectChooserHelper.chooseJavaProject(getProjectName(),
    610                 "Please select a project to launch");
    611         if (project == null) {
    612             return;
    613         }
    614 
    615         String projectName = project.getElementName();
    616         mProjText.setText(projectName);
    617         loadInstrumentations(project.getProject());
    618     }
    619 
    620     /**
    621      * Return the IJavaProject corresponding to the project name in the project name
    622      * text field, or null if the text does not match a Android project name.
    623      */
    624     private IJavaProject getJavaProject() {
    625         String projectName = getProjectName();
    626         return getJavaModel().getJavaProject(projectName);
    627     }
    628 
    629     /**
    630      * Returns the name of the currently specified project. Null if no project is selected.
    631      */
    632     private String getProjectName() {
    633         String projectName = mProjText.getText().trim();
    634         if (projectName.length() < 1) {
    635             return null;
    636         }
    637         return projectName;
    638     }
    639 
    640     /**
    641      * Convenience method to get the workspace root.
    642      */
    643     private IWorkspaceRoot getWorkspaceRoot() {
    644         return ResourcesPlugin.getWorkspace().getRoot();
    645     }
    646 
    647     /**
    648      * Convenience method to get access to the java model.
    649      */
    650     private IJavaModel getJavaModel() {
    651         return JavaCore.create(getWorkspaceRoot());
    652     }
    653 
    654     /* (non-Javadoc)
    655      * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration)
    656      */
    657     @Override
    658     public boolean isValid(ILaunchConfiguration config) {
    659         validatePage();
    660         return getErrorMessage() == null;
    661     }
    662 
    663     private void testModeChanged() {
    664         boolean isSingleTestMode = mTestRadioButton.getSelection();
    665         setEnableSingleTestGroup(isSingleTestMode);
    666         setEnableContainerTestGroup(!isSingleTestMode);
    667         if (!isSingleTestMode && mContainerText.getText().length() == 0) {
    668             String projText = mProjText.getText();
    669             if (Path.EMPTY.isValidSegment(projText)) {
    670                 IJavaProject javaProject = getJavaModel().getJavaProject(projText);
    671                 if (javaProject != null && javaProject.exists()) {
    672                     setContainerElement(javaProject);
    673                 }
    674             }
    675         }
    676         validatePage();
    677         updateLaunchConfigurationDialog();
    678     }
    679 
    680     private void validatePage() {
    681         setErrorMessage(null);
    682         setMessage(null);
    683 
    684         if (mTestContainerRadioButton.getSelection()) {
    685             if (mContainerElement == null) {
    686                 setErrorMessage(JUnitMessages.JUnitLaunchConfigurationTab_error_noContainer);
    687                 return;
    688             }
    689             validateJavaProject(mContainerElement.getJavaProject());
    690             return;
    691         }
    692 
    693         String projectName = mProjText.getText().trim();
    694         if (projectName.length() == 0) {
    695             setErrorMessage(JUnitMessages.JUnitLaunchConfigurationTab_error_projectnotdefined);
    696             return;
    697         }
    698 
    699         IStatus status = ResourcesPlugin.getWorkspace().validatePath(IPath.SEPARATOR + projectName,
    700                 IResource.PROJECT);
    701         if (!status.isOK() || !Path.ROOT.isValidSegment(projectName)) {
    702             setErrorMessage(Messages.format(
    703                     JUnitMessages.JUnitLaunchConfigurationTab_error_invalidProjectName,
    704                     projectName));
    705             return;
    706         }
    707 
    708         IProject project = getWorkspaceRoot().getProject(projectName);
    709         if (!project.exists()) {
    710             setErrorMessage(JUnitMessages.JUnitLaunchConfigurationTab_error_projectnotexists);
    711             return;
    712         }
    713         IJavaProject javaProject = JavaCore.create(project);
    714         validateJavaProject(javaProject);
    715 
    716         try {
    717             if (!project.hasNature(AdtConstants.NATURE_DEFAULT)) {
    718                 setErrorMessage(
    719                         LaunchMessages.NonAndroidProjectError);
    720                 return;
    721             }
    722             String className = mTestText.getText().trim();
    723             if (className.length() == 0) {
    724                 setErrorMessage(JUnitMessages.JUnitLaunchConfigurationTab_error_testnotdefined);
    725                 return;
    726             }
    727             if (javaProject.findType(className) == null) {
    728                 setErrorMessage(Messages.format(
    729                         JUnitMessages.JUnitLaunchConfigurationTab_error_test_class_not_found,
    730                         new String[] { className, projectName }));
    731                 return;
    732             }
    733         } catch (CoreException e) {
    734             AdtPlugin.log(e, "validatePage failed"); //$NON-NLS-1$
    735         }
    736 
    737         validateInstrumentation();
    738     }
    739 
    740     private void validateJavaProject(IJavaProject javaProject) {
    741         if (!TestSearchEngine.hasTestCaseType(javaProject)) {
    742             setErrorMessage(JUnitMessages.JUnitLaunchConfigurationTab_error_testcasenotonpath);
    743             return;
    744         }
    745     }
    746 
    747     private void validateInstrumentation() {
    748         String instrumentation = getSelectedInstrumentation();
    749         if (instrumentation == null) {
    750             setErrorMessage(LaunchMessages.AndroidJUnitTab_NoRunnerError);
    751             return;
    752         }
    753         String result = mInstrValidator.validateInstrumentationRunner(instrumentation);
    754         if (result != InstrumentationRunnerValidator.INSTRUMENTATION_OK) {
    755             setErrorMessage(result);
    756             return;
    757         }
    758     }
    759 
    760     private String getSelectedInstrumentation() {
    761         int selectionIndex = mInstrumentationCombo.getSelectionIndex();
    762         if (mInstrumentations != null && selectionIndex >= 0 &&
    763                 selectionIndex < mInstrumentations.length) {
    764             return mInstrumentations[selectionIndex];
    765         }
    766         return null;
    767     }
    768 
    769     private String getSelectedTestSize() {
    770         if (mTestSizeCombo != null) {
    771             int index = mTestSizeCombo.getSelectionIndex();
    772             return TEST_SIZE_OPTIONS.get(index);
    773         } else {
    774             return null;
    775         }
    776     }
    777 
    778     private void setEnableContainerTestGroup(boolean enabled) {
    779         mContainerSearchButton.setEnabled(enabled);
    780         mContainerText.setEnabled(enabled);
    781     }
    782 
    783     private void setEnableSingleTestGroup(boolean enabled) {
    784         mProjLabel.setEnabled(enabled);
    785         mProjText.setEnabled(enabled);
    786         mProjButton.setEnabled(enabled);
    787         mTestLabel.setEnabled(enabled);
    788         mTestText.setEnabled(enabled);
    789         mSearchButton.setEnabled(enabled && mProjText.getText().length() > 0);
    790         mTestMethodLabel.setEnabled(enabled);
    791     }
    792 
    793     /* (non-Javadoc)
    794      * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
    795      */
    796     @Override
    797     public void setDefaults(ILaunchConfigurationWorkingCopy config) {
    798         IJavaElement javaElement = getContext();
    799         if (javaElement != null) {
    800             initializeJavaProject(javaElement, config);
    801         } else {
    802             // We set empty attributes for project & main type so that when one config is
    803             // compared to another, the existence of empty attributes doesn't cause an
    804             // incorrect result (the performApply() method can result in empty values
    805             // for these attributes being set on a config if there is nothing in the
    806             // corresponding text boxes)
    807             config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, EMPTY_STRING);
    808             config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER,
    809                     EMPTY_STRING);
    810         }
    811         initializeTestAttributes(javaElement, config);
    812     }
    813 
    814     private void initializeTestAttributes(IJavaElement javaElement,
    815             ILaunchConfigurationWorkingCopy config) {
    816         if (javaElement != null && javaElement.getElementType() < IJavaElement.COMPILATION_UNIT) {
    817             initializeTestContainer(javaElement, config);
    818         } else {
    819             initializeTestType(javaElement, config);
    820         }
    821     }
    822 
    823     private void initializeTestContainer(IJavaElement javaElement,
    824             ILaunchConfigurationWorkingCopy config) {
    825         config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER,
    826                 javaElement.getHandleIdentifier());
    827         initializeName(config, javaElement.getElementName());
    828     }
    829 
    830     private void initializeName(ILaunchConfigurationWorkingCopy config, String name) {
    831         if (name == null) {
    832             name = EMPTY_STRING;
    833         }
    834         if (name.length() > 0) {
    835             int index = name.lastIndexOf('.');
    836             if (index > 0) {
    837                 name = name.substring(index + 1);
    838             }
    839             name = getLaunchConfigurationDialog().generateName(name);
    840             config.rename(name);
    841         }
    842     }
    843 
    844     /**
    845      * Sets the main type & name attributes on the working copy based on the IJavaElement
    846      */
    847     private void initializeTestType(IJavaElement javaElement,
    848             ILaunchConfigurationWorkingCopy config) {
    849         String name = EMPTY_STRING;
    850         String testKindId = null;
    851         try {
    852             // only do a search for compilation units or class files or source references
    853             if (javaElement instanceof ISourceReference) {
    854                 ITestKind testKind = TestKindRegistry.getContainerTestKind(javaElement);
    855                 testKindId = testKind.getId();
    856 
    857                 IType[] types = TestSearchEngine.findTests(getLaunchConfigurationDialog(),
    858                         javaElement, testKind);
    859                 if ((types == null) || (types.length < 1)) {
    860                     return;
    861                 }
    862                 // Simply grab the first main type found in the searched element
    863                 name = types[0].getFullyQualifiedName('.');
    864 
    865             }
    866         } catch (InterruptedException ie) {
    867             // ignore
    868         } catch (InvocationTargetException ite) {
    869             // ignore
    870         }
    871         config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, name);
    872         if (testKindId != null) {
    873             config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
    874                     testKindId);
    875         }
    876         initializeName(config, name);
    877     }
    878 
    879     /* (non-Javadoc)
    880      * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName()
    881      */
    882     @Override
    883     public String getName() {
    884         return JUnitMessages.JUnitLaunchConfigurationTab_tab_label;
    885     }
    886 
    887     private IJavaElement chooseContainer(IJavaElement initElement) {
    888         @SuppressWarnings("rawtypes")
    889         Class[] acceptedClasses = new Class[] { IJavaProject.class,
    890                 IPackageFragment.class };
    891         TypedElementSelectionValidator validator = new TypedElementSelectionValidator(
    892                 acceptedClasses, false) {
    893             @Override
    894             public boolean isSelectedValid(Object element) {
    895                 return true;
    896             }
    897         };
    898 
    899         acceptedClasses = new Class[] { IJavaModel.class, IPackageFragmentRoot.class,
    900                 IJavaProject.class, IPackageFragment.class };
    901         ViewerFilter filter = new TypedViewerFilter(acceptedClasses) {
    902             @Override
    903             public boolean select(Viewer viewer, Object parent, Object element) {
    904                 if (element instanceof IPackageFragmentRoot &&
    905                         ((IPackageFragmentRoot) element).isArchive()) {
    906                     return false;
    907                 }
    908                 try {
    909                     if (element instanceof IPackageFragment &&
    910                             !((IPackageFragment) element).hasChildren()) {
    911                         return false;
    912                     }
    913                 } catch (JavaModelException e) {
    914                     return false;
    915                 }
    916                 return super.select(viewer, parent, element);
    917             }
    918         };
    919 
    920         AndroidJavaElementContentProvider provider = new AndroidJavaElementContentProvider();
    921         ILabelProvider labelProvider = new JavaElementLabelProvider(
    922                 JavaElementLabelProvider.SHOW_DEFAULT);
    923         ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(),
    924                 labelProvider, provider);
    925         dialog.setValidator(validator);
    926         dialog.setComparator(new JavaElementComparator());
    927         dialog.setTitle(JUnitMessages.JUnitLaunchConfigurationTab_folderdialog_title);
    928         dialog.setMessage(JUnitMessages.JUnitLaunchConfigurationTab_folderdialog_message);
    929         dialog.addFilter(filter);
    930         dialog.setInput(JavaCore.create(getWorkspaceRoot()));
    931         dialog.setInitialSelection(initElement);
    932         dialog.setAllowMultiple(false);
    933 
    934         if (dialog.open() == Window.OK) {
    935             Object element = dialog.getFirstResult();
    936             return (IJavaElement) element;
    937         }
    938         return null;
    939     }
    940 
    941     private String getPresentationName(IJavaElement element) {
    942         return mJavaElementLabelProvider.getText(element);
    943     }
    944 
    945     /**
    946      * Returns the current Java element context from which to initialize
    947      * default settings, or <code>null</code> if none.
    948      *
    949      * @return Java element context.
    950      */
    951     private IJavaElement getContext() {
    952         IWorkbenchWindow activeWorkbenchWindow =
    953             PlatformUI.getWorkbench().getActiveWorkbenchWindow();
    954         if (activeWorkbenchWindow == null) {
    955             return null;
    956         }
    957         IWorkbenchPage page = activeWorkbenchWindow.getActivePage();
    958         if (page != null) {
    959             ISelection selection = page.getSelection();
    960             if (selection instanceof IStructuredSelection) {
    961                 IStructuredSelection ss = (IStructuredSelection) selection;
    962                 if (!ss.isEmpty()) {
    963                     Object obj = ss.getFirstElement();
    964                     if (obj instanceof IJavaElement) {
    965                         return (IJavaElement) obj;
    966                     }
    967                     if (obj instanceof IResource) {
    968                         IJavaElement je = JavaCore.create((IResource) obj);
    969                         if (je == null) {
    970                             IProject pro = ((IResource) obj).getProject();
    971                             je = JavaCore.create(pro);
    972                         }
    973                         if (je != null) {
    974                             return je;
    975                         }
    976                     }
    977                 }
    978             }
    979             IEditorPart part = page.getActiveEditor();
    980             if (part != null) {
    981                 IEditorInput input = part.getEditorInput();
    982                 return (IJavaElement) input.getAdapter(IJavaElement.class);
    983             }
    984         }
    985         return null;
    986     }
    987 
    988     private void initializeJavaProject(IJavaElement javaElement,
    989             ILaunchConfigurationWorkingCopy config) {
    990         IJavaProject javaProject = javaElement.getJavaProject();
    991         String name = null;
    992         if (javaProject != null && javaProject.exists()) {
    993             name = javaProject.getElementName();
    994         }
    995         config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, name);
    996     }
    997 
    998     private void setButtonGridData(Button button) {
    999         GridData gridData = new GridData();
   1000         button.setLayoutData(gridData);
   1001         LayoutUtil.setButtonDimensionHint(button);
   1002     }
   1003 
   1004     /* (non-Javadoc)
   1005      * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#getId()
   1006      */
   1007     @Override
   1008     public String getId() {
   1009         return "com.android.ide.eclipse.adt.launch.AndroidJUnitLaunchConfigurationTab"; //$NON-NLS-1$
   1010     }
   1011 
   1012     /**
   1013      * Loads the UI with the instrumentations of the specified project, and stores the
   1014      * instrumentations in <code>mInstrumentations</code>.
   1015      *
   1016      * @param project the {@link IProject} to load the instrumentations from.
   1017      */
   1018     private void loadInstrumentations(IProject project) {
   1019         try {
   1020             mInstrValidator = new InstrumentationRunnerValidator(project);
   1021             mInstrumentations = mInstrValidator.getInstrumentationNames();
   1022             if (mInstrumentations.length > 0) {
   1023                 mInstrumentationCombo.removeAll();
   1024                 for (String instrumentation : mInstrumentations) {
   1025                     mInstrumentationCombo.add(instrumentation);
   1026                 }
   1027                 // the selection will be set when we update the ui from the current
   1028                 // config object.
   1029                 return;
   1030             }
   1031         } catch (CoreException e) {
   1032             AdtPlugin.logAndPrintError(e, project.getName(),
   1033                     LaunchMessages.AndroidJUnitTab_LoadInstrError_s,
   1034                     SdkConstants.FN_ANDROID_MANIFEST_XML);
   1035         }
   1036         // if we reach this point, either project is null, or we got an exception during
   1037         // the parsing. In either case, we empty the instrumentation list.
   1038         mInstrValidator = null;
   1039         mInstrumentations = null;
   1040         mInstrumentationCombo.removeAll();
   1041     }
   1042 
   1043     /**
   1044      * Overrides the {@link StandardJavaElementContentProvider} to only display Android projects
   1045      */
   1046     private static class AndroidJavaElementContentProvider
   1047             extends StandardJavaElementContentProvider {
   1048 
   1049         /**
   1050          * Override parent to return only Android projects if at the root. Otherwise, use parent
   1051          * functionality.
   1052          */
   1053         @Override
   1054         public Object[] getChildren(Object element) {
   1055             if (element instanceof IJavaModel) {
   1056                 return BaseProjectHelper.getAndroidProjects((IJavaModel) element, null /*filter*/);
   1057             }
   1058             return super.getChildren(element);
   1059         }
   1060     }
   1061 }
   1062