Merge pull request #1325 from madrang/CryptoToolsCtorCheck
[mono.git] / mcs / nunit24 / NUnitFramework / framework / TextMessageWriter.cs
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
6 \r
7 using System;\r
8 using System.IO;\r
9 using System.Text;\r
10 using System.Collections;\r
11 using System.Globalization;\r
12 using NUnit.Framework.Constraints;\r
13 \r
14 namespace NUnit.Framework\r
15 {\r
16         /// <summary>\r
17         /// TextMessageWriter writes constraint descriptions and messages\r
18         /// in displayable form as a text stream. It tailors the display\r
19         /// of individual message components to form the standard message\r
20         /// format of NUnit assertion failure messages.\r
21         /// </summary>\r
22     public class TextMessageWriter : MessageWriter\r
23     {\r
24         #region Message Formats and Constants\r
25         private static readonly int DEFAULT_LINE_LENGTH = 78;\r
26 \r
27                 // Prefixes used in all failure messages. All must be the same\r
28                 // length, which is held in the PrefixLength field. Should not\r
29                 // contain any tabs or newline characters.\r
30                 /// <summary>\r
31                 /// Prefix used for the expected value line of a message\r
32                 /// </summary>\r
33                 public static readonly string Pfx_Expected = "  Expected: ";\r
34                 /// <summary>\r
35                 /// Prefix used for the actual value line of a message\r
36                 /// </summary>\r
37                 public static readonly string Pfx_Actual = "  But was:  ";\r
38                 /// <summary>\r
39                 /// Length of a message prefix\r
40                 /// </summary>\r
41                 public static readonly int PrefixLength = Pfx_Expected.Length;\r
42                 \r
43                 private static readonly string Fmt_Connector = " {0} ";\r
44         private static readonly string Fmt_Predicate = "{0} ";\r
45         //private static readonly string Fmt_Label = "{0}";\r
46                 private static readonly string Fmt_Modifier = ", {0}";\r
47 \r
48         private static readonly string Fmt_Null = "null";\r
49         private static readonly string Fmt_EmptyString = "<string.Empty>";\r
50         private static readonly string Fmt_EmptyCollection = "<empty>";\r
51 \r
52         private static readonly string Fmt_String = "\"{0}\"";\r
53         private static readonly string Fmt_Char = "'{0}'";\r
54                 private static readonly string Fmt_DateTime = "yyyy-MM-dd HH:mm:ss.fff";\r
55         private static readonly string Fmt_ValueType = "{0}";\r
56         private static readonly string Fmt_Default = "<{0}>";\r
57         #endregion\r
58 \r
59                 private int maxLineLength = DEFAULT_LINE_LENGTH;\r
60 \r
61         #region Constructors\r
62                 /// <summary>\r
63                 /// Construct a TextMessageWriter\r
64                 /// </summary>\r
65         public TextMessageWriter() { }\r
66 \r
67         /// <summary>\r
68         /// Construct a TextMessageWriter, specifying a user message\r
69         /// and optional formatting arguments.\r
70         /// </summary>\r
71         /// <param name="userMessage"></param>\r
72         /// <param name="args"></param>\r
73                 public TextMessageWriter(string userMessage, params object[] args)\r
74         {\r
75                         if ( userMessage != null && userMessage != string.Empty)\r
76                                 this.WriteMessageLine(userMessage, args);\r
77         }\r
78         #endregion\r
79 \r
80         #region Properties\r
81         /// <summary>\r
82         /// Gets or sets the maximum line length for this writer\r
83         /// </summary>\r
84         public override int MaxLineLength\r
85         {\r
86             get { return maxLineLength; }\r
87             set { maxLineLength = value; }\r
88         }\r
89         #endregion\r
90 \r
91         #region Public Methods - High Level\r
92         /// <summary>\r
93         /// Method to write single line  message with optional args, usually\r
94         /// written to precede the general failure message, at a givel \r
95         /// indentation level.\r
96         /// </summary>\r
97         /// <param name="level">The indentation level of the message</param>\r
98         /// <param name="message">The message to be written</param>\r
99         /// <param name="args">Any arguments used in formatting the message</param>\r
100         public override void WriteMessageLine(int level, string message, params object[] args)\r
101         {\r
102             if (message != null)\r
103             {\r
104                 while (level-- >= 0) Write("  ");\r
105 \r
106                 if (args != null && args.Length > 0)\r
107                     message = string.Format(message, args);\r
108 \r
109                 WriteLine(message);\r
110             }\r
111         }\r
112 \r
113         /// <summary>\r
114         /// Display Expected and Actual lines for a constraint. This\r
115         /// is called by MessageWriter's default implementation of \r
116         /// WriteMessageTo and provides the generic two-line display. \r
117         /// </summary>\r
118         /// <param name="constraint">The constraint that failed</param>\r
119         public override void DisplayDifferences(Constraint constraint)\r
120         {\r
121             WriteExpectedLine(constraint);\r
122             WriteActualLine(constraint);\r
123         }\r
124 \r
125                 /// <summary>\r
126                 /// Display Expected and Actual lines for given values. This\r
127                 /// method may be called by constraints that need more control over\r
128                 /// the display of actual and expected values than is provided\r
129                 /// by the default implementation.\r
130                 /// </summary>\r
131                 /// <param name="expected">The expected value</param>\r
132                 /// <param name="actual">The actual value causing the failure</param>\r
133                 public override void DisplayDifferences(object expected, object actual)\r
134                 {\r
135                         WriteExpectedLine(expected);\r
136                         WriteActualLine(actual);\r
137                 }\r
138 \r
139                 /// <summary>\r
140                 /// Display Expected and Actual lines for given values, including\r
141                 /// a tolerance value on the expected line.\r
142                 /// </summary>\r
143                 /// <param name="expected">The expected value</param>\r
144                 /// <param name="actual">The actual value causing the failure</param>\r
145                 /// <param name="tolerance">The tolerance within which the test was made</param>\r
146                 public override void DisplayDifferences(object expected, object actual, object tolerance)\r
147                 {\r
148                         WriteExpectedLine(expected, tolerance);\r
149                         WriteActualLine(actual);\r
150                 }\r
151 \r
152                 /// <summary>\r
153         /// Display the expected and actual string values on separate lines.\r
154         /// If the mismatch parameter is >=0, an additional line is displayed\r
155         /// line containing a caret that points to the mismatch point.\r
156         /// </summary>\r
157         /// <param name="expected">The expected string value</param>\r
158         /// <param name="actual">The actual string value</param>\r
159         /// <param name="mismatch">The point at which the strings don't match or -1</param>\r
160         /// <param name="ignoreCase">If true, case is ignored in string comparisons</param>\r
161         /// <param name="clipping">If true, clip the strings to fit the max line length</param>\r
162         public override void DisplayStringDifferences(string expected, string actual, int mismatch, bool ignoreCase, bool clipping)\r
163         {\r
164             // Maximum string we can display without truncating\r
165             int maxDisplayLength = MaxLineLength\r
166                 - PrefixLength   // Allow for prefix\r
167                 - 2;             // 2 quotation marks\r
168 \r
169             if ( clipping )\r
170                 MsgUtils.ClipExpectedAndActual(ref expected, ref actual, maxDisplayLength, mismatch);\r
171 \r
172             expected = MsgUtils.ConvertWhitespace(expected);\r
173             actual = MsgUtils.ConvertWhitespace(actual);\r
174 \r
175             // The mismatch position may have changed due to clipping or white space conversion\r
176             mismatch = MsgUtils.FindMismatchPosition(expected, actual, 0, ignoreCase);\r
177 \r
178                         Write( Pfx_Expected );\r
179                         WriteExpectedValue( expected );\r
180                         if ( ignoreCase )\r
181                                 WriteModifier( "ignoring case" );\r
182                         WriteLine();\r
183                         WriteActualLine( actual );\r
184             //DisplayDifferences(expected, actual);\r
185             if (mismatch >= 0)\r
186                 WriteCaretLine(mismatch);\r
187         }\r
188         #endregion\r
189 \r
190         #region Public Methods - Low Level\r
191                 /// <summary>\r
192                 /// Writes the text for a connector.\r
193                 /// </summary>\r
194                 /// <param name="connector">The connector.</param>\r
195                 public override void WriteConnector(string connector)\r
196         {\r
197             Write(Fmt_Connector, connector);\r
198         }\r
199 \r
200                 /// <summary>\r
201                 /// Writes the text for a predicate.\r
202                 /// </summary>\r
203                 /// <param name="predicate">The predicate.</param>\r
204                 public override void WritePredicate(string predicate)\r
205         {\r
206             Write(Fmt_Predicate, predicate);\r
207         }\r
208 \r
209         //public override void WriteLabel(string label)\r
210         //{\r
211         //    Write(Fmt_Label, label);\r
212         //}\r
213 \r
214         /// <summary>\r
215         /// Write the text for a modifier.\r
216         /// </summary>\r
217         /// <param name="modifier">The modifier.</param>\r
218                 public override void WriteModifier(string modifier)\r
219                 {\r
220                         Write(Fmt_Modifier, modifier);\r
221                 }\r
222 \r
223 \r
224                 /// <summary>\r
225                 /// Writes the text for an expected value.\r
226                 /// </summary>\r
227                 /// <param name="expected">The expected value.</param>\r
228                 public override void WriteExpectedValue(object expected)\r
229         {\r
230             WriteValue(expected);\r
231         }\r
232 \r
233                 /// <summary>\r
234                 /// Writes the text for an actual value.\r
235                 /// </summary>\r
236                 /// <param name="actual">The actual value.</param>\r
237                 public override void WriteActualValue(object actual)\r
238         {\r
239             WriteValue(actual);\r
240         }\r
241 \r
242                 /// <summary>\r
243                 /// Writes the text for a generalized value.\r
244                 /// </summary>\r
245                 /// <param name="val">The value.</param>\r
246                 public override void WriteValue(object val)\r
247         {\r
248             if (val == null)\r
249                 Write(Fmt_Null);\r
250             else if (val.GetType().IsArray)\r
251                 WriteArray((Array)val);\r
252             else if (val is ICollection)\r
253                 WriteCollectionElements((ICollection)val, 0, 10);\r
254             else if (val is string)\r
255                 WriteString((string)val);\r
256             else if (val is char)\r
257                 WriteChar((char)val);\r
258             else if (val is double)\r
259                 WriteDouble((double)val);\r
260             else if (val is float)\r
261                 WriteFloat((float)val);\r
262             else if (val is decimal)\r
263                 WriteDecimal((decimal)val);\r
264                         else if (val is DateTime)\r
265                                 WriteDateTime((DateTime)val);\r
266             else if (val.GetType().IsValueType)\r
267                 Write(Fmt_ValueType, val);\r
268             else\r
269                 Write(Fmt_Default, val);\r
270         }\r
271 \r
272         /// <summary>\r
273         /// Writes the text for a collection value,\r
274         /// starting at a particular point, to a max length\r
275         /// </summary>\r
276         /// <param name="collection">The collection containing elements to write.</param>\r
277         /// <param name="start">The starting point of the elements to write</param>\r
278         /// <param name="max">The maximum number of elements to write</param>\r
279                 public override void WriteCollectionElements(ICollection collection, int start, int max)\r
280                 {\r
281                         if ( collection.Count == 0 )\r
282                         {\r
283                                 Write(Fmt_EmptyCollection);\r
284                                 return;\r
285                         }\r
286 \r
287                         int count = 0;\r
288                         int index = 0;\r
289                         Write("< ");\r
290 \r
291                         foreach (object obj in collection)\r
292                         {\r
293                                 if ( index++ >= start )\r
294                                 {\r
295                                         if (count > 0)\r
296                                                 Write(", ");\r
297                                         WriteValue(obj);\r
298                                         if ( ++count >= max )\r
299                                                 break;\r
300                                 }\r
301                         }\r
302 \r
303                         if ( index < collection.Count )\r
304                                 Write("...");\r
305 \r
306                         Write(" >");\r
307                 }\r
308 \r
309                 private void WriteArray(Array array)\r
310         {\r
311                         if ( array.Length == 0 )\r
312                         {\r
313                                 Write( Fmt_EmptyCollection );\r
314                                 return;\r
315                         }\r
316                         \r
317                         int rank = array.Rank;\r
318             int[] products = new int[rank];\r
319 \r
320             for (int product = 1, r = rank; --r >= 0; )\r
321                 products[r] = product *= array.GetLength(r);\r
322 \r
323             int count = 0;\r
324             foreach (object obj in array)\r
325             {\r
326                 if (count > 0)\r
327                     Write(", ");\r
328 \r
329                 bool startSegment = false;\r
330                 for (int r = 0; r < rank; r++)\r
331                 {\r
332                     startSegment = startSegment || count % products[r] == 0;\r
333                     if (startSegment) Write("< ");\r
334                 }\r
335 \r
336                 WriteValue(obj);\r
337 \r
338                 ++count;\r
339 \r
340                 bool nextSegment = false;\r
341                 for (int r = 0; r < rank; r++)\r
342                 {\r
343                     nextSegment = nextSegment || count % products[r] == 0;\r
344                     if (nextSegment) Write(" >");\r
345                 }\r
346             }\r
347         }\r
348 \r
349         private void WriteString(string s)\r
350         {\r
351             if (s == string.Empty)\r
352                 Write(Fmt_EmptyString);\r
353             else\r
354                 Write(Fmt_String, s);\r
355         }\r
356 \r
357         private void WriteChar(char c)\r
358         {\r
359             Write(Fmt_Char, c);\r
360         }\r
361 \r
362         private void WriteDouble(double d)\r
363         {\r
364 \r
365             if (double.IsNaN(d) || double.IsInfinity(d))\r
366                 Write(d);\r
367             else\r
368             {\r
369                 string s = d.ToString("G17", CultureInfo.InvariantCulture);\r
370 \r
371                 if (s.IndexOf('.') > 0)\r
372                     Write(s + "d");\r
373                 else\r
374                     Write(s + ".0d");\r
375             }\r
376         }\r
377 \r
378         private void WriteFloat(float f)\r
379         {\r
380             if (float.IsNaN(f) || float.IsInfinity(f))\r
381                 Write(f);\r
382             else\r
383             {\r
384                 string s = f.ToString("G9", CultureInfo.InvariantCulture);\r
385 \r
386                 if (s.IndexOf('.') > 0)\r
387                     Write(s + "f");\r
388                 else\r
389                     Write(s + ".0f");\r
390             }\r
391         }\r
392 \r
393         private void WriteDecimal(Decimal d)\r
394         {\r
395             Write(d.ToString("G29", CultureInfo.InvariantCulture) + "m");\r
396         }\r
397 \r
398                 private void WriteDateTime(DateTime dt)\r
399                 {\r
400                         Write(dt.ToString(Fmt_DateTime, CultureInfo.InvariantCulture));\r
401                 }\r
402         #endregion\r
403 \r
404         #region Helper Methods\r
405         /// <summary>\r
406         /// Write the generic 'Expected' line for a constraint\r
407         /// </summary>\r
408         /// <param name="constraint">The constraint that failed</param>\r
409         private void WriteExpectedLine(Constraint constraint)\r
410         {\r
411             Write(Pfx_Expected);\r
412             constraint.WriteDescriptionTo(this);\r
413             WriteLine();\r
414         }\r
415 \r
416                 /// <summary>\r
417                 /// Write the generic 'Expected' line for a given value\r
418                 /// </summary>\r
419                 /// <param name="expected">The expected value</param>\r
420                 private void WriteExpectedLine(object expected)\r
421                 {\r
422             WriteExpectedLine(expected, null);\r
423                 }\r
424 \r
425                 /// <summary>\r
426                 /// Write the generic 'Expected' line for a given value\r
427                 /// and tolerance.\r
428                 /// </summary>\r
429                 /// <param name="expected">The expected value</param>\r
430                 /// <param name="tolerance">The tolerance within which the test was made</param>\r
431                 private void WriteExpectedLine(object expected, object tolerance)\r
432                 {\r
433                         Write(Pfx_Expected);\r
434                         WriteExpectedValue(expected);\r
435 \r
436             if (tolerance != null)\r
437             {\r
438                 WriteConnector("+/-");\r
439                 WriteExpectedValue(tolerance);\r
440             }\r
441 \r
442                         WriteLine();\r
443                 }\r
444 \r
445                 /// <summary>\r
446                 /// Write the generic 'Actual' line for a constraint\r
447                 /// </summary>\r
448                 /// <param name="constraint">The constraint for which the actual value is to be written</param>\r
449                 private void WriteActualLine(Constraint constraint)\r
450                 {\r
451                         Write(Pfx_Actual);\r
452                         constraint.WriteActualValueTo(this);\r
453                         WriteLine();\r
454                 }\r
455 \r
456                 /// <summary>\r
457                 /// Write the generic 'Actual' line for a given value\r
458                 /// </summary>\r
459                 /// <param name="actual">The actual value causing a failure</param>\r
460                 private void WriteActualLine(object actual)\r
461                 {\r
462                         Write(Pfx_Actual);\r
463                         WriteActualValue(actual);\r
464                         WriteLine();\r
465                 }\r
466 \r
467                 private void WriteCaretLine(int mismatch)\r
468         {\r
469             // We subtract 2 for the initial 2 blanks and add back 1 for the initial quote\r
470             WriteLine("  {0}^", new string('-', PrefixLength + mismatch - 2 + 1));\r
471         }\r
472         #endregion\r
473     }\r
474 }\r