#2088 new
Tobin Stelling

Cannot run tests from eclipse if unit test and unit-under-test are in same package and a package-info exists

Reported by Tobin Stelling | April 6th, 2017 @ 02:57 PM

Framework version: 1.4.3
Platform you're using: SUSE 42.2
Eclipse: Neon.2 (4.6.2)

Reproduction steps:
1. Create a new play application and configure it for use with Eclipse
2. Under "app," create a new package named "foobar" and create a "package-info.java" file for the package
3. Under "test," create a new package named "foobar" and create a class that extends play's UnitTest. Name it anything. Give the class a single @Test method that does nothing.
4. Try to run this class via Eclipse's "Run As" -> "Junit Test"

Details:

The test will fail with an "initializationError:"

play.exceptions.UnexpectedException: Unexpected Error
        at play.Play.start(Play.java:585)
        at play.test.PlayJUnitRunner.<init>(PlayJUnitRunner.java:38)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
        at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createUnfilteredTest(JUnit4TestLoader.java:84)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:70)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:43)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.IllegalArgumentException: foobar
        at java.lang.ClassLoader.definePackage(ClassLoader.java:1594)
        at play.classloading.ApplicationClassloader.loadApplicationClass(ApplicationClassloader.java:153)
        at play.classloading.ApplicationClassloader.loadPackage(ApplicationClassloader.java:211)
        at play.classloading.ApplicationClassloader.loadApplicationClass(ApplicationClassloader.java:155)
        at play.classloading.ApplicationClassloader.getAllClasses(ApplicationClassloader.java:438)
        at play.Play.start(Play.java:539)
        ... 18 more

The problem is in play.classloading.ApplicationClassloader's loadApplicationClass() method:

            if (!applicationClass.isClass()) {
                definePackage(applicationClass.getPackage(), null, null, null, null, null, null, null);
            } else {
                loadPackage(name);
            }

Eclipse's junit test runner has already resolved the junit test class in the root class loader (thus adding the package "foobar"), so by the time Play starts up and performs its classloading, the package "foobar" is already defined. Thus, when it calls definePackge("foobar"), definePackage() throws a IllegalArgumentException because this package already exists.

I verified this by adding a breakpoint in play.Play, just before it executes Play.classloader.getAllClasses() in Play.start(). Adding the expression:

Thread.currentThread().getContextClassLoader().getPackage("foobar")

Showed that package foobar was loaded by sun.misc.Launcher$AppClassLoader

Note that this is not a problem when running tests via play's test harness.

A code change in ApplicationClassloader seems to fix the problem:

            if (!applicationClass.isClass()) {
                if(null == getPackage(applicationClass.getPackage())) {
                    definePackage(applicationClass.getPackage(), null, null, null, null, null, null, null);
                }
            } else {
                loadPackage(name);
            }

ApplicationClassloader.loadApplicationClass() has two blocks of code that call definePackage, one for precompiled and the other for non-precompiled. I only tested the non-precompiled code path, because it seemed unlikely that one would run tests in eclipse using play's precompiled flag.

Comments and changes to this ticket

  • Robert Rettig

    Robert Rettig June 30th, 2017 @ 09:52 AM

    I only tested the non-precompiled code path, because it seemed unlikely that one would run tests in eclipse using play's precompiled flag.

    That is not unlikely! Precompiled means Play! is running in production mode. Production testing is and should the only valid test environment.
    See https://github.com/playframework/play1/issues/1154

  • Tobin Stelling

    Tobin Stelling July 3rd, 2017 @ 01:07 PM

    I reran the steps with play set to PROD mode and got the same error. I'll look into adding the same fix on the PROD code path.

    Your tests should work in both DEV and PROD mode. When I'm running the tests from eclipse, this typically means I'm in the middle of development. In this case I'm not likely to have application.mode set to PROD. Perhaps there is a way to configure the eclipse test launcher to have play run in PROD mode while having the application.conf's application.mode set to DEV mode; I'll look into this.

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2>Play framework</h2>

Play makes it easier to build Web applications with Java. It is a clean alternative to bloated Enterprise Java stacks. It focuses on developer productivity and targets RESTful architectures. Learn more on the <a href="http://www.playframework.org">http://www.playframework.org</a> website.<br><br>

<h2>Source code is hosted on github</h2>Check out our repository at <a href="http://github.com/playframework/play">http://github.com/playframework/play</a><br><br>

<h2>Contributing, creating a patch</h2> Please read the <a href="http://play.lighthouseapp.com/projects/57987/contributor-guide">contributor guide</a><br><br>

<h2>Reporting Security Vulnerabilities</h2> Since all bug reports are public, please report any security vulnerability directly to <em>guillaume dot bort at gmail dot com</em>.<br><br>

<h2>Creating a bug report</h2> Bug reports are incredibly helpful, so take time to report bugs and request features in our ticket tracker. We’re always grateful for patches to Play’s code. Indeed, bug reports with attached patches will get fixed far quickly than those without any.<br><br>

Please include as much relevant information as possible including the exact framework version you're using and a code snippet that reproduces the problem.<br><br>

Don't have too much expectations. Unless the bug is really a serious "everything is broken" thing, you're creating a ticket to start a discussion. Having a patch (or a branch on Github we can pull from) is better, but then again we'll only pull high quality branches that make sense to be in the core of Play.

Pages