/**
* Copyright (c) 2009 Digital Primates IT Consulting Group
*
* 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.
*
* @author Michael Labriola
* @version
**/
package org.flexunit.async
{
import flash.events.IEventDispatcher;
import mx.rpc.IResponder;
import org.flexunit.internals.runners.statements.IAsyncHandlingStatement;
/**
* The Async class contains static methods used in the handling of events in asynchronous
* methods in a particular test case. These methods may be called in an asynchronous test method in
* order to exhibit specific behavior once the proper conditions are met.
*
* The asynchronous test methods must be labeled as asynchronous in order
* to successfully use Async's static methods; otherwise, the test will not be registered as
* asynchronous and an AssertionError will be thrown.
*
*
* [Test(async)]
* public function asyncTest():void {
* Async.proceedOnEvent(...);
* }
*
*/
public class Async
{
/**
* This method is used when you want to ensure that a specific event fires during an asynchronous test. When the event fires, the flex unit
* framework simply acknowledges it internally. If there are additional outstanding asynchronous events, those will be processed individually.
*
* This method is generally used when the existance of the event, and not the even't data is sufficient to indicate success. If you need to inspect
* the event's data before making a decision, then use handleEvent instead.
*
* @param testCase The current asynchronous test case.
* @param target The target that will listen for the dispatched eventName.
* @param eventName The name of the event being listend for by the target.
* @param timeout The length of time, in milliseconds, before the calling the timeoutHandler
* if the eventName event is not dispatched.
* @param timeoutHandler The function that will be executed if the target does not
* receive expected eventName before the timeout time is reached.
*/
public static function proceedOnEvent( testCase:Object, target:IEventDispatcher, eventName:String, timeout:int=500, timeoutHandler:Function = null ):void {
var asyncHandlingStatement:IAsyncHandlingStatement = AsyncLocator.getCallableForTest( testCase );
var handler:Function;
handler = asyncHandlingStatement.asyncHandler( asyncHandlingStatement.pendUntilComplete, timeout, null, timeoutHandler );
target.addEventListener( eventName, handler, false, 0, true );
}
/**
* This method is used when you want to fail if a given event occurs, within a given amount of time, during an asynchronous test. When the event fires,
* the flex unit framework causes the test to fail. If the timout is reached before the failure occurs, then the framework will no longer watch for
* this event. So, for example, if you want to verify that you do not receive a failure within 300ms, this would be a good method to use.
*
* This method is generally used when the existance of the event, and not the even't data is sufficient to indicate failure. If you need to inspect
* the event's data before making a decision, then use handleEvent instead.
*
* @param testCase The current asynchronous test case.
* @param target The target that will listen for the dispatched eventName.
* @param eventName The name of the event being listend for by the target.
* @param timeout The length of time, in milliseconds, before the calling the timeoutHandler
* if the eventName event is not dispatched.
* @param timeoutHandler The function that will be executed if the target does not
* receive expected eventName before the timeout time is reached.
*/
public static function failOnEvent( testCase:Object, target:IEventDispatcher, eventName:String, timeout:int=500, timeoutHandler:Function = null ):void {
var asyncHandlingStatement:IAsyncHandlingStatement = AsyncLocator.getCallableForTest( testCase );
var handler:Function;
handler = asyncHandlingStatement.asyncHandler( asyncHandlingStatement.failOnComplete, timeout, null, asyncHandlingStatement.pendUntilComplete );
target.addEventListener( eventName, handler, false, 0, true );
}
/**
* Causes the failure of the existing block (Before, After or the Test itself dependent upon where this statement is located) when an event occurs. In
* practice, this method is used to handle an event dispatched from an object under test that, while not necessarily part of the test itself, would indicate
* a failure if dispatched. A valid example might be an service call. You may want to test that the data is correct and returns within a given period of time,
* however, if at any time during that test a Failure event is dispatched, you likely wish to end the test.
*
* @param testCase The current asynchronous test case.
* @param target The target that will listen for the dispatched eventName.
* @param eventName The name of the event being listend for by the target.
*
* Example:
* [Test(async)]
* public function doTest():void {
* Async.registerFailureEvent( this, httpService, FaultEvent.FAULT );
* Async.handleEvent( this, httpService, ResultEvent.RESULT, handleResult, 2000 );
* httpService.send();
* }
*
* Without the registerFailureEvent, you would need to wait 2 full seconds for the timeout to occur before declaring this test a failure when a fault
* event occurs.
*
*/
public static function registerFailureEvent( testCase:Object, target:IEventDispatcher, eventName:String ):void {
var asyncHandlingStatement:IAsyncHandlingStatement = AsyncLocator.getCallableForTest( testCase );
var handler:Function;
handler = asyncHandlingStatement.asyncErrorConditionHandler( asyncHandlingStatement.failOnComplete );
target.addEventListener( eventName, handler );
//Do not use a weak reference here or there is nothing to keep it in memory
}
/**
* Allow you to continue a test while waiting for a given asynchronous event to occur. Normally a test ends when you reach the method closure at the end
* of your test method. This event tells the FlexUnit framework to continue that test pending the dispatch of an event by the target of an
* event named eventName. If that event does not occur within the timeOut then the timeout handler (if specified) will be called,
* else the test will be declared a failure.
*
* @param testCase The current asynchronous test case.
* @param target The target that will listen for the dispatched eventName.
* @param eventName The name of the event being listend for by the target.
* @param eventHandler The function that will be executed if the the target dispatches an event with
* a name of eventName within the provided timemout period.
* @param timeout The length of time, in milliseconds, before the calling the timeoutHandler
* if the eventName event is not dispatched.
* @param passThroughData An Object that can be given information about the current test, this information will be
* available to both the eventHandler and timeoutHandler.
* @param timeoutHandler The function that will be executed if the target does not
* receive expected eventName before the timeout time is reached.
*/
public static function handleEvent( testCase:Object, target:IEventDispatcher, eventName:String, eventHandler:Function, timeout:int=500, passThroughData:Object = null, timeoutHandler:Function = null ):void {
var asyncHandlingStatement:IAsyncHandlingStatement = AsyncLocator.getCallableForTest( testCase );
var handler:Function;
handler = asyncHandlingStatement.asyncHandler( eventHandler, timeout, passThroughData, timeoutHandler );
target.addEventListener( eventName, handler, false, 0, true );
}
/**
* This method works similarly to the handleEvent, however, whereas the handleEvent does all of the work to handle a specific event,
* this method simply returns an eventHandler (function) which you use within your own addEventListener() methods.
*
* @param testCase The current asynchronous test case.
* @param eventHandler The function that will be executed if the timemout period has not been reached.
* @param timeout The length of time, in milliseconds, before the calling the timeoutHandler
* if the eventName event is not dispatched.
* @param passThroughData An Object that can be given information about the current test, this information will be
* available to both the eventHandler and timeoutHandler.
* @param timeoutHandler The function that will be executed if the timeout period is reached.
*/
public static function asyncHandler( testCase:Object, eventHandler:Function, timeout:int, passThroughData:Object = null, timeoutHandler:Function = null ):Function {
var asyncHandlingStatement:IAsyncHandlingStatement = AsyncLocator.getCallableForTest( testCase );
return asyncHandlingStatement.asyncHandler( eventHandler, timeout, passThroughData, timeoutHandler );
}
// We have a toggle in the compiler arguments so that we can choose whether or not the flex classes should
// be compiled into the FlexUnit swc. For actionscript only projects we do not want to compile the
// flex classes since it will cause errors.
/**
* This method works in a similar fashion to handleEvent, however, it is intended to work with AsyncTokens and Responders as opposed to events.
*
* @param testCase The current asynchronous test case.
* @param responder The responder that will be executed if the timemout period has not been reached.
* @param timeout The length of time, in milliseconds, before the calling the timeoutHandler
* if the eventName event is not dispatched.
* @param passThroughData An Object that can be given information about the current test, this information will be
* available to both the eventHandler and timeoutHandler.
* @param timeoutHandler The function that will be executed if the timeout period is reached.
*/
CONFIG::useFlexClasses
public static function asyncResponder( testCase:Object, responder:*, timeout:int, passThroughData:Object = null, timeoutHandler:Function = null ):IResponder {
var asyncHandlingStatement:IAsyncHandlingStatement = AsyncLocator.getCallableForTest( testCase );
return asyncHandlingStatement.asyncResponder( responder, timeout, passThroughData, timeoutHandler );
}
}
}