1 // ****************************************************************
\r
2 // Copyright 2007, Charlie Poole
\r
3 // This is free software licensed under the NUnit license. You may
\r
4 // obtain a copy of the license at http://nunit.org/?p=license&r=2.4
\r
5 // ****************************************************************
\r
9 using System.Collections;
\r
11 namespace NUnit.Framework
\r
14 /// Static methods used in creating messages
\r
16 public class MsgUtils
\r
19 /// Static string used when strings are clipped
\r
21 public static readonly string ELLIPSIS = "...";
\r
24 /// Returns the representation of a type as used in NUnitLite.
\r
25 /// This is the same as Type.ToString() except for arrays,
\r
26 /// which are displayed with their declared sizes.
\r
28 /// <param name="obj"></param>
\r
29 /// <returns></returns>
\r
30 public static string GetTypeRepresentation(object obj)
\r
32 Array array = obj as Array;
\r
33 if ( array == null )
\r
34 return string.Format( "<{0}>", obj.GetType() );
\r
36 StringBuilder sb = new StringBuilder();
\r
37 Type elementType = array.GetType();
\r
39 while (elementType.IsArray)
\r
41 elementType = elementType.GetElementType();
\r
44 sb.Append(elementType.ToString());
\r
46 for (int r = 0; r < array.Rank; r++)
\r
48 if (r > 0) sb.Append(',');
\r
49 sb.Append(array.GetLength(r));
\r
56 return string.Format( "<{0}>", sb.ToString() );
\r
59 /// Converts any control characters in a string
\r
60 /// to their escaped representation.
\r
62 /// <param name="s">The string to be converted</param>
\r
63 /// <returns>The converted string</returns>
\r
64 public static string ConvertWhitespace(string s)
\r
68 s = s.Replace( "\\", "\\\\" );
\r
69 s = s.Replace( "\r", "\\r" );
\r
70 s = s.Replace( "\n", "\\n" );
\r
71 s = s.Replace( "\t", "\\t" );
\r
77 /// Return the a string representation for a set of indices into an array
\r
79 /// <param name="indices">Array of indices for which a string is needed</param>
\r
80 public static string GetArrayIndicesAsString(int[] indices)
\r
82 StringBuilder sb = new StringBuilder();
\r
84 for (int r = 0; r < indices.Length; r++)
\r
86 if (r > 0) sb.Append(',');
\r
87 sb.Append(indices[r].ToString());
\r
90 return sb.ToString();
\r
94 /// Get an array of indices representing the point in a collection or
\r
95 /// array corresponding to a single int index into the collection.
\r
97 /// <param name="collection">The collection to which the indices apply</param>
\r
98 /// <param name="index">Index in the collection</param>
\r
99 /// <returns>Array of indices</returns>
\r
100 public static int[] GetArrayIndicesFromCollectionIndex(ICollection collection, int index)
\r
102 Array array = collection as Array;
\r
104 if ( array == null || array.Rank == 1)
\r
105 return new int[] { index };
\r
107 int[] result = new int[array.Rank];
\r
109 for (int r = array.Rank; --r > 0; )
\r
111 int l = array.GetLength(r);
\r
112 result[r] = index % l;
\r
121 /// Clip a string to a given length, starting at a particular offset, returning the clipped
\r
122 /// string with ellipses representing the removed parts
\r
124 /// <param name="s">The string to be clipped</param>
\r
125 /// <param name="maxStringLength">The maximum permitted length of the result string</param>
\r
126 /// <param name="clipStart">The point at which to start clipping</param>
\r
127 /// <returns>The clipped string</returns>
\r
128 public static string ClipString(string s, int maxStringLength, int clipStart)
\r
130 int clipLength = maxStringLength;
\r
131 StringBuilder sb = new StringBuilder();
\r
135 clipLength -= ELLIPSIS.Length;
\r
136 sb.Append( ELLIPSIS );
\r
139 if (s.Length - clipStart > clipLength)
\r
141 clipLength -= ELLIPSIS.Length;
\r
142 sb.Append( s.Substring( clipStart, clipLength ));
\r
143 sb.Append(ELLIPSIS);
\r
145 else if (clipStart > 0)
\r
146 sb.Append( s.Substring(clipStart));
\r
150 return sb.ToString();
\r
154 /// Clip the expected and actual strings in a coordinated fashion,
\r
155 /// so that they may be displayed together.
\r
157 /// <param name="expected"></param>
\r
158 /// <param name="actual"></param>
\r
159 /// <param name="maxDisplayLength"></param>
\r
160 /// <param name="mismatch"></param>
\r
161 public static void ClipExpectedAndActual(ref string expected, ref string actual, int maxDisplayLength, int mismatch)
\r
163 // Case 1: Both strings fit on line
\r
164 int maxStringLength = Math.Max(expected.Length, actual.Length);
\r
165 if (maxStringLength <= maxDisplayLength)
\r
168 // Case 2: Assume that the tail of each string fits on line
\r
169 int clipLength = maxDisplayLength - ELLIPSIS.Length;
\r
170 int tailLength = clipLength - mismatch;
\r
171 int clipStart = maxStringLength - clipLength;
\r
173 // Case 3: If it doesn't, center the mismatch position
\r
174 if ( clipStart > mismatch )
\r
175 clipStart = Math.Max( 0, mismatch - clipLength / 2 );
\r
177 expected = ClipString(expected, maxDisplayLength, clipStart);
\r
178 actual = ClipString(actual, maxDisplayLength, clipStart);
\r
182 /// Shows the position two strings start to differ. Comparison
\r
183 /// starts at the start index.
\r
185 /// <param name="expected">The expected string</param>
\r
186 /// <param name="actual">The actual string</param>
\r
187 /// <param name="istart">The index in the strings at which comparison should start</param>
\r
188 /// <param name="ignoreCase">Boolean indicating whether case should be ignored</param>
\r
189 /// <returns>-1 if no mismatch found, or the index where mismatch found</returns>
\r
190 static public int FindMismatchPosition(string expected, string actual, int istart, bool ignoreCase)
\r
192 int length = Math.Min(expected.Length, actual.Length);
\r
194 string s1 = ignoreCase ? expected.ToLower() : expected;
\r
195 string s2 = ignoreCase ? actual.ToLower() : actual;
\r
197 for (int i = istart; i < length; i++)
\r
199 if (s1[i] != s2[i])
\r
204 // Strings have same content up to the length of the shorter string.
\r
205 // Mismatch occurs because string lengths are different, so show
\r
206 // that they start differing where the shortest string ends
\r
208 if (expected.Length != actual.Length)
\r
212 // Same strings : We shouldn't get here
\r