/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.lang3.builder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Test; /** * Unit tests {@link org.apache.commons.lang3.builder.HashCodeBuilder}. * * @version $Id$ */ public class HashCodeBuilderTest { /** * A reflection test fixture. */ static class ReflectionTestCycleA { ReflectionTestCycleB b; @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } } /** * A reflection test fixture. */ static class ReflectionTestCycleB { ReflectionTestCycleA a; @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } } // ----------------------------------------------------------------------- @Test(expected=IllegalArgumentException.class) public void testConstructorExZero() { new HashCodeBuilder(0, 0); } @Test(expected=IllegalArgumentException.class) public void testConstructorExEven() { new HashCodeBuilder(2, 2); } @Test(expected=IllegalArgumentException.class) public void testConstructorExEvenNegative() { new HashCodeBuilder(-2, -2); } static class TestObject { private int a; public TestObject(final int a) { this.a = a; } @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof TestObject)) { return false; } final TestObject rhs = (TestObject) o; return a == rhs.a; } @Override public int hashCode() { return a; } public void setA(final int a) { this.a = a; } public int getA() { return a; } } static class TestSubObject extends TestObject { private int b; @SuppressWarnings("unused") transient private int t; public TestSubObject() { super(0); } public TestSubObject(final int a, final int b, final int t) { super(a); this.b = b; this.t = t; } @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof TestSubObject)) { return false; } final TestSubObject rhs = (TestSubObject) o; return super.equals(o) && b == rhs.b; } @Override public int hashCode() { return b*17 + super.hashCode(); } } @Test public void testReflectionHashCode() { assertEquals(17 * 37, HashCodeBuilder.reflectionHashCode(new TestObject(0))); assertEquals(17 * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestObject(123456))); } @Test public void testReflectionHierarchyHashCode() { assertEquals(17 * 37 * 37, HashCodeBuilder.reflectionHashCode(new TestSubObject(0, 0, 0))); assertEquals(17 * 37 * 37 * 37, HashCodeBuilder.reflectionHashCode(new TestSubObject(0, 0, 0), true)); assertEquals((17 * 37 + 7890) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject(123456, 7890, 0))); assertEquals(((17 * 37 + 7890) * 37 + 0) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject( 123456, 7890, 0), true)); } @Test(expected=IllegalArgumentException.class) public void testReflectionHierarchyHashCodeEx1() { HashCodeBuilder.reflectionHashCode(0, 0, new TestSubObject(0, 0, 0), true); } @Test(expected=IllegalArgumentException.class) public void testReflectionHierarchyHashCodeEx2() { HashCodeBuilder.reflectionHashCode(2, 2, new TestSubObject(0, 0, 0), true); } @Test(expected=IllegalArgumentException.class) public void testReflectionHashCodeEx1() { HashCodeBuilder.reflectionHashCode(0, 0, new TestObject(0), true); } @Test(expected=IllegalArgumentException.class) public void testReflectionHashCodeEx2() { HashCodeBuilder.reflectionHashCode(2, 2, new TestObject(0), true); } @Test(expected=IllegalArgumentException.class) public void testReflectionHashCodeEx3() { HashCodeBuilder.reflectionHashCode(13, 19, null, true); } @Test public void testSuper() { final Object obj = new Object(); assertEquals(17 * 37 + 19 * 41 + obj.hashCode(), new HashCodeBuilder(17, 37).appendSuper( new HashCodeBuilder(19, 41).append(obj).toHashCode()).toHashCode()); } @Test public void testObject() { Object obj = null; assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj = new Object(); assertEquals(17 * 37 + obj.hashCode(), new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testObjectBuild() { Object obj = null; assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(obj).build().intValue()); obj = new Object(); assertEquals(17 * 37 + obj.hashCode(), new HashCodeBuilder(17, 37).append(obj).build().intValue()); } @Test @SuppressWarnings("cast") // cast is not really needed, keep for consistency public void testLong() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long) 0L).toHashCode()); assertEquals(17 * 37 + (int) (123456789L ^ 123456789L >> 32), new HashCodeBuilder(17, 37).append( (long) 123456789L).toHashCode()); } @Test @SuppressWarnings("cast") // cast is not really needed, keep for consistency public void testInt() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((int) 0).toHashCode()); assertEquals(17 * 37 + 123456, new HashCodeBuilder(17, 37).append((int) 123456).toHashCode()); } @Test public void testShort() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((short) 0).toHashCode()); assertEquals(17 * 37 + 12345, new HashCodeBuilder(17, 37).append((short) 12345).toHashCode()); } @Test public void testChar() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((char) 0).toHashCode()); assertEquals(17 * 37 + 1234, new HashCodeBuilder(17, 37).append((char) 1234).toHashCode()); } @Test public void testByte() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((byte) 0).toHashCode()); assertEquals(17 * 37 + 123, new HashCodeBuilder(17, 37).append((byte) 123).toHashCode()); } @Test @SuppressWarnings("cast") // cast is not really needed, keep for consistency public void testDouble() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((double) 0d).toHashCode()); final double d = 1234567.89; final long l = Double.doubleToLongBits(d); assertEquals(17 * 37 + (int) (l ^ l >> 32), new HashCodeBuilder(17, 37).append(d).toHashCode()); } @Test @SuppressWarnings("cast") // cast is not really needed, keep for consistency public void testFloat() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((float) 0f).toHashCode()); final float f = 1234.89f; final int i = Float.floatToIntBits(f); assertEquals(17 * 37 + i, new HashCodeBuilder(17, 37).append(f).toHashCode()); } @Test public void testBoolean() { assertEquals(17 * 37 + 0, new HashCodeBuilder(17, 37).append(true).toHashCode()); assertEquals(17 * 37 + 1, new HashCodeBuilder(17, 37).append(false).toHashCode()); } @Test public void testObjectArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((Object[]) null).toHashCode()); final Object[] obj = new Object[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = new Object(); assertEquals((17 * 37 + obj[0].hashCode()) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = new Object(); assertEquals((17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append(obj) .toHashCode()); } @Test public void testObjectArrayAsObject() { final Object[] obj = new Object[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = new Object(); assertEquals((17 * 37 + obj[0].hashCode()) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = new Object(); assertEquals((17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append( (Object) obj).toHashCode()); } @Test public void testLongArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long[]) null).toHashCode()); final long[] obj = new long[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = 5L; final int h1 = (int) (5L ^ 5L >> 32); assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = 6L; final int h2 = (int) (6L ^ 6L >> 32); assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testLongArrayAsObject() { final long[] obj = new long[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = 5L; final int h1 = (int) (5L ^ 5L >> 32); assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = 6L; final int h2 = (int) (6L ^ 6L >> 32); assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testIntArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((int[]) null).toHashCode()); final int[] obj = new int[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testIntArrayAsObject() { final int[] obj = new int[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testShortArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((short[]) null).toHashCode()); final short[] obj = new short[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = (short) 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = (short) 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testShortArrayAsObject() { final short[] obj = new short[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = (short) 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = (short) 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testCharArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((char[]) null).toHashCode()); final char[] obj = new char[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = (char) 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = (char) 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testCharArrayAsObject() { final char[] obj = new char[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = (char) 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = (char) 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testByteArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((byte[]) null).toHashCode()); final byte[] obj = new byte[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = (byte) 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = (byte) 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testByteArrayAsObject() { final byte[] obj = new byte[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = (byte) 5; assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = (byte) 6; assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testDoubleArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((double[]) null).toHashCode()); final double[] obj = new double[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = 5.4d; final long l1 = Double.doubleToLongBits(5.4d); final int h1 = (int) (l1 ^ l1 >> 32); assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = 6.3d; final long l2 = Double.doubleToLongBits(6.3d); final int h2 = (int) (l2 ^ l2 >> 32); assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testDoubleArrayAsObject() { final double[] obj = new double[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = 5.4d; final long l1 = Double.doubleToLongBits(5.4d); final int h1 = (int) (l1 ^ l1 >> 32); assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = 6.3d; final long l2 = Double.doubleToLongBits(6.3d); final int h2 = (int) (l2 ^ l2 >> 32); assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testFloatArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((float[]) null).toHashCode()); final float[] obj = new float[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = 5.4f; final int h1 = Float.floatToIntBits(5.4f); assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = 6.3f; final int h2 = Float.floatToIntBits(6.3f); assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testFloatArrayAsObject() { final float[] obj = new float[2]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = 5.4f; final int h1 = Float.floatToIntBits(5.4f); assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = 6.3f; final int h2 = Float.floatToIntBits(6.3f); assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testBooleanArray() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((boolean[]) null).toHashCode()); final boolean[] obj = new boolean[2]; assertEquals((17 * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = true; assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = false; assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testBooleanArrayAsObject() { final boolean[] obj = new boolean[2]; assertEquals((17 * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[0] = true; assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); obj[1] = false; assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); } @Test public void testBooleanMultiArray() { final boolean[][] obj = new boolean[2][]; assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = new boolean[0]; assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = new boolean[1]; assertEquals((17 * 37 + 1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0] = new boolean[2]; assertEquals(((17 * 37 + 1) * 37 + 1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[0][0] = true; assertEquals(((17 * 37 + 0) * 37 + 1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); obj[1] = new boolean[1]; assertEquals(((17 * 37 + 0) * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); } @Test public void testReflectionHashCodeExcludeFields() throws Exception { final TestObjectWithMultipleFields x = new TestObjectWithMultipleFields(1, 2, 3); assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x)); assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, (String[]) null)); assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, new String[]{})); assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, new String[]{"xxx"})); assertEquals((17 * 37 + 1) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, new String[]{"two"})); assertEquals((17 * 37 + 1) * 37 + 2, HashCodeBuilder.reflectionHashCode(x, new String[]{"three"})); assertEquals(17 * 37 + 1, HashCodeBuilder.reflectionHashCode(x, new String[]{"two", "three"})); assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[]{"one", "two", "three"})); assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[]{"one", "two", "three", "xxx"})); } static class TestObjectWithMultipleFields { @SuppressWarnings("unused") private int one = 0; @SuppressWarnings("unused") private int two = 0; @SuppressWarnings("unused") private int three = 0; public TestObjectWithMultipleFields(final int one, final int two, final int three) { this.one = one; this.two = two; this.three = three; } } /** * Test Objects pointing to each other. */ @Test public void testReflectionObjectCycle() { final ReflectionTestCycleA a = new ReflectionTestCycleA(); final ReflectionTestCycleB b = new ReflectionTestCycleB(); a.b = b; b.a = a; // Used to caused: // java.lang.StackOverflowError // at java.lang.ClassLoader.getCallerClassLoader(Native Method) // at java.lang.Class.getDeclaredFields(Class.java:992) // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionAppend(HashCodeBuilder.java:373) // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:349) // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:155) // at // org.apache.commons.lang.builder.HashCodeBuilderTest$ReflectionTestCycleB.hashCode(HashCodeBuilderTest.java:53) // at org.apache.commons.lang.builder.HashCodeBuilder.append(HashCodeBuilder.java:422) // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionAppend(HashCodeBuilder.java:383) // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:349) // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:155) // at // org.apache.commons.lang.builder.HashCodeBuilderTest$ReflectionTestCycleA.hashCode(HashCodeBuilderTest.java:42) // at org.apache.commons.lang.builder.HashCodeBuilder.append(HashCodeBuilder.java:422) a.hashCode(); assertNull(HashCodeBuilder.getRegistry()); b.hashCode(); assertNull(HashCodeBuilder.getRegistry()); } /** * Ensures LANG-520 remains true */ @Test public void testToHashCodeEqualsHashCode() { final HashCodeBuilder hcb = new HashCodeBuilder(17, 37).append(new Object()).append('a'); assertEquals("hashCode() is no longer returning the same value as toHashCode() - see LANG-520", hcb.toHashCode(), hcb.hashCode()); } }