1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.concurrent.atomic.AtomicInteger;
30
31 import org.apache.commons.lang3.AbstractLangTest;
32 import org.junit.jupiter.api.Test;
33
34
35
36
37 class ReflectionDiffBuilderTest extends AbstractLangTest {
38
39 private static class AtomicIntegerWrapper {
40
41 private AtomicInteger value;
42
43 AtomicIntegerWrapper(final int a) {
44 value = new AtomicInteger(a);
45 }
46 }
47
48 private static class FloatWrapper {
49
50 private float value;
51
52 FloatWrapper(final float a) {
53 value = a;
54 }
55 }
56
57 private static class FloatWrapperEquals {
58
59 private float value;
60
61 FloatWrapperEquals(final float a) {
62 value = a;
63 }
64
65 @Override
66 public boolean equals(final Object obj) {
67 if (this == obj) {
68 return true;
69 }
70 if (obj == null || getClass() != obj.getClass()) {
71 return false;
72 }
73 final FloatWrapperEquals other = (FloatWrapperEquals) obj;
74 return Float.floatToIntBits(value) == Float.floatToIntBits(other.value);
75 }
76
77 @Override
78 public int hashCode() {
79 return Objects.hash(value);
80 }
81 }
82
83 private static class FloatWrapperWrapper {
84
85 private FloatWrapper value;
86
87 FloatWrapperWrapper(final float a) {
88 value = new FloatWrapper(a);
89 }
90 }
91
92 private static class FloatWrapperWrapperDeepEquals {
93
94 private FloatWrapperEquals value;
95
96 FloatWrapperWrapperDeepEquals(final float a) {
97 value = new FloatWrapperEquals(a);
98 }
99
100 FloatWrapperWrapperDeepEquals(final FloatWrapperEquals a) {
101 value = a;
102 }
103
104 @Override
105 public boolean equals(final Object obj) {
106 if (this == obj) {
107 return true;
108 }
109 if (obj == null || getClass() != obj.getClass()) {
110 return false;
111 }
112 final FloatWrapperWrapperDeepEquals other = (FloatWrapperWrapperDeepEquals) obj;
113 return Objects.equals(value, other.value);
114 }
115
116 @Override
117 public int hashCode() {
118 return Objects.hash(value);
119 }
120
121 }
122
123 private static class FloatWrapperWrapperEquals {
124
125 private FloatWrapper value;
126
127 FloatWrapperWrapperEquals(final float a) {
128 value = new FloatWrapper(a);
129 }
130
131 FloatWrapperWrapperEquals(final FloatWrapper a) {
132 value = a;
133 }
134
135 @Override
136 public boolean equals(final Object obj) {
137 if (this == obj) {
138 return true;
139 }
140 if (obj == null || getClass() != obj.getClass()) {
141 return false;
142 }
143 final FloatWrapperWrapperEquals other = (FloatWrapperWrapperEquals) obj;
144 return Objects.equals(value, other.value);
145 }
146
147 @Override
148 public int hashCode() {
149 return Objects.hash(value);
150 }
151
152 }
153
154 @SuppressWarnings("unused")
155 private static final class TypeTestChildClass extends TypeTestClass {
156 String field = "a";
157 }
158
159 @SuppressWarnings("unused")
160 private static class TypeTestClass implements Diffable<TypeTestClass> {
161
162 private static int staticField;
163 private final ToStringStyle style = SHORT_STYLE;
164 private final boolean booleanField = true;
165 private final boolean[] booleanArrayField = { true };
166 private final byte byteField = (byte) 0xFF;
167 private final byte[] byteArrayField = { (byte) 0xFF };
168 private char charField = 'a';
169 private char[] charArrayField = { 'a' };
170 private final double doubleField = 1.0;
171 private final double[] doubleArrayField = { 1.0 };
172 private final float floatField = 1.0f;
173 private final float[] floatArrayField = { 1.0f };
174 int intField = 1;
175 private final int[] intArrayField = { 1 };
176 private final long longField = 1L;
177 private final long[] longArrayField = { 1L };
178 private final short shortField = 1;
179 private final short[] shortArrayField = { 1 };
180 private final Object objectField = null;
181 private final Object[] objectArrayField = { null };
182 private transient String transientField;
183 private BigDecimal bigDecimal = BigDecimal.valueOf(20, 1);
184 private BigInteger bigInteger = BigInteger.valueOf(2);
185 @DiffExclude
186 private String annotatedField = "a";
187 private String excludedField = "a";
188
189 @Override
190 public DiffResult<TypeTestClass> diff(final TypeTestClass obj) {
191
192 return ReflectionDiffBuilder.<TypeTestClass>builder()
193 .setDiffBuilder(diffBuilder(obj))
194 .setExcludeFieldNames("excludedField")
195 .build()
196 .build();
197
198 }
199
200 DiffBuilder<TypeTestClass> diffBuilder(final TypeTestClass obj) {
201
202 return DiffBuilder.<TypeTestClass>builder()
203 .setLeft(this)
204 .setRight(obj)
205 .setStyle(style)
206 .build();
207
208 }
209
210 public DiffResult<TypeTestClass> diffDeprecated(final TypeTestClass obj) {
211 return new ReflectionDiffBuilder<>(this, obj, style).setExcludeFieldNames("excludedField").build();
212 }
213
214 @Override
215 public boolean equals(final Object obj) {
216 return EqualsBuilder.reflectionEquals(this, obj, false);
217 }
218
219 @Override
220 public int hashCode() {
221 return HashCodeBuilder.reflectionHashCode(this, false);
222 }
223 }
224
225 private static final ToStringStyle SHORT_STYLE = ToStringStyle.SHORT_PREFIX_STYLE;
226
227 @Test
228 void testArrayDifference() {
229 final TypeTestClass firstObject = new TypeTestClass();
230 firstObject.charArrayField = new char[] { 'c' };
231 final TypeTestClass secondObject = new TypeTestClass();
232
233 DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
234 assertEquals(1, list.getNumberOfDiffs());
235
236 list = firstObject.diffDeprecated(secondObject);
237 assertEquals(1, list.getNumberOfDiffs());
238 }
239
240 @Test
241 void testBigDecimalDifference() {
242 final TypeTestClass firstObject = new TypeTestClass();
243
244 firstObject.bigDecimal = BigDecimal.valueOf(200, 2);
245 final TypeTestClass secondObject = new TypeTestClass();
246 final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
247 assertEquals(1, list.getNumberOfDiffs());
248 }
249
250 @Test
251 void testBigIntegerDifference() {
252 final TypeTestClass firstObject = new TypeTestClass();
253 firstObject.bigInteger = BigInteger.valueOf(100);
254 final TypeTestClass secondObject = new TypeTestClass();
255
256 final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
257 assertEquals(1, list.getNumberOfDiffs());
258 }
259
260 @Test
261 void testDifferenceInInherited_field() {
262 final TypeTestChildClass firstObject = new TypeTestChildClass();
263 firstObject.intField = 99;
264 final TypeTestChildClass secondObject = new TypeTestChildClass();
265
266 final DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
267 assertEquals(1, list.getNumberOfDiffs());
268 }
269
270
271
272
273 @Test
274 void testGetDiffAtomicInteger() {
275 final AtomicInteger a = new AtomicInteger(1);
276 final AtomicInteger b = new AtomicInteger(1);
277 assertEquals(0, new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs().size());
278 assertEquals(0, new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs().size());
279 assertEquals(1,
280 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new AtomicInteger(1), new AtomicInteger(2), ToStringStyle.JSON_STYLE).build().getDiffs()).size());
281 }
282
283
284
285
286 @Test
287 void testGetDiffAtomicIntegerWrapper() {
288 final AtomicIntegerWrapper a = new AtomicIntegerWrapper(1);
289 final AtomicIntegerWrapper b = new AtomicIntegerWrapper(1);
290 final List<Diff<?>> diffList = new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs();
291 assertEquals(1, diffList.size());
292 final Diff<?> diff = diffList.get(0);
293 assertFalse(diffList.isEmpty(), diff.toString());
294 assertSame(a.value, diff.getKey());
295 assertSame(b.value, diff.getValue());
296 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
297 }
298
299
300
301
302 @Test
303 void testGetDiffFloatWrapper() {
304 final FloatWrapper a = new FloatWrapper(1f);
305 final FloatWrapper b = new FloatWrapper(1f);
306 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
307 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
308 assertEquals(1,
309 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapper(1f), new FloatWrapper(2f), ToStringStyle.JSON_STYLE).build().getDiffs()).size());
310 }
311
312
313
314
315 @Test
316 void testGetDiffFloatWrapperDeepEquals() {
317 final FloatWrapperWrapperDeepEquals a = new FloatWrapperWrapperDeepEquals(1f);
318 final FloatWrapperWrapperDeepEquals b = new FloatWrapperWrapperDeepEquals(1f);
319 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
320 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
321 assertEquals(1, ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperDeepEquals(1f), new FloatWrapperWrapperDeepEquals(2f),
322 ToStringStyle.JSON_STYLE).build().getDiffs()).size());
323 final FloatWrapperEquals fw1 = new FloatWrapperEquals(1f);
324 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperDeepEquals(fw1), new FloatWrapperWrapperDeepEquals(fw1),
325 ToStringStyle.JSON_STYLE).build().getDiffs()).size());
326 final FloatWrapperEquals fw2 = new FloatWrapperEquals(2f);
327 assertEquals(1, ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperDeepEquals(fw1), new FloatWrapperWrapperDeepEquals(fw2),
328 ToStringStyle.JSON_STYLE).build().getDiffs()).size());
329 }
330
331
332
333
334 @Test
335 void testGetDiffFloatWrapperEquals() {
336 final FloatWrapperEquals a = new FloatWrapperEquals(1f);
337 final FloatWrapperEquals b = new FloatWrapperEquals(1f);
338 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
339 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
340 assertEquals(1,
341 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperEquals(1f), new FloatWrapperEquals(2f), ToStringStyle.JSON_STYLE).build().getDiffs())
342 .size());
343 }
344
345
346
347
348 @Test
349 void testGetDiffFloatWrapperWrapper() {
350 final FloatWrapperWrapper a = new FloatWrapperWrapper(1f);
351 final FloatWrapperWrapper b = new FloatWrapperWrapper(1f);
352 final List<Diff<?>> diffList = new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs();
353 assertEquals(1, diffList.size());
354 final Diff<?> diff = diffList.get(0);
355 assertFalse(diffList.isEmpty(), diff.toString());
356 assertSame(a.value, diff.getKey());
357 assertSame(b.value, diff.getValue());
358 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
359 assertEquals(1,
360 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapper(1f), new FloatWrapperWrapper(2f), ToStringStyle.JSON_STYLE)
361 .build().getDiffs()).size());
362 }
363
364
365
366
367 @Test
368 void testGetDiffFloatWrapperWrapperEquals() {
369 final FloatWrapperWrapperEquals a = new FloatWrapperWrapperEquals(1f);
370 final FloatWrapperWrapperEquals b = new FloatWrapperWrapperEquals(1f);
371 final List<Diff<?>> diffList = new ReflectionDiffBuilder<>(a, b, ToStringStyle.JSON_STYLE).build().getDiffs();
372 assertEquals(1, diffList.size());
373 final Diff<?> diff = diffList.get(0);
374 assertFalse(diffList.isEmpty(), diff.toString());
375 assertSame(a.value, diff.getKey());
376 assertSame(b.value, diff.getValue());
377 assertEquals(0, ((List<Diff<?>>) new ReflectionDiffBuilder<>(a, a, ToStringStyle.JSON_STYLE).build().getDiffs()).size());
378 assertEquals(1,
379 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperEquals(1f), new FloatWrapperWrapperEquals(2f), ToStringStyle.JSON_STYLE)
380 .build().getDiffs()).size());
381 final FloatWrapper fw1 = new FloatWrapper(1f);
382 assertEquals(0,
383 ((List<Diff<?>>) new ReflectionDiffBuilder<>(new FloatWrapperWrapperEquals(fw1), new FloatWrapperWrapperEquals(fw1), ToStringStyle.JSON_STYLE)
384 .build().getDiffs()).size());
385 }
386
387 @Test
388 void testGetExcludeFieldNamesEmpty() {
389 final ReflectionDiffBuilder<?> reflectionDiffBuilder = new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
390 final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
391 assertNotNull(excludeFieldNames);
392 assertEquals(0, excludeFieldNames.length);
393 }
394
395 @Test
396 void testGetExcludeFieldNamesWithNullExcludedFieldNames() {
397
398 final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
399 .setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
400 .setLeft(new TypeTestClass())
401 .setRight(new TypeTestChildClass())
402 .setStyle(SHORT_STYLE)
403 .build())
404 .build();
405
406 final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
407 assertNotNull(excludeFieldNames);
408 assertEquals(0, excludeFieldNames.length);
409 assertNotNull(reflectionDiffBuilder.build());
410 }
411
412 @Test
413 void testGetExcludeFieldNamesWithNullExcludedFieldNamesCtor() {
414
415 final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
416 new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
417
418 reflectionDiffBuilder.setExcludeFieldNames((String[]) null);
419 final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
420 assertNotNull(excludeFieldNames);
421 assertEquals(0, excludeFieldNames.length);
422 assertNotNull(reflectionDiffBuilder.build());
423 }
424
425 @Test
426 void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNames() {
427
428 final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder = ReflectionDiffBuilder.<TypeTestClass>builder()
429 .setDiffBuilder(DiffBuilder.<TypeTestClass>builder()
430 .setLeft(new TypeTestClass())
431 .setRight(new TypeTestChildClass())
432 .setStyle(SHORT_STYLE)
433 .build())
434 .setExcludeFieldNames("charField", null)
435 .build();
436
437 final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
438 assertNotNull(excludeFieldNames);
439 assertEquals(1, excludeFieldNames.length);
440 assertEquals("charField", excludeFieldNames[0]);
441 assertNotNull(reflectionDiffBuilder.build());
442 }
443
444 @Test
445 void testGetExcludeFieldNamesWithNullValuesInExcludedFieldNamesCtor() {
446
447 final ReflectionDiffBuilder<TypeTestClass> reflectionDiffBuilder =
448 new ReflectionDiffBuilder<>(new TypeTestClass(), new TypeTestChildClass(), SHORT_STYLE);
449
450 reflectionDiffBuilder.setExcludeFieldNames("charField", null);
451 final String[] excludeFieldNames = reflectionDiffBuilder.getExcludeFieldNames();
452 assertNotNull(excludeFieldNames);
453 assertEquals(1, excludeFieldNames.length);
454 assertEquals("charField", excludeFieldNames[0]);
455 assertNotNull(reflectionDiffBuilder.build());
456 }
457
458 @Test
459 void testNoDiffBuilderSet() {
460 assertThrows(NullPointerException.class, () -> ReflectionDiffBuilder.<TypeTestClass>builder().build());
461 }
462
463 @Test
464 void testNoDifferences() {
465 final TypeTestClass firstObject = new TypeTestClass();
466 final TypeTestClass secondObject = new TypeTestClass();
467 assertEquals(0, firstObject.diff(secondObject).getNumberOfDiffs());
468 assertEquals(0, firstObject.diffDeprecated(secondObject).getNumberOfDiffs());
469 }
470
471 @Test
472 void testNoDifferencesDiffExcludeAnnotatedField() {
473 final TypeTestClass firstObject = new TypeTestClass();
474 firstObject.annotatedField = "b";
475 final TypeTestClass secondObject = new TypeTestClass();
476 assertEquals(0, firstObject.diff(secondObject).getNumberOfDiffs());
477 assertEquals(0, firstObject.diffDeprecated(secondObject).getNumberOfDiffs());
478 }
479
480 @Test
481 void testNoDifferencesDiffExcludedFieldAndExcludeAnnotatedField() {
482 final TypeTestClass firstObject = new TypeTestClass();
483 firstObject.excludedField = "b";
484 firstObject.annotatedField = "b";
485 final TypeTestClass secondObject = new TypeTestClass();
486 DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
487 assertEquals(0, list.getNumberOfDiffs());
488 list = firstObject.diffDeprecated(secondObject);
489 assertEquals(0, list.getNumberOfDiffs());
490 }
491
492 @Test
493 void testNoDifferencesExcludedField() {
494 final TypeTestClass firstObject = new TypeTestClass();
495 firstObject.excludedField = "b";
496 final TypeTestClass secondObject = new TypeTestClass();
497 DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
498 assertEquals(0, list.getNumberOfDiffs());
499 list = firstObject.diffDeprecated(secondObject);
500 assertEquals(0, list.getNumberOfDiffs());
501 }
502
503 @Test
504 void testNoDifferencesInheritance() {
505 final TypeTestChildClass firstObject = new TypeTestChildClass();
506 final TypeTestChildClass secondObject = new TypeTestChildClass();
507 DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
508 assertEquals(0, list.getNumberOfDiffs());
509 list = firstObject.diffDeprecated(secondObject);
510 assertEquals(0, list.getNumberOfDiffs());
511 }
512
513 @Test
514 void testPrimitiveDifference() {
515 final TypeTestClass firstObject = new TypeTestClass();
516 firstObject.charField = 'c';
517 final TypeTestClass secondObject = new TypeTestClass();
518 DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
519 assertEquals(1, list.getNumberOfDiffs());
520 list = firstObject.diffDeprecated(secondObject);
521 assertEquals(1, list.getNumberOfDiffs());
522 }
523
524 @Test
525 void testRetention() throws Exception {
526
527 for (int i = 0; i < Integer.getInteger("testRecursive", 10_000); i++) {
528 final Class<?> clazz = TestClassBuilder.defineSimpleClass(getClass().getPackage().getName(), i);
529 final Object firstObject = clazz.newInstance();
530 final Object secondObject = clazz.newInstance();
531 final ReflectionDiffBuilder<Object> reflectionDiffBuilder = new ReflectionDiffBuilder<>(firstObject, secondObject, SHORT_STYLE);
532 assertNotNull(reflectionDiffBuilder.build());
533 }
534 }
535
536 @Test
537 void testTransientFieldDifference() {
538 final TypeTestClass firstObject = new TypeTestClass();
539 firstObject.transientField = "a";
540 final TypeTestClass secondObject = new TypeTestClass();
541 secondObject.transientField = "b";
542 DiffResult<TypeTestClass> list = firstObject.diff(secondObject);
543 assertEquals(0, list.getNumberOfDiffs());
544 list = firstObject.diffDeprecated(secondObject);
545 assertEquals(0, list.getNumberOfDiffs());
546 }
547
548 }