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

Default UUIDs used by EqualsVerifier have the same hashCode() #81

Closed
GoogleCodeExporter opened this issue Mar 29, 2015 · 8 comments
Closed

Comments

@GoogleCodeExporter
Copy link

What steps will reproduce the problem?
1. EqualsVerifier.forClass(Client.class).suppress(Warning.NONFINAL_FIELDS, 
Warning.STRICT_INHERITANCE).verify();


What is the code (equals method, hashCode method, relevant fields) that
triggers the problem?
(simplified)
public class Client implements Serializable {
private UUID id;
@Override
public int hashCode() {
    return id!=null ? id.hashCode() : 0;
}
@Override
public boolean equals(Object object) {
    if (!(object instanceof Client)) {
        return false;
    }
    Client other=(Client) object;
    if ((this.id==null&&other.id!=null)||(this.id!=null&&!this.id.equals(other.id))) {
        return false;
    }
    return true;
}
}


What error message does EqualsVerifier give?
java.lang.AssertionError: Significant fields: equals relies on id, but hashCode 
does not.

What stacktrace does EqualsVerifier print, when called with the debug()
method?
java.lang.AssertionError: Significant fields: equals relies on id, but hashCode 
does not.
    at nl.jqno.equalsverifier.util.Assert.assertFalse(Assert.java:55)
    at nl.jqno.equalsverifier.FieldsChecker$SignificanceFieldCheck.execute(FieldsChecker.java:183)
    at nl.jqno.equalsverifier.FieldInspector.check(FieldInspector.java:37)
    at nl.jqno.equalsverifier.FieldsChecker.check(FieldsChecker.java:55)
    at nl.jqno.equalsverifier.EqualsVerifier.verifyWithExamples(EqualsVerifier.java:395)
    at nl.jqno.equalsverifier.EqualsVerifier.performVerification(EqualsVerifier.java:364)
    at nl.jqno.equalsverifier.EqualsVerifier.verify(EqualsVerifier.java:334)
    at ...ClientNGTest.testEquals(ClientNGTest.java:25)
    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:601)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
    at org.testng.TestRunner.privateRun(TestRunner.java:767)
    at org.testng.TestRunner.run(TestRunner.java:617)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
    at org.testng.SuiteRunner.run(SuiteRunner.java:240)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
    at org.testng.TestNG.run(TestNG.java:1057)
    at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:77)
    at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:189)
    at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:105)
    at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:117)
    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:601)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:159)
    at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:87)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:153)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:95)

What did you expect?
Success

What version of EqualsVerifier are you using?
1.2

Please provide any additional information below.

UUIDs used by EqualsVerifier have the same hashCode()

System.err.println(UUID.fromString("00000000-0000-0002-0000-000000000002").hashC
ode());
System.err.println(UUID.fromString("00000000-0000-0001-0000-000000000001").hashC
ode());

Original issue reported on code.google.com by [email protected] on 21 May 2013 at 9:21

@GoogleCodeExporter
Copy link
Author

Hi,

Sorry for the late reaction. It's been one of those weeks.

Anyway, your Client class passes just fine for me. However, it wouldn't be the 
first time that a Java API class is implemented slightly differently on 
different editions of the JDK, and that one of them works fine with EV whereas 
the other does not. So can you please tell me which JDK you use, and on which 
OS?


Also, can you tell me whether using .withPrefabValues solves the problem for 
you? You can use it like this:

EqualsVerifier.forClass(Client.class)
        .suppress(Warning.NONFINAL_FIELDS, Warning.STRICT_INHERITANCE)
        .withPrefabValues(UUID.class, UUID.randomUUID(), UUID.randomUUID())
        .verify();


Thanks!
Jan

Original comment by [email protected] on 26 May 2013 at 1:32

  • Added labels: ****
  • Removed labels: ****

@GoogleCodeExporter
Copy link
Author

Hi :)

$ java -version
java version "1.7.0_21"
OpenJDK Runtime Environment (IcedTea 2.3.9) (ArchLinux build 
7.u21_2.3.9-4-x86_64)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)


Yes, withPrefabValues was my workaround(except that I picked constant UUIDs for 
the slight chance that the random ones produce the same hashCode())

Regards,
Andreas

Original comment by [email protected] on 26 May 2013 at 5:02

  • Added labels: ****
  • Removed labels: ****

@GoogleCodeExporter
Copy link
Author

Hi Andreas,

Interesting; we use the same JDK, so that can't be it. (Although mine is an 
Ubuntu release.) I still can't reproduce the error with the code you give me, 
though. However, looking at the implementation of UUID, I noticed that it uses 
a cached hashCode, which EqualsVerifier is unable to deal with. (I have Issue 
60 still open to solve that.)

So I will add UUID to the "default" list of prefabValues in the next version. 
Until then, the only way around it is to keep adding them to the list manually.

Original comment by [email protected] on 28 May 2013 at 8:45

  • Added labels: ****
  • Removed labels: ****

@GoogleCodeExporter
Copy link
Author

Ok, thank you :)

But just our of curiosity, do:
00000000-0000-0001-0000-000000000001
00000000-0000-0002-0000-000000000002

also both produce hashCode 0 on your system?

Original comment by [email protected] on 28 May 2013 at 9:04

  • Added labels: ****
  • Removed labels: ****

@GoogleCodeExporter
Copy link
Author

UUID::hashCode():

public int hashCode() {
        long hilo = mostSigBits ^ leastSigBits;
        return ((int)(hilo >> 32)) ^ (int) hilo;
    }

Original comment by [email protected] on 28 May 2013 at 9:06

  • Added labels: ****
  • Removed labels: ****

@GoogleCodeExporter
Copy link
Author

Hi Andreas,

Turns out we're using different JDKs after all: the Java on my path is Java 7, 
but Eclipse builds against Java 6. There I get the following implementation for 
UUID's hashCode method:

    public int hashCode() {
        if (hashCode == -1) {
            hashCode = (int)((mostSigBits >> 32) ^
                             mostSigBits ^
                             (leastSigBits >> 32) ^
                             leastSigBits);
        }
        return hashCode;
    }

So that explains why we're getting different results. And indeed, if I switch 
it to Java 7, I get the same results you do.

In both cases, I do get a hashCode of 0 for both UUIDs you mentioned.

So, thanks for the update! It was very enlightening for me :).

Original comment by [email protected] on 29 May 2013 at 8:14

  • Added labels: ****
  • Removed labels: ****

@GoogleCodeExporter
Copy link
Author

Hi Andreas,
I 
added UUID to the default list of prefabValues, in version 1.3.1, which is just 
released.

Jan

Original comment by [email protected] on 9 Jun 2013 at 3:31

  • Changed state: Fixed
  • Added labels: ****
  • Removed labels: ****

@GoogleCodeExporter
Copy link
Author

http://muhammadkhojaye.blogspot.co.uk/2010/02/java-hashing.html

Original comment by [email protected] on 10 Dec 2013 at 12:11

  • Added labels: ****
  • Removed labels: ****

jqno pushed a commit that referenced this issue Aug 23, 2016
removed unused <a> tag in _author-bio.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant