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