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