GHTBase.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. // Authors:
  2. // Rafael Mizrahi <[email protected]>
  3. // Erez Lotan <[email protected]>
  4. // Oren Gurfinkel <[email protected]>
  5. // Ofer Borstein
  6. //
  7. // Copyright (c) 2004 Mainsoft Co.
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.IO;
  30. using System.Collections;
  31. using NUnit.Framework;
  32. namespace GHTUtils.Base
  33. {
  34. public class GHTBase
  35. {
  36. #region Constructors
  37. /// <summary>Constructor
  38. /// <param name="Logger">Custom TextWriter to log to</param>
  39. /// <param name="LogOnSuccess">False to log only failed TestCases, True to log all</param>
  40. /// </summary>
  41. protected GHTBase(TextWriter Logger, bool LogOnSuccess)
  42. {
  43. this._logger = Logger;
  44. this._logOnSuccess = LogOnSuccess;
  45. this._testName = this.GetType().Name;
  46. }
  47. /// <summary>Constructor, log to Console
  48. /// <param name="LogOnSuccess">False to log only failed TestCases, True to log all</param>
  49. /// </summary>
  50. protected GHTBase(bool LogOnSuccess):this(Console.Out, LogOnSuccess){}
  51. /// <summary>Constructor, log to Console only when Failed
  52. /// </summary>
  53. protected GHTBase():this(Console.Out, false){}
  54. #endregion
  55. #region protected methods
  56. public void GHTSetLogger(TextWriter Logger)
  57. {
  58. this._logger = Logger;
  59. }
  60. /// <summary>Begin Test which containes TestCases
  61. /// <param name="testName">Test name, used on logs</param>
  62. /// </summary>
  63. public virtual void BeginTest(string testName)
  64. {
  65. //set test name
  66. this._testName = testName;
  67. //reset the Failure Counter and the TestCase Number
  68. UniqueId.ResetCounters();
  69. if(this._logOnSuccess == true)
  70. Log(string.Format("*** Starting Test: [{0}] ***", this._testName));
  71. }
  72. /// <summary>Begin TestCase
  73. /// <param name="Description">TestCase Description, used on logs</param>
  74. /// </summary>
  75. public void BeginCase(string Description)
  76. {
  77. //init the new TestCase with Unique TestCase Number and Description
  78. _testCase = new UniqueId(Description);
  79. if(this._logOnSuccess == true)
  80. Log(string.Format("Starting Case: [{0}]", _testCase.ToString()));
  81. }
  82. /// <summary>Compare two objects (using Object.Equals)
  83. /// </summary>
  84. protected bool Compare(object a, object b)
  85. {
  86. //signal that the Compare method has been called
  87. if (_testCase == null) {
  88. _testCase = new UniqueId(_testName);
  89. }
  90. this._testCase.CompareInvoked = true;
  91. //a string that holds the description of the objects for log
  92. string ObjectData;
  93. //check if one of the objects is null
  94. if (a == null && b != null)
  95. {
  96. ObjectData = "Object a = null" + ", Object b.ToString() = '" + b.ToString() + "'(" + b.GetType().FullName + ")";
  97. this._testCase.Success = false; //objects are different, TestCase Failed
  98. LogCompareResult(ObjectData);
  99. return this._testCase.Success;
  100. }
  101. //check if the other object is null
  102. if (a != null && b == null)
  103. {
  104. ObjectData = "Object a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + "), Object b = null";
  105. this._testCase.Success = false; //objects are different, TestCase Failed
  106. LogCompareResult(ObjectData);
  107. return this._testCase.Success;
  108. }
  109. //check if both objects are null
  110. if ( (a == null && b == null) )
  111. {
  112. ObjectData = "Object a = null, Object b = null";
  113. this._testCase.Success = true; //both objects are null, TestCase Succeed
  114. LogCompareResult(ObjectData);
  115. return this._testCase.Success;
  116. }
  117. ObjectData = "Object a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + "), Object b.ToString = '" + b.ToString() + "'(" + b.GetType().FullName + ")";
  118. //use Object.Equals to compare the objects
  119. this._testCase.Success = (a.Equals(b));
  120. LogCompareResult(ObjectData);
  121. return this._testCase.Success;
  122. }
  123. /// <summary>Compare two Object Arrays.
  124. /// <param name="a">First array.</param>
  125. /// <param name="b">Second array.</param>
  126. /// <param name="Sorted">Used to indicate if both arrays are sorted.</param>
  127. /// </summary>
  128. protected bool Compare(Array a, Array b)
  129. {
  130. //signal that the Compare method has been called
  131. this._testCase.CompareInvoked=true;
  132. //a string that holds the description of the objects for log
  133. string ObjectData;
  134. //check if both objects are null
  135. if ( (a == null && b == null) )
  136. {
  137. ObjectData = "Array a = null, Array b = null";
  138. this._testCase.Success = true; //both objects are null, TestCase Succeed
  139. LogCompareResult(ObjectData);
  140. return this._testCase.Success;
  141. }
  142. //Check if one of the objects is null.
  143. //(If both were null, we wouldn't have reached here).
  144. if (a == null || b == null)
  145. {
  146. string aData = (a==null) ? "null" : "'" + a.ToString() + "' (" + a.GetType().FullName + ")";
  147. string bData = (b==null) ? "null" : "'" +b.ToString() + "' (" + b.GetType().FullName + ")";
  148. ObjectData = "Array a = " + aData + ", Array b = " + bData;
  149. this._testCase.Success = false; //objects are different, testCase Failed.
  150. LogCompareResult(ObjectData);
  151. return this._testCase.Success;
  152. }
  153. //check if both arrays are of the same rank.
  154. if (a.Rank != b.Rank)
  155. {
  156. this._testCase.Success = false;
  157. ObjectData = string.Format("Array a.Rank = {0}, Array b.Rank = {1}", a.Rank, b.Rank);
  158. LogCompareResult(ObjectData);
  159. return this._testCase.Success;
  160. }
  161. //Do not handle multi dimentional arrays.
  162. if (a.Rank != 1)
  163. {
  164. this._testCase.Success = false;
  165. ObjectData = "Multi-dimension array comparison is not supported";
  166. LogCompareResult(ObjectData);
  167. return this._testCase.Success;
  168. }
  169. //Check if both arrays are of the same length.
  170. if (a.Length != b.Length)
  171. {
  172. this._testCase.Success = false;
  173. ObjectData = string.Format("Array a.Length = {0}, Array b.Length = {1}", a.Length, b.Length);
  174. LogCompareResult(ObjectData);
  175. return this._testCase.Success;
  176. }
  177. ObjectData = "Array a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + ") Array b.ToString = '" + b.ToString() + "'(" + b.GetType().FullName + ")";
  178. //Compare elements of the Array.
  179. int iLength = a.Length;
  180. for (int i=0; i<iLength; i++)
  181. {
  182. object aValue = a.GetValue(i);
  183. object bValue = b.GetValue(i);
  184. if (aValue == null && bValue == null)
  185. {
  186. continue;
  187. }
  188. if (aValue == null || bValue == null || !aValue.Equals(bValue) )
  189. {
  190. string aData = (aValue==null) ? "null" : "'" + aValue.ToString() + "' (" + aValue.GetType().FullName + ")";
  191. string bData = (bValue==null) ? "null" : "'" + bValue.ToString() + "' (" + bValue.GetType().FullName + ")";
  192. ObjectData = string.Format("Array a[{0}] = {1}, Array b[{0}] = {2}", i, aData, bData);
  193. this._testCase.Success = false; //objects are different, testCase Failed.
  194. LogCompareResult(ObjectData);
  195. return this._testCase.Success;
  196. }
  197. }
  198. this._testCase.Success = true;
  199. LogCompareResult(ObjectData);
  200. return this._testCase.Success;
  201. }
  202. /// <summary>
  203. /// Intentionally fail a testcase, without calling the compare method.
  204. /// </summary>
  205. /// <param name="message">The reason for the failure.</param>
  206. protected void Fail(string message)
  207. {
  208. this._testCase.CompareInvoked = true;
  209. this._testCase.Success = false;
  210. string msg = string.Format("TestCase \"{0}\" Failed: [{1}]", _testCase.ToString(), message);
  211. if (_failAtTestEnd == null)
  212. Assert.Fail(msg);
  213. Log(msg);
  214. }
  215. /// <summary>
  216. /// Intentionally cause a testcase to pass, without calling the compare message.
  217. /// </summary>
  218. /// <param name="message">The reason for passing the test.</param>
  219. protected void Pass(string message)
  220. {
  221. this._testCase.CompareInvoked = true;
  222. this._testCase.Success = true;
  223. if (this._logOnSuccess)
  224. {
  225. Log(string.Format("TestCase \"{0}\" Passed: [{1}]", _testCase.ToString(), message));
  226. }
  227. }
  228. /// <summary>
  229. /// Marks this testcase as success, but logs the reason for skipping regardless of _logOnSuccess value.
  230. /// </summary>
  231. /// <param name="message">The reason for skipping the test.</param>
  232. protected void Skip(string message)
  233. {
  234. this._testCase.CompareInvoked = true;
  235. this._testCase.Success = true;
  236. Log(string.Format("TestCase \"{0}\" Skipped: [{1}]", _testCase.ToString(), message));
  237. }
  238. /// <summary>
  239. /// Intentionally fail a testcase when an expected exception is not thrown.
  240. /// </summary>
  241. /// <param name="exceptionName">The name of the expected exception type.</param>
  242. protected void ExpectedExceptionNotCaught(string exceptionName)
  243. {
  244. this.Fail(string.Format("Expected {0} was not caught.", exceptionName));
  245. }
  246. /// <summary>
  247. /// Intentionally cause a testcase to pass, when an expected exception is thrown.
  248. /// </summary>
  249. /// <param name="ex"></param>
  250. protected void ExpectedExceptionCaught(Exception ex)
  251. {
  252. this.Pass(string.Format("Expected {0} was caught.", ex.GetType().FullName));
  253. }
  254. /// <summary>End TestCase
  255. /// <param name="ex">Exception object if exception occured during the TestCase, null if not</param>
  256. /// </summary>
  257. protected void EndCase(Exception ex)
  258. {
  259. //check if BeginCase was called. cannot end an unopen TestCase
  260. if(_testCase == null)
  261. {
  262. throw new Exception("BeginCase was not called");
  263. }
  264. else
  265. {
  266. // if Exception occured during the test - log the error and faile the TestCase.
  267. if(ex != null)
  268. {
  269. _testCase.Success=false;
  270. if (_failAtTestEnd == null)
  271. throw ex;
  272. Log(string.Format("TestCase: \"{0}\" Error: [Failed With Unexpected {1}: \n\t{2}]", _testCase.ToString(), ex.GetType().FullName, ex.Message + "\n" + ex.StackTrace ));
  273. }
  274. else
  275. {
  276. //check if Compare was called
  277. if (_testCase.CompareInvoked == true)
  278. {
  279. if(this._logOnSuccess == true) Log(string.Format("Finished Case: [{0}] ", _testCase.ToString()));
  280. }
  281. else
  282. {
  283. //if compare was not called, log error message
  284. //Log(string.Format("TestCase \"{0}\" Warning: [TestCase didn't invoke the Compare mehtod] ", _testCase.ToString()));
  285. }
  286. }
  287. //Terminate TestCase (set TestCase to null)
  288. _testCase = null;
  289. }
  290. }
  291. /// <summary>End Test
  292. /// <param name="ex">Exception object if exception occured during the Test, null if not</param>
  293. /// </summary>
  294. public void EndTest(Exception ex)
  295. {
  296. if (ex != null)
  297. throw ex;
  298. else if (UniqueId.FailureCounter != 0)
  299. Assert.Fail(String.Format("Test {0} failed in {1} scenarios.", this._testName, UniqueId.FailureCounter));
  300. if(this._logOnSuccess)
  301. {
  302. Log(string.Format("*** Finished Test: [{0}] ***", this._testName));
  303. }
  304. }
  305. public int GHTGetExitCode()
  306. {
  307. return UniqueId.FailureCounter;
  308. }
  309. /// <summary>logger
  310. /// <param name="text">string message to log</param>
  311. /// </summary>
  312. protected void Log(string text)
  313. {
  314. _loggerBuffer = _loggerBuffer + "\n" + "GHTBase:Logger - " + text;
  315. _logger.WriteLine("GHTBase:Logger - " + text);
  316. }
  317. //used to log the results from the compare methods
  318. private void LogCompareResult(string ObjectData)
  319. {
  320. if(this._testCase.Success == false)
  321. {
  322. string msg = string.Format("TeseCase \"{0}\" Error: [Failed while comparing(" + ObjectData + ")] ", _testCase.ToString() );
  323. if (_failAtTestEnd == null)
  324. Assert.Fail(msg);
  325. Log("Test: " + _testName + " " + msg);
  326. }
  327. else if(this._logOnSuccess == true)
  328. Log(string.Format("TestCase \"{0}\" Passed ", _testCase.ToString()));
  329. }
  330. protected int TestCaseNumber
  331. {
  332. get
  333. {
  334. return _testCase.CaseNumber;
  335. }
  336. }
  337. #endregion
  338. #region private fields
  339. private TextWriter _logger;
  340. public string _loggerBuffer; // a public clone string of the _logger (used in web tests)
  341. private string _testName;
  342. private UniqueId _testCase;
  343. private bool _logOnSuccess;
  344. private string _failAtTestEnd = Environment.GetEnvironmentVariable("MONOTEST_FailAtTestEnd");
  345. #endregion
  346. }
  347. //holds all the info on a TestCase
  348. internal class UniqueId
  349. {
  350. //holds the unique name of the test case
  351. //this name must be recieved from the test case itself
  352. //when calling BeginCase.
  353. //example: BeginCase("MyName")
  354. private string _caseName;
  355. //maintains the number generated for this test case
  356. private static int _caseNumber;
  357. //maintains the number of failed test case
  358. private static int _FailureCounter;
  359. internal static int FailureCounter
  360. {
  361. get
  362. {
  363. return _FailureCounter;
  364. }
  365. }
  366. //indicate if the Compare method has been invoked AND containes compare objects message (ToString)
  367. private bool _CompareInvoked;
  368. internal bool CompareInvoked
  369. {
  370. get
  371. {
  372. return _CompareInvoked;
  373. }
  374. set
  375. {
  376. _CompareInvoked = value;
  377. }
  378. }
  379. //reset the static counters when a new Test (not TestCase !!) begin
  380. internal static void ResetCounters()
  381. {
  382. _FailureCounter = 0;
  383. _caseNumber = 0;
  384. }
  385. //signal if a TestCase failed, if failed - increment the _FailureCounter
  386. private bool _success;
  387. internal bool Success
  388. {
  389. get
  390. {
  391. return this._success;
  392. }
  393. set
  394. {
  395. this._success = value;
  396. if (value == false)
  397. {
  398. _FailureCounter++;
  399. }
  400. }
  401. }
  402. //Ctor, Recieve the name for the test case
  403. //generate a unique number and apply it to the test case
  404. internal UniqueId(string Name)
  405. {
  406. this._caseName = Name;
  407. //this._caseNumber = ++UniqueId._counter;
  408. _caseNumber++;
  409. }
  410. internal int CaseNumber
  411. {
  412. get
  413. {
  414. return _caseNumber;
  415. }
  416. }
  417. public override string ToString()
  418. {
  419. return string.Format("{0} #{1}", this._caseName, _caseNumber);
  420. }
  421. }
  422. }