Skip to content

Latest commit

 

History

History
157 lines (118 loc) · 6.42 KB

CONTRIBUTING.md

File metadata and controls

157 lines (118 loc) · 6.42 KB

Contributing

This project follows the Scala Code of Conduct.

Requirements

You need the following tools:

To install Java and sbt we recommend using Coursier (the official Scala installer) and following these instructions.

Setting Up

  1. Clone the repository and its submodule
$ git clone [email protected]:scalacenter/scala-debug-adapter.git
$ cd scala-debug-adapter
$ git submodule update --init
  1. Start the sbt shell in the terminal and compile
$ sbt
sbt:root> compile
  1. Import the project in your code editor

Congratulations! You are now ready to start coding.

Project Structure

All the modules are in the modules folder. They are composed of:

  • java-debug: A fork of microsoft/java-debug on which the scala-debug-adapter depends. The java-debug project was originally a Maven project. The scala-debug-adapter only depends on the com.microsoft.java.debug.core module and we compile it directly using sbt. So we can ignore the Maven configuration and the other modules.
  • core: The scala-debug-adapter itself which contains the DebugServer and all its configuration classes.
  • expression-compiler: An extension of the Scala compiler for compiling a Scala expression in the context of debugging (the current stack frame of a paused JVM) into a class file that can be loaded and invoked. The expression-compiler is cross-compiled on all supported minor Scala versions. It is class-loaded by the ExpressionCompiler object from core.
  • scala-3-step-filter: The implementation of StepFilter for Scala 3. The scala-3-step-filter, compiled with Scala 3, is class-loaded by the Scala3StepFilter object from core in a separate class loader. (The Scala 2 step filter is in core directly because it is compiled in Scala 2 and does not need to be class-loaded separately)
  • tests: The test module. It contains the test infrastructure, in tests/src/main, and the unit tests, in tests/src/test.
  • sbt-plugin: The sbt plugin that allows sbt to start the DAP server.

Unit-Testing the debugger

Most of the tests are hosted in the tests module.

The tests/src/main folder contains the testing infrastructure:

  • the TestingDebugClient class: a client of the debug server used for testing
  • the TestingDebuggee object: to configure, compile and debug some Scala processes as debuggees for testing the debugger.
  • the DebugTestSuite abstract class: an extension of munit.TestSuite that allows describing a debug scenario and checking that it happens as described.

As instance, we can write and run this test:

package ch.epfl.scala.debugadapter

import ch.epfl.scala.debugadapter.testfmk.*

class MyDebugTests extends DebugTestSuite {
  private val scalaVersion: ScalaVersion = ScalaVersion.`3.2`

  test("my simple test") {
    val source =
      """|package example
         |
         |object Main {
         |  def main(args: Array[String]): Unit = {
         |    val x = 42
         |    val msg = s"Hello, $x"
         |    println(msg)
         |  }
         |}
         |""".stripMargin
    // configure the debuggee: a Scala 3 main class `example.Main`
    implicit val debuggee: TestingDebuggee = TestingDebuggee.mainClass(source, "example.Main", scalaVersion)
    check(
      // check that the debuggee stops on a breakpoint on line 7
      Breakpoint(7),
      // check that the evaluation of expression x is 42  
      Evaluation.success("x", 42),
      // check that a stepIn request goes into the Predef$.println method
      StepIn.method("Predef$.println(Object)")
    )
  }
}

The organization of the tests is:

  • DebugServerTests: general tests about starting and exiting debug sessions on all kinds of debugees
  • ScalaDebugTests: general tests about inspecting threads, stack frames and variables in Scala programs
  • ScalaEvaluationTests: tests about the Scala expression evaluator
  • SourceBreakpointTests: tests about the different kinds of source breakpoints: conditional breakpoints, hit count breakpoints, and log points.
  • StepFilterTests: tests about stepping in and out of Scala methods.

The tests are quite heavy to run because every test compiles a small Scala program, creates a debug server that starts a fresh JVM to run the program, and executes a debugging scenario. All of this is repeated on a few Scala versions.

Running all the test can take more than 30 minutes. We recommend that locally you run only some tests using the sbt testOnly task or using your IDE:

sbt:root> testOnly ch.epfl.scala.debugadapter.Scala32EvaluationTests

Running the debugger locally

You can install and start a local version of the scala-debug-adapter using Metals.

  1. In sbt, check out the version of the scala-debug-adapter project.
sbt:root> version
...
[info] 3.0.2-SNAPSHOT

In the following steps we will use 3.0.2-SNAPSHOT.

  1. Publish the scala-debug-adapter locally.
sbt:root> publishLocal
  1. Open an sbt project in VS Code with Metals. As we will run the debugger in this project, it must contain a main class or some test suites.

  2. Open or create the project/plugins.sbt file. This is where we can configure the version of the scala-debug-adapter that we want to use.

  3. Configure the sbt-debug-adapter plugin in the project/plugins.sbt file. Here you should use the local version that you just published, instead of "3.0.2-SNAPSHOT".

addSbtPlugin("ch.epfl.scala" % "sbt-debug-adapter" % "3.0.2-SNAPSHOT")
  1. Switch the build server. In VS Code, open the command palette, run Metals: switch build server and select sbt.

  2. Since we changed the build file, we may need to reload sbt and re-import the build. You can do so by opening the command palette and running the Metals: Import build command, or by clicking on Import build in Metals'tab.

  3. Start the debugger:

  • click on the debug lens on top of a main method
  • or right-click on a test and run Debug test
  • or add a Scala debug configuration in the .vscode/launch.json file