New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Classpath scanning fails for malformed class names #401
Comments
Thanks for the bug report -- we should definitely fix it for M2. |
Thanks, if you need any more info, just give me a heads up |
On second thought, I'm not sure how to fix this. I'm not comfortable with catching @junit-team/junit-lambda Any ideas? |
No, I don't have any ideas on how to check this. It might be fine if the exception is just wrapped with another InternalError with a different message though. If the filename of the offending class is included in the message it is trivial to diagnose the problem, that will already help tremendously. |
I think we should just add a try-catch block around the following lines in Optional<Class<?>> classForClassFile = loadClassForClassFile(file, packageName);
classForClassFile.filter(classFilter).ifPresent(classesCollector::add); Then we have two options:
In both cases, we should include a meaningful error message stating the canonical name of the class that caused the error, and even then the retrieval of the canonical name should be performed defensively within another try-catch block. |
A Java class name cannot start with a number but it can start with (and contain within) any letter, number or connecting character. Since you can put $ characters in a class name, you also can't assume that you can split a class, inner-class and inner-inner-class by the $ character. So I believe the class in question is a legal class identifier (though not a conventional one by any means). |
Why does |
If we catch
If we throw an exception it should be a |
That's true, but that's also why I prefer option # 2. Since this is about classpath scanning where multiple things can go wrong, I think the best solution is simply to swallow such exceptions and log it. If a test class doesn't get picked up via classpath scanning, the user can then check to the log to see what went wrong. And... in most cases when a failure occurs during classpath scanning, I think the average user simply won't care.
Yes. When I said "like an IllegalStateException", that could well be interpreted as a |
You ask hard questions! A couple hours of writing junk code and trying to reproduce this issue allows me to stand by my assertion regarding allowed character sets. The answer is that the innermost class named lambda is not allowed per the Java specification to hide it's parent class' name. It turns out that it doesn't matter to the JVM. But if you try to do reflection on that class, it's trying to reflect the Kotlin generated classes as though they're Java. I originally thought there might be a similar problem with generated proxy classes like those used in CDI or JPA but they actually don't hide classes when nested. There have been similar issues in Scala byte-code. So for JUnit 5, I don't think there's a choice except to catch the error. I would argue that Kotlin should probably fix this (they claim to be 100% compatible with Java). Even if Kotlin has it's own reflection library, if you include another Java library you never know when that library might use reflection. |
If it helps, here is a relatively small testcase that replicates the crash. I tried to extract only the parts relevant to this issue. |
in progress |
Thanks for providing the testcase project. I added it as a local gradle file dependency and was able to reproduce the bug with a simple unit test for the IsPotentialTestContainer predicate. |
A similiar crash happens when
This exception is triggered by |
When comparing both stacktraces one finds that both crashes are caused by invoking |
This exception comes from your tests |
yes, when executing a local test case for the malformed Kotlin class, i.e. running the predicate on the Kotlin class instance. The point is that the situation can occur both in classpath scanning and in execution. |
In any case I would consider it safest to catch the problem as soon as possible, i.e. as soon as there is a class implemented by JUnit on the stack trace. |
The root cause seems to be that calling getSimpleName() on a class instance with a malformed name, e.g. the Kotlin generated example leads to the |
Team decision: place the safeguard measure at the classpath scanner level for now. |
Awesome, thanks. For future reference, solved by 389de48 |
@kevinvandervlist, I made some additional refinements regarding this issue, none of which should have any adverse side effects on the fix. However, it would be great if you could take the latest snapshot for a spin and confirm that the modified fix still works for you. |
I just verified it with commit 43b33fa, and it works like a charm:
Thanks! |
Great! Thanks for letting us know, @kevinvandervlist. |
When using org.junit.platform:junit-platform-gradle-plugin:1.0.0-M1 (but also org.junit:junit-gradle:5.0.0-ALPHA) you can run into a crash scenario when the project somehow has classnames that are invalid.
Running
gradle check
:On closer inspection it crashes on the following class:
... which is a class generated by Kotlin.
I know you can't influence the code that is generated by Kotlin, but I don't think the TestEngine should fail with an InternalError, without at least signaling the user which class actually caused that error.
The text was updated successfully, but these errors were encountered: