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.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertSame;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.math.BigDecimal;
27  import java.math.BigInteger;
28  import java.util.List;
29  import java.util.Objects;
30  import java.util.concurrent.atomic.AtomicInteger;
31  
32  import org.apache.commons.lang3.AbstractLangTest;
33  import org.junit.jupiter.api.Test;
34  
35  /**
36   * Tests {@link ReflectionDiffBuilder}.
37   */
38  class ReflectionDiffBuilderTest extends AbstractLangTest {
39  
40      private static class AtomicIntegerWrapper {
41  
42          private /* not final might not matter for the test. */ AtomicInteger value;
43  
44          AtomicIntegerWrapper(final int a) {
45              value = new AtomicInteger(a);
46          }
47      }
48  
49      private static final class CycleDiffableNode implements Diffable<CycleDiffableNode> {
50          @SuppressWarnings("unused")
51          private CycleDiffableNode self;
52          @SuppressWarnings("unused")
53          private final String value;
54  
55          CycleDiffableNode(final String value) {
56              this.value = value;
57          }
58  
59          @Override
60          public DiffResult<CycleDiffableNode> diff(final CycleDiffableNode obj) {
61              return ReflectionDiffBuilder.<CycleDiffableNode>builder()
62                      .setDiffBuilder(DiffBuilder.<CycleDiffableNode>builder()
63                              .setLeft(this)
64                              .setRight(obj)
65                              .setStyle(ToStringStyle.SHORT_PREFIX_STYLE)
66                              .build())
67                      .build()
68                      .build();
69          }
70  
71          @Override
72          public boolean equals(final Object obj) {
73              return EqualsBuilder.reflectionEquals(this, obj);
74          }
75  
76          @Override
77          public int hashCode() {
78              return HashCodeBuilder.reflectionHashCode(this);
79          }
80      }
81  
82      private static class FloatWrapper {
83  
84          private /* not final might not matter for the test. */ float value;
85  
86          FloatWrapper(final float a) {
87              value = a;
88          }
89      }
90  
91      private static class FloatWrapperEquals {
92  
93          private /* not final might not matter for the test. */ float value;
94  
95          FloatWrapperEquals(final float a) {
96              value = a;
97          }
98  
99          @Override
100         public boolean equals(final Object obj) {
101             if (this == obj) {
102                 return true;
103             }
104             if (obj == null || getClass() != obj.getClass()) {
105                 return false;
106             }
107             final FloatWrapperEquals other = (FloatWrapperEquals) obj;
108             return Float.floatToIntBits(value) == Float.floatToIntBits(other.value);
109         }
110 
111         @Override
112         public int hashCode() {
113             return Objects.hash(value);
114         }
115     }
116 
117     private static class FloatWrapperWrapper {
118 
119         private /* not final might not matter for the test. */ FloatWrapper value;
120 
121         FloatWrapperWrapper(final float a) {
122             value = new FloatWrapper(a);
123         }
124     }
125 
126     private static class FloatWrapperWrapperDeepEquals {
127 
128         private /* not final might not matter for the test. */ FloatWrapperEquals value;
129 
130         FloatWrapperWrapperDeepEquals(final float a) {
131             value = new FloatWrapperEquals(a);
132         }
133 
134         FloatWrapperWrapperDeepEquals(final FloatWrapperEquals a) {
135             value = a;
136         }
137 
138         @Override
139         public boolean equals(final Object obj) {
140             if (this == obj) {
141                 return true;
142             }
143             if (obj == null || getClass() != obj.getClass()) {
144                 return false;
145             }
146             final FloatWrapperWrapperDeepEquals other = (FloatWrapperWrapperDeepEquals) obj;
147             return Objects.equals(value, other.value);
148         }
149 
150         @Override
151         public int hashCode() {
152             return Objects.hash(value);
153         }
154 
155     }
156 
157     private static class FloatWrapperWrapperEquals {
158 
159         private /* not final might not matter for the test. */ FloatWrapper value;
160 
161         FloatWrapperWrapperEquals(final float a) {
162             value = new FloatWrapper(a);
163         }
164 
165         FloatWrapperWrapperEquals(final FloatWrapper a) {
166             value = a;
167         }
168 
169         @Override
170         public boolean equals(final Object obj) {
171             if (this == obj) {
172                 return true;
173             }
174             if (obj == null || getClass() != obj.getClass()) {
175                 return false;
176             }
177             final FloatWrapperWrapperEquals other = (FloatWrapperWrapperEquals) obj;
178             return Objects.equals(value, other.value);
179         }
180 
181         @Override
182         public int hashCode() {
183             return Objects.hash(value);
184         }
185 
186     }
187 
188     private static final class MutualDiffableNode implements Diffable<MutualDiffableNode> {
189         @SuppressWarnings("unused")
190         private MutualDiffableNode other;
191         @SuppressWarnings("unused")
192         private final String name;
193 
194         MutualDiffableNode(final String name) {
195             this.name = name;
196         }
197 
198         @Override
199         public DiffResult<MutualDiffableNode> diff(final MutualDiffableNode obj) {
200             return ReflectionDiffBuilder.<MutualDiffableNode>builder()
201                     .setDiffBuilder(DiffBuilder.<MutualDiffableNode>builder()
202                             .setLeft(this)
203                             .setRight(obj)
204                             .setStyle(ToStringStyle.SHORT_PREFIX_STYLE)
205                             .build())
206                     .build()
207                     .build();
208         }
209 
210         @Override
211         public boolean equals(final Object obj) {
212             return EqualsBuilder.reflectionEquals(this, obj);
213         }
214 
215         @Override
216         public int hashCode() {
217             return HashCodeBuilder.reflectionHashCode(this);
218         }
219     }
220 
221     private static final class NodeDiffable implements Diffable<NodeDiffable> {
222         @SuppressWarnings("unused")
223         private NodeDiffable next;
224         @SuppressWarnings("unused")
225         private final String id;
226 
227         NodeDiffable(final String id) {
228             this.id = id;
229         }
230 
231         @Override
232         public DiffResult<NodeDiffable> diff(final NodeDiffable obj) {
233             return ReflectionDiffBuilder.<NodeDiffable>builder()
234                     .setDiffBuilder(DiffBuilder.<NodeDiffable>builder()
235                             .setLeft(this)
236                             .setRight(obj)
237                             .setStyle(ToStringStyle.SHORT_PREFIX_STYLE)
238                             .build())
239                     .build()
240                     .build();
241         }
242 
243         @Override
244         public boolean equals(final Object obj) {
245             return EqualsBuilder.reflectionEquals(this, obj);
246         }
247 
248         @Override
249         public int hashCode() {
250             return HashCodeBuilder.reflectionHashCode(this);
251         }
252     }
253 
254     @SuppressWarnings("unused")
255     private static final class TypeTestChildClass extends TypeTestClass {
256         String field = "a";
257     }
258 
259     @SuppressWarnings("unused")
260     private static class TypeTestClass implements Diffable<TypeTestClass> {
261 
262         private static int staticField;
263         private final ToStringStyle style = SHORT_STYLE;
264         private final boolean booleanField = true;
265         private final boolean[] booleanArrayField = { true };
266         private final byte byteField = (byte) 0xFF;
267         private final byte[] byteArrayField = { (byte) 0xFF };
268         private char charField = 'a';
269         private char[] charArrayField = { 'a' };
270         private final double doubleField = 1.0;
271         private final double[] doubleArrayField = { 1.0 };
272         private final float floatField = 1.0f;
273         private final float[] floatArrayField = { 1.0f };
274         int intField = 1;
275         private final int[] intArrayField = { 1 };
276         private final long longField = 1L;
277         private final long[] longArrayField = { 1L };
278         private final short shortField = 1;
279         private final short[] shortArrayField = { 1 };
280         private final Object objectField = null;
281         private final Object[] objectArrayField = { null };
282         private transient String transientField;
283         private BigDecimal bigDecimal = BigDecimal.valueOf(20, 1);
284         private BigInteger bigInteger = BigInteger.valueOf(2);
285         @DiffExclude
286         private String annotatedField = "a";
287         private String excludedField = "a";
288 
289         @Override
290         public DiffResult<TypeTestClass> diff(final TypeTestClass obj) {
291             // @formatter:off
292             return ReflectionDiffBuilder.<TypeTestClass>builder()
293                     .setDiffBuilder(diffBuilder(obj))
294                     .setExcludeFieldNames("excludedField")
295                     .build()
296                     .build();
297             // @formatter:on
298         }
299 
300         DiffBuilder<TypeTestClass> diffBuilder(final TypeTestClass obj) {
301             // @formatter:off
302             return DiffBuilder.<TypeTestClass>builder()
303                 .setLeft(this)
304                 .setRight(obj)
305                 .setStyle(style)
306                 .build();
307             // @formatter:on
308         }
309 
310         public DiffResult<TypeTestClass> diffDeprecated(final TypeTestClass obj) {
311             return new ReflectionDiffBuilder<>(this, obj, style).setExcludeFieldNames("excludedField").build();
312         }
313 
314         @Override
315         public boolean equals(final Object obj) {
316             return EqualsBuilder.reflectionEquals(this, obj, false);
317         }
318 
319         @Override
320         public int hashCode() {
321             return HashCodeBuilder.reflectionHashCode(this, false);
322         }
323     }
324 
325     private static final ToStringStyle SHORT_STYLE = ToStringStyle.SHORT_PREFIX_STYLE;
326 
327     @Test
328     void testArrayDifference() {
329         final TypeTestClass firstObject = new TypeTestClass();
330         firstObject.charArrayField = new char[] { 'c' };
331         final TypeTestClass secondObject = new TypeTestClass();
332         // Normal builder
333         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
334         assertEquals(1, list.getNumberOfDiffs());
335         // Deprecated constructor
336         list = firstObject.diffDeprecated(secondObject);
337         assertEquals(1, list.getNumberOfDiffs());
338     }
339 
340     @Test
341     void testBigDecimalDifference() {
342         final TypeTestClass firstObject = new TypeTestClass();
343         // 2.0 is not equal to 2.00, see BigDecimal#equals()
344         firstObject.bigDecimal = BigDecimal.valueOf(200, 2);
345         final TypeTestClass secondObject = new TypeTestClass();
346         final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
347         assertEquals(1, list.getNumberOfDiffs());
348     }
349 
350     @Test
351     void testBigIntegerDifference() {
352         final TypeTestClass firstObject = new TypeTestClass();
353         firstObject.bigInteger = BigInteger.valueOf(100);
354         final TypeTestClass secondObject = new TypeTestClass();
355 
356         final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
357         assertEquals(1, list.getNumberOfDiffs());
358     }
359 
360     @Test
361     void testBuilderGetAndSetForceAccessible() {
362         final TypeTestClass first = new TypeTestClass();
363         final TypeTestClass second = new TypeTestClass();
364         final ReflectionDiffBuilder.Builder<TypeTestClass> builder = ReflectionDiffBuilder.<TypeTestClass>builder()
365                 .setDiffBuilder(DiffBuilder.<TypeTestClass>builder().setLeft(first).setRight(second).build())
366                 .setForceAccessible(true);
367         final ReflectionDiffBuilder<TypeTestClass> diffBuilder = builder.get();
368         assertNotNull(diffBuilder);
369         assertTrue(diffBuilder.isForceAccessible());
370         assertEquals(0, diffBuilder.build().getNumberOfDiffs());
371     }
372 
373     @Test
374     void testCycleAsymmetric() {
375         final CycleDiffableNode first = new CycleDiffableNode("a");
376         final CycleDiffableNode second = new CycleDiffableNode("a");
377         first.self = first;
378         second.self = null;
379 
380         final DiffResult<CycleDiffableNode> result = first.diff(second);
381         assertEquals(1, result.getNumberOfDiffs());
382         assertEquals("self", result.getDiffs().get(0).getFieldName());
383         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
384     }
385 
386     @Test
387     void testCycleMutuallyReferential() {
388         final MutualDiffableNode a = new MutualDiffableNode("node");
389         final MutualDiffableNode b = new MutualDiffableNode("node");
390         a.other = b;
391         b.other = a;
392 
393         final MutualDiffableNode c = new MutualDiffableNode("node");
394         final MutualDiffableNode d = new MutualDiffableNode("node");
395         c.other = d;
396         d.other = c;
397 
398         final DiffResult<MutualDiffableNode> result = a.diff(c);
399         assertEquals(0, result.getNumberOfDiffs());
400         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
401     }
402 
403     @Test
404     void testCycleMutuallyReferentialWithDifference() {
405         final MutualDiffableNode a = new MutualDiffableNode("nodeA");
406         final MutualDiffableNode b = new MutualDiffableNode("nodeB");
407         a.other = b;
408         b.other = a;
409 
410         final MutualDiffableNode c = new MutualDiffableNode("nodeA");
411         final MutualDiffableNode d = new MutualDiffableNode("nodeChanged");
412         c.other = d;
413         d.other = c;
414 
415         final DiffResult<MutualDiffableNode> result = a.diff(c);
416         assertEquals(1, result.getNumberOfDiffs());
417         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
418     }
419 
420     @Test
421     void testCycleSelfReferential() {
422         final CycleDiffableNode first = new CycleDiffableNode("a");
423         final CycleDiffableNode second = new CycleDiffableNode("a");
424         first.self = first;
425         second.self = second;
426 
427         final DiffResult<CycleDiffableNode> result = first.diff(second);
428         assertEquals(0, result.getNumberOfDiffs());
429         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
430     }
431 
432     @Test
433     void testCycleSelfReferentialWithDifference() {
434         final CycleDiffableNode first = new CycleDiffableNode("a");
435         final CycleDiffableNode second = new CycleDiffableNode("b");
436         first.self = first;
437         second.self = second;
438 
439         final DiffResult<CycleDiffableNode> result = first.diff(second);
440         assertEquals(2, result.getNumberOfDiffs());
441         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
442     }
443 
444     @Test
445     void testCycleThreeNode() {
446         final NodeDiffable a1 = new NodeDiffable("a");
447         final NodeDiffable b1 = new NodeDiffable("b");
448         final NodeDiffable c1 = new NodeDiffable("c");
449         a1.next = b1;
450         b1.next = c1;
451         c1.next = a1;
452 
453         final NodeDiffable a2 = new NodeDiffable("a");
454         final NodeDiffable b2 = new NodeDiffable("b");
455         final NodeDiffable c2 = new NodeDiffable("c");
456         a2.next = b2;
457         b2.next = c2;
458         c2.next = a2;
459 
460         final DiffResult<NodeDiffable> result = a1.diff(a2);
461         assertEquals(0, result.getNumberOfDiffs());
462         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
463     }
464 
465     @Test
466     void testCycleThreeNodeWithDifference() {
467         final NodeDiffable a1 = new NodeDiffable("a");
468         final NodeDiffable b1 = new NodeDiffable("b");
469         final NodeDiffable c1 = new NodeDiffable("c");
470         a1.next = b1;
471         b1.next = c1;
472         c1.next = a1;
473 
474         final NodeDiffable a2 = new NodeDiffable("a");
475         final NodeDiffable b2 = new NodeDiffable("b");
476         final NodeDiffable c2 = new NodeDiffable("changed");
477         a2.next = b2;
478         b2.next = c2;
479         c2.next = a2;
480 
481         final DiffResult<NodeDiffable> result = a1.diff(a2);
482         assertEquals(1, result.getNumberOfDiffs());
483         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
484     }
485 
486     @Test
487     void testCycleWithNullReference() {
488         final CycleDiffableNode first = new CycleDiffableNode("a");
489         final CycleDiffableNode second = new CycleDiffableNode("a");
490         first.self = null;
491         second.self = null;
492 
493         final DiffResult<CycleDiffableNode> result = first.diff(second);
494         assertEquals(0, result.getNumberOfDiffs());
495         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
496     }
497 
498     @Test
499     void testDifferenceInInherited_field() {
500         final TypeTestChildClass firstObject = new TypeTestChildClass();
501         firstObject.intField = 99;
502         final TypeTestChildClass secondObject = new TypeTestChildClass();
503 
504         final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
505         assertEquals(1, list.getNumberOfDiffs());
506     }
507 
508     @Test
509     void testForceAccessibleFalseWithCycle() {
510         final CycleDiffableNode first = new CycleDiffableNode("a");
511         final CycleDiffableNode second = new CycleDiffableNode("a");
512         first.self = first;
513         second.self = second;
514 
515         final ReflectionDiffBuilder.Builder<CycleDiffableNode> builder = ReflectionDiffBuilder.<CycleDiffableNode>builder()
516                 .setDiffBuilder(DiffBuilder.<CycleDiffableNode>builder()
517                         .setLeft(first)
518                         .setRight(second)
519                         .setStyle(ToStringStyle.SHORT_PREFIX_STYLE)
520                         .build())
521                 .setForceAccessible(false);
522         final ReflectionDiffBuilder<CycleDiffableNode> diffBuilder = builder.get();
523         assertNotNull(diffBuilder);
524         assertFalse(diffBuilder.isForceAccessible());
525         final DiffResult<CycleDiffableNode> result = diffBuilder.build();
526         assertEquals(0, result.getNumberOfDiffs());
527         assertTrue(ReflectionDiffBuilder.getRegistry().isEmpty(), "Registry must be empty after diff");
528     }
529 
530     /*
531      * See https://issues.apache.org/jira/browse/LANG-1708
532      */
533     @Test
534     void testGetDiffAtomicInteger() {
535         final AtomicInteger a = new AtomicInteger(1);
536         final AtomicInteger b = new AtomicInteger(1);
537         assertEquals(0, new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs().size());
538         assertEquals(0, new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs().size());
539         assertEquals(1,
540                 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new AtomicInteger(1), new AtomicInteger(2), ToStringStyle.JSON_STYLE).build().getDiffs()).size());
541     }
542 
543     /*
544      * See https://issues.apache.org/jira/browse/LANG-1708
545      */
546     @Test
547     void testGetDiffAtomicIntegerWrapper() {
548         final AtomicIntegerWrapper a = new AtomicIntegerWrapper(1);
549         final AtomicIntegerWrapper b = new AtomicIntegerWrapper(1);
550         final List<Diff<?>> diffList = new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs();
551         assertEquals(1, diffList.size());
552         final Diff<?> diff = diffList.get(0);
553         assertFalse(diffList.isEmpty(), diff.toString());
554         assertSame(a.value, diff.getKey());
555         assertSame(b.value, diff.getValue());
556         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
557     }
558 
559     /*
560      * See https://issues.apache.org/jira/browse/LANG-1708
561      */
562     @Test
563     void testGetDiffFloatWrapper() {
564         final FloatWrapper a = new FloatWrapper(1f);
565         final FloatWrapper b = new FloatWrapper(1f);
566         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
567         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
568         assertEquals(1,
569                 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapper(1f), new FloatWrapper(2f), ToStringStyle.JSON_STYLE).build().getDiffs()).size());
570     }
571 
572     /*
573      * See https://issues.apache.org/jira/browse/LANG-1708
574      */
575     @Test
576     void testGetDiffFloatWrapperDeepEquals() {
577         final FloatWrapperWrapperDeepEquals a = new FloatWrapperWrapperDeepEquals(1f);
578         final FloatWrapperWrapperDeepEquals b = new FloatWrapperWrapperDeepEquals(1f);
579         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
580         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
581         assertEquals(1, ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperDeepEquals(1f), new FloatWrapperWrapperDeepEquals(2f),
582                 ToStringStyle.JSON_STYLE).build().getDiffs()).size());
583         final FloatWrapperEquals fw1 = new FloatWrapperEquals(1f);
584         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperDeepEquals(fw1), new FloatWrapperWrapperDeepEquals(fw1),
585                 ToStringStyle.JSON_STYLE).build().getDiffs()).size());
586         final FloatWrapperEquals fw2 = new FloatWrapperEquals(2f);
587         assertEquals(1, ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperDeepEquals(fw1), new FloatWrapperWrapperDeepEquals(fw2),
588                 ToStringStyle.JSON_STYLE).build().getDiffs()).size());
589     }
590 
591     /*
592      * See https://issues.apache.org/jira/browse/LANG-1708
593      */
594     @Test
595     void testGetDiffFloatWrapperEquals() {
596         final FloatWrapperEquals a = new FloatWrapperEquals(1f);
597         final FloatWrapperEquals b = new FloatWrapperEquals(1f);
598         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
599         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
600         assertEquals(1,
601                 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperEquals(1f), new FloatWrapperEquals(2f), ToStringStyle.JSON_STYLE).build().getDiffs())
602                         .size());
603     }
604 
605     /*
606      * See https://issues.apache.org/jira/browse/LANG-1708
607      */
608     @Test
609     void testGetDiffFloatWrapperWrapper() {
610         final FloatWrapperWrapper a = new FloatWrapperWrapper(1f);
611         final FloatWrapperWrapper b = new FloatWrapperWrapper(1f);
612         final List<Diff<?>> diffList = new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs();
613         assertEquals(1, diffList.size());
614         final Diff<?> diff = diffList.get(0);
615         assertFalse(diffList.isEmpty(), diff.toString());
616         assertSame(a.value, diff.getKey());
617         assertSame(b.value, diff.getValue());
618         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
619         assertEquals(1,
620                 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapper(1f), new FloatWrapperWrapper(2f), ToStringStyle.JSON_STYLE)
621                         .build().getDiffs()).size());
622     }
623 
624     /*
625      * See https://issues.apache.org/jira/browse/LANG-1708
626      */
627     @Test
628     void testGetDiffFloatWrapperWrapperEquals() {
629         final FloatWrapperWrapperEquals a = new FloatWrapperWrapperEquals(1f);
630         final FloatWrapperWrapperEquals b = new FloatWrapperWrapperEquals(1f);
631         final List<Diff<?>> diffList = new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs();
632         assertEquals(1, diffList.size());
633         final Diff<?> diff = diffList.get(0);
634         assertFalse(diffList.isEmpty(), diff.toString());
635         assertSame(a.value, diff.getKey());
636         assertSame(b.value, diff.getValue());
637         assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
638         assertEquals(1,
639                 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperEquals(1f), new FloatWrapperWrapperEquals(2f), ToStringStyle.JSON_STYLE)
640                         .build().getDiffs()).size());
641         final FloatWrapper fw1 = new FloatWrapper(1f);
642         assertEquals(0,
643                 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperEquals(fw1), new FloatWrapperWrapperEquals(fw1), ToStringStyle.JSON_STYLE)
644                         .build().getDiffs()).size());
645     }
646 
647     @Test
648     void testGetExcludeFieldNamesEmpty() {
649         final ReflectionDiffBuilder<?> reflectionDiffBuilder = new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
650         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
651         assertNotNull(excludeFieldNames);
652         assertEquals(0, excludeFieldNames.length);
653     }
654 
655     @Test
656     void testGetExcludeFieldNamesWithNullExcludedFieldNames() {
657         // @formatter:off
658         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
659                 .setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
660                         .setLeft(new TypeTestClass())
661                         .setRight(new TypeTestChildClass())
662                         .setStyle(SHORT_STYLE)
663                         .build())
664                 .build();
665         // @formatter:on
666         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
667         assertNotNull(excludeFieldNames);
668         assertEquals(0, excludeFieldNames.length);
669         assertNotNull(reflectionDiffBuilder.build());
670     }
671 
672     @Test
673     void testGetExcludeFieldNamesWithNullExcludedFieldNamesCtor() {
674         // @formatter:off
675         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
676                 new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
677         // @formatter:on
678         reflectionDiffBuilder.setExcludeFieldNames((String[]) null);
679         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
680         assertNotNull(excludeFieldNames);
681         assertEquals(0, excludeFieldNames.length);
682         assertNotNull(reflectionDiffBuilder.build());
683     }
684 
685     @Test
686     void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNames() {
687         // @formatter:off
688         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
689                 .setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
690                         .setLeft(new TypeTestClass())
691                         .setRight(new TypeTestChildClass())
692                         .setStyle(SHORT_STYLE)
693                         .build())
694                 .setExcludeFieldNames("charField", null)
695                 .build();
696         // @formatter:on
697         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
698         assertNotNull(excludeFieldNames);
699         assertEquals(1, excludeFieldNames.length);
700         assertEquals("charField", excludeFieldNames[0]);
701         assertNotNull(reflectionDiffBuilder.build());
702     }
703 
704     @Test
705     void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNamesCtor() {
706         // @formatter:off
707         final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
708                 new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
709         // @formatter:on
710         reflectionDiffBuilder.setExcludeFieldNames("charField", null);
711         final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
712         assertNotNull(excludeFieldNames);
713         assertEquals(1, excludeFieldNames.length);
714         assertEquals("charField", excludeFieldNames[0]);
715         assertNotNull(reflectionDiffBuilder.build());
716     }
717 
718     @Test
719     void testNoDiffBuilderSet() {
720         assertThrows(NullPointerException.class, () -> ReflectionDiffBuilder.<TypeTestClass>builder().build());
721     }
722 
723     @Test
724     void testNoDifferences() {
725         final TypeTestClass firstObject = new TypeTestClass();
726         final TypeTestClass secondObject = new TypeTestClass();
727         assertEquals(0, firstObject.diff(secondObject).getNumberOfDiffs());
728         assertEquals(0, firstObject.diffDeprecated(secondObject).getNumberOfDiffs());
729     }
730 
731     @Test
732     void testNoDifferencesDiffExcludeAnnotatedField() {
733         final TypeTestClass firstObject = new TypeTestClass();
734         firstObject.annotatedField = "b";
735         final TypeTestClass secondObject = new TypeTestClass();
736         assertEquals(0, firstObject.diff(secondObject).getNumberOfDiffs());
737         assertEquals(0, firstObject.diffDeprecated(secondObject).getNumberOfDiffs());
738     }
739 
740     @Test
741     void testNoDifferencesDiffExcludedFieldAndExcludeAnnotatedField() {
742         final TypeTestClass firstObject = new TypeTestClass();
743         firstObject.excludedField = "b";
744         firstObject.annotatedField = "b";
745         final TypeTestClass secondObject = new TypeTestClass();
746         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
747         assertEquals(0, list.getNumberOfDiffs());
748         list = firstObject.diffDeprecated(secondObject);
749         assertEquals(0, list.getNumberOfDiffs());
750     }
751 
752     @Test
753     void testNoDifferencesExcludedField() {
754         final TypeTestClass firstObject = new TypeTestClass();
755         firstObject.excludedField = "b";
756         final TypeTestClass secondObject = new TypeTestClass();
757         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
758         assertEquals(0, list.getNumberOfDiffs());
759         list = firstObject.diffDeprecated(secondObject);
760         assertEquals(0, list.getNumberOfDiffs());
761     }
762 
763     @Test
764     void testNoDifferencesInheritance() {
765         final TypeTestChildClass firstObject = new TypeTestChildClass();
766         final TypeTestChildClass secondObject = new TypeTestChildClass();
767         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
768         assertEquals(0, list.getNumberOfDiffs());
769         list = firstObject.diffDeprecated(secondObject);
770         assertEquals(0, list.getNumberOfDiffs());
771     }
772 
773     @Test
774     void testPrimitiveDifference() {
775         final TypeTestClass firstObject = new TypeTestClass();
776         firstObject.charField = 'c';
777         final TypeTestClass secondObject = new TypeTestClass();
778         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
779         assertEquals(1, list.getNumberOfDiffs());
780         list = firstObject.diffDeprecated(secondObject);
781         assertEquals(1, list.getNumberOfDiffs());
782     }
783 
784     @Test
785     void testRetention() throws Exception {
786         // The following should not retain memory.
787         for (int i = 0; i < Integer.getInteger("testRecursive", 10_000); i++) {
788             final Class<?> clazz = TestClassBuilder.defineSimpleClass(getClass().getPackage().getName(), i);
789             final Object firstObject = clazz.newInstance();
790             final Object secondObject = clazz.newInstance();
791             final ReflectionDiffBuilder<Object> reflectionDiffBuilder = new ReflectionDiffBuilder<>(firstObject, secondObject, SHORT_STYLE);
792             assertNotNull(reflectionDiffBuilder.build());
793         }
794     }
795 
796     @Test
797     void testTransientFieldDifference() {
798         final TypeTestClass firstObject = new TypeTestClass();
799         firstObject.transientField = "a";
800         final TypeTestClass secondObject = new TypeTestClass();
801         secondObject.transientField = "b";
802         DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
803         assertEquals(0, list.getNumberOfDiffs());
804         list = firstObject.diffDeprecated(secondObject);
805         assertEquals(0, list.getNumberOfDiffs());
806     }
807 
808 }