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