// Authors: // Rafael Mizrahi // Erez Lotan // Oren Gurfinkel // Ofer Borstein // // Copyright (c) 2004 Mainsoft Co. // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.IO; using System.Collections; using NUnit.Framework; namespace GHTUtils.Base { public class GHTBase { #region Constructors /// Constructor /// Custom TextWriter to log to /// False to log only failed TestCases, True to log all /// protected GHTBase(TextWriter Logger, bool LogOnSuccess) { this._logger = Logger; this._logOnSuccess = LogOnSuccess; this._testName = this.GetType().Name; } /// Constructor, log to Console /// False to log only failed TestCases, True to log all /// protected GHTBase(bool LogOnSuccess):this(Console.Out, LogOnSuccess){} /// Constructor, log to Console only when Failed /// protected GHTBase():this(Console.Out, false){} #endregion #region protected methods public void GHTSetLogger(TextWriter Logger) { this._logger = Logger; } /// Begin Test which containes TestCases /// Test name, used on logs /// public virtual void BeginTest(string testName) { //set test name this._testName = testName; //reset the Failure Counter and the TestCase Number UniqueId.ResetCounters(); if(this._logOnSuccess == true) Log(string.Format("*** Starting Test: [{0}] ***", this._testName)); } /// Begin TestCase /// TestCase Description, used on logs /// public void BeginCase(string Description) { //init the new TestCase with Unique TestCase Number and Description _testCase = new UniqueId(Description); if(this._logOnSuccess == true) Log(string.Format("Starting Case: [{0}]", _testCase.ToString())); } /// Compare two objects (using Object.Equals) /// protected bool Compare(object a, object b) { //signal that the Compare method has been called if (_testCase == null) { _testCase = new UniqueId(_testName); } this._testCase.CompareInvoked = true; //a string that holds the description of the objects for log string ObjectData; //check if one of the objects is null if (a == null && b != null) { ObjectData = "Object a = null" + ", Object b.ToString() = '" + b.ToString() + "'(" + b.GetType().FullName + ")"; this._testCase.Success = false; //objects are different, TestCase Failed LogCompareResult(ObjectData); return this._testCase.Success; } //check if the other object is null if (a != null && b == null) { ObjectData = "Object a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + "), Object b = null"; this._testCase.Success = false; //objects are different, TestCase Failed LogCompareResult(ObjectData); return this._testCase.Success; } //check if both objects are null if ( (a == null && b == null) ) { ObjectData = "Object a = null, Object b = null"; this._testCase.Success = true; //both objects are null, TestCase Succeed LogCompareResult(ObjectData); return this._testCase.Success; } ObjectData = "Object a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + "), Object b.ToString = '" + b.ToString() + "'(" + b.GetType().FullName + ")"; //use Object.Equals to compare the objects this._testCase.Success = (a.Equals(b)); LogCompareResult(ObjectData); return this._testCase.Success; } /// Compare two Object Arrays. /// First array. /// Second array. /// Used to indicate if both arrays are sorted. /// protected bool Compare(Array a, Array b) { //signal that the Compare method has been called this._testCase.CompareInvoked=true; //a string that holds the description of the objects for log string ObjectData; //check if both objects are null if ( (a == null && b == null) ) { ObjectData = "Array a = null, Array b = null"; this._testCase.Success = true; //both objects are null, TestCase Succeed LogCompareResult(ObjectData); return this._testCase.Success; } //Check if one of the objects is null. //(If both were null, we wouldn't have reached here). if (a == null || b == null) { string aData = (a==null) ? "null" : "'" + a.ToString() + "' (" + a.GetType().FullName + ")"; string bData = (b==null) ? "null" : "'" +b.ToString() + "' (" + b.GetType().FullName + ")"; ObjectData = "Array a = " + aData + ", Array b = " + bData; this._testCase.Success = false; //objects are different, testCase Failed. LogCompareResult(ObjectData); return this._testCase.Success; } //check if both arrays are of the same rank. if (a.Rank != b.Rank) { this._testCase.Success = false; ObjectData = string.Format("Array a.Rank = {0}, Array b.Rank = {1}", a.Rank, b.Rank); LogCompareResult(ObjectData); return this._testCase.Success; } //Do not handle multi dimentional arrays. if (a.Rank != 1) { this._testCase.Success = false; ObjectData = "Multi-dimension array comparison is not supported"; LogCompareResult(ObjectData); return this._testCase.Success; } //Check if both arrays are of the same length. if (a.Length != b.Length) { this._testCase.Success = false; ObjectData = string.Format("Array a.Length = {0}, Array b.Length = {1}", a.Length, b.Length); LogCompareResult(ObjectData); return this._testCase.Success; } ObjectData = "Array a.ToString() = '" + a.ToString() + "'(" + a.GetType().FullName + ") Array b.ToString = '" + b.ToString() + "'(" + b.GetType().FullName + ")"; //Compare elements of the Array. int iLength = a.Length; for (int i=0; i /// Intentionally fail a testcase, without calling the compare method. /// /// The reason for the failure. protected void Fail(string message) { this._testCase.CompareInvoked = true; this._testCase.Success = false; string msg = string.Format("TestCase \"{0}\" Failed: [{1}]", _testCase.ToString(), message); if (_failAtTestEnd == null) Assert.Fail(msg); Log(msg); } /// /// Intentionally cause a testcase to pass, without calling the compare message. /// /// The reason for passing the test. protected void Pass(string message) { this._testCase.CompareInvoked = true; this._testCase.Success = true; if (this._logOnSuccess) { Log(string.Format("TestCase \"{0}\" Passed: [{1}]", _testCase.ToString(), message)); } } /// /// Marks this testcase as success, but logs the reason for skipping regardless of _logOnSuccess value. /// /// The reason for skipping the test. protected void Skip(string message) { this._testCase.CompareInvoked = true; this._testCase.Success = true; Log(string.Format("TestCase \"{0}\" Skipped: [{1}]", _testCase.ToString(), message)); } /// /// Intentionally fail a testcase when an expected exception is not thrown. /// /// The name of the expected exception type. protected void ExpectedExceptionNotCaught(string exceptionName) { this.Fail(string.Format("Expected {0} was not caught.", exceptionName)); } /// /// Intentionally cause a testcase to pass, when an expected exception is thrown. /// /// protected void ExpectedExceptionCaught(Exception ex) { this.Pass(string.Format("Expected {0} was caught.", ex.GetType().FullName)); } /// End TestCase /// Exception object if exception occured during the TestCase, null if not /// protected void EndCase(Exception ex) { //check if BeginCase was called. cannot end an unopen TestCase if(_testCase == null) { throw new Exception("BeginCase was not called"); } else { // if Exception occured during the test - log the error and faile the TestCase. if(ex != null) { _testCase.Success=false; if (_failAtTestEnd == null) throw ex; Log(string.Format("TestCase: \"{0}\" Error: [Failed With Unexpected {1}: \n\t{2}]", _testCase.ToString(), ex.GetType().FullName, ex.Message + "\n" + ex.StackTrace )); } else { //check if Compare was called if (_testCase.CompareInvoked == true) { if(this._logOnSuccess == true) Log(string.Format("Finished Case: [{0}] ", _testCase.ToString())); } else { //if compare was not called, log error message //Log(string.Format("TestCase \"{0}\" Warning: [TestCase didn't invoke the Compare mehtod] ", _testCase.ToString())); } } //Terminate TestCase (set TestCase to null) _testCase = null; } } /// End Test /// Exception object if exception occured during the Test, null if not /// public void EndTest(Exception ex) { if (ex != null) throw ex; else if (UniqueId.FailureCounter != 0) Assert.Fail(String.Format("Test {0} failed in {1} scenarios.", this._testName, UniqueId.FailureCounter)); if(this._logOnSuccess) { Log(string.Format("*** Finished Test: [{0}] ***", this._testName)); } } public int GHTGetExitCode() { return UniqueId.FailureCounter; } /// logger /// string message to log /// protected void Log(string text) { _loggerBuffer = _loggerBuffer + "\n" + "GHTBase:Logger - " + text; _logger.WriteLine("GHTBase:Logger - " + text); } //used to log the results from the compare methods private void LogCompareResult(string ObjectData) { if(this._testCase.Success == false) { string msg = string.Format("TeseCase \"{0}\" Error: [Failed while comparing(" + ObjectData + ")] ", _testCase.ToString() ); if (_failAtTestEnd == null) Assert.Fail(msg); Log("Test: " + _testName + " " + msg); } else if(this._logOnSuccess == true) Log(string.Format("TestCase \"{0}\" Passed ", _testCase.ToString())); } protected int TestCaseNumber { get { return _testCase.CaseNumber; } } #endregion #region private fields private TextWriter _logger; public string _loggerBuffer; // a public clone string of the _logger (used in web tests) private string _testName; private UniqueId _testCase; private bool _logOnSuccess; private string _failAtTestEnd = Environment.GetEnvironmentVariable("MONOTEST_FailAtTestEnd"); #endregion } //holds all the info on a TestCase internal class UniqueId { //holds the unique name of the test case //this name must be recieved from the test case itself //when calling BeginCase. //example: BeginCase("MyName") private string _caseName; //maintains the number generated for this test case private static int _caseNumber; //maintains the number of failed test case private static int _FailureCounter; internal static int FailureCounter { get { return _FailureCounter; } } //indicate if the Compare method has been invoked AND containes compare objects message (ToString) private bool _CompareInvoked; internal bool CompareInvoked { get { return _CompareInvoked; } set { _CompareInvoked = value; } } //reset the static counters when a new Test (not TestCase !!) begin internal static void ResetCounters() { _FailureCounter = 0; _caseNumber = 0; } //signal if a TestCase failed, if failed - increment the _FailureCounter private bool _success; internal bool Success { get { return this._success; } set { this._success = value; if (value == false) { _FailureCounter++; } } } //Ctor, Recieve the name for the test case //generate a unique number and apply it to the test case internal UniqueId(string Name) { this._caseName = Name; //this._caseNumber = ++UniqueId._counter; _caseNumber++; } internal int CaseNumber { get { return _caseNumber; } } public override string ToString() { return string.Format("{0} #{1}", this._caseName, _caseNumber); } } }