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