2 // Rafael Mizrahi <rafim@mainsoft.com>
3 // Erez Lotan <erezl@mainsoft.com>
4 // Oren Gurfinkel <oreng@mainsoft.com>
7 // Copyright (c) 2004 Mainsoft Co.
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections;
32 using NUnit.Framework;
34 namespace GHTUtils.Base
39 /// <summary>Constructor
40 /// <param name="Logger">Custom TextWriter to log to</param>
41 /// <param name="LogOnSuccess">False to log only failed TestCases, True to log all</param>
43 protected GHTBase(TextWriter Logger, bool LogOnSuccess)
45 this._logger = Logger;
46 this._logOnSuccess = LogOnSuccess;
47 this._testName = this.GetType().Name;
50 /// <summary>Constructor, log to Console
51 /// <param name="LogOnSuccess">False to log only failed TestCases, True to log all</param>
53 protected GHTBase(bool LogOnSuccess):this(Console.Out, LogOnSuccess){}
55 /// <summary>Constructor, log to Console only when Failed
57 protected GHTBase():this(Console.Out, false){}
60 #region protected methods
62 public void GHTSetLogger(TextWriter Logger)
64 this._logger = Logger;
66 /// <summary>Begin Test which containes TestCases
67 /// <param name="testName">Test name, used on logs</param>
69 public virtual void BeginTest(string testName)
72 this._testName = testName;
73 //reset the Failure Counter and the TestCase Number
74 UniqueId.ResetCounters();
76 if(this._logOnSuccess == true)
77 Log(string.Format("*** Starting Test: [{0}] ***", this._testName));
80 /// <summary>Begin TestCase
81 /// <param name="Description">TestCase Description, used on logs</param>
83 public void BeginCase(string Description)
85 //init the new TestCase with Unique TestCase Number and Description
86 _testCase = new UniqueId(Description);
88 if(this._logOnSuccess == true)
89 Log(string.Format("Starting Case: [{0}]", _testCase.ToString()));
93 /// <summary>Compare two objects (using Object.Equals)
95 protected bool Compare(object a, object b)
97 //signal that the Compare method has been called
98 if (_testCase == null) {
99 _testCase = new UniqueId(_testName);
101 this._testCase.CompareInvoked = true;
102 //a string that holds the description of the objects for log
105 //check if one of the objects is null
106 if (a == null && b != null)
108 ObjectData = "Object a = null" + ", Object b.ToString() = '" + b.ToString() + "'(" + b.GetType().FullName + ")";
109 this._testCase.Success = false; //objects are different, TestCase Failed
110 LogCompareResult(ObjectData);
111 return this._testCase.Success;
114 //check if the other object is null
115 if (a != null && b == null)
117 ObjectData = "Object a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + "), Object b = null";
118 this._testCase.Success = false; //objects are different, TestCase Failed
119 LogCompareResult(ObjectData);
120 return this._testCase.Success;
123 //check if both objects are null
124 if ( (a == null && b == null) )
126 ObjectData = "Object a = null, Object b = null";
127 this._testCase.Success = true; //both objects are null, TestCase Succeed
128 LogCompareResult(ObjectData);
129 return this._testCase.Success;
132 ObjectData = "Object a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + "), Object b.ToString = '" + b.ToString() + "'(" + b.GetType().FullName + ")";
133 //use Object.Equals to compare the objects
134 this._testCase.Success = (a.Equals(b));
135 LogCompareResult(ObjectData);
136 return this._testCase.Success;
139 /// <summary>Compare two Object Arrays.
140 /// <param name="a">First array.</param>
141 /// <param name="b">Second array.</param>
142 /// <param name="Sorted">Used to indicate if both arrays are sorted.</param>
144 protected bool Compare(Array a, Array b)
146 //signal that the Compare method has been called
147 this._testCase.CompareInvoked=true;
148 //a string that holds the description of the objects for log
151 //check if both objects are null
152 if ( (a == null && b == null) )
154 ObjectData = "Array a = null, Array b = null";
155 this._testCase.Success = true; //both objects are null, TestCase Succeed
156 LogCompareResult(ObjectData);
157 return this._testCase.Success;
160 //Check if one of the objects is null.
161 //(If both were null, we wouldn't have reached here).
162 if (a == null || b == null)
164 string aData = (a==null) ? "null" : "'" + a.ToString() + "' (" + a.GetType().FullName + ")";
165 string bData = (b==null) ? "null" : "'" +b.ToString() + "' (" + b.GetType().FullName + ")";
166 ObjectData = "Array a = " + aData + ", Array b = " + bData;
167 this._testCase.Success = false; //objects are different, testCase Failed.
168 LogCompareResult(ObjectData);
169 return this._testCase.Success;
172 //check if both arrays are of the same rank.
173 if (a.Rank != b.Rank)
175 this._testCase.Success = false;
176 ObjectData = string.Format("Array a.Rank = {0}, Array b.Rank = {1}", a.Rank, b.Rank);
177 LogCompareResult(ObjectData);
178 return this._testCase.Success;
181 //Do not handle multi dimentional arrays.
184 this._testCase.Success = false;
185 ObjectData = "Multi-dimension array comparison is not supported";
186 LogCompareResult(ObjectData);
187 return this._testCase.Success;
190 //Check if both arrays are of the same length.
191 if (a.Length != b.Length)
193 this._testCase.Success = false;
194 ObjectData = string.Format("Array a.Length = {0}, Array b.Length = {1}", a.Length, b.Length);
195 LogCompareResult(ObjectData);
196 return this._testCase.Success;
199 ObjectData = "Array a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + ") Array b.ToString = '" + b.ToString() + "'(" + b.GetType().FullName + ")";
201 //Compare elements of the Array.
202 int iLength = a.Length;
203 for (int i=0; i<iLength; i++)
205 object aValue = a.GetValue(i);
206 object bValue = b.GetValue(i);
208 if (aValue == null && bValue == null)
213 if (aValue == null || bValue == null || !aValue.Equals(bValue) )
215 string aData = (aValue==null) ? "null" : "'" + aValue.ToString() + "' (" + aValue.GetType().FullName + ")";
216 string bData = (bValue==null) ? "null" : "'" + bValue.ToString() + "' (" + bValue.GetType().FullName + ")";
217 ObjectData = string.Format("Array a[{0}] = {1}, Array b[{0}] = {2}", i, aData, bData);
218 this._testCase.Success = false; //objects are different, testCase Failed.
219 LogCompareResult(ObjectData);
220 return this._testCase.Success;
225 this._testCase.Success = true;
226 LogCompareResult(ObjectData);
227 return this._testCase.Success;
232 /// Intentionally fail a testcase, without calling the compare method.
234 /// <param name="message">The reason for the failure.</param>
235 protected void Fail(string message)
237 this._testCase.CompareInvoked = true;
238 this._testCase.Success = false;
239 string msg = string.Format("TestCase \"{0}\" Failed: [{1}]", _testCase.ToString(), message);
240 if (_failAtTestEnd == null)
246 /// Intentionally cause a testcase to pass, without calling the compare message.
248 /// <param name="message">The reason for passing the test.</param>
249 protected void Pass(string message)
251 this._testCase.CompareInvoked = true;
252 this._testCase.Success = true;
253 if (this._logOnSuccess)
255 Log(string.Format("TestCase \"{0}\" Passed: [{1}]", _testCase.ToString(), message));
260 /// Marks this testcase as success, but logs the reason for skipping regardless of _logOnSuccess value.
262 /// <param name="message">The reason for skipping the test.</param>
263 protected void Skip(string message)
265 this._testCase.CompareInvoked = true;
266 this._testCase.Success = true;
267 Log(string.Format("TestCase \"{0}\" Skipped: [{1}]", _testCase.ToString(), message));
271 /// Intentionally fail a testcase when an expected exception is not thrown.
273 /// <param name="exceptionName">The name of the expected exception type.</param>
274 protected void ExpectedExceptionNotCaught(string exceptionName)
276 this.Fail(string.Format("Expected {0} was not caught.", exceptionName));
280 /// Intentionally cause a testcase to pass, when an expected exception is thrown.
282 /// <param name="ex"></param>
283 protected void ExpectedExceptionCaught(Exception ex)
285 this.Pass(string.Format("Expected {0} was caught.", ex.GetType().FullName));
288 /// <summary>End TestCase
289 /// <param name="ex">Exception object if exception occured during the TestCase, null if not</param>
291 protected void EndCase(Exception ex)
293 //check if BeginCase was called. cannot end an unopen TestCase
294 if(_testCase == null)
296 throw new Exception("BeginCase was not called");
300 // if Exception occured during the test - log the error and faile the TestCase.
303 _testCase.Success=false;
304 if (_failAtTestEnd == null)
306 Log(string.Format("TestCase: \"{0}\" Error: [Failed With Unexpected {1}: \n\t{2}]", _testCase.ToString(), ex.GetType().FullName, ex.Message + "\n" + ex.StackTrace ));
310 //check if Compare was called
311 if (_testCase.CompareInvoked == true)
313 if(this._logOnSuccess == true) Log(string.Format("Finished Case: [{0}] ", _testCase.ToString()));
317 //if compare was not called, log error message
318 //Log(string.Format("TestCase \"{0}\" Warning: [TestCase didn't invoke the Compare mehtod] ", _testCase.ToString()));
321 //Terminate TestCase (set TestCase to null)
327 /// <summary>End Test
328 /// <param name="ex">Exception object if exception occured during the Test, null if not</param>
330 public void EndTest(Exception ex)
334 else if (UniqueId.FailureCounter != 0)
335 Assert.Fail(String.Format("Test {0} failed in {1} scenarios.", this._testName, UniqueId.FailureCounter));
337 if(this._logOnSuccess)
339 Log(string.Format("*** Finished Test: [{0}] ***", this._testName));
344 public int GHTGetExitCode()
346 return UniqueId.FailureCounter;
350 /// <param name="text">string message to log</param>
352 protected void Log(string text)
354 _loggerBuffer = _loggerBuffer + "\n" + "GHTBase:Logger - " + text;
355 _logger.WriteLine("GHTBase:Logger - " + text);
358 //used to log the results from the compare methods
359 private void LogCompareResult(string ObjectData)
361 if(this._testCase.Success == false)
363 string msg = string.Format("TeseCase \"{0}\" Error: [Failed while comparing(" + ObjectData + ")] ", _testCase.ToString() );
364 if (_failAtTestEnd == null)
366 Log("Test: " + _testName + " " + msg);
368 else if(this._logOnSuccess == true)
369 Log(string.Format("TestCase \"{0}\" Passed ", _testCase.ToString()));
373 protected int TestCaseNumber
377 return _testCase.CaseNumber;
383 #region private fields
385 private TextWriter _logger;
386 public string _loggerBuffer; // a public clone string of the _logger (used in web tests)
388 private string _testName;
389 private UniqueId _testCase;
390 private bool _logOnSuccess;
391 private string _failAtTestEnd = Environment.GetEnvironmentVariable("MONOTEST_FailAtTestEnd");
396 //holds all the info on a TestCase
397 internal class UniqueId
399 //holds the unique name of the test case
400 //this name must be recieved from the test case itself
401 //when calling BeginCase.
402 //example: BeginCase("MyName")
403 private string _caseName;
405 //maintains the number generated for this test case
406 private static int _caseNumber;
408 //maintains the number of failed test case
409 private static int _FailureCounter;
410 internal static int FailureCounter
414 return _FailureCounter;
418 //indicate if the Compare method has been invoked AND containes compare objects message (ToString)
419 private bool _CompareInvoked;
420 internal bool CompareInvoked
424 return _CompareInvoked;
428 _CompareInvoked = value;
433 //reset the static counters when a new Test (not TestCase !!) begin
434 internal static void ResetCounters()
440 //signal if a TestCase failed, if failed - increment the _FailureCounter
441 private bool _success;
442 internal bool Success
446 return this._success;
450 this._success = value;
460 //Ctor, Recieve the name for the test case
461 //generate a unique number and apply it to the test case
462 internal UniqueId(string Name)
464 this._caseName = Name;
465 //this._caseNumber = ++UniqueId._counter;
469 internal int CaseNumber
477 public override string ToString()
479 return string.Format("{0} #{1}", this._caseName, _caseNumber);