• 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.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      */
createControl(Composite parent)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 
createSpacer(Composite comp)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 
createSingleTestSection(Composite comp)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 
createTestContainerSelectionGroup(Composite comp)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 
createInstrumentationGroup(Composite comp)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 
handleContainerSearchButtonSelected()301     private void handleContainerSearchButtonSelected() {
302         IJavaElement javaElement = chooseContainer(mContainerElement);
303         if (javaElement != null) {
304             setContainerElement(javaElement);
305         }
306     }
307 
setContainerElement(IJavaElement javaElement)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      */
initializeFrom(ILaunchConfiguration config)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 
updateInstrumentationFromConfig(ILaunchConfiguration config)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 
updateProjectFromConfig(ILaunchConfiguration config)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 
updateTestTypeFromConfig(ILaunchConfiguration config)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 
setTestMethodLabel(String testMethodName)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 
updateTestContainerFromConfig(ILaunchConfiguration config)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      */
performApply(ILaunchConfigurationWorkingCopy config)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 
mapResources(ILaunchConfigurationWorkingCopy config)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
dispose()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
getImage()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      */
handleSearchButtonSelected()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 
getTestKind()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      */
handleProjectButtonSelected()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      */
getJavaProject()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      */
getProjectName()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      */
getWorkspaceRoot()588     private IWorkspaceRoot getWorkspaceRoot() {
589         return ResourcesPlugin.getWorkspace().getRoot();
590     }
591 
592     /**
593      * Convenience method to get access to the java model.
594      */
getJavaModel()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
isValid(ILaunchConfiguration config)603     public boolean isValid(ILaunchConfiguration config) {
604         validatePage();
605         return getErrorMessage() == null;
606     }
607 
testModeChanged()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 
validatePage()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 
validateJavaProject(IJavaProject javaProject)685     private void validateJavaProject(IJavaProject javaProject) {
686         if (!TestSearchEngine.hasTestCaseType(javaProject)) {
687             setErrorMessage(JUnitMessages.JUnitLaunchConfigurationTab_error_testcasenotonpath);
688             return;
689         }
690     }
691 
validateInstrumentation()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 
getSelectedInstrumentation()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 
setEnableContainerTestGroup(boolean enabled)714     private void setEnableContainerTestGroup(boolean enabled) {
715         mContainerSearchButton.setEnabled(enabled);
716         mContainerText.setEnabled(enabled);
717     }
718 
setEnableSingleTestGroup(boolean enabled)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      */
setDefaults(ILaunchConfigurationWorkingCopy config)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 
initializeTestAttributes(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config)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 
initializeTestContainer(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config)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 
initializeName(ILaunchConfigurationWorkingCopy config, String name)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      */
initializeTestType(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config)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      */
getName()817     public String getName() {
818         return JUnitMessages.JUnitLaunchConfigurationTab_tab_label;
819     }
820 
821     @SuppressWarnings("unchecked")
chooseContainer(IJavaElement initElement)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 
getPresentationName(IJavaElement element)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      */
getContext()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 
initializeJavaProject(IJavaElement javaElement, ILaunchConfigurationWorkingCopy config)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 
setButtonGridData(Button button)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
getId()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      */
loadInstrumentations(IProject project)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
getChildren(Object element)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