It has been a long time since my last post. I was very busy. Sorry for that.
Here comes another technical article on a book: “Contributing to Eclipse: Principles, Patterns, and Plug-Ins”, published by Addison-Wesley. It is written by Erich Gamma (co-author of the “OO-Bible” Design Patterns, technical director of the IBM Research lab Zurich and developer of the Eclipse Platform) and Kent Beck (creator of Extreme Programming).
The book is obviously on how to extend the Eclipse platform with your own “contributions”, aka Plug-Ins. The book is very interesting as it comes from two authors who “come from the inside” of Eclipse. Thus they reveal all kinds of background information that a normal author could easily have missed.
However the book was written in 2003 and builds on a previous Eclipse release, prior version 3.0. When reading through the examples in the book, I eventually got to chapter 7, where Erich and Kent are developing the JUnit Plug-In. I think the one nowadays shipped with the standard Eclipse distribution.
There, page 62, they say: “For now, we can’t imagine how your understanding of Eclipse would be helped by seeing the details of starting a new virtual machine and communicating with it via sockets. See Appendix A for the details.”
Here they talk about how to properly run your test cases. That is “outside” the Eclipse JVM instance. To easily get above that bump, I downloaded their source code from here. In this archive, there are two classes that are senseless for the reader to implement himself, that is
SocketTestRunner. So I imported them into my own Eclipse Project (Note: Circle_1!).
First I changed the obvious stuff, like in TestRunner.java, line 77 and 130:
JUnitPlugin plugin = JUnitPlugin.getPlugin();
to match my slightly different class names:
MyJUnitPlugin plugin = MyJUnitPlugin.getPlugin();
However, when I started my Plug-In Project (“run as Eclipse Application”) and selected an object type in a dummy project (“ADemoProject”) and then tried to start my own JUnit implementation (“Run MyTest”), nothing happened.
The Eclipse View “Error Log” gave the hint that there was a NullPointerException in TestRunner, line 78:
java.lang.NullPointerException at org.eclipse.core.runtime.Plugin.getDescriptor(Plugin.java:268) at org.theyellowmarker.gamma.TestRunner.computeClasspath(TestRunner.java:78)
But then looking at my Eclipse source of TestRunner, the deprecation warnings gave me the hint to do a bit of research, have a look at the screenshot below:
Obviously, plugin.getDescriptor() was the reason for the NullPointerException. Used to the standard Java JDK philosophy, where deprecated methods usually still do their job somehow, it took my a bit of Eclipse API reading to figure out how to recode the whole section. Again, see the screenshot below:
To get the plug-in path for our plug-in, we have to get “the Bundle” from the static method of
// has to be unique id of activation plugin. Bundle bundle = Platform.getBundle("org.theyellowmarker.MyJUnit");
Note that the string “org.theyellowmarker.MyJUnit” is the unique id of my plug-in in the MANIFEST.MF file. You have to change it according to your setup. The url of the project is then found by the subsequent lines:
URL url = FileLocator.find(bundle, new Path("/"), null); classPath = FileLocator.toFileURL(new URL(url, "bin")).getFile(); classPath = FileLocator.toFileURL(new URL(url, "junit.jar")).getFile();
Note: I am not sure about the last line here. Could be useless.
Next, also replace the deprecated SocketUtil.findUnusedLocalPort on line 61 with:
port = SocketUtil.findFreePort();
Last, the SocketTestRunner class is given to the external JVM by name, so you have to change the first line of the class, where MAIN_CLASS is defined, to match your own package structure:
static final String MAIN_CLASS = "org.eclipse.contribution.junit.SocketTestRunner";
static final String MAIN_CLASS = "org.theyellowmarker.gamma.SocketTestRunner";
Now everything works again, as expected. You can download my plug-project here: myjunit.zip
and the dummy project containing the test cases here: ademoproject.zip
Make sure, when running the plug-in “in action” (that is in the second Eclipse workbench) to right click on the ASillyClassTest symbol with the green class dot on its left, otherwise, you don’t get the “Run My Test” menu entry in the context menu:
On test will fail, one will succeed. The console output on the first Eclipse workbench should be something like this:
2 test[s] started... Test org.theyellowmarker.test.ASillyClassTest->testDemoTrue() started. Test org.theyellowmarker.test.ASillyClassTest->testDemoFalse() started. Test org.theyellowmarker.test.ASillyClassTest->testDemoFalse() failed. junit.framework.AssertionFailedError: null at junit.framework.Assert.fail(Assert.java:47) at junit.framework.Assert.assertTrue(Assert.java:20) at junit.framework.Assert.assertFalse(Assert.java:34) at junit.framework.Assert.assertFalse(Assert.java:41) at org.theyellowmarker.test.ASillyClassTest.testDemoFalse(ASillyClassTest.java:27) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at junit.framework.TestCase.runTest(TestCase.java:168) at junit.framework.TestCase.runBare(TestCase.java:134) at junit.framework.TestResult$1.protect(TestResult.java:110) at junit.framework.TestResult.runProtected(TestResult.java:128) at junit.framework.TestResult.run(TestResult.java:113) at junit.framework.TestCase.run(TestCase.java:124) at junit.framework.TestSuite.runTest(TestSuite.java:232) at junit.framework.TestSuite.run(TestSuite.java:227) at junit.framework.TestSuite.runTest(TestSuite.java:232) at junit.framework.TestSuite.run(TestSuite.java:227) at org.theyellowmarker.gamma.SocketTestRunner.runTests(SocketTestRunner.java:39) at org.theyellowmarker.gamma.SocketTestRunner.main(SocketTestRunner.java:26) All tests finished
As I move forward in the Book, I will publish eventual updates on this topic.
P.S: Here is the full set of files after “Circle 1″: first-circle.zip