/* * 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.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.apache.commons.lang3.AbstractLangTest; import org.junit.jupiter.api.Test; /** * Unit tests {@link org.apache.commons.lang3.builder.HashCodeBuilder}. */ public class HashCodeBuilderTest extends AbstractLangTest { /** * 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 public void testConstructorExZero() { assertThrows(IllegalArgumentException.class, () -> new HashCodeBuilder(0, 0)); } @Test public void testConstructorExEvenFirst() { assertThrows(IllegalArgumentException.class, () -> new HashCodeBuilder(2, 3)); } @Test public void testConstructorExEvenSecond() { assertThrows(IllegalArgumentException.class, () -> new HashCodeBuilder(3, 2)); } @Test public void testConstructorExEvenNegative() { assertThrows(IllegalArgumentException.class, () -> new HashCodeBuilder(-2, -2)); } static class TestObject { private int a; 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") private transient int t; TestSubObject() { super(0); } 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 public void testReflectionHierarchyHashCodeEx1() { assertThrows(IllegalArgumentException.class, () -> HashCodeBuilder.reflectionHashCode(0, 0, new TestSubObject(0, 0, 0), true)); } @Test public void testReflectionHierarchyHashCodeEx2() { assertThrows(IllegalArgumentException.class, () -> HashCodeBuilder.reflectionHashCode(2, 2, new TestSubObject(0, 0, 0), true)); } @Test public void testReflectionHashCodeEx1() { assertThrows(IllegalArgumentException.class, () -> HashCodeBuilder.reflectionHashCode(0, 0, new TestObject(0), true)); } @Test public void testReflectionHashCodeEx2() { assertThrows(IllegalArgumentException.class, () -> HashCodeBuilder.reflectionHashCode(2, 2, new TestObject(0), true)); } @Test public void testReflectionHashCodeEx3() { assertThrows(NullPointerException.class, () -> 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 public void testLong() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(0L).toHashCode()); assertEquals(17 * 37 + (int) (123456789L ^ 123456789L >> 32), new HashCodeBuilder(17, 37).append( 123456789L).toHashCode()); } @Test public void testInt() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(0).toHashCode()); assertEquals(17 * 37 + 123456, new HashCodeBuilder(17, 37).append(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 public void testDouble() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(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 public void testFloat() { assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(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() { final TestObjectWithMultipleFields x = new TestObjectWithMultipleFields(1, 2, 3); assertEquals(((17 * 37 + 1) * 37 + 3) * 37 + 2, HashCodeBuilder.reflectionHashCode(x)); assertEquals(((17 * 37 + 1) * 37 + 3) * 37 + 2, HashCodeBuilder.reflectionHashCode(x, (String[]) null)); assertEquals(((17 * 37 + 1) * 37 + 3) * 37 + 2, HashCodeBuilder.reflectionHashCode(x)); assertEquals(((17 * 37 + 1) * 37 + 3) * 37 + 2, HashCodeBuilder.reflectionHashCode(x, "xxx")); assertEquals((17 * 37 + 1) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, "two")); assertEquals((17 * 37 + 1) * 37 + 2, HashCodeBuilder.reflectionHashCode(x, "three")); assertEquals(17 * 37 + 1, HashCodeBuilder.reflectionHashCode(x, "two", "three")); assertEquals(17, HashCodeBuilder.reflectionHashCode(x, "one", "two", "three")); assertEquals(17, HashCodeBuilder.reflectionHashCode(x, "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; 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(hcb.toHashCode(), hcb.hashCode(), "hashCode() is no longer returning the same value as toHashCode() - see LANG-520"); } static class TestObjectHashCodeExclude { @HashCodeExclude private final int a; private final int b; TestObjectHashCodeExclude(final int a, final int b) { this.a = a; this.b = b; } public int getA() { return a; } public int getB() { return b; } } static class TestObjectHashCodeExclude2 { @HashCodeExclude private final int a; @HashCodeExclude private final int b; TestObjectHashCodeExclude2(final int a, final int b) { this.a = a; this.b = b; } public int getA() { return a; } public int getB() { return b; } } @Test public void testToHashCodeExclude() { final TestObjectHashCodeExclude one = new TestObjectHashCodeExclude(1, 2); final TestObjectHashCodeExclude2 two = new TestObjectHashCodeExclude2(1, 2); assertEquals(17 * 37 + 2, HashCodeBuilder.reflectionHashCode(one)); assertEquals(17, HashCodeBuilder.reflectionHashCode(two)); } }