PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/nunit24/NUnitFramework/framework/AssertionFailureMessage.cs

https://bitbucket.org/danipen/mono
C# | 533 lines | 258 code | 60 blank | 215 comment | 64 complexity | 402b312a57ed3403073cc123151f6942 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. // ****************************************************************
  2. // This is free software licensed under the NUnit license. You
  3. // may obtain a copy of the license as well as information regarding
  4. // copyright ownership at http://nunit.org/?p=license&r=2.4.
  5. // ****************************************************************
  6. using System;
  7. using System.Text;
  8. using System.IO;
  9. using System.Collections;
  10. namespace NUnit.Framework
  11. {
  12. /// <summary>
  13. /// AssertionFailureMessage encapsulates a failure message
  14. /// issued as a result of an Assert failure.
  15. /// </summary>
  16. [Obsolete( "Use MessageWriter for new work" )]
  17. public class AssertionFailureMessage : StringWriter
  18. {
  19. #region Static Constants
  20. /// <summary>
  21. /// Number of characters before a highlighted position before
  22. /// clipping will occur. Clipped text is replaced with an
  23. /// elipsis "..."
  24. /// </summary>
  25. static public readonly int PreClipLength = 35;
  26. /// <summary>
  27. /// Number of characters after a highlighted position before
  28. /// clipping will occur. Clipped text is replaced with an
  29. /// elipsis "..."
  30. /// </summary>
  31. static public readonly int PostClipLength = 35;
  32. /// <summary>
  33. /// Prefix used to start an expected value line.
  34. /// Must be same length as actualPrefix.
  35. /// </summary>
  36. static protected readonly string expectedPrefix = "expected:";
  37. /// <summary>
  38. /// Prefix used to start an actual value line.
  39. /// Must be same length as expectedPrefix.
  40. /// </summary>
  41. static protected readonly string actualPrefix = " but was:";
  42. static private readonly string expectedAndActualFmt = "\t{0} {1}";
  43. static private readonly string diffStringLengthsFmt
  44. = "\tString lengths differ. Expected length={0}, but was length={1}.";
  45. static private readonly string sameStringLengthsFmt
  46. = "\tString lengths are both {0}.";
  47. static private readonly string diffArrayLengthsFmt
  48. = "Array lengths differ. Expected length={0}, but was length={1}.";
  49. static private readonly string sameArrayLengthsFmt
  50. = "Array lengths are both {0}.";
  51. static private readonly string stringsDifferAtIndexFmt
  52. = "\tStrings differ at index {0}.";
  53. static private readonly string arraysDifferAtIndexFmt
  54. = "Arrays differ at index {0}.";
  55. #endregion
  56. #region Constructors
  57. /// <summary>
  58. /// Construct an AssertionFailureMessage with a message
  59. /// and optional arguments.
  60. /// </summary>
  61. /// <param name="message"></param>
  62. /// <param name="args"></param>
  63. public AssertionFailureMessage( string message, params object[] args )
  64. {
  65. if ( message != null && message != string.Empty )
  66. if ( args != null )
  67. WriteLine( message, args );
  68. else
  69. WriteLine( message );
  70. }
  71. /// <summary>
  72. /// Construct an empty AssertionFailureMessage
  73. /// </summary>
  74. public AssertionFailureMessage() : this( null, null ) { }
  75. #endregion
  76. /// <summary>
  77. /// Add an expected value line to the message containing
  78. /// the text provided as an argument.
  79. /// </summary>
  80. /// <param name="text">Text describing what was expected.</param>
  81. public void WriteExpectedLine( string text )
  82. {
  83. WriteLine( string.Format( expectedAndActualFmt, expectedPrefix, text ) );
  84. }
  85. /// <summary>
  86. /// Add an actual value line to the message containing
  87. /// the text provided as an argument.
  88. /// </summary>
  89. /// <param name="text">Text describing the actual value.</param>
  90. public void WriteActualLine( string text )
  91. {
  92. WriteLine( string.Format( expectedAndActualFmt, actualPrefix, text ) );
  93. }
  94. /// <summary>
  95. /// Add an expected value line to the message containing
  96. /// a string representation of the object provided.
  97. /// </summary>
  98. /// <param name="expected">An object representing the expected value</param>
  99. public void DisplayExpectedValue( object expected )
  100. {
  101. WriteExpectedLine( FormatObjectForDisplay( expected ) );
  102. }
  103. /// <summary>
  104. /// Add an expected value line to the message containing a double
  105. /// and the tolerance used in making the comparison.
  106. /// </summary>
  107. /// <param name="expected">The expected value</param>
  108. /// <param name="tolerance">The tolerance specified in the Assert</param>
  109. public void DisplayExpectedValue( double expected, double tolerance )
  110. {
  111. WriteExpectedLine( FormatObjectForDisplay( expected ) + " +/- " + tolerance.ToString() );
  112. }
  113. /// <summary>
  114. /// Add an actual value line to the message containing
  115. /// a string representation of the object provided.
  116. /// </summary>
  117. /// <param name="actual">An object representing what was actually found</param>
  118. public void DisplayActualValue( object actual )
  119. {
  120. WriteActualLine( FormatObjectForDisplay( actual ) );
  121. }
  122. /// <summary>
  123. /// Display two lines that communicate the expected value, and the actual value
  124. /// </summary>
  125. /// <param name="expected">The expected value</param>
  126. /// <param name="actual">The actual value found</param>
  127. public void DisplayExpectedAndActual( Object expected, Object actual )
  128. {
  129. DisplayExpectedValue( expected );
  130. DisplayActualValue( actual );
  131. }
  132. /// <summary>
  133. /// Display two lines that communicate the expected value, the actual value and
  134. /// the tolerance used in comparing two doubles.
  135. /// </summary>
  136. /// <param name="expected">The expected value</param>
  137. /// <param name="actual">The actual value found</param>
  138. /// <param name="tolerance">The tolerance specified in the Assert</param>
  139. public void DisplayExpectedAndActual( double expected, double actual, double tolerance )
  140. {
  141. DisplayExpectedValue( expected, tolerance );
  142. DisplayActualValue( actual );
  143. }
  144. /// <summary>
  145. /// Draws a marker under the expected/actual strings that highlights
  146. /// where in the string a mismatch occurred.
  147. /// </summary>
  148. /// <param name="iPosition">The position of the mismatch</param>
  149. public void DisplayPositionMarker( int iPosition )
  150. {
  151. WriteLine( "\t{0}^", new String( '-', expectedPrefix.Length + iPosition + 3 ) );
  152. }
  153. /// <summary>
  154. /// Reports whether the string lengths are the same or different, and
  155. /// what the string lengths are.
  156. /// </summary>
  157. /// <param name="sExpected">The expected string</param>
  158. /// <param name="sActual">The actual string value</param>
  159. protected void BuildStringLengthReport( string sExpected, string sActual )
  160. {
  161. if( sExpected.Length != sActual.Length )
  162. WriteLine( diffStringLengthsFmt, sExpected.Length, sActual.Length );
  163. else
  164. WriteLine( sameStringLengthsFmt, sExpected.Length );
  165. }
  166. /// <summary>
  167. /// Called to create additional message lines when two objects have been
  168. /// found to be unequal. If the inputs are strings, a special message is
  169. /// rendered that can help track down where the strings are different,
  170. /// based on differences in length, or differences in content.
  171. ///
  172. /// If the inputs are not strings, the ToString method of the objects
  173. /// is used to show what is different about them.
  174. /// </summary>
  175. /// <param name="expected">The expected value</param>
  176. /// <param name="actual">The actual value</param>
  177. /// <param name="caseInsensitive">True if a case-insensitive comparison is being performed</param>
  178. public void DisplayDifferences( object expected, object actual, bool caseInsensitive )
  179. {
  180. if( InputsAreStrings( expected, actual ) )
  181. {
  182. DisplayStringDifferences(
  183. (string)expected,
  184. (string)actual,
  185. caseInsensitive );
  186. }
  187. else
  188. {
  189. DisplayExpectedAndActual( expected, actual );
  190. }
  191. }
  192. /// <summary>
  193. /// Called to create additional message lines when two doubles have been
  194. /// found to be unequal, within the specified tolerance.
  195. /// </summary>
  196. public void DisplayDifferencesWithTolerance( double expected, double actual, double tolerance )
  197. {
  198. DisplayExpectedAndActual( expected, actual, tolerance );
  199. }
  200. /// <summary>
  201. /// Constructs a message that can be displayed when the content of two
  202. /// strings are different, but the string lengths are the same. The
  203. /// message will clip the strings to a reasonable length, centered
  204. /// around the first position where they are mismatched, and draw
  205. /// a line marking the position of the difference to make comparison
  206. /// quicker.
  207. /// </summary>
  208. /// <param name="sExpected">The expected string value</param>
  209. /// <param name="sActual">The actual string value</param>
  210. /// <param name="caseInsensitive">True if a case-insensitive comparison is being performed</param>
  211. protected void DisplayStringDifferences( string sExpected, string sActual, bool caseInsensitive )
  212. {
  213. //
  214. // If they mismatch at a specified position, report the
  215. // difference.
  216. //
  217. int iPosition = caseInsensitive
  218. ? FindMismatchPosition( sExpected.ToLower(), sActual.ToLower(), 0 )
  219. : FindMismatchPosition( sExpected, sActual, 0 );
  220. //
  221. // If the lengths differ, but they match up to the length,
  222. // show the difference just past the length of the shorter
  223. // string
  224. //
  225. if( iPosition == -1 )
  226. iPosition = Math.Min( sExpected.Length, sActual.Length );
  227. BuildStringLengthReport( sExpected, sActual );
  228. WriteLine( stringsDifferAtIndexFmt, iPosition );
  229. //
  230. // Clips the strings, then turns any hidden whitespace into visible
  231. // characters
  232. //
  233. string sClippedExpected = ConvertWhitespace(ClipAroundPosition( sExpected, iPosition ));
  234. string sClippedActual = ConvertWhitespace(ClipAroundPosition( sActual, iPosition ));
  235. DisplayExpectedAndActual(
  236. sClippedExpected,
  237. sClippedActual );
  238. // Add a line showing where they differ. If the string lengths are
  239. // different, they start differing just past the length of the
  240. // shorter string
  241. DisplayPositionMarker( caseInsensitive
  242. ? FindMismatchPosition( sClippedExpected.ToLower(), sClippedActual.ToLower(), 0 )
  243. : FindMismatchPosition( sClippedExpected, sClippedActual, 0 ) );
  244. }
  245. /// <summary>
  246. /// Display a standard message showing the differences found between
  247. /// two arrays that were expected to be equal.
  248. /// </summary>
  249. /// <param name="expected">The expected array value</param>
  250. /// <param name="actual">The actual array value</param>
  251. /// <param name="index">The index at which a difference was found</param>
  252. public void DisplayArrayDifferences( Array expected, Array actual, int index )
  253. {
  254. if( expected.Length != actual.Length )
  255. WriteLine( diffArrayLengthsFmt, expected.Length, actual.Length );
  256. else
  257. WriteLine( sameArrayLengthsFmt, expected.Length );
  258. WriteLine( arraysDifferAtIndexFmt, index );
  259. if ( index < expected.Length && index < actual.Length )
  260. {
  261. DisplayDifferences( GetValueFromCollection(expected, index ), GetValueFromCollection(actual, index), false );
  262. }
  263. else if( expected.Length < actual.Length )
  264. DisplayListElements( " extra:", actual, index, 3 );
  265. else
  266. DisplayListElements( " missing:", expected, index, 3 );
  267. }
  268. /// <summary>
  269. /// Display a standard message showing the differences found between
  270. /// two collections that were expected to be equal.
  271. /// </summary>
  272. /// <param name="expected">The expected collection value</param>
  273. /// <param name="actual">The actual collection value</param>
  274. /// <param name="index">The index at which a difference was found</param>
  275. // NOTE: This is a temporary method for use until the code from NUnitLite
  276. // is integrated into NUnit.
  277. public void DisplayCollectionDifferences( ICollection expected, ICollection actual, int index )
  278. {
  279. if( expected.Count != actual.Count )
  280. WriteLine( diffArrayLengthsFmt, expected.Count, actual.Count );
  281. else
  282. WriteLine( sameArrayLengthsFmt, expected.Count );
  283. WriteLine( arraysDifferAtIndexFmt, index );
  284. if ( index < expected.Count && index < actual.Count )
  285. {
  286. DisplayDifferences( GetValueFromCollection(expected, index ), GetValueFromCollection(actual, index), false );
  287. }
  288. // else if( expected.Count < actual.Count )
  289. // DisplayListElements( " extra:", actual, index, 3 );
  290. // else
  291. // DisplayListElements( " missing:", expected, index, 3 );
  292. }
  293. private static object GetValueFromCollection(ICollection collection, int index)
  294. {
  295. Array array = collection as Array;
  296. if (array != null && array.Rank > 1)
  297. return array.GetValue(GetArrayIndicesFromCollectionIndex(array, index));
  298. if (collection is IList)
  299. return ((IList)collection)[index];
  300. foreach (object obj in collection)
  301. if (--index < 0)
  302. return obj;
  303. return null;
  304. }
  305. /// <summary>
  306. /// Get an array of indices representing the point in a collection or
  307. /// array corresponding to a single int index into the collection.
  308. /// </summary>
  309. /// <param name="collection">The collection to which the indices apply</param>
  310. /// <param name="index">Index in the collection</param>
  311. /// <returns>Array of indices</returns>
  312. private static int[] GetArrayIndicesFromCollectionIndex(ICollection collection, int index)
  313. {
  314. Array array = collection as Array;
  315. int rank = array == null ? 1 : array.Rank;
  316. int[] result = new int[rank];
  317. for (int r = array.Rank; --r > 0; )
  318. {
  319. int l = array.GetLength(r);
  320. result[r] = index % l;
  321. index /= l;
  322. }
  323. result[0] = index;
  324. return result;
  325. }
  326. /// <summary>
  327. /// Displays elements from a list on a line
  328. /// </summary>
  329. /// <param name="label">Text to prefix the line with</param>
  330. /// <param name="list">The list of items to display</param>
  331. /// <param name="index">The index in the list of the first element to display</param>
  332. /// <param name="max">The maximum number of elements to display</param>
  333. public void DisplayListElements( string label, IList list, int index, int max )
  334. {
  335. Write( "{0}<", label );
  336. if ( list == null )
  337. Write( "null" );
  338. else if ( list.Count == 0 )
  339. Write( "empty" );
  340. else
  341. {
  342. for( int i = 0; i < max && index < list.Count; i++ )
  343. {
  344. Write( FormatObjectForDisplay( list[index++] ) );
  345. if ( index < list.Count )
  346. Write( "," );
  347. }
  348. if ( index < list.Count )
  349. Write( "..." );
  350. }
  351. WriteLine( ">" );
  352. }
  353. #region Static Methods
  354. /// <summary>
  355. /// Formats an object for display in a message line
  356. /// </summary>
  357. /// <param name="obj">The object to be displayed</param>
  358. /// <returns></returns>
  359. static public string FormatObjectForDisplay( object obj )
  360. {
  361. if ( obj == null )
  362. return "<(null)>";
  363. else if ( obj is string )
  364. return string.Format( "<\"{0}\">", obj );
  365. else if ( obj is double )
  366. return string.Format( "<{0}>", ((double)obj).ToString( "G17" ) );
  367. else if ( obj is float )
  368. return string.Format( "<{0}>", ((float)obj).ToString( "G9" ) );
  369. else
  370. return string.Format( "<{0}>", obj );
  371. }
  372. /// <summary>
  373. /// Tests two objects to determine if they are strings.
  374. /// </summary>
  375. /// <param name="expected"></param>
  376. /// <param name="actual"></param>
  377. /// <returns></returns>
  378. static protected bool InputsAreStrings( Object expected, Object actual )
  379. {
  380. return expected != null && actual != null &&
  381. expected is string && actual is string;
  382. }
  383. /// <summary>
  384. /// Renders up to M characters before, and up to N characters after
  385. /// the specified index position. If leading or trailing text is
  386. /// clipped, and elipses "..." is added where the missing text would
  387. /// be.
  388. ///
  389. /// Clips strings to limit previous or post newline characters,
  390. /// since these mess up the comparison
  391. /// </summary>
  392. /// <param name="sString"></param>
  393. /// <param name="iPosition"></param>
  394. /// <returns></returns>
  395. static protected string ClipAroundPosition( string sString, int iPosition )
  396. {
  397. if( sString == null || sString.Length == 0 )
  398. return "";
  399. bool preClip = iPosition > PreClipLength;
  400. bool postClip = iPosition + PostClipLength < sString.Length;
  401. int start = preClip
  402. ? iPosition - PreClipLength : 0;
  403. int length = postClip
  404. ? iPosition + PostClipLength - start : sString.Length - start;
  405. if ( start + length > iPosition + PostClipLength )
  406. length = iPosition + PostClipLength - start;
  407. StringBuilder sb = new StringBuilder();
  408. if ( preClip ) sb.Append("...");
  409. sb.Append( sString.Substring( start, length ) );
  410. if ( postClip ) sb.Append("...");
  411. return sb.ToString();
  412. }
  413. /// <summary>
  414. /// Shows the position two strings start to differ. Comparison
  415. /// starts at the start index.
  416. /// </summary>
  417. /// <param name="sExpected"></param>
  418. /// <param name="sActual"></param>
  419. /// <param name="iStart"></param>
  420. /// <returns>-1 if no mismatch found, or the index where mismatch found</returns>
  421. static private int FindMismatchPosition( string sExpected, string sActual, int iStart )
  422. {
  423. int iLength = Math.Min( sExpected.Length, sActual.Length );
  424. for( int i=iStart; i<iLength; i++ )
  425. {
  426. //
  427. // If they mismatch at a specified position, report the
  428. // difference.
  429. //
  430. if( sExpected[i] != sActual[i] )
  431. {
  432. return i;
  433. }
  434. }
  435. //
  436. // Strings have same content up to the length of the shorter string.
  437. // Mismatch occurs because string lengths are different, so show
  438. // that they start differing where the shortest string ends
  439. //
  440. if( sExpected.Length != sActual.Length )
  441. {
  442. return iLength;
  443. }
  444. //
  445. // Same strings
  446. //
  447. Assert.IsTrue( sExpected.Equals( sActual ) );
  448. return -1;
  449. }
  450. /// <summary>
  451. /// Turns CR, LF, or TAB into visual indicator to preserve visual marker
  452. /// position. This is done by replacing the '\r' into '\\' and 'r'
  453. /// characters, and the '\n' into '\\' and 'n' characters, and '\t' into
  454. /// '\\' and 't' characters.
  455. ///
  456. /// Thus the single character becomes two characters for display.
  457. /// </summary>
  458. /// <param name="sInput"></param>
  459. /// <returns></returns>
  460. static protected string ConvertWhitespace( string sInput )
  461. {
  462. if( null != sInput )
  463. {
  464. sInput = sInput.Replace( "\\", "\\\\" );
  465. sInput = sInput.Replace( "\r", "\\r" );
  466. sInput = sInput.Replace( "\n", "\\n" );
  467. sInput = sInput.Replace( "\t", "\\t" );
  468. }
  469. return sInput;
  470. }
  471. #endregion
  472. }
  473. }