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