Permalink
Browse files

Merge pull request #229 from dragos/issue/fix-launch-tests-1000740

Use the proper platform methods in the Scala launcher
  • Loading branch information...
2 parents 6b9ceb4 + c74dde4 commit 8a424af48d564df94b5eb0592b6c1c4419920a08 @dragos dragos committed Nov 14, 2012
@@ -15,6 +15,13 @@ import org.junit.runners.JUnit4
import org.eclipse.core.resources.IncrementalProjectBuilder
import org.eclipse.core.runtime.NullProgressMonitor
import org.mockito.verification.VerificationMode
+import org.eclipse.debug.core.ILaunchConfiguration
+import org.eclipse.debug.core.DebugPlugin
+import org.junit.Assert
+import org.eclipse.debug.core.ILaunchManager
+import scala.tools.eclipse.testsetup.FileUtils
+import scala.tools.eclipse.testsetup.SDTTestUtils
+import org.junit.Ignore
@RunWith(classOf[JUnit4])
class MainClassVerifierTest {
@@ -36,7 +43,16 @@ class MainClassVerifierTest {
}
}
- @Test
+ /** This test is ignored becase there is no way to test it without seriously risking impairing the IDE
+ *
+ * The launcher works by checking if there are build errors, and if yes delegating to a 'status handler'
+ * The status handler displays a dialog and decides whether to proceed or not. If the status handler
+ * extension point is not available, it always continues (`finalLaunchCheck` returns true).
+ *
+ * If we add our own status handler in org.scala-ide.sdt.core.tests, we risk replacing the default
+ * dialog if the user installed the 'tests' plugin, and never seeing the warning for build errors.
+ */
+ @Test @Ignore("There is no way to test launching without using an extension point that intercepts all status requests")
def reportErrorWhenMainContainsCompilationErrors() {
val mainName = "MainWithCompilationErrors"
val main = """
@@ -45,10 +61,33 @@ class MainClassVerifierTest {
}
""".format(mainName)
+ val launchMemento = """
+ <launchConfiguration type="scala.application">
+ <stringAttribute key="bad_container_name" value="/main-launcher/f"/>
+ <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+ <listEntry value="/main-launcher/src/MainWithCompilationErrors.scala"/>
+ </listAttribute>
+ <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+ <listEntry value="1"/>
+ </listAttribute>
+ <mapAttribute key="org.eclipse.debug.core.preferred_launchers">
+ <mapEntry key="[debug]" value="scala.application.new"/>
+ </mapAttribute>
+ <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="MainWithCompilationErrors"/>
+ <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="main-launcher"/>
+ </launchConfiguration>
+"""
+
createSource(EmptyPackage, mainName, main)
- val mainTypeName = mainName
+ SDTTestUtils.addFileToProject(project.underlying, "Main.launch", launchMemento)
+
+ val config = DebugPlugin.getDefault().getLaunchManager().getLaunchConfiguration(project.underlying.getFile("Main.launch"))
+ val delegate = new ScalaLaunchDelegate()
+ project.underlying.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor)
- runTest(mainTypeName, main, times(1)) // if there are compilation errors then no binaries are produced!
+ Assert.assertFalse("Compilation errors prevent launching",
+ delegate.preLaunchCheck(config, ILaunchManager.DEBUG_MODE, new NullProgressMonitor)
+ && delegate.finalLaunchCheck(config, ILaunchManager.DEBUG_MODE, new NullProgressMonitor))
}
@Test
@@ -100,6 +100,9 @@ class ScalaPlugin extends AbstractUIPlugin with PluginLogConfigurator with IReso
def classpathProblemMarkerId = pluginId + ".classpathProblem"
def settingProblemMarkerId = pluginId + ".settingProblem"
+ /** All Scala error markers. */
+ val scalaErrorMarkers = Set(classpathProblemMarkerId, problemMarkerId, settingProblemMarkerId)
+
// Retained for backwards compatibility
val oldPluginId = "ch.epfl.lamp.sdt.core"
val oldLibraryPluginId = "scala.library"
@@ -18,29 +18,18 @@ class MainClassVerifier(reporter: MainClassVerifier.type#ErrorReporter) {
* @typeName The fully-qualified main type name.
*/
def execute(project: ScalaProject, mainTypeName: String): IStatus = {
- // 1. No binaries are produced if the project contains compilation errors.
- if (project.buildManager.hasErrors) {
- return projectHasBuildErrors(project.underlying.getName)
- }
-
// Try to locate the type
val element = project.javaProject.findType(mainTypeName)
- // 2. The main type won't be found if the provided ``mainTypeName`` (fully-qualified name) doesn't
- // reference an existing type. (this is a workaround for #1000541).
+ // The main type won't be found if the provided ``mainTypeName`` (fully-qualified name) doesn't
+ // reference an existing type. (this is a workaround for #1000541).
if (element == null) {
return mainTypeCannotBeLocated(project.underlying.getName, mainTypeName)
}
new Status(IStatus.OK, ScalaPlugin.plugin.pluginId, "")
}
- private def projectHasBuildErrors(projectName: String): IStatus = {
- val errMsg = "Project '%s' contains compilation errors (therefore, no binaries have been produced).".format(projectName)
- reporter.report(errMsg)
- new Status(IStatus.ERROR, ScalaPlugin.plugin.pluginId, errMsg)
- }
-
private def mainTypeCannotBeLocated(projectName: String, mainTypeName: String): IStatus = {
val errMsg = ("Cannot locate main type '%s' in project '%s'. For this to work, the package name in " +
"the source needs to match the source's physical location.\n" +
@@ -17,9 +17,12 @@ import org.eclipse.core.runtime.IPath
import org.eclipse.jdt.core.IJavaProject
import org.eclipse.core.runtime.Status
import org.eclipse.core.runtime.IStatus
+import org.eclipse.core.resources.IMarker
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate
class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
- def launch(configuration: ILaunchConfiguration, mode: String, launch: ILaunch, monitor0: IProgressMonitor) {
+ /** This code is very heavily inspired from `AbstractJavaLaunchConfigurationDelegate`. */
+ def launch(configuration: ILaunchConfiguration, mode: String, launch: ILaunch, monitor0: IProgressMonitor) {
val monitor = if (monitor0 == null) new NullProgressMonitor() else monitor0
@@ -48,6 +51,8 @@ class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
// VM-specific attributes
val vmAttributesMap = getVMSpecificAttributesMap(configuration)
+ // adding Scala libraries is the only difference compared to the Java Launcher
+ // TODO: do we still need this?
val modifiedAttrMap: mutable.Map[String, Array[String]] =
if (vmAttributesMap == null) mutable.Map() else vmAttributesMap.asInstanceOf[java.util.Map[String,Array[String]]]
val classpath0 = getClasspath(configuration)
@@ -76,14 +81,6 @@ class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
// stop in main
prepareStopInMain(configuration)
- // verify that the main classfile exists
- val project = getJavaProject(configuration)
- ScalaPlugin.plugin.asScalaProject(project.getProject).foreach { scalaProject =>
- val mainClassVerifier = new MainClassVerifier(new UIErrorReporter)
- val status = mainClassVerifier.execute(scalaProject, mainTypeName)
- if (status.getCode() != IStatus.OK) throw new CoreException(status)
- }
-
// done the verification phase
monitor.worked(1)
@@ -105,6 +102,27 @@ class ScalaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
monitor.done()
}
}
+
+ /** Scala problem markers should prevent a launch. This integrates with the platform and correctly displays a dialog. */
+ override protected def isLaunchProblem(problemMarker: IMarker): Boolean =
+ super.isLaunchProblem(problemMarker) || {
+ val isError = Option(problemMarker.getAttribute(IMarker.SEVERITY)).map(_.asInstanceOf[Integer].intValue >= IMarker.SEVERITY_ERROR).getOrElse(false)
+ isError && ScalaPlugin.plugin.scalaErrorMarkers.contains(problemMarker.getType())
+ }
+
+ /** Stop a launch if the main class does not exist. */
+ override def finalLaunchCheck(configuration: ILaunchConfiguration, mode: String, monitor: IProgressMonitor): Boolean = {
+ super.finalLaunchCheck(configuration, mode, monitor) && {
+ // verify that the main classfile exists
+ val project = getJavaProject(configuration)
+ val mainTypeName = getMainTypeName(configuration)
+ ScalaPlugin.plugin.asScalaProject(project.getProject) map { scalaProject =>
+ val mainClassVerifier = new MainClassVerifier(new UIErrorReporter)
+ val status = mainClassVerifier.execute(scalaProject, mainTypeName)
+ status.getCode() == IStatus.OK
+ } getOrElse false
+ }
+ }
private def toInclude(vmMap: mutable.Map[String, Array[String]], classpath: List[String],
configuration: ILaunchConfiguration): List[String] =

0 comments on commit 8a424af

Please sign in to comment.