~nunit-core/nunitv2/2.5

« back to all changes in this revision

Viewing changes to src/NUnitFramework/framework/AssertionFailureMessage.cs

  • Committer: jnewkirk
  • Date: 2002-07-10 20:05:24 UTC
  • Revision ID: vcs-imports@canonical.com-20020710200524-z33q2om2qvsgs6kg
initialĀ load

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
using System;
 
2
using System.Text;
 
3
 
 
4
namespace Nunit.Framework
 
5
{
 
6
        /// <summary>
 
7
        /// Summary description for AssertionFailureMessage.
 
8
        /// </summary>
 
9
        public class AssertionFailureMessage
 
10
        {
 
11
                protected AssertionFailureMessage() 
 
12
                {}
 
13
 
 
14
        /// <summary>
 
15
        /// Number of characters before a highlighted position before
 
16
        /// clipping will occur.  Clipped text is replaced with an
 
17
        /// elipses "..."
 
18
        /// </summary>
 
19
        static protected int PreClipLength
 
20
        {
 
21
            get
 
22
            {
 
23
                return 35;
 
24
            }
 
25
        }
 
26
 
 
27
        /// <summary>
 
28
        /// Number of characters after a highlighted position before
 
29
        /// clipping will occur.  Clipped text is replaced with an
 
30
        /// elipses "..."
 
31
        /// </summary>
 
32
        static protected int PostClipLength
 
33
        {
 
34
            get
 
35
            {
 
36
                return 35;
 
37
            }
 
38
        }   
 
39
 
 
40
        /// <summary>
 
41
        /// Called to test if the position will cause clipping
 
42
        /// to occur in the early part of a string.
 
43
        /// </summary>
 
44
        /// <param name="iPosition"></param>
 
45
        /// <returns></returns>
 
46
        static private bool IsPreClipped( int position )
 
47
        {
 
48
            if( position > PreClipLength )
 
49
            {
 
50
                return true;
 
51
            }
 
52
            return false;
 
53
        }
 
54
 
 
55
        /// <summary>
 
56
        /// Called to test if the position will cause clipping
 
57
        /// to occur in the later part of a string past the
 
58
        /// specified position.
 
59
        /// </summary>
 
60
        /// <param name="sString"></param>
 
61
        /// <param name="iPosition"></param>
 
62
        /// <returns></returns>
 
63
        static private bool IsPostClipped( string sString, int iPosition )
 
64
        {
 
65
            if( sString.Length - iPosition > PostClipLength )
 
66
            {
 
67
                return true;
 
68
            }
 
69
            return false;
 
70
        }
 
71
 
 
72
        /// <summary>
 
73
        /// Property called to insert newline characters into a string
 
74
        /// </summary>
 
75
        static private string NewLine
 
76
        {
 
77
            get
 
78
            {
 
79
                return "\r\n";
 
80
            }
 
81
        }
 
82
 
 
83
        /// <summary>
 
84
        /// Renders up to M characters before, and up to N characters after
 
85
        /// the specified index position.  If leading or trailing text is
 
86
        /// clipped, and elipses "..." is added where the missing text would
 
87
        /// be.
 
88
        /// 
 
89
        /// Clips strings to limit previous or post newline characters,
 
90
        /// since these mess up the comparison
 
91
        /// </summary>
 
92
        /// <param name="sString"></param>
 
93
        /// <param name="iPosition"></param>
 
94
        /// <returns></returns>
 
95
        static protected string ClipAroundPosition( string sString, int iPosition )
 
96
        {
 
97
            if( null == sString || 0 == sString.Length )
 
98
            {
 
99
                return "";
 
100
            }
 
101
 
 
102
            return BuildBefore( sString, iPosition ) + BuildAfter(  sString, iPosition );
 
103
        }
 
104
 
 
105
        /// <summary>
 
106
        /// Clips the string before the specified position, and appends
 
107
        /// ellipses (...) to show that clipping has occurred 
 
108
        /// </summary>
 
109
        /// <param name="sString"></param>
 
110
        /// <param name="iPosition"></param>
 
111
        /// <returns></returns>
 
112
        static protected string PreClip( string sString, int iPosition )
 
113
        {
 
114
            return "..." + sString.Substring( iPosition - PreClipLength, PreClipLength );
 
115
        }
 
116
 
 
117
        /// <summary>
 
118
        /// Clips the string after the specified position, and appends
 
119
        /// ellipses (...) to show that clipping has occurred 
 
120
        /// </summary>
 
121
        /// <param name="sString"></param>
 
122
        /// <param name="iPosition"></param>
 
123
        /// <returns></returns>
 
124
        static protected string PostClip( string sString, int iPosition )
 
125
        {
 
126
            return sString.Substring( iPosition, PostClipLength ) + "...";
 
127
        }
 
128
 
 
129
        /// <summary>
 
130
        /// Builds the first half of a string, limiting the number of
 
131
        /// characters before the position, and removing newline
 
132
        /// characters.  If the leading string is truncated, the
 
133
        /// ellipses (...) characters are appened.
 
134
        /// </summary>
 
135
        /// <param name="sString"></param>
 
136
        /// <param name="iPosition"></param>
 
137
        /// <returns></returns>
 
138
        static private string BuildBefore( string sString, int iPosition )
 
139
        {
 
140
            if( IsPreClipped(iPosition) )
 
141
            {
 
142
                return PreClip( sString, iPosition );
 
143
            }
 
144
            return sString.Substring( 0, iPosition );
 
145
        }
 
146
 
 
147
        /// <summary>
 
148
        /// Builds the last half of a string, limiting the number of
 
149
        /// characters after the position, and removing newline
 
150
        /// characters.  If the string is truncated, the
 
151
        /// ellipses (...) characters are appened.
 
152
        /// </summary>
 
153
        /// <param name="sString"></param>
 
154
        /// <param name="iPosition"></param>
 
155
        /// <returns></returns>
 
156
        static private string BuildAfter( string sString, int iPosition )
 
157
        {
 
158
            if( IsPostClipped(sString, iPosition) )
 
159
            {
 
160
                return PostClip( sString, iPosition );
 
161
            }
 
162
            return sString.Substring( iPosition );
 
163
        }
 
164
 
 
165
        /// <summary>
 
166
        /// Text that is rendered for the expected value
 
167
        /// </summary>
 
168
        /// <returns></returns>
 
169
        static protected string ExpectedText()
 
170
        {
 
171
            return "expected:<";
 
172
        }
 
173
 
 
174
        /// <summary>
 
175
        /// Text rendered for the actual value.  This text should
 
176
        /// be the same length as the Expected text, so leading
 
177
        /// spaces should pad this string to ensure they match.
 
178
        /// </summary>
 
179
        /// <returns></returns>
 
180
        static protected string ButWasText()
 
181
        {
 
182
            return " but was:<";
 
183
        }
 
184
 
 
185
        /// <summary>
 
186
        /// Raw line that communicates the expected value, and the actual value
 
187
        /// </summary>
 
188
        /// <param name="sbOutput"></param>
 
189
        /// <param name="expected"></param>
 
190
        /// <param name="actual"></param>
 
191
        static protected void AppendExpectedAndActual( StringBuilder sbOutput, Object expected, Object actual )
 
192
        {
 
193
            sbOutput.Append( NewLine );
 
194
            sbOutput.Append( ExpectedText() );
 
195
            sbOutput.Append( (expected != null) ? expected : "(null)" );
 
196
            sbOutput.Append( ">" );
 
197
            sbOutput.Append( NewLine );
 
198
            sbOutput.Append( ButWasText() );
 
199
            sbOutput.Append( (actual != null) ? actual : "(null)" );
 
200
            sbOutput.Append( ">" );
 
201
        }
 
202
 
 
203
        /// <summary>
 
204
        /// Draws a marker under the expected/actual strings that highlights
 
205
        /// where in the string a mismatch occurred.
 
206
        /// </summary>
 
207
        /// <param name="sbOutput"></param>
 
208
        /// <param name="iPosition"></param>
 
209
        static protected void AppendPositionMarker( StringBuilder sbOutput, int iPosition )
 
210
        {
 
211
            sbOutput.Append( new String( '-', ButWasText().Length ) );
 
212
            if( iPosition > 0 )
 
213
            {
 
214
                sbOutput.Append( new string( '-', iPosition ) );
 
215
            }
 
216
            sbOutput.Append( "^" );
 
217
        }
 
218
 
 
219
        /// <summary>
 
220
        /// Tests two objects to determine if they are strings.
 
221
        /// </summary>
 
222
        /// <param name="expected"></param>
 
223
        /// <param name="actual"></param>
 
224
        /// <returns></returns>
 
225
        static protected bool InputsAreStrings( Object expected, Object actual )
 
226
        {
 
227
            if( null != expected  &&
 
228
                null != actual    &&
 
229
                expected is string &&
 
230
                actual   is string )
 
231
            {
 
232
                return true;
 
233
            }
 
234
            return false;
 
235
        }
 
236
 
 
237
        /// <summary>
 
238
        /// Tests if two strings are different lengths.
 
239
        /// </summary>
 
240
        /// <param name="sExpected"></param>
 
241
        /// <param name="sActual"></param>
 
242
        /// <returns>True if string lengths are different</returns>
 
243
        static protected bool LengthsDifferent( string sExpected, string sActual )
 
244
        {
 
245
            if( sExpected.Length != sActual.Length )
 
246
            {
 
247
                return true;
 
248
            }
 
249
            return false;
 
250
        }
 
251
 
 
252
        /// <summary>
 
253
        /// Used to construct a message when the lengths of two strings are
 
254
        /// different.  Also includes the strings themselves, to allow them
 
255
        /// to be compared visually.
 
256
        /// </summary>
 
257
        /// <param name="sbOutput"></param>
 
258
        /// <param name="sExpected"></param>
 
259
        /// <param name="sActual"></param>
 
260
        static protected void BuildLengthsDifferentMessage( StringBuilder sbOutput, string sExpected, string sActual )
 
261
        {
 
262
            BuildContentDifferentMessage( sbOutput, sExpected, sActual );
 
263
        }
 
264
 
 
265
        /// <summary>
 
266
        /// Reports the length of two strings that are different lengths
 
267
        /// </summary>
 
268
        /// <param name="sbOutput"></param>
 
269
        /// <param name="sExpected"></param>
 
270
        /// <param name="sActual"></param>
 
271
        static protected void BuildStringLengthDifferentReport( StringBuilder sbOutput, string sExpected, string sActual )
 
272
        {
 
273
            sbOutput.Append( "String lengths differ.  Expected length=" );
 
274
            sbOutput.Append( sExpected.Length );
 
275
            sbOutput.Append( ", but was length=" );
 
276
            sbOutput.Append( sActual.Length );
 
277
            sbOutput.Append( "." );
 
278
            sbOutput.Append( NewLine );
 
279
        }
 
280
 
 
281
        /// <summary>
 
282
        /// Reports the length of two strings that are the same length
 
283
        /// </summary>
 
284
        /// <param name="sbOutput"></param>
 
285
        /// <param name="sExpected"></param>
 
286
        /// <param name="sActual"></param>
 
287
        static protected void BuildStringLengthSameReport(  StringBuilder sbOutput, string sExpected, string sActual )
 
288
        {
 
289
            sbOutput.Append( "String lengths are both " );
 
290
            sbOutput.Append( sExpected.Length );
 
291
            sbOutput.Append( "." );
 
292
            sbOutput.Append( NewLine );
 
293
        }
 
294
 
 
295
        /// <summary>
 
296
        /// Reports whether the string lengths are the same or different, and
 
297
        /// what the string lengths are.
 
298
        /// </summary>
 
299
        /// <param name="sbOutput"></param>
 
300
        /// <param name="sExpected"></param>
 
301
        /// <param name="sActual"></param>
 
302
        static protected void BuildStringLengthReport( StringBuilder sbOutput, string sExpected, string sActual )
 
303
        {
 
304
            if( sExpected.Length != sActual.Length )
 
305
            {
 
306
                BuildStringLengthDifferentReport( sbOutput, sExpected, sActual );
 
307
            }
 
308
            else
 
309
            {
 
310
                BuildStringLengthSameReport( sbOutput, sExpected, sActual );
 
311
            }
 
312
        }
 
313
 
 
314
        /// <summary>
 
315
        /// 
 
316
        /// </summary>
 
317
        /// <param name="sbOutput"></param>
 
318
        /// <param name="sExpected"></param>
 
319
        /// <param name="sActual"></param>
 
320
        /// <param name="iPosition"></param>
 
321
        static private void BuildContentDifferentAtPosition( StringBuilder sbOutput, string sExpected, string sActual, int iPosition )
 
322
        {
 
323
            BuildStringLengthReport( sbOutput, sExpected, sActual );
 
324
 
 
325
            sbOutput.Append( "Strings differ at index " );
 
326
            sbOutput.Append( iPosition );
 
327
            sbOutput.Append( "." );
 
328
            sbOutput.Append( NewLine );
 
329
 
 
330
            //
 
331
            // Clips the strings, then turns any hidden newlines into visible
 
332
            // characters by replacing the '\r' into '\\' and 'r' characters,
 
333
            // and the '\n' into '\\' and 'n' characters.  Thus the single 
 
334
            // character becomes two characters for display.
 
335
            //
 
336
            string sClippedExpected = ConvertNewlines(ClipAroundPosition( sExpected, iPosition ));
 
337
            string sClippedActual   = ConvertNewlines(ClipAroundPosition( sActual,   iPosition ));
 
338
 
 
339
            AppendExpectedAndActual( 
 
340
                sbOutput, 
 
341
                sClippedExpected, 
 
342
                sClippedActual );
 
343
            sbOutput.Append( NewLine );
 
344
 
 
345
            // Add a line showing where they differ.  If the string lengths are
 
346
            // different, they start differing just past the length of the 
 
347
            // shorter string
 
348
            AppendPositionMarker( 
 
349
                sbOutput, 
 
350
                FindMismatchPosition( sClippedExpected, sClippedActual, 0 ) );
 
351
            sbOutput.Append( NewLine );
 
352
        }
 
353
 
 
354
        /// <summary>
 
355
        /// Turns CR or LF into visual indicator to preserve visual marker 
 
356
        /// position.
 
357
        /// </summary>
 
358
        /// <param name="sInput"></param>
 
359
        /// <returns></returns>
 
360
        static protected string ConvertNewlines( string sInput )
 
361
        {
 
362
            if( null != sInput )
 
363
            {
 
364
                sInput = sInput.Replace( "\r", "\\r" );
 
365
                sInput = sInput.Replace( "\n", "\\n" );
 
366
            }
 
367
            return sInput;
 
368
        }
 
369
 
 
370
        /// <summary>
 
371
        /// Shows the position two strings start to differ.  Comparison 
 
372
        /// starts at the start index.
 
373
        /// </summary>
 
374
        /// <param name="sExpected"></param>
 
375
        /// <param name="sActual"></param>
 
376
        /// <param name="iStart"></param>
 
377
        /// <returns>-1 if no mismatch found, or the index where mismatch found</returns>
 
378
        static private int FindMismatchPosition( string sExpected, string sActual, int iStart )
 
379
        {
 
380
            int iLength = Math.Min( sExpected.Length, sActual.Length );
 
381
            for( int i=iStart; i<iLength; i++ )
 
382
            {
 
383
                //
 
384
                // If they mismatch at a specified position, report the
 
385
                // difference.
 
386
                //
 
387
                if( sExpected[i] != sActual[i] )
 
388
                {
 
389
                    return i;
 
390
                }
 
391
            }
 
392
            //
 
393
            // Strings have same content up to the length of the shorter string.
 
394
            // Mismatch occurs because string lengths are different, so show
 
395
            // that they start differing where the shortest string ends
 
396
            //
 
397
            if( sExpected.Length != sActual.Length )
 
398
            {
 
399
                return iLength;
 
400
            }
 
401
            
 
402
            //
 
403
            // Same strings
 
404
            //
 
405
            Assertion.Assert( sExpected.Equals( sActual ) );
 
406
            return -1;
 
407
        }
 
408
 
 
409
        /// <summary>
 
410
        /// Constructs a message that can be displayed when the content of two
 
411
        /// strings are different, but the string lengths are the same.  The
 
412
        /// message will clip the strings to a reasonable length, centered
 
413
        /// around the first position where they are mismatched, and draw 
 
414
        /// a line marking the position of the difference to make comparison
 
415
        /// quicker.
 
416
        /// </summary>
 
417
        /// <param name="sbOutput"></param>
 
418
        /// <param name="sExpected"></param>
 
419
        /// <param name="sActual"></param>
 
420
        static protected void BuildContentDifferentMessage( StringBuilder sbOutput, string sExpected, string sActual )
 
421
        {
 
422
            //
 
423
            // If they mismatch at a specified position, report the
 
424
            // difference.
 
425
            //
 
426
            int iMismatch = FindMismatchPosition( sExpected, sActual, 0 );
 
427
            if( -1 != iMismatch )
 
428
            {
 
429
                BuildContentDifferentAtPosition( 
 
430
                    sbOutput, 
 
431
                    sExpected, 
 
432
                    sActual, 
 
433
                    iMismatch );
 
434
                return;
 
435
            }
 
436
 
 
437
            //
 
438
            // If the lengths differ, but they match up to the length,
 
439
            // show the difference just past the length of the shorter
 
440
            // string
 
441
            //
 
442
            if( sExpected.Length != sActual.Length )
 
443
            {
 
444
                BuildContentDifferentAtPosition( 
 
445
                    sbOutput, 
 
446
                    sExpected, 
 
447
                    sActual, 
 
448
                    Math.Min(sExpected.Length, sActual.Length) );
 
449
            }
 
450
        }
 
451
 
 
452
        /// <summary>
 
453
        /// Called to append a message when the input strings are different.
 
454
        /// A different message is rendered when the lengths are mismatched,
 
455
        /// and when the lengths match but content is mismatched.
 
456
        /// </summary>
 
457
        /// <param name="sbOutput"></param>
 
458
        /// <param name="expected"></param>
 
459
        /// <param name="actual"></param>
 
460
        static private void BuildStringsDifferentMessage( StringBuilder sbOutput, string expected, string actual )
 
461
        {
 
462
            sbOutput.Append( NewLine );
 
463
            if( LengthsDifferent( expected, actual ) )
 
464
            {
 
465
                BuildLengthsDifferentMessage( sbOutput, expected, actual );
 
466
            }
 
467
            else
 
468
            {
 
469
                BuildContentDifferentMessage( sbOutput, expected, actual );
 
470
            }
 
471
        }
 
472
 
 
473
        /// <summary>
 
474
        /// Used to create a StringBuilder that is used for constructing
 
475
        /// the output message when text is different.  Handles initialization
 
476
        /// when a message is provided.  If message is null, an empty
 
477
        /// StringBuilder is returned.
 
478
        /// </summary>
 
479
        /// <param name="message"></param>
 
480
        /// <returns></returns>
 
481
        static protected StringBuilder CreateStringBuilder( string message )
 
482
        {
 
483
            StringBuilder sbOutput;
 
484
            if (message != null) 
 
485
            {
 
486
                sbOutput = new StringBuilder( message );
 
487
            }
 
488
            else
 
489
            {
 
490
                sbOutput = new StringBuilder();
 
491
            }
 
492
            return sbOutput;
 
493
        }
 
494
 
 
495
        /// <summary>
 
496
        /// Called to create a message when two objects have been found to
 
497
        /// be unequal.  If the inputs are strings, a special message is
 
498
        /// rendered that can help track down where the strings are different,
 
499
        /// based on differences in length, or differences in content.
 
500
        /// 
 
501
        /// If the inputs are not strings, the ToString method of the objects
 
502
        /// is used to show what is different about them.
 
503
        /// </summary>
 
504
        /// <param name="message"></param>
 
505
        /// <param name="expected"></param>
 
506
        /// <param name="actual"></param>
 
507
        /// <returns></returns>
 
508
        static public string FormatMessageForFailNotEquals(string message, Object expected, Object actual) 
 
509
        {
 
510
            StringBuilder sbOutput = CreateStringBuilder( message );
 
511
            if( null != message )
 
512
            {
 
513
                if( message.Length > 0 )
 
514
                {
 
515
                    sbOutput.Append( " " );
 
516
                }
 
517
            }
 
518
 
 
519
            if( InputsAreStrings( expected, actual ) )
 
520
            {
 
521
                BuildStringsDifferentMessage( 
 
522
                    sbOutput, 
 
523
                    (string)expected, 
 
524
                    (string)actual );
 
525
            }
 
526
            else
 
527
            {
 
528
                AppendExpectedAndActual( sbOutput, expected, actual );
 
529
            }
 
530
            return sbOutput.ToString();
 
531
        }
 
532
        }
 
533
}