1  /**
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.core.BasicClass;
    18  import org.as2lib.data.holder.array.ArrayIterator;
    19  import org.as2lib.data.holder.array.TypedArray;
    20  import org.as2lib.data.holder.Iterator;
    21  import org.as2lib.test.unit.TestResult;
    22  import org.as2lib.test.unit.TestCase;
    23  import org.as2lib.test.unit.TestCaseMethodInfo;
    24  import org.as2lib.env.reflect.ClassInfo;
    25  import org.as2lib.env.reflect.MethodInfo;
    26  import org.as2lib.util.StringUtil;
    27  import org.as2lib.data.type.Time;
    28  
    29  /**
    30   * {@code TestCaseResult} contains all informations about the execution of a {@code TestCase}.
    31   * 
    32   * <p>{@link TestCaseRunner} contains all states of execution of the {@code TestCase}
    33   * and {@code TestCaseResult} contains all informations about the execution.
    34   * 
    35   * @author Martin Heidegger.
    36   * @version 1.0
    37   * @see TestCase
    38   * @see TestCaseRunner
    39   */
    40  class org.as2lib.test.unit.TestCaseResult extends BasicClass implements TestResult {
    41  	
    42  	/** Reference to the related testcase. */
    43  	private var testCase:TestCase;
    44  	
    45  	/** All methods contained in the Testcase. */
    46  	private var testCaseMethodInfos:TypedArray;
    47  	
    48  	/** Flag if the TestCase has been finished. */
    49  	private var finished:Boolean;
    50  	
    51  	/** Flag if the TestCase has been started. */
    52  	private var started:Boolean;
    53  	
    54  	/**
    55  	 * Constructs a new {@code TestCaseResult}.
    56  	 * 
    57  	 * @param testCase {@coce TestCase} related to the informations
    58  	 */
    59  	public function TestCaseResult(testCase:TestCase) {
    60  		this.testCase = testCase;
    61  		this.started = false;
    62  		this.finished = false;
    63  	}
    64  	
    65  	/**
    66  	 * Returns all informations in a list about the methods contained within the
    67  	 * {@code TestCase}.
    68  	 *
    69  	 * <p>All methods get wrapped within {@code TestCaseMethodInfo}s. Only methods
    70  	 * that start with "test" are contained within this list.
    71  	 * 
    72  	 * @return list of all methods contained within the related {@code TestCase}
    73  	 */
    74  	public function getMethodInfos(Void):TypedArray {
    75  		// Lacy Initialisation for load balancing. All Methods get evaluated by starting this TestCaseResult
    76  		// But not by starting all Available TestCaseResult, as it wood if this would be directly inside the
    77  		// Constructor.
    78  		if(!testCaseMethodInfos){
    79  			testCaseMethodInfos = fetchTestCaseMethodInfos();
    80  		}
    81  		return testCaseMethodInfos;
    82  	}
    83  	
    84  	/**
    85  	 * Fetches all methods starting with "test" within the {@code TestCase}
    86  	 * 
    87  	 * @return list of all methods contained within the related {@code TestCase}
    88  	 */
    89  	private function fetchTestCaseMethodInfos(Void):TypedArray {
    90  		var result:TypedArray = new TypedArray(TestCaseMethodInfo);
    91  		var methods:Array = ClassInfo.forInstance(testCase).getMethods();
    92  		if(methods) {
    93  			for (var i:Number = methods.length-1; i >= 0; i--) {
    94  				var method:MethodInfo = methods[i];
    95  				if (StringUtil.startsWith(method.getName(), "test")) {
    96  					result.push(new TestCaseMethodInfo(method));
    97  				}
    98  			}
    99  		}
   100  		return result;
   101  	}
   102  	
   103  	/**
   104  	 * Returns the related {@code TestCase}.
   105  	 * 
   106  	 * @return instance of the related {@code TestCase}
   107  	 */
   108  	public function getTestCase(Void):TestCase {
   109  		return testCase;
   110  	}
   111  	
   112  	/**
   113  	 * Returns the class name of the related {@code TestCase}.
   114  	 * 
   115  	 * @return class name of the related TestCase.
   116  	 */
   117  	public function getName(Void):String {
   118  		return ClassInfo.forInstance(getTestCase()).getFullName();
   119  	}
   120  	
   121  	/**
   122  	 * Implementation of @see TestResult#getTestResults.
   123  	 * 
   124  	 * @return This TestCaseResult in a new list for Results.
   125  	 */
   126  	public function getTestResults(Void):TypedArray {
   127  		var result:TypedArray = new TypedArray(TestResult);
   128  		result.push(this);
   129  		return result;
   130  	}
   131  	
   132  	/**
   133  	 * Returns all result to the TestCase results.
   134  	 * Implementation of @see TestResult#getTestCaseResults.
   135  	 * 
   136  	 * @return The Testcase in a list of TestCaseResults.
   137  	 */
   138  	public function getTestCaseResults(Void):TypedArray {
   139  		var result:TypedArray = new TypedArray(TestCaseResult);
   140  		result.push(this);
   141  		return result;
   142  	}
   143  	
   144  	/**
   145  	 * Returns the percentage ({@code 0}-{@code 100}) of the executed methods.
   146  	 * 
   147  	 * @return percentage of execution
   148  	 */
   149  	public function getPercentage(Void):Number {
   150  		var finished:Number = 0;
   151  		
   152  		var a:Array = getMethodInfos();
   153  		var total:Number = a.length;
   154  		var i:Number = a.length;
   155  		
   156  		while(--i-(-1)) {
   157  			if(a[i].hasFinished()) {
   158  				finished ++;
   159  			}
   160  		}
   161  		
   162  		return (100/total*finished);
   163  	}
   164  	
   165  	/**
   166  	 * Returns {@code true} if the {@code TestCase} has been finished.
   167  	 * 
   168  	 * @return {@code true} if the {@code TestCase} has been finished
   169  	 */
   170  	public function hasFinished(Void):Boolean {
   171  		if (finished) return true; // Caching of a true result as performance enhancement.
   172  		var methodIterator:Iterator = new ArrayIterator(getMethodInfos());
   173  		while (methodIterator.hasNext()) {
   174  			if(!methodIterator.next().hasFinished()) {
   175  				return false;
   176  			}
   177  		}
   178  		return (finished=true);
   179  	}
   180  	
   181  	/**
   182  	 * Returns {@code true} if the {@code TestCase} has been started.
   183  	 * 
   184  	 * @return {@code true} if the {@code TestCase} has been started
   185  	 */
   186  	public function hasStarted(Void):Boolean {
   187  		if (started) return true; // Caching of a true result as performance enhancement.
   188  		var methodIterator:Iterator = new ArrayIterator(getMethodInfos());
   189  		while (methodIterator.hasNext()) {
   190  			if (methodIterator.next().hasFinished()) {
   191  				return (started=true);
   192  			}
   193  		}
   194  		return false;
   195  	}
   196  	
   197  	/**
   198  	 * Returns the total operation time for all methods executed for the {@code TestCase}.
   199  	 * 
   200  	 * @return total operation time of the {@code TestCase}
   201  	 */
   202  	public function getOperationTime(Void):Time {
   203  		var result:Number = 0;
   204  		var methodIterator:Iterator = new ArrayIterator(getMethodInfos());
   205  		while (methodIterator.hasNext()) {
   206  			result += methodIterator.next().getStopWatch().getTimeInMilliSeconds();
   207  		}
   208  		return (new Time(result));
   209  	}
   210  	
   211  	/**
   212  	 * Returns {@code true} if the errors occured during the execution of {@code TestCase}.
   213  	 * 
   214  	 * @return {@code true} if the errors occured during the execution of {@code TestCase}.
   215  	 */
   216  	public function hasErrors(Void):Boolean {
   217  		var methodIterator:Iterator = new ArrayIterator(getMethodInfos());
   218  		while (methodIterator.hasNext()) {
   219  			if (methodIterator.next().hasErrors()) {
   220  				return true;
   221  			}
   222  		}
   223  		return false;
   224  	}
   225  	
   226  	/**
   227  	 * Extended .toString implementation.
   228  	 * 
   229  	 * @return {@code TestCaseResult} as well formated {@code String}
   230  	 */
   231  	public function toString():String {
   232  		var result:String;
   233  		var methodResult:String = "";
   234  		var ms:Number = 0;
   235  		var errors:Number = 0;
   236  		var methodInfos:Array = getMethodInfos();
   237  		var iter:Iterator = new ArrayIterator(methodInfos);
   238  		while (iter.hasNext()) {
   239  			var method:TestCaseMethodInfo = iter.next();
   240  			ms += method.getStopWatch().getTimeInMilliSeconds();
   241  			if(method.hasErrors()) {
   242  				errors += method.getErrors().length;
   243  				methodResult += "\n"+StringUtil.addSpaceIndent(method.toString(), 3);
   244  			}
   245  		}
   246  		
   247  		result = getName()+" run "+methodInfos.length+" methods in ["+ms+"ms]. ";
   248  		
   249  		result += (errors>0) ? errors + ((errors > 1) ? " errors" : " error") + " occured" + methodResult : "no error occured";
   250  		
   251  		return result;
   252  	}
   253  }