Skip to content
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

ComparisonFailure reports all failures as "expected: null<null> but was: null<null>" #976

Closed
dsaff opened this issue Aug 13, 2014 · 28 comments
Milestone

Comments

@dsaff
Copy link
Member

dsaff commented Aug 13, 2014

Still debugging locally, but since my local gradle build picked up 4.12-beta-1, all string comparison failures are reporting

org.junit.ComparisonFailure: expected: null but was: null
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144)

This is on comparison of two obviously non-null strings, for example assertEquals("a", "b").

This appears to be affecting our whole local team, but still may be something odd about our setup. Continuing to investigate.

@dsaff dsaff added this to the 4.12 milestone Aug 13, 2014
@marcphilipp
Copy link
Member

Works for me. Can you still reproduce it?

@priav03
Copy link
Contributor

priav03 commented Aug 18, 2014

Works for me as well. Here is the trace I get -
org.junit.ComparisonFailure: expected:<[a]> but was:<[b]>
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144)
...

@avh4
Copy link

avh4 commented Aug 18, 2014

We have been seeing this error on our CI box (ubuntu), but not on our dev workstations (Mac).

Switching from 4.12-beta-1 back to 4.11 fixed the problem on CI.

With 4.12-beta-1:

org.junit.ComparisonFailure: expected: null<null> but was: null<null>
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at <snipped>.AssociationTest.<snipped>(AssociationTest.java:41)

With 4.11:

org.junit.ComparisonFailure: expected:<[43534]L> but was:<[1408388044091]L>
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at <snipped>.AssociationTest.<snipped>(AssociationTest.java:41)

In this case, the test is using assertj's assertThat(long).isEqualTo(long) to perform the assertion.

@marcphilipp
Copy link
Member

@dsaff What's your setup?

@dsaff
Copy link
Member Author

dsaff commented Aug 18, 2014

Oddly, Mac. Likewise, setting back to 4.11 fixed the problem.

@avh4
Copy link

avh4 commented Aug 18, 2014

I just realized that the test quoted above was not failing on our workstations (only on CI), so I'll have to report back later whether or not the null<null> message problem happened on our Macs or not.

@marcphilipp
Copy link
Member

@dsaff @avh4 Can one of you please provide a minimal example to reproduce your problem? The 4.12 release is blocked by this issue.

@dsaff
Copy link
Member Author

dsaff commented Sep 11, 2014

I'm part-way there. If you un-tar this, and run

./gradlew :tests:test

(That's on OS X, command may be somewhat different on different platforms), then the output shows the problem.

Can someone confirm they're seeing the same thing?

Could very very well be a gradle/junit compatibility kerfuffle.

Tar file:

https://drive.google.com/a/google.com/file/d/0B343vp5qh978THRKVnRDeHlIdjQ/view?usp=sharing

@marcphilipp
Copy link
Member

@dsaff What version of Gradle are you using?

@marcphilipp
Copy link
Member

Oh, I guess Gradle is in the tar file as well. Never mind then. I've sent you an authorization request for the tar file via Google Drive.

@marcphilipp
Copy link
Member

@dsaff I still cannot access the tar file on your Google Drive account.

@marcphilipp
Copy link
Member

@dsaff Thanks for the file. I can reproduce the problem.

I tried remote debugging via ./gradlew :tests:test --debug-jvm and Eclipse with the current HEAD. However, I did see that we correctly build the error message -- which does not make sense. When I run the same test in Eclipse using the same Hamcrest and JUnit jars as Gradle it works, too.

@avh4 Are you also using Gradle?

If so, we should try to get help from someone that knows how Gradle's test runner works internally since I'm not a Gradle user.

@marcphilipp
Copy link
Member

I have slightly edited the example test case:

EqualityTest.java

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class EqualityTest {
    @Test public void testEquality() {
        String expected = "a";
        String actual = "b";
        System.out.println("expected: " + expected);
        System.out.println("actual:   " + actual);
        try {
            assertEquals(expected, actual);
        } catch (Throwable t) {
            t.printStackTrace();
            throw new RuntimeException("wrapper", t);
        }
    }
}

System.out

expected: a
actual:   b

System.err

org.junit.ComparisonFailure: expected:<[a]> but was:<[b]>
    at org.junit.Assert.assertEquals(Assert.java:115)
    at org.junit.Assert.assertEquals(Assert.java:144)
    at EqualityTest.testEquality(EqualityTest.java:12)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:55)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

Gradle report

java.lang.RuntimeException: wrapper
    at EqualityTest.testEquality(EqualityTest.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:55)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
Caused by: org.junit.ComparisonFailure: expected: null<null> but was: null<null>
    at org.junit.Assert.assertEquals(Assert.java:115)
    at org.junit.Assert.assertEquals(Assert.java:144)
    at EqualityTest.testEquality(EqualityTest.java:12)
    ... 42 more

So, at debug time I see the correct error message (see System.err). However, when Gradle reports the exception, the field values (expected and actual) seem to have gone (and are null then???).

@dsaff You said it works with 4.11, right?

@kcooney
Copy link
Member

kcooney commented Sep 16, 2014

I wonder if this has something to do with is changing the field names in
ComparisonFailure.

Do you see the same problem if you compare two integers? Or is it unique to
failures that use ComparisonFailure?
On Sep 16, 2014 1:58 AM, "Marc Philipp" notifications@github.com wrote:

I have slightly edited the example test case:
EqualityTest.java

import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class EqualityTest {
@test public void testEquality() {
String expected = "a";
String actual = "b";
System.out.println("expected: " + expected);
System.out.println("actual: " + actual);
try {
assertEquals(expected, actual);
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException("wrapper", t);
}
}}

System.out:

expected: a
actual: b

System.err:

org.junit.ComparisonFailure: expected:<[a]> but was:<[b]>
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144)
at EqualityTest.testEquality(EqualityTest.java:12)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:55)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)

Gradle report

java.lang.RuntimeException: wrapper
at EqualityTest.testEquality(EqualityTest.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:55)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Caused by: org.junit.ComparisonFailure: expected: null but was: null
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144)
at EqualityTest.testEquality(EqualityTest.java:12)
... 42 more

So, at debug time I see the correct error message (see System.err).
However, when Gradle reports the exception, the field values (expected
and actual) seem to have gone and are null.

@dsaff https://github.com/dsaff You said it works with 4.11, right?


Reply to this email directly or view it on GitHub
#976 (comment).

@avh4
Copy link

avh4 commented Sep 16, 2014

Yes, I was also using gradle when I saw this.

@anba
Copy link

anba commented Sep 21, 2014

I wonder if this has something to do with is changing the field names in ComparisonFailure.

That's exactly right. Fixing this issue in ComparisonFailure is dead simple, other serializable classes where fields names were changed need to be updated in a similar manner.

diff --git a/src/main/java/org/junit/ComparisonFailure.java b/src/main/java/org/junit/ComparisonFailure.java
index 85aed73..f7d2c3f 100644
--- a/src/main/java/org/junit/ComparisonFailure.java
+++ b/src/main/java/org/junit/ComparisonFailure.java
@@ -16,7 +16,7 @@ public class ComparisonFailure extends AssertionError {
      * @see ComparisonCompactor
      */
     private static final int MAX_CONTEXT_LENGTH = 20;
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 2L;

     private String expected;
     private String actual;

@marcphilipp
Copy link
Member

@anba Does Gradle serialize the exceptions thrown by JUnit tests?

@anba
Copy link

anba commented Sep 21, 2014

Apparently it does. I'm neither a Gradle developer nor user, so I don't actually know the internals of Gradle. Updating the serial UID did resolve this problem, though.

Later: I've just added a writeObject method to ComparisonFailure to check if and from where Gradle uses serialization. Here's the call stack:

java.lang.Throwable
    at org.junit.ComparisonFailure.writeObject(ComparisonFailure.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at org.gradle.messaging.remote.internal.Message$ExceptionPlaceholder.<init>(Message.java:121)
    at org.gradle.messaging.remote.internal.Message$ExceptionPlaceholder.<init>(Message.java:130)
    at org.gradle.messaging.remote.internal.Message$TopLevelExceptionPlaceholder.<init>(Message.java:187)
    at org.gradle.messaging.remote.internal.Message$TopLevelExceptionPlaceholder.<init>(Message.java:185)
    at org.gradle.messaging.remote.internal.Message$ExceptionReplacingObjectOutputStream.replaceObject(Message.java:200)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1144)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at org.gradle.messaging.remote.internal.Message.send(Message.java:40)
    at org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$ThrowableSerializer.write(TestEventSerializer.java:117)
    at org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$ThrowableSerializer.write(TestEventSerializer.java:111)
    at org.gradle.messaging.serialize.DefaultSerializerRegistry$TaggedTypeSerializer.write(DefaultSerializerRegistry.java:82)
    at org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$2.write(TestEventSerializer.java:64)
    at org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$2.write(TestEventSerializer.java:60)
    at org.gradle.messaging.remote.internal.hub.MethodInvocationSerializer$MethodInvocationWriter.writeArguments(MethodInvocationSerializer.java:67)
    at org.gradle.messaging.remote.internal.hub.MethodInvocationSerializer$MethodInvocationWriter.write(MethodInvocationSerializer.java:63)
    at org.gradle.messaging.remote.internal.hub.MethodInvocationSerializer$MethodInvocationWriter.write(MethodInvocationSerializer.java:48)
    at org.gradle.messaging.serialize.kryo.TypeSafeSerializer$2.write(TypeSafeSerializer.java:46)
    at org.gradle.messaging.remote.internal.hub.InterHubMessageSerializer$MessageWriter.write(InterHubMessageSerializer.java:108)
    at org.gradle.messaging.remote.internal.hub.InterHubMessageSerializer$MessageWriter.write(InterHubMessageSerializer.java:93)
    at org.gradle.messaging.remote.internal.inet.SocketConnection.dispatch(SocketConnection.java:112)
    at org.gradle.messaging.remote.internal.hub.Mes$ConnectionDispatch.run(MessageHub.java:279)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

@marcphilipp
Copy link
Member

@anba Thanks for digging into this!

The key to understanding this issue indeed seems to be Gradle's Message class. Apparently, Gradle uses it for communication between its test runner and the system under test.

In the above stacktrace the ExceptionPlaceholder constructor serializes the exception and sends it to the test runner. The test runner then calls read where a number of things are tried to restore the exception. As a first step seems it tries to deserialize the exception. If that does not succeed it will create a PlaceholderException with the same message, cause, stacktrace and so on.

Gradle uses two separate classpathes for the test runner and the system under test. The first is called "implementation classpath", the latter "application classpath". When you enable the debug output you'll get a trace of the two. I spare you the detailed output, essentially junit-4.12-beta-1.jar is on the application classpath while junit-4.11.jar is on the implementation classpath.

With the current beta the serialVersionUID of ComparisonFailure from either jar is identical. Thus, deserializing succeeds. However, the fields have been renamed, thus fExpected and fActual are both null.

When changing the serialVersionUID (like @anba did), deserialization will fail with an InvalidClassException. Gradle will then create a PlaceholderException with the original message and stacktrace.

Summing up, I think we need to go through all our classes that are Serializable and check whether we have renamed any fields since 4.11.

@kcooney
Copy link
Member

kcooney commented Sep 21, 2014

wow! amazing sleuthing, @anba and @marcphilipp !

@kcooney
Copy link
Member

kcooney commented Sep 21, 2014

I think we should undo the field renames for fields that are Serializable. Changing the serialization format between minor releases is probably not a good idea. I suspect that many build tools and IDEs use serialization to send test results from the test process to the tool's process.

The alternative is to see if we could find a way that we can add writeObject() methods to these classes in a way that 4.12 would be able to deserialize these objects, but it would be really hard to write tests to ensure that it works.

@kcooney
Copy link
Member

kcooney commented Sep 21, 2014

Here's a link to a page that describes how we would keep the fields with the new names but not break serialization:

http://www.coderanch.com/t/582933/java/java/Serialization

But, again, it would be challenging to write tests to verify that we don't break backwards compatibility. We could write a tool that compiles against the 4.11 jars and serializes these objects, and then write tests in 4.11 that serializes the objects and compares the output with the expected output.

Note we have changes serialVersionId in the past for some exceptions (AssumptionViolatedException changed in #3bb48f0) but I don't think we should change it for ComparisonFailure. While Gradle would handle it fine, other tools might not.

@marcphilipp
Copy link
Member

I've submitted pull request #994 which restores the old field names for all exceptions and almost all other class that are Serializable.

@marcphilipp
Copy link
Member

Now that #994 has been merged, I'll publish another beta in the next few days.

@marcphilipp
Copy link
Member

Works using 4.12-beta-2 now:

bildschirmfoto 2014-09-25 um 15 29 14

@marcphilipp
Copy link
Member

@dsaff Can you give it a try in your real project as well?

@dsaff
Copy link
Member Author

dsaff commented Sep 25, 2014

Confirmed it's fixed in our real project. Thanks!

@navels
Copy link

navels commented Oct 6, 2015

FYI, you missed ArrayComparisonFailure.fCause, which disappeared in 4.12. This caused much hair-pulling for us as it caused a gradle test task to fail with no apparent cause. We had upgraded to gradle 2.4 which bundles junit 4.12 whereas our application classpath pointed to 4.11.

aishahalim added a commit to aishahalim/junit4 that referenced this issue May 31, 2016
…tCause()) to avoid npe and unused field warnings, respectively

Issue junit-team#1178, junit-team#976
aishahalim added a commit to aishahalim/junit4 that referenced this issue May 31, 2016
Add back field fCause, initialize and use in the constructor (via initCause()) to avoid npe and unused field warnings, respectively
Issue junit-team#1178, junit-team#976
aishahalim added a commit to aishahalim/junit4 that referenced this issue Jun 13, 2016
Add back field fCause, initialize and use in the constructor (via initCause()) to avoid npe and unused field warnings, respectively
Issue junit-team#1178, junit-team#976
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants