1 // ****************************************************************
2 // Copyright 2007, Charlie Poole
3 // This is free software licensed under the NUnit license. You may
4 // obtain a copy of the license at http://nunit.org/?p=license&r=2.4
5 // ****************************************************************
6 
7 using System;
8 using System.Text;
9 using System.Collections;
10 
11 namespace NUnit.Framework
12 {
13     /// <summary>
14     /// Static methods used in creating messages
15     /// </summary>
16     public class MsgUtils
17     {
18         /// <summary>
19         /// Static string used when strings are clipped
20         /// </summary>
21         public static readonly string ELLIPSIS = "...";
22 
23         /// <summary>
24         /// Returns the representation of a type as used in NUnitLite.
25         /// This is the same as Type.ToString() except for arrays,
26         /// which are displayed with their declared sizes.
27         /// </summary>
28         /// <param name="obj"></param>
29         /// <returns></returns>
GetTypeRepresentation(object obj)30         public static string GetTypeRepresentation(object obj)
31         {
32             Array array = obj as Array;
33             if ( array == null )
34                 return string.Format( "<{0}>", obj.GetType() );
35 
36             StringBuilder sb = new StringBuilder();
37             Type elementType = array.GetType();
38             int nest = 0;
39             while (elementType.IsArray)
40             {
41                 elementType = elementType.GetElementType();
42                 ++nest;
43             }
44             sb.Append(elementType.ToString());
45             sb.Append('[');
46             for (int r = 0; r < array.Rank; r++)
47             {
48                 if (r > 0) sb.Append(',');
49                 sb.Append(array.GetLength(r));
50             }
51             sb.Append(']');
52 
53             while (--nest > 0)
54                 sb.Append("[]");
55 
56             return string.Format( "<{0}>", sb.ToString() );
57         }
58         /// <summary>
59         /// Converts any control characters in a string
60         /// to their escaped representation.
61         /// </summary>
62         /// <param name="s">The string to be converted</param>
63         /// <returns>The converted string</returns>
ConvertWhitespace(string s)64         public static string ConvertWhitespace(string s)
65         {
66 			if( s != null )
67 			{
68 				s = s.Replace( "\\", "\\\\" );
69 				s = s.Replace( "\r", "\\r" );
70 				s = s.Replace( "\n", "\\n" );
71 				s = s.Replace( "\t", "\\t" );
72 			}
73 			return s;
74         }
75 
76         /// <summary>
77         /// Return the a string representation for a set of indices into an array
78         /// </summary>
79         /// <param name="indices">Array of indices for which a string is needed</param>
GetArrayIndicesAsString(int[] indices)80         public static string GetArrayIndicesAsString(int[] indices)
81         {
82             StringBuilder sb = new StringBuilder();
83             sb.Append('[');
84             for (int r = 0; r < indices.Length; r++)
85             {
86                 if (r > 0) sb.Append(',');
87                 sb.Append(indices[r].ToString());
88             }
89             sb.Append(']');
90             return sb.ToString();
91         }
92 
93         /// <summary>
94         /// Get an array of indices representing the point in a collection or
95         /// array corresponding to a single int index into the collection.
96         /// </summary>
97         /// <param name="collection">The collection to which the indices apply</param>
98         /// <param name="index">Index in the collection</param>
99         /// <returns>Array of indices</returns>
GetArrayIndicesFromCollectionIndex(ICollection collection, int index)100         public static int[] GetArrayIndicesFromCollectionIndex(ICollection collection, int index)
101         {
102             Array array = collection as Array;
103 
104             if ( array == null || array.Rank == 1)
105                 return new int[] { index };
106 
107             int[] result = new int[array.Rank];
108 
109             for (int r = array.Rank; --r > 0; )
110             {
111                 int l = array.GetLength(r);
112                 result[r] = index % l;
113                 index /= l;
114             }
115 
116             result[0] = index;
117             return result;
118         }
119 
120         /// <summary>
121         /// Clip a string to a given length, starting at a particular offset, returning the clipped
122         /// string with ellipses representing the removed parts
123         /// </summary>
124         /// <param name="s">The string to be clipped</param>
125         /// <param name="maxStringLength">The maximum permitted length of the result string</param>
126         /// <param name="clipStart">The point at which to start clipping</param>
127         /// <returns>The clipped string</returns>
ClipString(string s, int maxStringLength, int clipStart)128         public static string ClipString(string s, int maxStringLength, int clipStart)
129         {
130             int clipLength = maxStringLength;
131             StringBuilder sb = new StringBuilder();
132 
133             if (clipStart > 0)
134             {
135                 clipLength -= ELLIPSIS.Length;
136                 sb.Append( ELLIPSIS );
137             }
138 
139             if (s.Length - clipStart > clipLength)
140             {
141                 clipLength -= ELLIPSIS.Length;
142                 sb.Append( s.Substring( clipStart, clipLength ));
143                 sb.Append(ELLIPSIS);
144             }
145             else if (clipStart > 0)
146                 sb.Append( s.Substring(clipStart));
147             else
148                 sb.Append( s );
149 
150             return sb.ToString();
151         }
152 
153         /// <summary>
154         /// Clip the expected and actual strings in a coordinated fashion,
155         /// so that they may be displayed together.
156         /// </summary>
157         /// <param name="expected"></param>
158         /// <param name="actual"></param>
159         /// <param name="maxDisplayLength"></param>
160         /// <param name="mismatch"></param>
ClipExpectedAndActual(ref string expected, ref string actual, int maxDisplayLength, int mismatch)161         public static void ClipExpectedAndActual(ref string expected, ref string actual, int maxDisplayLength, int mismatch)
162         {
163             // Case 1: Both strings fit on line
164             int maxStringLength = Math.Max(expected.Length, actual.Length);
165             if (maxStringLength <= maxDisplayLength)
166                 return;
167 
168             // Case 2: Assume that the tail of each string fits on line
169             int clipLength = maxDisplayLength - ELLIPSIS.Length;
170             int tailLength = clipLength - mismatch;
171             int clipStart = maxStringLength - clipLength;
172 
173             // Case 3: If it doesn't, center the mismatch position
174             if ( clipStart > mismatch )
175                 clipStart = Math.Max( 0, mismatch - clipLength / 2 );
176 
177             expected = ClipString(expected, maxDisplayLength, clipStart);
178             actual = ClipString(actual, maxDisplayLength, clipStart);
179         }
180 
181         /// <summary>
182         /// Shows the position two strings start to differ.  Comparison
183         /// starts at the start index.
184         /// </summary>
185         /// <param name="expected">The expected string</param>
186         /// <param name="actual">The actual string</param>
187         /// <param name="istart">The index in the strings at which comparison should start</param>
188         /// <param name="ignoreCase">Boolean indicating whether case should be ignored</param>
189         /// <returns>-1 if no mismatch found, or the index where mismatch found</returns>
FindMismatchPosition(string expected, string actual, int istart, bool ignoreCase)190         static public int FindMismatchPosition(string expected, string actual, int istart, bool ignoreCase)
191         {
192             int length = Math.Min(expected.Length, actual.Length);
193 
194             string s1 = ignoreCase ? expected.ToLower() : expected;
195             string s2 = ignoreCase ? actual.ToLower() : actual;
196 
197             for (int i = istart; i < length; i++)
198             {
199                 if (s1[i] != s2[i])
200                     return i;
201             }
202 
203             //
204             // Strings have same content up to the length of the shorter string.
205             // Mismatch occurs because string lengths are different, so show
206             // that they start differing where the shortest string ends
207             //
208             if (expected.Length != actual.Length)
209                 return length;
210 
211             //
212             // Same strings : We shouldn't get here
213             //
214             return -1;
215         }
216     }
217 }
218