Clone, Equals and HashCode test fails on the server but not locally

Hello,

I currently fail Prog2.tests.daily.TestsCorrect::all due to a test I added yesterday which causes no problems locally. In the test body I repeatedly (for-loop) create a random new piece, clone it and then check that they are equal and have the same hash code.

p = pf.getNextRandomPiece();
q = p.clone();
assertTrue("...", p.equals(q)); // cloned pieces must be equal
assertTrue("...", p.hashCode() == q.hashCode()); // equal pieces must have the same hashCode

and the stack trace for the failed test on the server is:

java.lang.AssertionError: The following test cases fail on a correct implementation:
	tetris.tests.PieceTest.testUtility(PieceTest.java:144): AssertionError: cloned pieces didn't have same hash
	at org.junit.Assert.fail(Assert.java:89)
	at prog2.tests.daily.TestsCorrect.all(TestsCorrect.java:25)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	...[cut after 10 lines]

So the reference thinks the pieces are equal but assigns different hashes to them?

Yes, that is the behavior of the autoplayer reference implementation.
There is a contract that dictates that every class which overrides equals also should override hashCode in order to allow usage in hash-based collections.
But it is up to the programmer to ignore this contract and never use the class near anything hash-based.

We now added a suitable hashCode function. So the autoplayer is free to use the game pieces in hash-based collections.
Keep up the testing :slight_smile: .

1 Like

Of course, this is rather non-standard and should be documented. We did neither (Marcel fixed it), sorry!

So is having a valid hashCode override now a requirement for the project to fulfill said contract?

Yes, for the contract, a hashCode would be necessary.
https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

However, simply:

public int hashCode() {
  return 42;
}

would be sufficient for this specification.
If you use anything hash-based with your classes, you probably want a better hashing function.

2 Likes