View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.builder;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  
21  import org.apache.commons.lang3.AbstractLangTest;
22  import org.junit.jupiter.api.Test;
23  
24  /**
25   * Tests {@link org.apache.commons.lang3.builder.HashCodeBuilder} and
26   * {@link org.apache.commons.lang3.builder.EqualsBuilderTest} to ensure that equal
27   * objects must have equal hash codes.
28   */
29  class HashCodeBuilderAndEqualsBuilderTest extends AbstractLangTest {
30  
31      static class AllTransientFixture {
32          transient int i;
33          transient char c;
34          transient String string;
35          transient short s;
36  
37          AllTransientFixture(final int i, final char c, final String string, final short s) {
38              this.i = i;
39              this.c = c;
40              this.string = string;
41              this.s = s;
42          }
43      }
44  
45      static class SubAllTransientFixture extends AllTransientFixture {
46          transient String tString;
47  
48          SubAllTransientFixture(final int i, final char c, final String string, final short s, final String tString) {
49              super(i, c, string, s);
50              this.tString = tString;
51          }
52      }
53  
54      static class SubTestFixture extends TestFixture {
55          transient String tString;
56  
57          SubTestFixture(final int i, final char c, final String string, final short s, final String tString) {
58              super(i, c, string, s);
59              this.tString = tString;
60          }
61      }
62  
63      static class TestFixture {
64          int i;
65          char c;
66          String string;
67          short s;
68  
69          TestFixture(final int i, final char c, final String string, final short s) {
70              this.i = i;
71              this.c = c;
72              this.string = string;
73              this.s = s;
74          }
75      }
76  
77      /**
78       * Asserts that if {@code lhs} equals {@code rhs}
79       * then their hash codes MUST be identical.
80       *
81       * @param lhs The Left-Hand-Side of the equals test
82       * @param rhs The Right-Hand-Side of the equals test
83       * @param testTransients whether to test transient fields
84       */
85      private void assertEqualsAndHashCodeContract(final Object lhs, final Object rhs, final boolean testTransients) {
86          if (EqualsBuilder.reflectionEquals(lhs, rhs, testTransients)) {
87              // test a couple of times for consistency.
88              assertEquals(HashCodeBuilder.reflectionHashCode(lhs, testTransients), HashCodeBuilder.reflectionHashCode(rhs, testTransients));
89              assertEquals(HashCodeBuilder.reflectionHashCode(lhs, testTransients), HashCodeBuilder.reflectionHashCode(rhs, testTransients));
90              assertEquals(HashCodeBuilder.reflectionHashCode(lhs, testTransients), HashCodeBuilder.reflectionHashCode(rhs, testTransients));
91          }
92      }
93  
94      @Test
95      void testFixture() {
96          testFixture(false);
97      }
98  
99      private void testFixture(final boolean testTransients) {
100         assertEqualsAndHashCodeContract(new TestFixture(2, 'c', "Test", (short) 2), new TestFixture(2, 'c', "Test", (short) 2), testTransients);
101         assertEqualsAndHashCodeContract(
102             new AllTransientFixture(2, 'c', "Test", (short) 2),
103             new AllTransientFixture(2, 'c', "Test", (short) 2),
104             testTransients);
105         assertEqualsAndHashCodeContract(
106             new SubTestFixture(2, 'c', "Test", (short) 2, "Same"),
107             new SubTestFixture(2, 'c', "Test", (short) 2, "Same"),
108             testTransients);
109         assertEqualsAndHashCodeContract(
110             new SubAllTransientFixture(2, 'c', "Test", (short) 2, "Same"),
111             new SubAllTransientFixture(2, 'c', "Test", (short) 2, "Same"),
112             testTransients);
113 
114     }
115 
116     @Test
117     void testFixtureWithTransients() {
118         testFixture(true);
119     }
120 
121     @Test
122     void testInteger() {
123         testInteger(false);
124     }
125 
126     private void testInteger(final boolean testTransients) {
127         final Integer i1 = Integer.valueOf(12345);
128         final Integer i2 = Integer.valueOf(12345);
129         assertEqualsAndHashCodeContract(i1, i2, testTransients);
130     }
131 
132     @Test
133     void testIntegerWithTransients() {
134         testInteger(true);
135     }
136 
137     @Test
138     void testRetention() throws Exception {
139         // The following should not retain memory.
140         for (int i = 0; i < Integer.getInteger("testRecursive", 10_000); i++) {
141             final Class<?> clazz = TestClassBuilder.defineSimpleClass(getClass().getPackage().getName(), i);
142             assertEqualsAndHashCodeContract(clazz.newInstance(), clazz.newInstance(), false);
143         }
144     }
145 
146 }