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  import static org.junit.jupiter.api.Assertions.assertNotNull;
21  
22  import java.math.BigDecimal;
23  import java.math.BigInteger;
24  
25  import org.apache.commons.lang3.AbstractLangTest;
26  import org.junit.jupiter.api.Test;
27  
28  /**
29   * Tests {@link ReflectionDiffBuilder}.
30   */
31  class ReflectionDiffBuilderTest extends AbstractLangTest {
32  
33      @SuppressWarnings("unused")
34      private static final class TypeTestChildClass extends TypeTestClass {
35          String field = "a";
36      }
37  
38      @SuppressWarnings("unused")
39      private static class TypeTestClass implements Diffable<TypeTestClass> {
40  
41          private static int staticField;
42          private final ToStringStyle style = SHORT_STYLE;
43          private final boolean booleanField = true;
44          private final boolean[] booleanArrayField = { true };
45          private final byte byteField = (byte) 0xFF;
46          private final byte[] byteArrayField = { (byte) 0xFF };
47          private char charField = 'a';
48          private char[] charArrayField = { 'a' };
49          private final double doubleField = 1.0;
50          private final double[] doubleArrayField = { 1.0 };
51          private final float floatField = 1.0f;
52          private final float[] floatArrayField = { 1.0f };
53          int intField = 1;
54          private final int[] intArrayField = { 1 };
55          private final long longField = 1L;
56          private final long[] longArrayField = { 1L };
57          private final short shortField = 1;
58          private final short[] shortArrayField = { 1 };
59          private final Object objectField = null;
60          private final Object[] objectArrayField = { null };
61          private transient String transientField;
62          private BigDecimal bigDecimal = BigDecimal.valueOf(20, 1);
63          private BigInteger bigInteger = BigInteger.valueOf(2);
64          @DiffExclude
65          private String annotatedField = "a";
66          private String excludedField = "a";
67  
68          @Override
69          public DiffResult<TypeTestClass> diff(final TypeTestClass obj) {
70              // @formatter:off
71              return ReflectionDiffBuilder.<TypeTestClass>builder()
72                      .setDiffBuilder(diffBuilder(obj))
73                      .setExcludeFieldNames("excludedField")
74                      .build()
75                      .build();
76              // @formatter:on
77          }
78  
79          DiffBuilder<TypeTestClass> diffBuilder(final TypeTestClass obj) {
80              // @formatter:off
81              return DiffBuilder.<TypeTestClass>builder()
82                  .setLeft(this)
83                  .setRight(obj)
84                  .setStyle(style)
85                  .build();
86              // @formatter:on
87          }
88  
89          public DiffResult<TypeTestClass> diffDeprecated(final TypeTestClass obj) {
90              return new ReflectionDiffBuilder<>(this, obj, style).setExcludeFieldNames("excludedField").build();
91          }
92  
93          @Override
94          public boolean equals(final Object obj) {
95              return EqualsBuilder.reflectionEquals(this, obj, false);
96          }
97  
98          @Override
99          public int hashCode() {
100             return HashCodeBuilder.reflectionHashCode(this, false);
101         }
102     }
103 
104     private static final ToStringStyle SHORT_STYLE = ToStringStyle.SHORT_PREFIX_STYLE;
105 
106     @Test
107     void testArrayDifference() {
108         final TypeTestClass firstObject = new TypeTestClass();
109         firstObject.charArrayField = new char[] { 'c' };
110         final TypeTestClass secondObject = new TypeTestClass();
111         // Normal builder
112         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
113         assertEquals(1, list.getNumberOfDiffs());
114         // Deprecated constructor
115         list = firstObject.diffDeprecated(secondObject);
116         assertEquals(1, list.getNumberOfDiffs());
117     }
118 
119     @Test
120     void testBigDecimalDifference() {
121         final TypeTestClass firstObject = new TypeTestClass();
122         // 2.0 is not equal to 2.00, see BigDecimal#equals()
123         firstObject.bigDecimal = BigDecimal.valueOf(200, 2);
124         final TypeTestClass secondObject = new TypeTestClass();
125         final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
126         assertEquals(1, list.getNumberOfDiffs());
127     }
128 
129     @Test
130     void testBigIntegerDifference() {
131         final TypeTestClass firstObject = new TypeTestClass();
132         firstObject.bigInteger = BigInteger.valueOf(100);
133         final TypeTestClass secondObject = new TypeTestClass();
134 
135         final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
136         assertEquals(1, list.getNumberOfDiffs());
137     }
138 
139     @Test
140     void testDifferenceInInherited_field() {
141         final TypeTestChildClass firstObject = new TypeTestChildClass();
142         firstObject.intField = 99;
143         final TypeTestChildClass secondObject = new TypeTestChildClass();
144 
145         final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
146         assertEquals(1, list.getNumberOfDiffs());
147     }
148 
149     @Test
150     void testGetExcludeFieldNamesWithNullExcludedFieldNames() {
151         // @formatter:off
152         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
153                 .setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
154                         .setLeft(new TypeTestClass())
155                         .setRight(new TypeTestChildClass())
156                         .setStyle(SHORT_STYLE)
157                         .build())
158                 .build();
159         // @formatter:on
160         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
161         assertNotNull(excludeFieldNames);
162         assertEquals(0, excludeFieldNames.length);
163         assertNotNull(reflectionDiffBuilder.build());
164     }
165 
166     @Test
167     void testGetExcludeFieldNamesWithNullExcludedFieldNamesCtor() {
168         // @formatter:off
169         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
170                 new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
171         // @formatter:on
172         reflectionDiffBuilder.setExcludeFieldNames(null);
173         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
174         assertNotNull(excludeFieldNames);
175         assertEquals(0, excludeFieldNames.length);
176         assertNotNull(reflectionDiffBuilder.build());
177     }
178 
179     @Test
180     void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNames() {
181         // @formatter:off
182         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
183                 .setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
184                         .setLeft(new TypeTestClass())
185                         .setRight(new TypeTestChildClass())
186                         .setStyle(SHORT_STYLE)
187                         .build())
188                 .setExcludeFieldNames("charField", null)
189                 .build();
190         // @formatter:on
191         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
192         assertNotNull(excludeFieldNames);
193         assertEquals(1, excludeFieldNames.length);
194         assertEquals("charField", excludeFieldNames[0]);
195         assertNotNull(reflectionDiffBuilder.build());
196     }
197 
198     @Test
199     void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNamesCtor() {
200         // @formatter:off
201         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
202                 new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
203         // @formatter:on
204         reflectionDiffBuilder.setExcludeFieldNames("charField", null);
205         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
206         assertNotNull(excludeFieldNames);
207         assertEquals(1, excludeFieldNames.length);
208         assertEquals("charField", excludeFieldNames[0]);
209         assertNotNull(reflectionDiffBuilder.build());
210     }
211 
212     @Test
213     void testNoDifferences() {
214         final TypeTestClass firstObject = new TypeTestClass();
215         final TypeTestClass secondObject = new TypeTestClass();
216         assertEquals(0, firstObject.diff(secondObject).getNumberOfDiffs());
217         assertEquals(0, firstObject.diffDeprecated(secondObject).getNumberOfDiffs());
218     }
219 
220     @Test
221     void testNoDifferencesDiffExcludeAnnotatedField() {
222         final TypeTestClass firstObject = new TypeTestClass();
223         firstObject.annotatedField = "b";
224         final TypeTestClass secondObject = new TypeTestClass();
225         assertEquals(0, firstObject.diff(secondObject).getNumberOfDiffs());
226         assertEquals(0, firstObject.diffDeprecated(secondObject).getNumberOfDiffs());
227     }
228 
229     @Test
230     void testNoDifferencesDiffExcludedFieldAndExcludeAnnotatedField() {
231         final TypeTestClass firstObject = new TypeTestClass();
232         firstObject.excludedField = "b";
233         firstObject.annotatedField = "b";
234         final TypeTestClass secondObject = new TypeTestClass();
235         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
236         assertEquals(0, list.getNumberOfDiffs());
237         list = firstObject.diffDeprecated(secondObject);
238         assertEquals(0, list.getNumberOfDiffs());
239     }
240 
241     @Test
242     void testNoDifferencesExcludedField() {
243         final TypeTestClass firstObject = new TypeTestClass();
244         firstObject.excludedField = "b";
245         final TypeTestClass secondObject = new TypeTestClass();
246         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
247         assertEquals(0, list.getNumberOfDiffs());
248         list = firstObject.diffDeprecated(secondObject);
249         assertEquals(0, list.getNumberOfDiffs());
250     }
251 
252     @Test
253     void testNoDifferencesInheritance() {
254         final TypeTestChildClass firstObject = new TypeTestChildClass();
255         final TypeTestChildClass secondObject = new TypeTestChildClass();
256         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
257         assertEquals(0, list.getNumberOfDiffs());
258         list = firstObject.diffDeprecated(secondObject);
259         assertEquals(0, list.getNumberOfDiffs());
260     }
261 
262     @Test
263     void testPrimitiveDifference() {
264         final TypeTestClass firstObject = new TypeTestClass();
265         firstObject.charField = 'c';
266         final TypeTestClass secondObject = new TypeTestClass();
267         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
268         assertEquals(1, list.getNumberOfDiffs());
269         list = firstObject.diffDeprecated(secondObject);
270         assertEquals(1, list.getNumberOfDiffs());
271     }
272 
273     @Test
274     void testRetention() throws Exception {
275         // The following should not retain memory.
276         for (int i = 0; i < Integer.getInteger("testRecursive", 10_000); i++) {
277             final Class<?> clazz = TestClassBuilder.defineSimpleClass(getClass().getPackage().getName(), i);
278             final Object firstObject = clazz.newInstance();
279             final Object secondObject = clazz.newInstance();
280             final ReflectionDiffBuilder<Object> reflectionDiffBuilder = new ReflectionDiffBuilder<>(firstObject, secondObject, SHORT_STYLE);
281             assertNotNull(reflectionDiffBuilder.build());
282         }
283     }
284 
285     @Test
286     void testTransientFieldDifference() {
287         final TypeTestClass firstObject = new TypeTestClass();
288         firstObject.transientField = "a";
289         final TypeTestClass secondObject = new TypeTestClass();
290         secondObject.transientField = "b";
291         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
292         assertEquals(0, list.getNumberOfDiffs());
293         list = firstObject.diffDeprecated(secondObject);
294         assertEquals(0, list.getNumberOfDiffs());
295     }
296 
297 }