19
19
import java .util .Objects ;
20
20
import java .util .SequencedMap ;
21
21
22
- import static com .github .cowwoc .requirements10 .java .internal .util .ValidationTarget .valid ;
23
22
import static com .github .cowwoc .requirements10 .java .internal .util .ValidationTarget .invalid ;
23
+ import static com .github .cowwoc .requirements10 .java .internal .util .ValidationTarget .valid ;
24
24
25
25
/**
26
26
* Generates the contextual information to add to the exception message.
@@ -299,28 +299,44 @@ private List<MessageSection> getContextOfObjects()
299
299
assert actualValue .isValid () || expectedValue .isValid () :
300
300
"actualValue and expectedValue were both undefined" ;
301
301
302
+ // Calculate the diff
302
303
StringMappers stringMappers = configuration .stringMappers ();
303
304
String actualAsString = actualValue .map (stringMappers ::toString ).or ("" );
304
305
String expectedAsString = expectedValue .map (stringMappers ::toString ).or ("" );
305
306
DiffResult lines = diffGenerator .diff (actualAsString , expectedAsString );
306
- boolean diffLinesExist = !lines .getDiffLines ().isEmpty ();
307
307
308
+ // Don't show diff lines for boolean values
309
+ if (actualValue .map (v -> v instanceof Boolean ).or (false ) ||
310
+ expectedValue .map (v -> v instanceof Boolean ).or (false ))
311
+ {
312
+ return getContextForSingleLine (lines );
313
+ }
314
+
315
+ List <MessageSection > context = new ArrayList <>();
316
+ context .addAll (diffToMessageSections (lines ));
317
+ context .addAll (compareTypes (lines ));
318
+ return context ;
319
+ }
320
+
321
+ /**
322
+ * @param lines the difference between the actual and expected value
323
+ * @return the difference represented as an exception message
324
+ */
325
+ private List <MessageSection > diffToMessageSections (DiffResult lines )
326
+ {
308
327
// When comparing multiline strings, this method is invoked one line at a time. If the actual or expected
309
328
// value is invalid, it indicates that one of the values contains more lines than the other. The value
310
329
// with fewer lines will be considered invalid on a per-line basis.
311
330
int numberOfLines = Math .max (lines .getActualLines ().size (), lines .getExpectedLines ().size ());
312
- // Don't diff boolean values
313
- if (!allowDiff || numberOfLines == 1 ||
314
- actualValue .map (v -> v instanceof Boolean ).or (false ) ||
315
- expectedValue .map (v -> v instanceof Boolean ).or (false ))
316
- {
331
+ if (!allowDiff || numberOfLines == 1 )
317
332
return getContextForSingleLine (lines );
318
- }
333
+
319
334
int actualLineNumber = 0 ;
320
335
int expectedLineNumber = 0 ;
321
336
List <String > actualLines = lines .getActualLines ();
322
337
List <String > expectedLines = lines .getExpectedLines ();
323
338
List <Boolean > equalLines = lines .getEqualLines ();
339
+ boolean diffLinesExist = !lines .getDiffLines ().isEmpty ();
324
340
325
341
// Indicates if the previous line was equal
326
342
boolean skippedEqualLines = false ;
@@ -421,20 +437,11 @@ private List<MessageSection> getContextForSingleLine(DiffResult lines)
421
437
else
422
438
diffLine = "" ;
423
439
440
+ // We need to check if the values are equal because some collection elements may be equal even if the
441
+ // overall collections differ.
424
442
List <MessageSection > context = new ArrayList <>();
425
443
context .add (getDiffSection (actualName , actualAsString , diffLine , expectedName , expectedAsString ));
426
-
427
- if (!actualValue .equals (expectedValue ) && stringRepresentationsAreEqual (lines ))
428
- {
429
- // If the String representation of the values is equal, output getClass(), hashCode(),
430
- // or System.identityHashCode() to figure out why they differ.
431
- List <MessageSection > optionalContext = compareTypes ();
432
- if (!optionalContext .isEmpty ())
433
- {
434
- context .add (new StringSection ("" ));
435
- context .addAll (optionalContext );
436
- }
437
- }
444
+ context .addAll (compareTypes (lines ));
438
445
return context ;
439
446
}
440
447
@@ -448,11 +455,20 @@ private boolean stringRepresentationsAreEqual(DiffResult lines)
448
455
}
449
456
450
457
/**
451
- * @return the difference between the expected and actual values
458
+ * If the values differ but their string representation is the same adds lines which compare getClass(),
459
+ * hashCode() and/or System.identityHashCode() to figure out why they differ.
460
+ *
461
+ * @param lines the difference between the actual and expected value
462
+ * @return an empty List if the values are equal or the string representations differ
452
463
* @throws AssertionError if {@code actualName} or {@code expectedName} are null
453
464
*/
454
- private List <MessageSection > compareTypes ()
465
+ private List <MessageSection > compareTypes (DiffResult lines )
455
466
{
467
+ // We need to check if the values are equal because some collection elements may be equal even if the
468
+ // overall collections differ.
469
+ if (actualValue .equals (expectedValue ) || !stringRepresentationsAreEqual (lines ))
470
+ return List .of ();
471
+
456
472
assert actualValue .isValid () : "actualValue was undefined" ;
457
473
assert expectedValue .isValid () : "expectedValue was undefined" ;
458
474
Object actualValueOrNull = actualValue .orThrow (AssertionError ::new );
@@ -462,35 +478,45 @@ private List<MessageSection> compareTypes()
462
478
String expectedClassName = getClassName (getClass (actualValueOrNull ));
463
479
if (!actualClassName .equals (expectedClassName ))
464
480
{
465
- return new ContextGenerator (scope , configuration , actualName + ".class" , expectedName + ".class" ).
466
- actualValue (actualClassName ).
467
- expectedValue (expectedClassName ).
468
- allowDiff (false ).
469
- build ();
481
+ List <MessageSection > context = new ArrayList <>();
482
+ context .add (new StringSection ("" ));
483
+ context .addAll (
484
+ new ContextGenerator (scope , configuration , actualName + ".class" , expectedName + ".class" ).
485
+ actualValue (actualClassName ).
486
+ expectedValue (expectedClassName ).
487
+ allowDiff (false ).
488
+ build ());
489
+ return context ;
470
490
}
471
491
// Do not use config.toString() for hashCode values because their exact value does not matter, just the
472
492
// fact that they are different.
473
493
int actualHashCode = Objects .hashCode (actualValueOrNull );
474
494
int expectedHashCode = Objects .hashCode (expectedValueOrNull );
475
495
if (actualHashCode != expectedHashCode )
476
496
{
477
- return new ContextGenerator (scope , configuration , actualName + ".hashCode" ,
497
+ List <MessageSection > context = new ArrayList <>();
498
+ context .add (new StringSection ("" ));
499
+ context .addAll (new ContextGenerator (scope , configuration , actualName + ".hashCode" ,
478
500
expectedName + ".hashCode" ).
479
501
actualValue (actualHashCode ).
480
502
expectedValue (expectedHashCode ).
481
503
allowDiff (false ).
482
- build ();
504
+ build ());
505
+ return context ;
483
506
}
484
507
int actualIdentityHashCode = System .identityHashCode (actualValueOrNull );
485
508
int expectedIdentityHashCode = System .identityHashCode (expectedValueOrNull );
486
509
if (actualIdentityHashCode != expectedIdentityHashCode )
487
510
{
488
- return new ContextGenerator (scope , configuration , actualName + ".identityHashCode" ,
511
+ List <MessageSection > context = new ArrayList <>();
512
+ context .add (new StringSection ("" ));
513
+ context .addAll (new ContextGenerator (scope , configuration , actualName + ".identityHashCode" ,
489
514
expectedName + ".identityHashCode" ).
490
515
actualValue (actualIdentityHashCode ).
491
516
expectedValue (expectedIdentityHashCode ).
492
517
allowDiff (false ).
493
- build ();
518
+ build ());
519
+ return context ;
494
520
}
495
521
return List .of ();
496
522
}
0 commit comments