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    *      http://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.hamcrest.MatcherAssert.assertThat;
20  import static org.hamcrest.core.IsEqual.equalTo;
21  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotEquals;
24  import static org.junit.jupiter.api.Assertions.assertNotSame;
25  import static org.junit.jupiter.api.Assertions.assertSame;
26  import static org.junit.jupiter.api.Assertions.assertThrows;
27  
28  import org.apache.commons.lang3.AbstractLangTest;
29  import org.apache.commons.lang3.ArrayUtils;
30  import org.hamcrest.Matcher;
31  import org.junit.jupiter.api.Test;
32  
33  
34  /**
35   * Unit tests {@link DiffBuilder}.
36   */
37  public class DiffBuilderTest extends AbstractLangTest {
38  
39      private static final class TypeTestClass implements Diffable<TypeTestClass> {
40          private ToStringStyle style = SHORT_STYLE;
41          private boolean booleanField = true;
42          private boolean[] booleanArrayField = {true};
43          private byte byteField = (byte) 0xFF;
44          private byte[] byteArrayField = {(byte) 0xFF};
45          private char charField = 'a';
46          private char[] charArrayField = {'a'};
47          private double doubleField = 1.0;
48          private double[] doubleArrayField = {1.0};
49          private float floatField = 1.0f;
50          private float[] floatArrayField = {1.0f};
51          private int intField = 1;
52          private int[] intArrayField = {1};
53          private long longField = 1L;
54          private long[] longArrayField = {1L};
55          private short shortField = 1;
56          private short[] shortArrayField = {1};
57          private Object objectField;
58          private Object[] objectArrayField = {null};
59  
60          @Override
61          public DiffResult<TypeTestClass> diff(final TypeTestClass obj) {
62              return new DiffBuilder<>(this, obj, style)
63                  .append("boolean", booleanField, obj.booleanField)
64                  .append("booleanArray", booleanArrayField, obj.booleanArrayField)
65                  .append("byte", byteField, obj.byteField)
66                  .append("byteArray", byteArrayField, obj.byteArrayField)
67                  .append("char", charField, obj.charField)
68                  .append("charArray", charArrayField, obj.charArrayField)
69                  .append("double", doubleField, obj.doubleField)
70                  .append("doubleArray", doubleArrayField, obj.doubleArrayField)
71                  .append("float", floatField, obj.floatField)
72                  .append("floatArray", floatArrayField, obj.floatArrayField)
73                  .append("int", intField, obj.intField)
74                  .append("intArray", intArrayField, obj.intArrayField)
75                  .append("long", longField, obj.longField)
76                  .append("longArray", longArrayField, obj.longArrayField)
77                  .append("short", shortField, obj.shortField)
78                  .append("shortArray", shortArrayField, obj.shortArrayField)
79                  .append("objectField", objectField, obj.objectField)
80                  .append("objectArrayField", objectArrayField, obj.objectArrayField)
81                  .build();
82          }
83  
84          @Override
85          public boolean equals(final Object obj) {
86              return EqualsBuilder.reflectionEquals(this, obj, false);
87          }
88  
89          @Override
90          public int hashCode() {
91              return HashCodeBuilder.reflectionHashCode(this, false);
92          }
93      }
94  
95      private static final ToStringStyle SHORT_STYLE = ToStringStyle.SHORT_PREFIX_STYLE;
96  
97      @Test
98      public void testBoolean() {
99          final TypeTestClass class1 = new TypeTestClass();
100         final TypeTestClass class2 = new TypeTestClass();
101         class2.booleanField = false;
102         final DiffResult<TypeTestClass> list = class1.diff(class2);
103         assertEquals(1, list.getNumberOfDiffs());
104         final Diff<?> diff = list.getDiffs().get(0);
105         assertEquals(Boolean.class, diff.getType());
106         assertEquals(Boolean.TRUE, diff.getLeft());
107         assertEquals(Boolean.FALSE, diff.getRight());
108     }
109 
110     @Test
111     public void testBooleanArray() {
112         final TypeTestClass class1 = new TypeTestClass();
113         final TypeTestClass class2 = new TypeTestClass();
114         class2.booleanArrayField = new boolean[] {false, false};
115         final DiffResult<TypeTestClass> list = class1.diff(class2);
116         assertEquals(1, list.getNumberOfDiffs());
117         final Diff<?> diff = list.getDiffs().get(0);
118         assertArrayEquals(ArrayUtils.toObject(class1.booleanArrayField),
119                 (Object[]) diff.getLeft());
120         assertArrayEquals(ArrayUtils.toObject(class2.booleanArrayField),
121                 (Object[]) diff.getRight());
122     }
123 
124     @Test
125     public void testByte() {
126         final TypeTestClass class1 = new TypeTestClass();
127         final TypeTestClass class2 = new TypeTestClass();
128         class2.byteField = 0x01;
129         final DiffResult<TypeTestClass> list = class1.diff(class2);
130         assertEquals(1, list.getNumberOfDiffs());
131         final Diff<?> diff = list.getDiffs().get(0);
132         assertEquals(Byte.valueOf(class1.byteField), diff.getLeft());
133         assertEquals(Byte.valueOf(class2.byteField), diff.getRight());
134     }
135 
136     @Test
137     public void testByteArray() {
138         final TypeTestClass class1 = new TypeTestClass();
139         final TypeTestClass class2 = new TypeTestClass();
140         class2.byteArrayField= new byte[] {0x01, 0x02};
141         final DiffResult<TypeTestClass> list = class1.diff(class2);
142         assertEquals(1, list.getNumberOfDiffs());
143         final Diff<?> diff = list.getDiffs().get(0);
144         assertArrayEquals(ArrayUtils.toObject(class1.byteArrayField),
145                 (Object[]) diff.getLeft());
146         assertArrayEquals(ArrayUtils.toObject(class2.byteArrayField),
147                 (Object[]) diff.getRight());
148     }
149 
150     @Test
151     public void testByteArrayEqualAsObject() {
152         final DiffResult<String> list = new DiffBuilder<>("String1", "String2", SHORT_STYLE)
153             .append("foo", new boolean[] {false}, new boolean[] {false})
154             .append("foo", new byte[] {0x01}, new byte[] {0x01})
155             .append("foo", new char[] {'a'}, new char[] {'a'})
156             .append("foo", new double[] {1.0}, new double[] {1.0})
157             .append("foo", new float[] {1.0F}, new float[] {1.0F})
158             .append("foo", new int[] {1}, new int[] {1})
159             .append("foo", new long[] {1L}, new long[] {1L})
160             .append("foo", new short[] {1}, new short[] {1})
161             .append("foo", new Object[] {1, "two"}, new Object[] {1, "two"})
162             .build();
163 
164         assertEquals(0, list.getNumberOfDiffs());
165     }
166 
167     @Test
168     public void testChar() {
169         final TypeTestClass class1 = new TypeTestClass();
170         final TypeTestClass class2 = new TypeTestClass();
171         class2.charField = 'z';
172         final DiffResult<TypeTestClass> list = class1.diff(class2);
173         assertEquals(1, list.getNumberOfDiffs());
174         final Diff<?> diff = list.getDiffs().get(0);
175         assertEquals(Character.valueOf(class1.charField), diff.getLeft());
176         assertEquals(Character.valueOf(class2.charField), diff.getRight());
177     }
178 
179     @Test
180     public void testCharArray() {
181         final TypeTestClass class1 = new TypeTestClass();
182         final TypeTestClass class2 = new TypeTestClass();
183         class2.charArrayField = new char[] {'f', 'o', 'o'};
184         final DiffResult<TypeTestClass> list = class1.diff(class2);
185         assertEquals(1, list.getNumberOfDiffs());
186         final Diff<?> diff = list.getDiffs().get(0);
187         assertArrayEquals(ArrayUtils.toObject(class1.charArrayField),
188                 (Object[]) diff.getLeft());
189         assertArrayEquals(ArrayUtils.toObject(class2.charArrayField),
190                 (Object[]) diff.getRight());
191     }
192 
193     @Test
194     public void testDiffResult() {
195         final TypeTestClass class1 = new TypeTestClass();
196         final TypeTestClass class2 = new TypeTestClass();
197         class2.intField = 2;
198 
199         final DiffResult<TypeTestClass> list = new DiffBuilder<>(class1, class2, SHORT_STYLE)
200             .append("prop1", class1.diff(class2))
201             .build();
202         assertEquals(1, list.getNumberOfDiffs());
203         assertEquals("prop1.int", list.getDiffs().get(0).getFieldName());
204     }
205 
206     @Test
207     public void testDouble() {
208         final TypeTestClass class1 = new TypeTestClass();
209         final TypeTestClass class2 = new TypeTestClass();
210         class2.doubleField = 99.99;
211         final DiffResult<TypeTestClass> list = class1.diff(class2);
212         assertEquals(1, list.getNumberOfDiffs());
213         final Diff<?> diff = list.getDiffs().get(0);
214         assertEquals(Double.valueOf(class1.doubleField), diff.getLeft());
215         assertEquals(Double.valueOf(class2.doubleField), diff.getRight());
216     }
217 
218     @Test
219     public void testDoubleArray() {
220         final TypeTestClass class1 = new TypeTestClass();
221         final TypeTestClass class2 = new TypeTestClass();
222         class2.doubleArrayField = new double[] {3.0, 2.9, 2.8};
223         final DiffResult<TypeTestClass> list = class1.diff(class2);
224         assertEquals(1, list.getNumberOfDiffs());
225         final Diff<?> diff = list.getDiffs().get(0);
226         assertArrayEquals(ArrayUtils.toObject(class1.doubleArrayField),
227                 (Object[]) diff.getLeft());
228         assertArrayEquals(ArrayUtils.toObject(class2.doubleArrayField),
229                 (Object[]) diff.getRight());
230     }
231 
232     @Test
233     public void testFloat() {
234         final TypeTestClass class1 = new TypeTestClass();
235         final TypeTestClass class2 = new TypeTestClass();
236         class2.floatField = 99.99F;
237         final DiffResult<TypeTestClass> list = class1.diff(class2);
238         assertEquals(1, list.getNumberOfDiffs());
239         final Diff<?> diff = list.getDiffs().get(0);
240         assertEquals(Float.valueOf(class1.floatField), diff.getLeft());
241         assertEquals(Float.valueOf(class2.floatField), diff.getRight());
242     }
243 
244     @Test
245     public void testFloatArray() {
246         final TypeTestClass class1 = new TypeTestClass();
247         final TypeTestClass class2 = new TypeTestClass();
248         class2.floatArrayField = new float[] {3.0F, 2.9F, 2.8F};
249         final DiffResult<TypeTestClass> list = class1.diff(class2);
250         assertEquals(1, list.getNumberOfDiffs());
251         final Diff<?> diff = list.getDiffs().get(0);
252         assertArrayEquals(ArrayUtils.toObject(class1.floatArrayField),
253                 (Object[]) diff.getLeft());
254         assertArrayEquals(ArrayUtils.toObject(class2.floatArrayField),
255                 (Object[]) diff.getRight());
256     }
257 
258     @Test
259     public void testInt() {
260         final TypeTestClass class1 = new TypeTestClass();
261         final TypeTestClass class2 = new TypeTestClass();
262         class2.intField = 42;
263         final DiffResult<TypeTestClass> list = class1.diff(class2);
264         assertEquals(1, list.getNumberOfDiffs());
265         final Diff<?> diff = list.getDiffs().get(0);
266         assertEquals(Integer.valueOf(class1.intField), diff.getLeft());
267         assertEquals(Integer.valueOf(class2.intField), diff.getRight());
268     }
269 
270     @Test
271     public void testIntArray() {
272         final TypeTestClass class1 = new TypeTestClass();
273         final TypeTestClass class2 = new TypeTestClass();
274         class2.intArrayField = new int[] {3, 2, 1};
275         final DiffResult<TypeTestClass> list = class1.diff(class2);
276         assertEquals(1, list.getNumberOfDiffs());
277         final Diff<?> diff = list.getDiffs().get(0);
278         assertArrayEquals(ArrayUtils.toObject(class1.intArrayField),
279                 (Object[]) diff.getLeft());
280         assertArrayEquals(ArrayUtils.toObject(class2.intArrayField),
281                 (Object[]) diff.getRight());
282     }
283 
284     @Test
285     public void testLong() {
286         final TypeTestClass class1 = new TypeTestClass();
287         final TypeTestClass class2 = new TypeTestClass();
288         class2.longField = 42L;
289         final DiffResult<TypeTestClass> list = class1.diff(class2);
290         assertEquals(1, list.getNumberOfDiffs());
291         final Diff<?> diff = list.getDiffs().get(0);
292         assertEquals(Long.valueOf(class1.longField), diff.getLeft());
293         assertEquals(Long.valueOf(class2.longField), diff.getRight());
294     }
295 
296     @Test
297     public void testLongArray() {
298         final TypeTestClass class1 = new TypeTestClass();
299         final TypeTestClass class2 = new TypeTestClass();
300         class2.longArrayField = new long[] {3L, 2L, 1L};
301         final DiffResult<TypeTestClass> list = class1.diff(class2);
302         assertEquals(1, list.getNumberOfDiffs());
303         final Diff<?> diff = list.getDiffs().get(0);
304         assertArrayEquals(ArrayUtils.toObject(class1.longArrayField),
305                 (Object[]) diff.getLeft());
306         assertArrayEquals(ArrayUtils.toObject(class2.longArrayField),
307                 (Object[]) diff.getRight());
308     }
309 
310     @Test
311     public void testNullLhs() {
312         assertThrows(NullPointerException.class, () -> new DiffBuilder<>(null, this, ToStringStyle.DEFAULT_STYLE));
313     }
314 
315     @Test
316     public void testNullLhs_4args() {
317         assertThrows(NullPointerException.class, () -> new DiffBuilder<>(null, this, ToStringStyle.DEFAULT_STYLE, true));
318     }
319 
320     @Test
321     public void testNullRhs() {
322         assertThrows(NullPointerException.class, () -> new DiffBuilder<>(this, null, ToStringStyle.DEFAULT_STYLE));
323     }
324 
325     @Test
326     public void testNullRhs_4args() {
327         assertThrows(NullPointerException.class, () -> new DiffBuilder<>(this, null, ToStringStyle.DEFAULT_STYLE, true));
328     }
329 
330     @Test
331     public void testObject() {
332         final TypeTestClass class1 = new TypeTestClass();
333         final TypeTestClass class2 = new TypeTestClass();
334         class2.objectField = "Some string";
335         final DiffResult<TypeTestClass> list = class1.diff(class2);
336         assertEquals(1, list.getNumberOfDiffs());
337         final Diff<?> diff = list.getDiffs().get(0);
338         assertEquals(class1.objectField, diff.getLeft());
339         assertEquals(class2.objectField, diff.getRight());
340     }
341 
342     @Test
343     public void testObjectArray() {
344         final TypeTestClass class1 = new TypeTestClass();
345         final TypeTestClass class2 = new TypeTestClass();
346         class2.objectArrayField = new Object[] {"string", 1, 2};
347         final DiffResult<TypeTestClass> list = class1.diff(class2);
348         assertEquals(1, list.getNumberOfDiffs());
349         final Diff<?> diff = list.getDiffs().get(0);
350         assertArrayEquals(class1.objectArrayField, (Object[]) diff.getLeft());
351         assertArrayEquals(class2.objectArrayField, (Object[]) diff.getRight());
352     }
353 
354     @Test
355     public void testObjectArrayEqual() {
356         final TypeTestClass class1 = new TypeTestClass();
357         final TypeTestClass class2 = new TypeTestClass();
358         class1.objectArrayField = new Object[] {"string", 1, 2};
359         class2.objectArrayField = new Object[] {"string", 1, 2};
360         final DiffResult<TypeTestClass> list = class1.diff(class2);
361         assertEquals(0, list.getNumberOfDiffs());
362     }
363 
364     /**
365      * Test that "left" and "right" are the same instance but are equal.
366      */
367     @Test
368     public void testObjectsNotSameButEqual() {
369         final TypeTestClass left = new TypeTestClass();
370         left.objectField = Integer.valueOf(1000);
371         final TypeTestClass right = new TypeTestClass();
372         right.objectField = Integer.valueOf(1000);
373         assertNotSame(left.objectField, right.objectField);
374         assertEquals(left.objectField, right.objectField);
375 
376         final DiffResult<TypeTestClass> list = left.diff(right);
377         assertEquals(0, list.getNumberOfDiffs());
378     }
379 
380     /**
381      * Test that "left" and "right" are not the same instance and are not equal.
382      */
383     @Test
384     public void testObjectsNotSameNorEqual() {
385         final TypeTestClass left = new TypeTestClass();
386         left.objectField = 4;
387         final TypeTestClass right = new TypeTestClass();
388         right.objectField = 100;
389         assertNotSame(left.objectField, right.objectField);
390         assertNotEquals(left.objectField, right.objectField);
391 
392         final DiffResult<TypeTestClass> list = left.diff(right);
393         assertEquals(1, list.getNumberOfDiffs());
394     }
395 
396     /**
397      * Test that "left" and "right" are the same instance and are equal.
398      */
399     @Test
400     public void testObjectsSameAndEqual() {
401         final Integer sameObject = 1;
402         final TypeTestClass left = new TypeTestClass();
403         left.objectField = sameObject;
404         final TypeTestClass right = new TypeTestClass();
405         right.objectField = sameObject;
406         assertSame(left.objectField, right.objectField);
407         assertEquals(left.objectField, right.objectField);
408 
409         final DiffResult<TypeTestClass> list = left.diff(right);
410         assertEquals(0, list.getNumberOfDiffs());
411     }
412 
413     @Test
414     public void testSameObjectIgnoresAppends() {
415         final TypeTestClass testClass = new TypeTestClass();
416         final DiffResult<TypeTestClass> list = new DiffBuilder<>(testClass, testClass, SHORT_STYLE)
417             .append("ignored", false, true)
418             .build();
419         assertEquals(0, list.getNumberOfDiffs());
420     }
421 
422     @Test
423     public void testShort() {
424         final TypeTestClass class1 = new TypeTestClass();
425         final TypeTestClass class2 = new TypeTestClass();
426         class2.shortField = 42;
427         final DiffResult<TypeTestClass> list = class1.diff(class2);
428         assertEquals(1, list.getNumberOfDiffs());
429         final Diff<?> diff = list.getDiffs().get(0);
430         assertEquals(Short.valueOf(class1.shortField), diff.getLeft());
431         assertEquals(Short.valueOf(class2.shortField), diff.getRight());
432     }
433 
434     @Test
435     public void testShortArray() {
436         final TypeTestClass class1 = new TypeTestClass();
437         final TypeTestClass class2 = new TypeTestClass();
438         class2.shortArrayField = new short[] {3, 2, 1};
439         final DiffResult<TypeTestClass> list = class1.diff(class2);
440         assertEquals(1, list.getNumberOfDiffs());
441         final Diff<?> diff = list.getDiffs().get(0);
442         assertArrayEquals(ArrayUtils.toObject(class1.shortArrayField),
443                 (Object[]) diff.getLeft());
444         assertArrayEquals(ArrayUtils.toObject(class2.shortArrayField),
445                 (Object[]) diff.getRight());
446     }
447 
448     @Test
449     public void testSimilarObjectIgnoresAppends() {
450         final TypeTestClass testClass1 = new TypeTestClass();
451         final TypeTestClass testClass2 = new TypeTestClass();
452         final DiffResult<TypeTestClass> list = new DiffBuilder<>(testClass1, testClass2, SHORT_STYLE)
453             .append("ignored", false, true)
454             .build();
455         assertEquals(0, list.getNumberOfDiffs());
456     }
457 
458     @Test
459     public void testStylePassedToDiffResult() {
460         final TypeTestClass class1 = new TypeTestClass();
461         DiffResult<TypeTestClass> list = class1.diff(class1);
462         assertEquals(SHORT_STYLE, list.getToStringStyle());
463 
464         class1.style = ToStringStyle.MULTI_LINE_STYLE;
465         list = class1.diff(class1);
466         assertEquals(ToStringStyle.MULTI_LINE_STYLE, list.getToStringStyle());
467     }
468 
469     @Test
470     public void testTriviallyEqualTestDisabled() {
471         final Matcher<Integer> equalToOne = equalTo(1);
472 
473         // Constructor's arguments are not trivially equal, but not testing for that.
474         final DiffBuilder<Integer> explicitTestAndNotEqual1 = new DiffBuilder<>(1, 2, null, false);
475         explicitTestAndNotEqual1.append("letter", "X", "Y");
476         assertThat(explicitTestAndNotEqual1.build().getNumberOfDiffs(), equalToOne);
477 
478         // Constructor's arguments are trivially equal, but not testing for that.
479         final DiffBuilder<Integer> explicitTestAndNotEqual2 = new DiffBuilder<>(1, 1, null, false);
480         // This append(f, l, r) will not abort early.
481         explicitTestAndNotEqual2.append("letter", "X", "Y");
482         assertThat(explicitTestAndNotEqual2.build().getNumberOfDiffs(), equalToOne);
483     }
484 
485     @Test
486     public void testTriviallyEqualTestEnabled() {
487         final Matcher<Integer> equalToZero = equalTo(0);
488         final Matcher<Integer> equalToOne = equalTo(1);
489 
490         // The option to test if trivially equal is enabled by default.
491         final DiffBuilder<Integer> implicitTestAndEqual = new DiffBuilder<>(1, 1, null);
492         // This append(f, l, r) will abort without creating a Diff for letter.
493         implicitTestAndEqual.append("letter", "X", "Y");
494         assertThat(implicitTestAndEqual.build().getNumberOfDiffs(), equalToZero);
495 
496         final DiffBuilder<Integer> implicitTestAndNotEqual = new DiffBuilder<>(1, 2, null);
497         // This append(f, l, r) will not abort early
498         // because the constructor's arguments were not trivially equal.
499         implicitTestAndNotEqual.append("letter", "X", "Y");
500         assertThat(implicitTestAndNotEqual.build().getNumberOfDiffs(), equalToOne);
501 
502         // This is explicitly enabling the trivially equal test.
503         final DiffBuilder<Integer> explicitTestAndEqual = new DiffBuilder<>(1, 1, null, true);
504         explicitTestAndEqual.append("letter", "X", "Y");
505         assertThat(explicitTestAndEqual.build().getNumberOfDiffs(), equalToZero);
506     }
507 
508 }