Version 33, last updated by Mirco Dotta at December 15, 2011 16:37 UTC

Testing the Presentation Compiler in isolation

Sometimes when the Scala IDE plugin for Eclipse is not working as expected it is difficult to tell which component is actually causing the problem: is it because of the Presentation Compiler (and therefore the Scala compiler), or is it a bug in the Eclipse side?

You are simply dealing with too much complexity. The only pragmatic solution you had so far was to start a debugging session and step through code. That involved a fair amount of complexity (despite the Scala compiler is really well written) , and worse, it's usually very time consuming.

The solution now is to simply write a test first (folks used to TDD will surely appreciate it) if you are unsure if the presentation compiler is on blame for a specific issue. This will have two benefits:

  1. You can easily make up your mind and know if the problem is in the compiler, and
  2. Even if the compiler is not on blame, it can be very useful to provide the test as a regression test. This way we can prevent the feature to break in the future. 

Sweet, isn't it?

And defining a new test is really simple. But before being able to write a test we need some infrastructure work.

First thing, you will need to checkout sources of the Scala compiler. The Presentation Compiler is simply a thin interface to the compiler, specifically optimized for the IDE. Fire up your terminal and type (below command pasted from the official Scala website:

$ svn co http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk scala

Once the project is checked out, you will have to build the sources using Ant (not the most compelling building tool on Earth, we know. The good news is that the Scala team is moving to SBT right now, so soon enough we'll be relieved from using Ant). To build the compiler simply type `ant`. It will take some time to build the compiler and standard library, but once it's done you won't need to rebuild it anymore (unless you do an `svn update`).

Hopefully, you should have successfully built the scala compiler. It's finally time to write your first test for the Presentation Compiler.

Tests for the Presentation Compiler are placed in folder `<project-root>/test/files/presentation`. There, you will see several other directories, each of them is associated with (at least) a file that matches the folder's name. You will be soon know what each folder and file is for, but what is important to keep in mind at this point is that the test framework works by convention. So, folders and files with the same name are used for running the same test.

Defining a new test boils down to performing the following steps:

  1. Create a testXXX folder (the name is up to you, but please avoid spaces and don't use any esoteric character :)
  2. Inside testXXX, create a `src` folder (the name here is important, remember we use convention over configuration). In `src` you will put your `.scala` test file (we will see below what this file should contain to work). The test file is the definition of your test, i.e., it will tell the test framework how you want to exercise the Presentation Compiler.
  3. testXXX also needs to contain a `Runner.scala`. In its simplest form this scala file only contains the following object declaration (again, it's important to name the object `Test`)
    1. object Test extends scala.tools.nsc.interactive.tests.InteractiveTest

At this point you are almost done. Well, of course the one thing that is still missing is the test file (remember point 2. above). So, let's have a look at how we can create a new test, and let's do that with a practical example.

A while back we had some issue with completion. It was unclear why, but sometimes completion was just not working as expected. Since completion is a feature that is offered by the Presentation Compiler (via method askCompletionAt), it was difficult to tell where the problem was. We had a very simple code's snippet for which completion was not working in the IDE. Let's have a look at it (this is extracted from the bug report #1000475):

class SimpleCompletion {val v = new Object; v.toS /* ask completion here won't work */ }

So, to made up our mind and know if that was a problem with the Presentation Compiler, we simply created a test folder named ide-bug-1000475 with the folder structure described above. Then we added the following test (in a .scala file) in the `src` directory:

class SimpleCompletion { val v = new Object; v.toS/*!*/ }

You might wonder what `/*!*/` is for. We refer to it as a text marker. Text markers are used by the test framework to associate an action to a particular position in the source file. `/*!*/` will hence be mapped into a askCompletionAt request in the Presentation Compiler, passing the position of the marker as an argument.

Asking completion is not the only action you can perform in a test, there are actually other test markers that are defined out-of-the-box and you can immediately use, and more are likely to come in the future. The currently available markers are:

object CompletionMarker extends TestMarker("/*!*/")

object TypeMarker extends TestMarker("/*?*/")

object HyperlinkMarker extends TestMarker("/*#*/")

Each of them is very easy to use. Just look at the other tests if you have any doubt.

Now, we can try running the test. Go under `<project-root>`/test` and type

$ ./partest files/presentation/ide-bug-1000475

You should see something like

Testing individual files
testing: [...]/files/presentation/ide-bug-1000475                     [  OK  ]

Well, actually if you have written your own test, it will be likely fail and not succeed. The reason is actually pretty simple, we didn't yet discussed how to define a test's oracle, i.e., how do we assert that the test is correct/wrong?

This turns out to be very simple as well. Did you see the `.check` files in `<project-root>/test/files/presentation` folder? They are the test oracles. So, go ahead and create a `.check` file that matches the test folder's name (for our running example it would be `ide-bug-1000475.check`). Inside you simply have to append the output that was printed in the console when the test failed (some `sed` might be needed to clean up the text ;), assuming you think the output is correct (if the output is incorrect, you might just have discovered a bug in the Presentation Compiler!).

Re-run the test and hopefully it should pass. If it does succeed, then the odds are that you have to fix the issue in the Eclipse side, meaning that the Scala compiler is not on blame. The bad news is that you will need some more digging to narrow down the cause. The good news is that now you are now one step closer to catch it :)