2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / nunit20 / framework / AssertionFailureMessage.cs
1 #region Copyright (c) 2002-2003, James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole, Philip A. Craig, Douglas de la Torre
2 /************************************************************************************
3 '
4 ' Copyright  2002-2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
5 ' Copyright  2000-2002 Philip A. Craig
6 ' Copyright  2001 Douglas de la Torre
7 '
8 ' This software is provided 'as-is', without any express or implied warranty. In no 
9 ' event will the authors be held liable for any damages arising from the use of this 
10 ' software.
11
12 ' Permission is granted to anyone to use this software for any purpose, including 
13 ' commercial applications, and to alter it and redistribute it freely, subject to the 
14 ' following restrictions:
15 '
16 ' 1. The origin of this software must not be misrepresented; you must not claim that 
17 ' you wrote the original software. If you use this software in a product, an 
18 ' acknowledgment (see the following) in the product documentation is required.
19 '
20 ' Portions Copyright  2002 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov 
21 ' Copyright  2000-2002 Philip A. Craig, or Copyright  2001 Douglas de la Torre
22 '
23 ' 2. Altered source versions must be plainly marked as such, and must not be 
24 ' misrepresented as being the original software.
25 '
26 ' 3. This notice may not be removed or altered from any source distribution.
27 '
28 '***********************************************************************************/
29 #endregion
30
31 using System;
32 using System.Text;
33
34 namespace NUnit.Framework
35 {
36         /// <summary>
37         /// Summary description for AssertionFailureMessage.
38         /// </summary>
39         public class AssertionFailureMessage
40         {
41                 /// <summary>
42                 /// Protected constructor, used since this class is only used via
43                 /// static methods
44                 /// </summary>
45                 protected AssertionFailureMessage() 
46                 {}
47
48                 /// <summary>
49                 /// Number of characters before a highlighted position before
50                 /// clipping will occur.  Clipped text is replaced with an
51                 /// elipses "..."
52                 /// </summary>
53                 static protected int PreClipLength
54                 {
55                         get
56                         {
57                                 return 35;
58                         }
59                 }
60
61                 /// <summary>
62                 /// Number of characters after a highlighted position before
63                 /// clipping will occur.  Clipped text is replaced with an
64                 /// elipses "..."
65                 /// </summary>
66                 static protected int PostClipLength
67                 {
68                         get
69                         {
70                                 return 35;
71                         }
72                 }   
73
74                 /// <summary>
75                 /// Called to test if the position will cause clipping
76                 /// to occur in the early part of a string.
77                 /// </summary>
78                 /// <param name="iPosition"></param>
79                 /// <returns></returns>
80                 static private bool IsPreClipped( int iPosition )
81                 {
82                         if( iPosition > PreClipLength )
83                         {
84                                 return true;
85                         }
86                         return false;
87                 }
88
89                 /// <summary>
90                 /// Called to test if the position will cause clipping
91                 /// to occur in the later part of a string past the
92                 /// specified position.
93                 /// </summary>
94                 /// <param name="sString"></param>
95                 /// <param name="iPosition"></param>
96                 /// <returns></returns>
97                 static private bool IsPostClipped( string sString, int iPosition )
98                 {
99                         if( sString.Length - iPosition > PostClipLength )
100                         {
101                                 return true;
102                         }
103                         return false;
104                 }
105
106                 /// <summary>
107                 /// Property called to insert newline characters into a string
108                 /// </summary>
109                 static private string NewLine
110                 {
111                         get
112                         {
113                                 return "\r\n\t";
114                         }
115                 }
116
117                 /// <summary>
118                 /// Renders up to M characters before, and up to N characters after
119                 /// the specified index position.  If leading or trailing text is
120                 /// clipped, and elipses "..." is added where the missing text would
121                 /// be.
122                 /// 
123                 /// Clips strings to limit previous or post newline characters,
124                 /// since these mess up the comparison
125                 /// </summary>
126                 /// <param name="sString"></param>
127                 /// <param name="iPosition"></param>
128                 /// <returns></returns>
129                 static protected string ClipAroundPosition( string sString, int iPosition )
130                 {
131                         if( null == sString || 0 == sString.Length )
132                         {
133                                 return "";
134                         }
135
136                         return BuildBefore( sString, iPosition ) + BuildAfter(  sString, iPosition );
137                 }
138
139                 /// <summary>
140                 /// Clips the string before the specified position, and appends
141                 /// ellipses (...) to show that clipping has occurred 
142                 /// </summary>
143                 /// <param name="sString"></param>
144                 /// <param name="iPosition"></param>
145                 /// <returns></returns>
146                 static protected string PreClip( string sString, int iPosition )
147                 {
148                         return "..." + sString.Substring( iPosition - PreClipLength, PreClipLength );
149                 }
150
151                 /// <summary>
152                 /// Clips the string after the specified position, and appends
153                 /// ellipses (...) to show that clipping has occurred 
154                 /// </summary>
155                 /// <param name="sString"></param>
156                 /// <param name="iPosition"></param>
157                 /// <returns></returns>
158                 static protected string PostClip( string sString, int iPosition )
159                 {
160                         return sString.Substring( iPosition, PostClipLength ) + "...";
161                 }
162
163                 /// <summary>
164                 /// Builds the first half of a string, limiting the number of
165                 /// characters before the position, and removing newline
166                 /// characters.  If the leading string is truncated, the
167                 /// ellipses (...) characters are appened.
168                 /// </summary>
169                 /// <param name="sString"></param>
170                 /// <param name="iPosition"></param>
171                 /// <returns></returns>
172                 static private string BuildBefore( string sString, int iPosition )
173                 {
174                         if( IsPreClipped(iPosition) )
175                         {
176                                 return PreClip( sString, iPosition );
177                         }
178                         return sString.Substring( 0, iPosition );
179                 }
180
181                 /// <summary>
182                 /// Builds the last half of a string, limiting the number of
183                 /// characters after the position, and removing newline
184                 /// characters.  If the string is truncated, the
185                 /// ellipses (...) characters are appened.
186                 /// </summary>
187                 /// <param name="sString"></param>
188                 /// <param name="iPosition"></param>
189                 /// <returns></returns>
190                 static private string BuildAfter( string sString, int iPosition )
191                 {
192                         if( IsPostClipped(sString, iPosition) )
193                         {
194                                 return PostClip( sString, iPosition );
195                         }
196                         return sString.Substring( iPosition );
197                 }
198
199                 /// <summary>
200                 /// Text that is rendered for the expected value
201                 /// </summary>
202                 /// <returns></returns>
203                 static protected string ExpectedText()
204                 {
205                         return "expected:<";
206                 }
207
208                 /// <summary>
209                 /// Text rendered for the actual value.  This text should
210                 /// be the same length as the Expected text, so leading
211                 /// spaces should pad this string to ensure they match.
212                 /// </summary>
213                 /// <returns></returns>
214                 static protected string ButWasText()
215                 {
216                         return " but was:<";
217                 }
218
219                 /// <summary>
220                 /// Raw line that communicates the expected value, and the actual value
221                 /// </summary>
222                 /// <param name="sbOutput"></param>
223                 /// <param name="expected"></param>
224                 /// <param name="actual"></param>
225                 static protected void AppendExpectedAndActual( StringBuilder sbOutput, Object expected, Object actual )
226                 {
227                         sbOutput.Append( NewLine );
228                         sbOutput.Append( ExpectedText() );
229                         sbOutput.Append( DisplayString( expected ) );
230                         sbOutput.Append( ">" );
231                         sbOutput.Append( NewLine );
232                         sbOutput.Append( ButWasText() );
233                         sbOutput.Append( DisplayString( actual ) );
234                         sbOutput.Append( ">" );
235                 }
236
237                 /// <summary>
238                 /// Display an object as a string
239                 /// </summary>
240                 /// <param name="obj"></param>
241                 /// <returns></returns>
242                 static protected string DisplayString( object  obj )
243                 {
244                         if ( obj == null ) 
245                                 return "(null)";
246                         else if ( obj is string )
247                                 return Quoted( (string)obj );
248                         else
249                                 return obj.ToString();
250                 }
251
252                 /// <summary>
253                 /// Quote a string
254                 /// </summary>
255                 /// <param name="text"></param>
256                 /// <returns></returns>
257                 static protected string Quoted( string text )
258                 {
259                         return string.Format( "\"{0}\"", text );
260                 }
261
262                 /// <summary>
263                 /// Draws a marker under the expected/actual strings that highlights
264                 /// where in the string a mismatch occurred.
265                 /// </summary>
266                 /// <param name="sbOutput"></param>
267                 /// <param name="iPosition"></param>
268                 static protected void AppendPositionMarker( StringBuilder sbOutput, int iPosition )
269                 {
270                         sbOutput.Append( new String( '-', ButWasText().Length + 1 ) );
271                         if( iPosition > 0 )
272                         {
273                                 sbOutput.Append( new string( '-', iPosition ) );
274                         }
275                         sbOutput.Append( "^" );
276                 }
277
278                 /// <summary>
279                 /// Tests two objects to determine if they are strings.
280                 /// </summary>
281                 /// <param name="expected"></param>
282                 /// <param name="actual"></param>
283                 /// <returns></returns>
284                 static protected bool InputsAreStrings( Object expected, Object actual )
285                 {
286                         if( null != expected  &&
287                                 null != actual    &&
288                                 expected is string &&
289                                 actual   is string )
290                         {
291                                 return true;
292                         }
293                         return false;
294                 }
295
296                 /// <summary>
297                 /// Tests if two strings are different lengths.
298                 /// </summary>
299                 /// <param name="sExpected"></param>
300                 /// <param name="sActual"></param>
301                 /// <returns>True if string lengths are different</returns>
302                 static protected bool LengthsDifferent( string sExpected, string sActual )
303                 {
304                         if( sExpected.Length != sActual.Length )
305                         {
306                                 return true;
307                         }
308                         return false;
309                 }
310
311                 /// <summary>
312                 /// Tests if two arrays are different lengths.
313                 /// </summary>
314                 /// <param name="sExpected"></param>
315                 /// <param name="sActual"></param>
316                 /// <returns>True if array lengths are different</returns>
317                 static protected bool LengthsDifferent( object[] expected, object[] actual )
318                 {
319                         if( expected.Length != actual.Length )
320                         {
321                                 return true;
322                         }
323                         return false;
324                 }
325
326                 /// <summary>
327                 /// Used to construct a message when the lengths of two strings are
328                 /// different.  Also includes the strings themselves, to allow them
329                 /// to be compared visually.
330                 /// </summary>
331                 /// <param name="sbOutput"></param>
332                 /// <param name="sExpected"></param>
333                 /// <param name="sActual"></param>
334                 static protected void BuildLengthsDifferentMessage( StringBuilder sbOutput, string sExpected, string sActual )
335                 {
336                         BuildContentDifferentMessage( sbOutput, sExpected, sActual );
337                 }
338
339                 /// <summary>
340                 /// Reports the length of two strings that are different lengths
341                 /// </summary>
342                 /// <param name="sbOutput"></param>
343                 /// <param name="sExpected"></param>
344                 /// <param name="sActual"></param>
345                 static protected void BuildStringLengthDifferentReport( StringBuilder sbOutput, string sExpected, string sActual )
346                 {
347                         sbOutput.Append( "String lengths differ.  Expected length=" );
348                         sbOutput.Append( sExpected.Length );
349                         sbOutput.Append( ", but was length=" );
350                         sbOutput.Append( sActual.Length );
351                         sbOutput.Append( "." );
352                         sbOutput.Append( NewLine );
353                 }
354
355                 /// <summary>
356                 /// Reports the length of two strings that are the same length
357                 /// </summary>
358                 /// <param name="sbOutput"></param>
359                 /// <param name="sExpected"></param>
360                 /// <param name="sActual"></param>
361                 static protected void BuildStringLengthSameReport(  StringBuilder sbOutput, string sExpected, string sActual )
362                 {
363                         sbOutput.Append( "String lengths are both " );
364                         sbOutput.Append( sExpected.Length );
365                         sbOutput.Append( "." );
366                         sbOutput.Append( NewLine );
367                 }
368
369                 /// <summary>
370                 /// Reports whether the string lengths are the same or different, and
371                 /// what the string lengths are.
372                 /// </summary>
373                 /// <param name="sbOutput"></param>
374                 /// <param name="sExpected"></param>
375                 /// <param name="sActual"></param>
376                 static protected void BuildStringLengthReport( StringBuilder sbOutput, string sExpected, string sActual )
377                 {
378                         if( sExpected.Length != sActual.Length )
379                         {
380                                 BuildStringLengthDifferentReport( sbOutput, sExpected, sActual );
381                         }
382                         else
383                         {
384                                 BuildStringLengthSameReport( sbOutput, sExpected, sActual );
385                         }
386                 }
387
388                 /// <summary>
389                 /// Reports the length of two arrays that are different lengths
390                 /// </summary>
391                 /// <param name="sbOutput"></param>
392                 /// <param name="expected"></param>
393                 /// <param name="actual"></param>
394                 static protected void BuildArrayLengthDifferentReport( StringBuilder sbOutput, Array expected, Array actual )
395                 {
396                         sbOutput.Append( "Array lengths differ.  Expected length=" );
397                         sbOutput.Append( expected.Length );
398                         sbOutput.Append( ", but was length=" );
399                         sbOutput.Append( actual.Length );
400                         sbOutput.Append( "." );
401                         sbOutput.Append( NewLine );
402                 }
403
404                 /// <summary>
405                 /// Reports the length of two arrays that are the same length
406                 /// </summary>
407                 /// <param name="sbOutput"></param>
408                 /// <param name="expected"></param>
409                 /// <param name="actual"></param>
410                 static protected void BuildArrayLengthSameReport(  StringBuilder sbOutput, Array expected, Array actual )
411                 {
412                         sbOutput.Append( "Array lengths are both " );
413                         sbOutput.Append( expected.Length );
414                         sbOutput.Append( "." );
415                         sbOutput.Append( NewLine );
416                 }
417
418                 /// <summary>
419                 /// Reports whether the array lengths are the same or different, and
420                 /// what the array lengths are.
421                 /// </summary>
422                 /// <param name="sbOutput"></param>
423                 /// <param name="expected"></param>
424                 /// <param name="actual"></param>
425                 static protected void BuildArrayLengthReport( StringBuilder sbOutput, Array expected, Array actual )
426                 {
427                         if( expected.Length != actual.Length )
428                         {
429                                 BuildArrayLengthDifferentReport( sbOutput, expected, actual );
430                         }
431                         else
432                         {
433                                 BuildArrayLengthSameReport( sbOutput, expected, actual );
434                         }
435                 }
436
437                 /// <summary>
438                 /// 
439                 /// </summary>
440                 /// <param name="sbOutput"></param>
441                 /// <param name="sExpected"></param>
442                 /// <param name="sActual"></param>
443                 /// <param name="iPosition"></param>
444                 static private void BuildContentDifferentAtPosition( StringBuilder sbOutput, string sExpected, string sActual, int iPosition )
445                 {
446                         BuildStringLengthReport( sbOutput, sExpected, sActual );
447
448                         sbOutput.Append( "Strings differ at index " );
449                         sbOutput.Append( iPosition );
450                         sbOutput.Append( "." );
451                         sbOutput.Append( NewLine );
452
453                         //
454                         // Clips the strings, then turns any hidden whitespace into visible
455                         // characters
456                         //
457                         string sClippedExpected = ConvertWhitespace(ClipAroundPosition( sExpected, iPosition ));
458                         string sClippedActual   = ConvertWhitespace(ClipAroundPosition( sActual,   iPosition ));
459
460                         AppendExpectedAndActual( 
461                                 sbOutput, 
462                                 sClippedExpected, 
463                                 sClippedActual );
464                         sbOutput.Append( NewLine );
465
466                         // Add a line showing where they differ.  If the string lengths are
467                         // different, they start differing just past the length of the 
468                         // shorter string
469                         AppendPositionMarker( 
470                                 sbOutput, 
471                                 FindMismatchPosition( sClippedExpected, sClippedActual, 0 ) );
472                         sbOutput.Append( NewLine );
473                 }
474
475                 /// <summary>
476                 /// Turns CR, LF, or TAB into visual indicator to preserve visual marker 
477                 /// position.   This is done by replacing the '\r' into '\\' and 'r' 
478                 /// characters, and the '\n' into '\\' and 'n' characters, and '\t' into
479                 /// '\\' and 't' characters.  
480                 /// 
481                 /// Thus the single character becomes two characters for display.
482                 /// </summary>
483                 /// <param name="sInput"></param>
484                 /// <returns></returns>
485                 static protected string ConvertWhitespace( string sInput )
486                 {
487                         if( null != sInput )
488                         {
489                                 sInput = sInput.Replace( "\r", "\\r" );
490                                 sInput = sInput.Replace( "\n", "\\n" );
491                                 sInput = sInput.Replace( "\t", "\\t" );
492                         }
493                         return sInput;
494                 }
495
496                 /// <summary>
497                 /// Shows the position two strings start to differ.  Comparison 
498                 /// starts at the start index.
499                 /// </summary>
500                 /// <param name="sExpected"></param>
501                 /// <param name="sActual"></param>
502                 /// <param name="iStart"></param>
503                 /// <returns>-1 if no mismatch found, or the index where mismatch found</returns>
504                 static private int FindMismatchPosition( string sExpected, string sActual, int iStart )
505                 {
506                         int iLength = Math.Min( sExpected.Length, sActual.Length );
507                         for( int i=iStart; i<iLength; i++ )
508                         {
509                                 //
510                                 // If they mismatch at a specified position, report the
511                                 // difference.
512                                 //
513                                 if( sExpected[i] != sActual[i] )
514                                 {
515                                         return i;
516                                 }
517                         }
518                         //
519                         // Strings have same content up to the length of the shorter string.
520                         // Mismatch occurs because string lengths are different, so show
521                         // that they start differing where the shortest string ends
522                         //
523                         if( sExpected.Length != sActual.Length )
524                         {
525                                 return iLength;
526                         }
527             
528                         //
529                         // Same strings
530                         //
531                         Assert.IsTrue( sExpected.Equals( sActual ) );
532                         return -1;
533                 }
534
535                 /// <summary>
536                 /// Constructs a message that can be displayed when the content of two
537                 /// strings are different, but the string lengths are the same.  The
538                 /// message will clip the strings to a reasonable length, centered
539                 /// around the first position where they are mismatched, and draw 
540                 /// a line marking the position of the difference to make comparison
541                 /// quicker.
542                 /// </summary>
543                 /// <param name="sbOutput"></param>
544                 /// <param name="sExpected"></param>
545                 /// <param name="sActual"></param>
546                 static protected void BuildContentDifferentMessage( StringBuilder sbOutput, string sExpected, string sActual )
547                 {
548                         //
549                         // If they mismatch at a specified position, report the
550                         // difference.
551                         //
552                         int iMismatch = FindMismatchPosition( sExpected, sActual, 0 );
553                         if( -1 != iMismatch )
554                         {
555                                 BuildContentDifferentAtPosition( 
556                                         sbOutput, 
557                                         sExpected, 
558                                         sActual, 
559                                         iMismatch );
560                                 return;
561                         }
562
563                         //
564                         // If the lengths differ, but they match up to the length,
565                         // show the difference just past the length of the shorter
566                         // string
567                         //
568                         if( sExpected.Length != sActual.Length )
569                         {
570                                 BuildContentDifferentAtPosition( 
571                                         sbOutput, 
572                                         sExpected, 
573                                         sActual, 
574                                         Math.Min(sExpected.Length, sActual.Length) );
575                         }
576                 }
577
578                 /// <summary>
579                 /// Called to append a message when the input strings are different.
580                 /// A different message is rendered when the lengths are mismatched,
581                 /// and when the lengths match but content is mismatched.
582                 /// </summary>
583                 /// <param name="sbOutput"></param>
584                 /// <param name="expected"></param>
585                 /// <param name="actual"></param>
586                 static private void BuildStringsDifferentMessage( StringBuilder sbOutput, string expected, string actual )
587                 {
588                         sbOutput.Append( NewLine );
589                         if( LengthsDifferent( expected, actual ) )
590                         {
591                                 BuildLengthsDifferentMessage( sbOutput, expected, actual );
592                         }
593                         else
594                         {
595                                 BuildContentDifferentMessage( sbOutput, expected, actual );
596                         }
597                 }
598
599                 /// <summary>
600                 /// Called to append a message when the input arrays are different.
601                 /// A different message is rendered when the lengths are mismatched,
602                 /// and when the lengths match but content is mismatched.
603                 /// </summary>
604                 /// <param name="sbOutput"></param>
605                 /// <param name="expected"></param>
606                 /// <param name="actual"></param>
607                 static private void BuildArraysDifferentMessage( StringBuilder sbOutput, int index, Array expected, Array actual )
608                 {
609                         sbOutput.Append( NewLine );
610
611                         BuildArrayLengthReport( sbOutput, expected, actual );
612                         
613                         sbOutput.Append( "Arrays differ at index " );
614                         sbOutput.Append( index );
615                         sbOutput.Append( "." );
616                         sbOutput.Append( NewLine );
617                                 
618                         if ( index < expected.Length && index < actual.Length )
619                         {
620                                 if( InputsAreStrings( expected.GetValue(index), actual.GetValue(index) ) )
621                                 {
622                                         BuildStringsDifferentMessage( 
623                                                 sbOutput, 
624                                                 (string)expected.GetValue(index), 
625                                                 (string)actual.GetValue(index) );
626                                 }
627                                 else
628                                 {
629                                         AppendExpectedAndActual( sbOutput, expected.GetValue(index), actual.GetValue(index) );
630                                 }
631                         }
632                         else if( expected.Length < actual.Length )
633                         {
634                                 sbOutput.Append( NewLine );
635                                 sbOutput.Append( "   extra:<" );
636                                 DisplayElements( sbOutput, actual, index, 3 );
637                                 sbOutput.Append( ">" );
638                         }
639                         else
640                         {
641                                 sbOutput.Append( NewLine );
642                                 sbOutput.Append( " missing:<" );
643                                 DisplayElements( sbOutput, expected, index, 3 );
644                                 sbOutput.Append( ">" );
645                         }
646
647                         return;
648                 }
649
650                 static private void DisplayElements( StringBuilder sbOutput, Array array, int index, int max )
651                 {
652                         for( int i = 0; i < max; i++ )
653                         {
654                                 sbOutput.Append( DisplayString( array.GetValue(index++) ) );
655                                 
656                                 if ( index >= array.Length )
657                                         return;
658
659                                 sbOutput.Append( "," );
660                         }
661
662                         sbOutput.Append( "..." );
663                 }
664
665                 /// <summary>
666                 /// Used to create a StringBuilder that is used for constructing
667                 /// the output message when text is different.  Handles initialization
668                 /// when a message is provided.  If message is null, an empty
669                 /// StringBuilder is returned.
670                 /// </summary>
671                 /// <param name="message"></param>
672                 /// <returns></returns>
673                 static protected StringBuilder CreateStringBuilder( string message, params object[] args )
674                 {
675                         StringBuilder sbOutput;
676                         if (message != null) 
677                         {
678                                 if ( args != null && args.Length > 0 )
679                                         sbOutput = new StringBuilder( string.Format( message, args ) );
680                                 else
681                                         sbOutput = new StringBuilder( message );
682                         }
683                         else
684                         {
685                                 sbOutput = new StringBuilder();
686                         }
687                         return sbOutput;
688                 }
689
690                 /// <summary>
691                 /// Called to create a message when two objects have been found to
692                 /// be unequal.  If the inputs are strings, a special message is
693                 /// rendered that can help track down where the strings are different,
694                 /// based on differences in length, or differences in content.
695                 /// 
696                 /// If the inputs are not strings, the ToString method of the objects
697                 /// is used to show what is different about them.
698                 /// </summary>
699                 /// <param name="expected"></param>
700                 /// <param name="actual"></param>
701                 /// <param name="message"></param>
702                 /// <param name="args"></param>
703                 /// <returns></returns>
704                 static public string FormatMessageForFailNotEquals(Object expected, Object actual,
705                         string message, params object[] args) 
706                 {
707                         StringBuilder sbOutput = CreateStringBuilder( message, args );
708                         if( null != message )
709                         {
710                                 if( message.Length > 0 )
711                                 {
712                                         sbOutput.Append( " " );
713                                 }
714                         }
715
716                         if( InputsAreStrings( expected, actual ) )
717                         {
718                                 BuildStringsDifferentMessage( 
719                                         sbOutput, 
720                                         (string)expected, 
721                                         (string)actual );
722                         }
723                         else
724                         {
725                                 AppendExpectedAndActual( sbOutput, expected, actual );
726                         }
727                         return sbOutput.ToString();
728                 }
729
730                 /// <summary>
731                 /// Called to create a message when two arrays are not equal. 
732                 /// </summary>
733                 /// <param name="message"></param>
734                 /// <param name="expected"></param>
735                 /// <param name="actual"></param>
736                 /// <returns></returns>
737                 static public string FormatMessageForFailArraysNotEqual(int index, Array expected, Array actual, 
738                         string message, params object[] args) 
739                 {
740                         StringBuilder sbOutput = CreateStringBuilder( message, args );
741                         if( null != message )
742                         {
743                                 if( message.Length > 0 )
744                                 {
745                                         sbOutput.Append( " " );
746                                 }
747                         }
748
749                         BuildArraysDifferentMessage(
750                                 sbOutput, 
751                                 index,
752                                 expected, 
753                                 actual );
754
755                         return sbOutput.ToString();
756                 }
757         }
758 }