blob: 37fe2a1458f0ef00eb9b27ad78123b843f09b98d [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package {
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.InteractiveObject;
import flash.display.Stage;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.FocusEvent;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.events.UncaughtErrorEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.Socket;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.Security;
import flash.system.fscommand;
import flash.utils.Dictionary;
import flash.utils.Timer;
import flash.utils.getQualifiedClassName;
import flash.utils.setTimeout;
import mx.binding.Binding;
import mx.binding.BindingManager;
import mx.binding.FunctionReturnWatcher;
import mx.binding.PropertyWatcher;
import mx.binding.StaticPropertyWatcher;
import mx.binding.Watcher;
import mx.binding.XMLWatcher;
import mx.core.IMXMLObject;
import mx.core.mx_internal;
use namespace mx_internal;
[Mixin]
/**
* The test engine for unit testing Flex framework components.
* A UnitTester gets linked in as a mixin and when initialized
* finds a set of tests to run and runs them.
*
* Test scripts are written as MXML components derived from
* this class, and contain a bunch of TestCases with TestStep-
* derived child tags. They must also be [Mixin] and call
* setScript.
*/
public class UnitTester extends EventDispatcher
{
/**
* This holds settings which are considered in ConditionalValue
* work.
**/
public static var cv:ConditionalValue;
/**
* This tells whether to write baselines to disk.
* Set by MobileConfigWriter.
**/
public static var writeBaselinesToDisk:Boolean = false;
/**
* This tells UnitTester where it can write files.
* Set by MobileConfigWriter.
**/
public static var mustellaWriteLocation:String = "";
/**
* This is the name of the exclude file.
* Set by MobileConfigWriter.
**/
public static var excludeFile:String = "";
/**
* The location of the mustella test directory.
*/
public static var mustellaTestDir:String = "";
/**
* Set by either ExcludeFileLocation or ExcludeFileLocationApollo so they
* can be called back to load the exclude file after mustellaTestDir has
* been set in the init() method.
*/
public static var loadExcludeFile:Function;
/**
* This is a placeholder. We don't do portrait and landscape runs right now
* and probablay won't. Delete.
**/
//public static var deviceOrientation:String = null;
/**
* port number that will be used by tests that require a webserver.
*/
public static var portNumber : Number=80;
/**
* waitEvent, if we're waiting for one.
*/
public static var waitEvent : String;
/**
* UIComponentGlobals.
*/
public static var uiComponentGlobals : Object;
/**
* additional wait before exit for coverage
*/
public static var coverageTimeout : Number = 0;
/**
* Last executed step
*/
public static var lastStep:TestStep = null;
/**
* Step # of that last step
*/
public static var lastStepLine:int = -1;
/**
* IGNORE all failures and only report passing.
* This allows creation of multiple .bad.png files in testcases
* where there are many CompareBitmap tags
* DANGEROUS flag, for obvious reasons
*/
public static var noFail:Boolean = false;
/**
* a pixel tolerance multiplier that CompareBitmap will use to judge comparisons
*/
public static var pixelToleranceMultiplier:Number = 1;
/**
* Mixin callback that gets everything ready to go.
* The UnitTester waits for an event before starting
*/
public static function init(root:DisplayObject):void
{
if (waitForExcludes && loadExcludeFile != null)
loadExcludeFile();
// don't let child swfs override this
if (!_root)
_root = root;
/// set device if not set.
if (cv == null){
cv = new ConditionalValue();
}
if (cv.os == null)
{
cv.os = DeviceNames.getFromOS();
}
if (cv.device == null)
{
if (Security.sandboxType == Security.APPLICATION)
cv.device = "air";
}
if(root.loaderInfo != null && root.loaderInfo.parameters != null)
{
for (var ix:String in root.loaderInfo.parameters)
{
if(ix == "port")
{
portNumber = Number(root.loaderInfo.parameters[ix]);
}
else if(ix == "pixelToleranceMultiplier")
{
pixelToleranceMultiplier = Number(root.loaderInfo.parameters[ix]);
}
}
}
// load a run id if not loaded (used in full runs)
if (!run_id_loaded)
{
/// esp. for MP, avoid doing this twice:
run_id_loaded=true;
/// avoid 304 returns from the web server:
var endBit:String = "?" + Math.random() + new Date().time;
reader = new URLLoader();
var req:URLRequest = new URLRequest();
/// by convention, we use the /staging alias for the vetting run workspace
if (isVettingRun)
{
req.url = "http://localhost:" + portNumber + "/staging/runid.properties" + endBit;
}
else
{
req.url = "http://localhost:" + portNumber + "/runid.properties" + endBit;
}
reader.dataFormat=URLLoaderDataFormat.TEXT;
reader.addEventListener(Event.COMPLETE, readCompleteHandler);
reader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, readErrorHandler);
reader.addEventListener(IOErrorEvent.IO_ERROR, readErrorHandler);
reader.load(req);
}
// load a run id if not loaded (used in full runs)
if (!timeout_plus_loaded)
{
timeout_reader = new URLLoader();
var req2:URLRequest = new URLRequest();
/// by convention, we use the /staging alias for the vetting run workspace
req2.url = "http://localhost:" + runnerPort + "/step_timeout";
timeout_reader.dataFormat=URLLoaderDataFormat.TEXT;
timeout_reader.addEventListener(Event.COMPLETE, timeout_readCompleteHandler);
timeout_reader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, timeout_readErrorHandler);
timeout_reader.addEventListener(IOErrorEvent.IO_ERROR, timeout_readErrorHandler);
timeout_reader.load(req2);
}
var mixins:Array = root["info"]()["mixins"];
var appdom:ApplicationDomain = root["info"]().currentDomain;
if (!appdom)
appdom = ApplicationDomain.currentDomain;
for (var i:int = 0; i < mixins.length; i++)
{
var c:Class = Class(appdom.getDefinition(mixins[i]));
var o:Object = new c();
if (o is UnitTester && mixins[i] != "UnitTester")
{
var eventScripts:Array = scripts[o.startEvent];
if (!eventScripts)
{
eventScripts = scripts[o.startEvent] = new Array();
root.addEventListener(o.startEvent, pre_startEventHandler);
}
eventScripts.push(o);
}
}
var passive:Boolean;
try {
passive = !(root.loaderInfo.parentAllowsChild && root.loaderInfo.childAllowsParent);
} catch (e:Error)
{
// in single-frame apps, loaderInfo may not be ready
passive = false;
}
// if we're sandboxed and have no scripts, assume we're passive.
if (passive)
{
if (eventScripts == null)
{
sandboxed = true;
sandboxHelper = new UnitTester();
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.STRING_TO_OBJECT, sandboxStringToObjectHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.GET_BITMAP, sandboxGetBitmapHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.GET_EFFECTS, sandboxGetEffectsHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.GET_OBJECTS_UNDER_POINT, sandboxObjectsUnderPointHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.RESET_COMPONENT, sandboxResetComponentHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.MOUSEXY, sandboxMouseXYHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.GET_FOCUS, sandboxGetFocusHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.APP_READY, sandboxAppReadyHandler);
trace("sending mustellaStarted");
root.loaderInfo.sharedEvents.dispatchEvent(new MustellaSandboxEvent(MustellaSandboxEvent.MUSTELLA_STARTED));
root.addEventListener("applicationComplete", applicationCompleteHandler);
root.addEventListener("enterFrame", enterFrameHandler, false, -9999);
return;
}
}
else if(root.parent != root.stage && root.loaderInfo.applicationDomain != root.parent.parent.loaderInfo.applicationDomain)
{
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.GET_EFFECTS, sandboxGetEffectsHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.MOUSEXY, sandboxMouseXYHandler);
root.addEventListener("applicationComplete", applicationCompleteHandler);
root.loaderInfo.sharedEvents.addEventListener(MustellaSandboxEvent.APP_READY, sandboxAppReadyHandler);
}
root.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
/* uncaught exceptions should be grabbd by the global handler, making this
obsolete
try
{
if (RTESocketAddress)
{
RTESocket = new Socket();
RTESocket.connect(RTESocketAddress, 2561);
RTESocket.addEventListener(ProgressEvent.SOCKET_DATA, RTEDefaultHandler, false, -1);
RTESocket.addEventListener(IOErrorEvent.IO_ERROR, RTEIOErrorHandler);
RTESocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, RTEIOErrorHandler);
}
}
catch (e:Error)
{
}
*/
var g:Class = appdom.hasDefinition("mx.core.UIComponentGlobals") ?
Class(appdom.getDefinition("mx.core.UIComponentGlobals")) : null;
if (g)
{
g["catchCallLaterExceptions"] = true;
uiComponentGlobals = g;
}
if (eventScripts != null)
{
try
{
_root.stage.addEventListener("enterFrame", enterFrameHandler, false, -9999);
}
catch (e:Error)
{
_root.addEventListener("enterFrame", enterFrameHandler, false, -9999);
}
_root.addEventListener("focusIn", focusBlockingHandler, true);
_root.addEventListener("focusOut", focusBlockingHandler, true);
_root.addEventListener("deactivate", activateBlockingHandler, true);
_root.addEventListener("activate", activateBlockingHandler, true);
}
if (appdom.hasDefinition("spark.components.supportClasses.RichEditableTextContainerManager"))
{
g = Class(appdom.getDefinition("spark.components.supportClasses.RichEditableTextContainerManager"));
if (g)
{
var q:QName = new QName(mx_internal, "hideCursor");
g[q] = true;
}
}
}
/**
* Repeat variables. Used in leak testing. Set by mixin.
*/
public static var repeat:int = 0;
public static var repeatCount:int = 0;
/**
* A test run id. This is loaded from a web served file. No id by default.
*/
public static var run_id:String = "-1";
/**
* Indicate the run as vetting
*/
public static var isVettingRun:Boolean = false;
/**
* flag to skip reloading run id
*/
public static var run_id_loaded:Boolean = false;
/**
* flag to skip rechecking for timeout extender
*/
public static var timeout_plus_loaded:Boolean = false;
/**
* value to extend each test step timeout
*/
public static var timeout_plus:int = 0;
/**
* the URL loader for the run id
*/
public static var reader:URLLoader;
/**
* the URL loader for the run id
*/
public static var timeout_reader:URLLoader;
/**
* The directory that this test lives in
*/
public var testDir:String;
/**
* Whether or not we've seen the applicationComplete event
*/
public static var applicationComplete:Boolean = false;
/**
* Whether or not we're subordinate to another UnitTester in another sandbox
*/
public static var sandboxed:Boolean = false;
/**
* UnitTester used for sandbox work
*/
public static var sandboxHelper:UnitTester;
/**
* cache of known swfLoaders
*/
public static var swfLoaders:Dictionary = new Dictionary(true);
/**
* note if an RTE has been detected
*/
public static var hasRTE:Boolean = false;
/**
* The RTE trace
*/
public static var RTEMsg:String = "";
/**
* For mini_run, we want to show the RTE
*/
public static var showRTE:Boolean = false;
private static function uncaughtErrorHandler(e:flash.events.UncaughtErrorEvent):void
{
hasRTE = true;
/// Not yet seen
if (e is Error)
{
RTEMsg = format(e.error.getStackTrace());
}
else if (e is ErrorEvent)
{
RTEMsg = format(e.error.getStackTrace());
}
e.stopImmediatePropagation();
// preventDefault will swallow the dialog pop up that shows the RTE
// for mini run, we want to show that; for server runs, just swallow it
if (!showRTE)
{
e.preventDefault();
}
}
private static function format(msg0:String):String
{
var tmp:Array = null;
var ret:String = "";
/// collapse newlines in the messages:
//// TEST ON MAC
var culprit:String = "\n";
var replaceChar:String = "^";
var fileSepPat:RegExp = /\\/g;
var msg:String = msg0.replace (fileSepPat, "/");
if (msg.indexOf (culprit) != -1)
{
tmp = msg.split (culprit);
for (var i:int = 0;i<tmp.length;i++)
{
if (ret != "")
{
ret = ret + replaceChar + tmp[i];
}
else
{
ret = tmp[i];
}
}
}
else
{
return msg;
}
return ret;
}
private static function readCompleteHandler(event:Event):void
{
run_id_loaded=true;
run_id = reader.data;
if (run_id.indexOf ("=") != -1)
{
run_id = run_id.substring (run_id.indexOf ("=")+1);
}
}
private static function readErrorHandler(event:Event):void
{
trace ("runid.properties ERROR handler with: " + event);
run_id_loaded=true;
}
private static function timeout_readCompleteHandler(event:Event):void
{
timeout_plus_loaded=true;
var tmp:String = timeout_reader.data;
if (tmp.indexOf ("=") != -1)
{
timeout_plus = new int(tmp.substring (tmp.indexOf ("=")+1))
} else {
timeout_plus = new int(tmp);
}
TestOutput.logResult("timeout_plus is: " + timeout_plus);
}
private static function timeout_readErrorHandler(event:Event):void
{
timeout_plus_loaded=true;
}
/**
* set mouseXY in other SWFLoaders
*/
public static function getFocus():InteractiveObject
{
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
if (swfLoader)
{
var e:MustellaSandboxEvent = new MustellaSandboxEvent(MustellaSandboxEvent.GET_FOCUS);
swfLoader.contentHolder.contentLoaderInfo.sharedEvents.dispatchEvent(e);
if (e["obj"] != null)
return e["obj"];
}
}
return null;
}
/**
* set mouseXY in other SWFLoaders
*/
public static function setMouseXY(stagePt:Point):void
{
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
if (swfLoader)
{
try
{
if (stagePt)
{
var pt:Point = swfLoader.content.globalToLocal(stagePt);
swfLoader.content[mouseX] = pt.x;
swfLoader.content[mouseY] = pt.y;
}
else
{
swfLoader.content[mouseX] = undefined;
swfLoader.content[mouseY] = undefined;
}
}
catch (se:SecurityError)
{
var e:MustellaSandboxEvent = new MustellaSandboxEvent(MustellaSandboxEvent.MOUSEXY);
e.obj = stagePt;
swfLoader.contentHolder.contentLoaderInfo.sharedEvents.dispatchEvent(e);
}
catch (ee:Error)
{
}
}
}
}
/**
* ask other sandboxes for objects underneath them
*/
public static function getObjectsUnderPoint(target:DisplayObject, pt:Point):Array
{
var arr:Array = [];
var root:DisplayObject = target.root;
if (root != _root && !(root is Stage))
{
try
{
// Walk up as high as you can get
while (!(root.parent is Stage))
{
root = root.parent.root;
}
}
catch (e:Error)
{
// in another sandbox, start from our root.
// probably won't work in an AIR window with
// loaded content.
root = _root;
}
}
else if (root != _root && (root is Stage))
{
// seems to happen in AIR windows
root = target;
try
{
// Walk up as high as you can get
while (!(root.parent is Stage))
{
root = root.parent;
}
}
catch (e:Error)
{
// in another sandbox, start from our root.
// probably won't work in an AIR window with
// loaded content.
root = _root;
}
}
_getObjectsUnderPoint(root, pt, arr);
return arr;
}
private static var effectsInEffect:QName = new QName(mx_internal, "effectsInEffect");
private static var activeTweens:QName = new QName(mx_internal, "activeTweens");
/**
* ask other sandboxes if they are running effects
*/
public static function getSandboxedEffects():Boolean
{
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
if (swfLoader)
{
var e:MustellaSandboxEvent = new MustellaSandboxEvent(MustellaSandboxEvent.GET_EFFECTS);
if ("contentLoaderInfo" in swfLoader.contentHolder)
{
swfLoader.contentHolder.contentLoaderInfo.sharedEvents.dispatchEvent(e);
if (e.obj)
{
return e.obj;
}
}
}
}
return false;
}
/**
* get the trusted appdom from one of the loaded swfs
*/
public static function getApplicationDomain(target:String, actualTarget:Object, className:String):ApplicationDomain
{
var appdom:ApplicationDomain = _root["info"]().currentDomain;
if (!appdom)
appdom = ApplicationDomain.currentDomain;
if (appdom.hasDefinition(className))
{
var c:Class = Class(appdom.getDefinition(className));
if (actualTarget is c)
return appdom;
// try again to handle some cases in existing mustella tests that
// reset to a different class
var cn:String = getQualifiedClassName(actualTarget);
if (appdom.hasDefinition(cn))
{
c = Class(appdom.getDefinition(cn));
if (actualTarget is c)
return appdom;
}
}
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
if (swfLoader)
{
try
{
// if parent is null, then this is a sandboxed app loaded from file::
// and we don't want to return its appdom
if (swfLoader.content.parent)
{
if (swfLoader.content["info"]().currentDomain.hasDefinition(className))
{
c = Class(swfLoader.content["info"]().currentDomain.getDefinition(className));
if (actualTarget is c)
return swfLoader.content["info"]().currentDomain;
}
}
}
catch (se:SecurityError)
{
}
}
}
return null;
}
/**
* reset a component in another sandbox
*/
public static function resetSandboxedComponent(target:String, className:String):void
{
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
var path:String = swfLoaders[p];
if (swfLoader)
{
// find the right one based on path
var c:int = target.indexOf(path);
if (c == 0)
{
// clip off swfloader from path
target = target.substr(path.length + 1, target.length);
var e:MustellaSandboxEvent = new MustellaSandboxEvent(MustellaSandboxEvent.RESET_COMPONENT);
e.string = target;
e.obj = className;
swfLoader.contentHolder.contentLoaderInfo.sharedEvents.dispatchEvent(e);
return;
}
}
}
}
/**
* ask other sandboxes for their images
*/
public static function getSandboxBitmaps():Array
{
var arr:Array = new Array();
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
if (swfLoader)
{
var e:MustellaSandboxEvent = new MustellaSandboxEvent(MustellaSandboxEvent.GET_BITMAP);
e.obj = getSWFLoaderVisibleBounds(swfLoader as DisplayObject);
swfLoader.contentHolder.contentLoaderInfo.sharedEvents.dispatchEvent(e);
if (e.obj is Array && e.obj.length > 0)
{
arr = arr.concat(e.obj as Array);
}
}
}
return arr;
}
/**
* hide all sandboxed SWFLoaders
*/
public static function hideSandboxes():void
{
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
if (swfLoader)
{
swfLoader.removeChildAt(0);
}
}
}
/**
* hide all sandboxed SWFLoaders
*/
public static function showSandboxes():void
{
for (var p:* in swfLoaders)
{
var swfLoader:Object = p;
if (swfLoader)
{
swfLoader.addChildAt(swfLoader.contentHolder, 0);
}
}
}
private static function getSWFLoaderVisibleBounds(obj:DisplayObject):Object
{
var pt:Point = obj.localToGlobal(new Point(0, 0));
var rect:Rectangle = new Rectangle(pt.x, pt.y, obj.width, obj.height);
var p:DisplayObject = obj.parent;
while (rect.width && rect.height)
{
pt = p.localToGlobal(new Point(0, 0));
var prect:Rectangle = new Rectangle(pt.x, pt.y, p.width, p.height);
if ("viewMetrics" in p)
{
var o:Object = p;
o = o.viewMetrics;
prect.x += o.left;
prect.y += o.top;
prect.width -= o.right;
prect.height -= o.bottom;
}
rect = prect.intersection(rect);
p = p.parent;
if (p == _root)
break;
}
return { width: rect.width, height: rect.height };
}
/**
* see if sandbox has focus
*/
private static function applicationCompleteHandler(event:Event):void
{
applicationComplete = true
}
/**
* see if sandbox has focus
*/
private static function sandboxAppReadyHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
if (applicationComplete)
{
event["obj"] = true;
return;
}
}
/**
* see if sandbox has focus
*/
private static function sandboxGetFocusHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
if (_root.stage.focus)
{
event["obj"] = _root.stage.focus;
return;
}
var focus:InteractiveObject = getFocus();
if (focus)
event["obj"] = focus;
}
/**
* reset component in sandbox
*/
private static function sandboxMouseXYHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
var eventObj:Object = event;
var stagePt:Point = Point(eventObj.obj);
if (stagePt)
{
var pt:Point = _root.globalToLocal(stagePt);
_root[mouseX] = pt.x;
_root[mouseY] = pt.y;
}
else
{
_root[mouseX] = undefined;
_root[mouseY] = undefined;
}
}
/**
* reset component in sandbox
*/
private static function sandboxResetComponentHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
var rc:ResetComponent = new ResetComponent();
rc.target = Object(event).string;
rc.className = Object(event).obj;
sandboxHelper.startTests();
rc.execute(_root, sandboxHelper, new TestCase(), new TestResult());
}
/**
* get bitmap as bytearray
*/
private static function sandboxGetBitmapHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
var arr2:Array = [];
var data:Object = {};
var pt:Point = _root.localToGlobal(new Point(0, 0));
data.x = pt.x;
data.y = pt.y;
var bm:BitmapData = new BitmapData(event["obj"].width, event["obj"].height);
try
{
bm.draw(_root, new Matrix(1, 0, 0, 1, 0, 0));
}
catch (se:SecurityError)
{
hideSandboxes();
bm = new BitmapData(event["obj"].width, event["obj"].height);
showSandboxes();
arr2 = getSandboxBitmaps();
}
var arr:Array = [];
data.bits = bm.getPixels(new Rectangle(0, 0, bm.width, bm.height));
data.bits.position = 0;
data.width = bm.width;
data.height = bm.height;
arr.push(data);
event["obj"] = arr.concat(arr2);
}
/**
* get bitmap as bytearray
*/
private static function sandboxGetEffectsHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
var effects:Boolean = false;
var effectMgr:Class = Class(_root["topLevelSystemManager"]["info"]().currentDomain.getDefinition("mx.effects.EffectManager"));
if (effectMgr)
{
effects = effectMgr[effectsInEffect]();
}
if (!effects)
{
effectMgr = Class(_root["topLevelSystemManager"]["info"]().currentDomain.getDefinition("mx.effects.Tween"));
if (effectMgr)
{
effects = effectMgr[activeTweens].length > 0;
}
}
if (!effects)
effects = UnitTester.getSandboxedEffects();
if (effects)
event["obj"] = true;
}
/**
* Handle request from main Mustella UnitTester
*/
private static function sandboxStringToObjectHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
// we got here because someone tried to access .content which is the
// systemManager so we prepend that since stringToObject always starts
// with the root.document
event["obj"] = sandboxHelper.stringToObject(event["string"].length == 0 ? "" : "systemManager." + event["string"]);
}
/**
* Handle request from main Mustella UnitTester
*/
private static function sandboxObjectsUnderPointHandler(event:Event):void
{
// if we sent it, ignore it
if (event is MustellaSandboxEvent)
return;
var pt:Point = Object(event).obj as Point;
var arr:Array = new Array();
_getObjectsUnderPoint(_root, pt, arr);
Object(event).obj = arr;
}
/**
* Player doesn't handle this correctly so we have to do it ourselves
*/
private static function _getObjectsUnderPoint(obj:DisplayObject, pt:Point, arr:Array):void
{
if (!obj.visible)
return;
try
{
if (!obj[$visible])
return;
}
catch (e:Error)
{
}
if (obj.hitTestPoint(pt.x, pt.y, true))
{
arr.push(obj);
if (obj is DisplayObjectContainer)
{
var doc:DisplayObjectContainer = obj as DisplayObjectContainer;
if ("rawChildren" in doc)
{
var rc:Object = doc["rawChildren"];
n = rc.numChildren;
for (i = 0; i < n; i++)
{
_getObjectsUnderPoint(rc.getChildAt(i), pt, arr);
}
}
else
{
if (doc.numChildren)
{
var n:int = doc.numChildren;
for (var i:int = 0; i < n; i++)
{
var child:DisplayObject = doc.getChildAt(i);
if (swfLoaders[doc] && child is flash.display.Loader)
{
// if sandboxed then ask it for its targets
var e:MustellaSandboxEvent = new MustellaSandboxEvent(MustellaSandboxEvent.GET_OBJECTS_UNDER_POINT);
e.obj = pt;
flash.display.Loader(child).contentLoaderInfo.sharedEvents.dispatchEvent(e);
if (e.obj is Array && e.obj.length > 0)
{
// add them and we're done
var objs:Array = e.obj as Array;
while (objs.length)
arr.push(objs.shift());
}
else
_getObjectsUnderPoint(child, pt, arr);
}
else
_getObjectsUnderPoint(child, pt, arr);
}
}
}
}
}
}
/**
* Whether or not to block focus events
*/
public static var blockFocusEvents:Boolean = true;
/**
* Whether to wait for Excludes to load from a file
*/
public static var waitForExcludes:Boolean = false;
/**
* Whether to wait for Includes to load from a file
*/
public static var waitForIncludes:Boolean = false;
/**
* The handler for blocking focus events
*/
private static function focusBlockingHandler(event:FocusEvent):void
{
// yes, there is a chance that you've clicked on the test
// just as it is waiting for a focusIn event or
// deferring focus assignment
// but I think that's the best we can do for now
if (waitEvent == "focusIn" || (uiComponentGlobals && uiComponentGlobals.nextFocusObject != null))
return;
if (blockFocusEvents && event.relatedObject == null)
{
event.stopImmediatePropagation();
// attempt restore focus
if (event.type == "focusOut")
_root.stage.focus = InteractiveObject(event.target);
}
}
/**
* Whether or not to block activation events
*/
public static var blockActivationEvents:Boolean = true;
/**
* The handler for blocking activation events
*/
private static function activateBlockingHandler(event:Event):void
{
if (blockActivationEvents)
{
event.stopImmediatePropagation();
}
}
/**
* A simplified callLater mechanism for running our tests
*/
public static var callback:Function;
/**
* The handler for the enter frame
*/
private static function enterFrameHandler(event:Event):void
{
if (callback != null)
{
var cb:Function = callback;
callback = null;
cb();
}
}
/**
* holder of startEvent occurence
*/
public static var sawStartEvent:Boolean = false;
/**
* holder of the event to pass to the real start
*/
private static var saveEvent:Event = null;
public static function pre_startEventHandler(event:Event):void
{
if ("topLevelSystemManager" in _root)
_root["topLevelSystemManager"].addEventListener("callLaterError", callLaterErrorDefaultHandler, false, -1);
if (event.type == "applicationComplete")
{
sawStartEvent=true;
saveEvent= event;
}
if (sawStartEvent && !waitForExcludes && !waitForIncludes)
{
startEventHandler (saveEvent);
}
}
/**
* The handler for the start event that starts the sequence
* of tests.
*/
public static function startEventHandler(event:Event):void
{
var eventScripts:Array = scripts[event.type];
var actualScripts:Array = [];
var n:int = eventScripts.length;
for (var i:int = 0; i < n; i++)
{
var name:String = eventScripts[i].scriptName;
if (includeList)
{
if (!includeList[name])
{
TestOutput.logResult("Script: " + name + " not in include list but we don't care");
// continue;
}
}
if (excludeList)
{
if (excludeList[name])
{
TestOutput.logResult("Script: " + name + " in exclude list");
continue;
}
}
actualScripts.push(eventScripts[i]);
}
var scriptRunner:ScriptRunner = new ScriptRunner();
scriptRunner.addEventListener("scriptsComplete", scriptsCompleteHandler);
scriptRunner.scripts = actualScripts;
if (isApollo && waitForWindow)
{
callback = waitForWindowFunction;
waitForWindowScripts = scriptRunner.runScripts;
}
else
callback = scriptRunner.runScripts;
}
/**
* The handler for when the script runner finishes
*/
private static function scriptsCompleteHandler(event:Event):void
{
cleanUpAndExit();
}
private static function cleanUpAndExit():void
{
if (pendingOutput > 0)
{
if (lastPendingOutput == 0)
lastPendingOutput = pendingOutput;
if (pendingOutput == lastPendingOutput)
{
if (frameWaitCount < 30) // wait about 30 frames to see if results come back
{
trace("waiting on pending output", pendingOutput);
frameWaitCount++;
callback = cleanUpAndExit;
return;
}
}
else
{
lastPendingOutput = pendingOutput;
frameWaitCount = 0;
callback = cleanUpAndExit;
return;
}
}
var allDone:Boolean = true;
var n:int = scripts.length;
for (var i:int = 0; i < n; i++)
{
if (!scripts[i].isDone())
{
allDone = false;
break;
}
}
if (originalRoot)
_root = originalRoot;
TestOutput.logResult("ScriptComplete: completely done");
try {
_root[mouseX] = undefined;
_root[mouseY] = undefined;
setMouseXY(null);
} catch (e:Error)
{
// not all use cases support this
}
_root.removeEventListener("focusIn", focusBlockingHandler, true);
_root.removeEventListener("focusOut", focusBlockingHandler, true);
_root.removeEventListener("deactivate", activateBlockingHandler, true);
_root.removeEventListener("activate", activateBlockingHandler, true);
/*
try
{
if (RTESocket)
RTESocket.close();
}
catch (e:Error)
{
}
*/
if (exitWhenDone)
{
setTimeout(exit, UnitTester.coverageTimeout);
}
}
public static var pendingOutput:int = 0;
public static var lastPendingOutput:int = 0;
public static var frameWaitCount:int = 0;
private static var frameCounter:int = 0;
/**
* the callback that waits for an air window to be created
*/
private static function waitForWindowFunction():void
{
var window:Object = new UnitTester().stringToObject(waitForWindow);
if (window)
{
window.addEventListener("windowComplete", windowCompleteHandler);
callback = waitForWindowFunction;
frameCounter++;
if (frameCounter > 2) // see code in Window.as enterFrameHandler
{
callback = waitForWindowScripts;
window.removeEventListener("windowComplete", windowCompleteHandler);
originalRoot = _root;
_root = window["systemManager"];
}
}
else
callback = waitForWindowFunction;
}
/**
* the callback that waits for an air window to be ready
*/
private static function windowCompleteHandler(event:Event):void
{
callback = waitForWindowScripts;
event.target.removeEventListener("windowComplete", windowCompleteHandler);
originalRoot = _root;
_root = event.target["systemManager"];
}
public static var exit:Function = function ():void { fscommand ("quit"); };
private static var layoutManager:QName = new QName(mx_internal, "layoutManager");
private static var getTextField:QName = new QName(mx_internal, "getTextField");
private static var getTextInput:QName = new QName(mx_internal, "getTextInput");
private static var getLabel:QName = new QName(mx_internal, "getLabel");
private static var mouseX:QName = new QName(mx_internal, "_mouseX");
private static var mouseY:QName = new QName(mx_internal, "_mouseY");
private static var $visible:QName = new QName(mx_internal, "$visible");
/**
* The list of tests to run by start event
*/
private static var scripts:Array = new Array();
/**
* Whether we're running on Apollo. If true,
* a mixin will set this variable and
* CompareBitmaps will use a static call to Apollo methods
* to resolve baseline URLs
*/
public static var isApollo:Boolean = false;
/**
* If isApollo=true, then if this is set to a dot-path
* we will wait for the expression to become valid
* and wait for a windowComplete event from the
* object before actually running the test
*/
public static var waitForWindow:String;
/**
* function to call to run scripts when window is ready
*/
private static var waitForWindowScripts:Function;
/**
* remember the original root
*/
private static var originalRoot:DisplayObject;
/**
* Whether to check to see if the test is using
* embedded fonts. This is set by mixin and is expensive
* so it should only be used when new tests are created.
* This is checked by CompareBitmap.
*/
public static var checkEmbeddedFonts:Boolean = false;
/**
* Whether to save out the bitmaps or compare them
* Default is false, bitmaps are read in from the
* url and compared to the target.
* Include the CreateBitmapReferences class to
* cause all scripts to write out the target's bitmap
* to the url.
*/
public static var createBitmapReferences:Boolean = false;
/**
* Whether to display additional information during the
* running of a test
*/
public static var verboseMode:Boolean = false;
/**
* Which port to talk to the Runner on
*
*/
public static var runnerPort:int = 9999;
/**
* Whether to close the Standalone player when done
* running the tests
*/
public static var exitWhenDone:Boolean = false;
/**
* Control over the running of a test
*/
public static var playbackControl:String = "play";
/**
* When saving out bitmaps, the server to talk to
* to save them
*/
public static var bitmapServerPrefix:String;
/**
* To upload failed bitmaps, this is the url to talk. Set by SaveBitmapFailure mixin
*/
public static var serverCopy:String;
/**
* To upload failed bitmaps, this is function assembles the url
*/
public static function urlAssemble (type:String, testDir:String, testFile:String, testCase:String, run_id:String):String
{
testDir=encodeURIComponent(testDir);
testFile = encodeURIComponent(testFile);
testCase = encodeURIComponent(testCase);
var back:String = "type=" + type + "&testFile="+ testDir + testFile + "&testCase=" + testCase + "&runid=" + run_id;
return UnitTester.serverCopy + back;
}
/**
* currentTestID - a holder for other guys to know what's current
*/
public static var currentTestID:String;
/**
* currentScript - a holder for other guys to know what's current
*/
public static var currentScript:String;
/**
* the root display object (SystemManager)
*/
public static var _root:DisplayObject;
/**
* the object to use for property lookups in stringToObject
*/
public static var contextFunction:Function;
/**
* the list of tests to run (if not specified, runs all tests)
*/
public static var includeList:Object;
/**
* the list of tests not to run (if not specified, runs all tests)
*/
public static var excludeList:Object;
/**
* constructor
*/
public function UnitTester()
{
super();
scriptName = getQualifiedClassName(this);
if (scriptName.indexOf("::") >= 0)
scriptName = scriptName.substring(scriptName.indexOf("::") + 2);
}
//----------------------------------
// MXML Descriptor
//----------------------------------
/**
* The descriptor of MXML children.
*/
private var _MXMLDescriptor:Array;
public function get MXMLDescriptor():Array
{
return _MXMLDescriptor;
}
public function setMXMLDescriptor(value:Array):void
{
_MXMLDescriptor = value;
}
//----------------------------------
// MXML Properties
//----------------------------------
/**
* The attributes of MXML top tag.
*/
private var _MXMLProperties:Array;
public function get MXMLProperties():Array
{
return _MXMLProperties;
}
public function setMXMLProperties(value:Array):void
{
_MXMLProperties = value;
}
/**
* The name of the script
*/
public var scriptName:String;
/**
* The name of the swf this script test
*/
public var testSWF:String;
/**
* The event to wait for before starting this script
*/
public var startEvent:String = "applicationComplete";
/**
* The list of TestCases
*/
public var testCases:Array;
/**
* The last event object captured in an AssertEvent
*/
public var lastEvent:Event;
/**
* The index into the list of TestCases that we are currently running
*/
private var currentIndex:int = 0;
/**
* overall count of cases excluded, across scripts. Sent with ScriptDone to Runner
* if used.
*/
public static var excludedCount:int = 0;
/**
* The total number of testCases to run
*/
private var numTests:int;
/**
* a shortcut to the application's variables
*/
public function get application():Object
{
return _root["document"];
}
/**
* take an expression, find the object.
* handles mx_internal:propName
* a.b.c
* getChildAt()
*/
public function stringToObject(s:*):Object
{
var context:Object;
if (contextFunction != null)
context = contextFunction();
else
context = _root["document"];
if (s == null || s == "")
return context;
var original:String = s;
try
{
var propName:* = s;
if (s.indexOf("mx_internal:") == 0)
propName = new QName(mx_internal, s.substring(12));
if (s.indexOf("getChildAt(") == 0 && s.indexOf(".") == -1)
{
s = s.substring(11);
s = s.substring(0, s.indexOf(")"));
return context.getChildAt(parseInt(s));
}
if (s.indexOf("getLayoutElementAt(") == 0 && s.indexOf(".") == -1)
{
s = s.substring(19);
s = s.substring(0, s.indexOf(")"));
return context.getLayoutElementAt(parseInt(s));
}
if (s.indexOf("getElementAt(") == 0 && s.indexOf(".") == -1)
{
s = s.substring(13);
s = s.substring(0, s.indexOf(")"));
return context.getElementAt(parseInt(s));
}
if (s.indexOf("script:") == 0)
{
propName = s.substring(7);
return this[propName];
}
return context[propName];
}
catch (e:Error)
{
// maybe it is a class
var dot:int;
var test:Object;
var c:int;
var cc:int = s.indexOf("::");
var gd:int = -1;
var className:String = s;
var obj:Object = context;
if (cc > 0)
{
gd = s.indexOf("getDefinition");
if (gd == -1)
{
dot = s.indexOf(".", cc);
if (dot >= 0)
{
className = s.substring(0, dot);
s = s.substring(dot + 1);
}
else
s = "";
}
}
else
dot = s.indexOf(".");
try
{
if (gd == -1)
{
var appdom:ApplicationDomain = _root["info"]().currentDomain;
if (!appdom)
appdom = ApplicationDomain.currentDomain;
obj = appdom.getDefinition(className);
}
}
catch (e:Error)
{
if (dot == -1)
return null;
}
if (dot == -1 && gd == -1)
return obj;
var q:QName = new QName(mx_internal, "contentHolder");
var list:Array = s.split(".");
if (list[0].indexOf("script:") == 0)
{
obj = this;
list[0] = list[0].substring(7);
}
while (list.length)
{
try
{
s = list.shift();
if (s.indexOf("mx_internal:") == 0)
s = new QName(mx_internal, s.substring(12));
if (s is String && s.indexOf("getChildAt(") == 0)
{
s = s.substring(11);
s = s.substring(0, s.indexOf(")"));
obj = obj.getChildAt(parseInt(s));
}
else if (s is String && s.indexOf("getLayoutElementAt(") == 0)
{
s = s.substring(19);
s = s.substring(0, s.indexOf(")"));
obj = obj.getLayoutElementAt(parseInt(s));
}
else if (s is String && s.indexOf("getElementAt(") == 0)
{
s = s.substring(13);
s = s.substring(0, s.indexOf(")"));
obj = obj.getElementAt(parseInt(s));
}
else if (s is String && s == "getTextField()")
{
obj = obj[getTextField]();
}
else if (s is String && s == "getTextInput()")
{
obj = obj[getTextInput]();
}
else if (s is String && s == "getLabel()")
{
obj = obj[getLabel]();
}
else if (s is String && s == "getTextFormat()")
{
obj = obj.getTextFormat();
}
else if (s is String && s == "getInstance()")
{
obj = obj.getInstance();
}
else if (s is String && s == "info()")
{
obj = obj.info();
}
else if (s is String && s.indexOf("getDefinition(") == 0)
{
s = s.substring(14);
dot = s.indexOf(")");
while (dot == -1)
{
s += "." + list.shift();
dot = s.indexOf(")");
}
s = s.substring(0, dot);
obj = obj.getDefinition(s);
}
else
obj = obj[s];
}
catch (se:SecurityError)
{
try
{
test = obj[q];
}
catch (e:Error)
{
return null;
}
var event:MustellaSandboxEvent = new MustellaSandboxEvent(MustellaSandboxEvent.STRING_TO_OBJECT);
event.string = list.join(".");
if (!swfLoaders[obj])
{
// cache known swfloaders, associate with string path
c = original.lastIndexOf(event.string);
swfLoaders[obj] = original.substr(0, c);
}
test.contentLoaderInfo.sharedEvents.dispatchEvent(event);
return event.obj;
}
catch (e:Error)
{
return null;
}
// hunt for other swfloaders with other application domains
// we shouldn't get here if the object is in another security domain
// unless the object is sandboxed but the loading app is coming from file::
// This also assumes that the test script will access the SWFLoader's contentHolder
// before doing any steps that require the swfLoaders list to be set up properly
try
{
test = obj[q];
if (test is flash.display.Loader)
{
if (!swfLoaders[obj])
{
var path:String = list.join(".");
// cache known swfloaders, associate with string path
c = original.lastIndexOf(path);
swfLoaders[obj] = original.substr(0, c) + "content";
}
}
}
catch (e:Error)
{
}
}
return obj;
}
return null;
}
/**
* storage for value property
*/
private var _value:Object;
/**
* A variable used to hold results from valueExpressions
*/
public function get value():Object
{
return _value;
}
/**
* A variable used to hold results from valueExpressions
*/
public function set value(v:Object):void
{
_value = v;
valueChanged = true;
}
/**
* Whether or not the value changed
*/
mx_internal var valueChanged:Boolean;
/**
* Whether or not the value changed
*/
mx_internal function resetValue():void
{
valueChanged = false;
_value = null;
}
/**
* The set of display objects at the start of the script.
* Used to clean up by ResetComponent
*/
public var knownDisplayObjects:Dictionary = new Dictionary(true);
/**
* A timer used by TestCases to know when to give up waiting for something
*/
private var timer:Timer;
/**
* Create a timer that can check every second to see
* if we're hung, and then run the test cases
*/
public function startTests():void
{
var children:Array = this.MXMLDescriptor;
if (children)
generateMXMLInstances(this, children);
var r:Object = _root;
if ("topLevelSystemManager" in _root)
{
r = r["topLevelSystemManager"];
r = r.rawChildren;
}
var n:int = r.numChildren;
for (var i:int = 0; i < n; i++)
{
knownDisplayObjects[r.getChildAt(i)] = 1;
}
if (!timer)
{
timer = new Timer(1000);
timer.start();
}
// if (RTESocket)
// RTESocket.addEventListener(ProgressEvent.SOCKET_DATA, RTEHandler);
if ("topLevelSystemManager" in _root)
_root["topLevelSystemManager"].addEventListener("callLaterError", callLaterErrorHandler);
if (testCases)
numTests = testCases.length;
TestOutput.logResult("LengthOfTestcases: " + numTests);
if (runTests())
testComplete();
}
/**
* The current test that is running
*/
public function get currentTest():TestCase
{
return testCases[currentIndex];
}
/**
* Run the test cases
* Returns false if we have to wait for the TestCase to complete.
* Returns true if no tests required waiting.
*/
private function runTests():Boolean
{
if (testDir == null || testDir == "" )
{
testDir="";
}
while (currentIndex < numTests)
{
if (hasRTE)
{
break;
}
var testCase:TestCase = testCases[currentIndex];
currentTestID = testCase.testID;
var testName:String = testDir + scriptName + "$" + testCase.testID;
currentScript = scriptName;
if (includeList)
{
if (!includeList[testName])
{
currentIndex++;
continue;
}
}
if (excludeList)
{
if (excludeList[testName])
{
currentIndex++;
excludedCount++;
continue;
}
}
// TestOutput.logResult("TestCase Start: " + testCase.testID);
TestOutput.logResult("TestCase Start: " + testName);
// add listener early. If runTest catches an exception it will call
// runCompleteHandler before returning
testCase.addEventListener("runComplete", runCompleteHandler);
if (testCase.runTest(_root, timer, this))
{
testCase.removeEventListener("runComplete", runCompleteHandler);
var tr:TestResult = currentTest.testResult;
if (!tr.hasStatus())
tr.result = TestResult.PASS;
if (hasRTE)
tr.result = TestResult.FAIL;
tr.endTime = new Date().time;
TestOutput.logResult (tr.toString());
if (hasRTE)
return true;
}
else
{
return false;
}
currentIndex++;
}
return true;
}
/**
* The handler that receives notice from the current TestCase that it
* is done
*/
private function runCompleteHandler(event:Event):void
{
var tr:TestResult = currentTest.testResult;
if (!tr.hasStatus())
tr.result = TestResult.PASS;
tr.endTime = new Date().time;
TestOutput.logResult (tr.toString());
currentIndex++;
if (UnitTester.playbackControl == "play")
UnitTester.callback = runMoreTests;
else
UnitTester.callback = pauseHandler;
}
private function runMoreTests():void
{
if (runTests())
testComplete();
}
/**
* called when test script is finished
*/
private function testComplete():void
{
// if (RTESocket)
// RTESocket.removeEventListener(ProgressEvent.SOCKET_DATA, RTEHandler);
if ("topLevelSystemManager" in _root)
_root["topLevelSystemManager"].removeEventListener("callLaterError", callLaterErrorHandler);
TestOutput.logResult("testComplete");
dispatchEvent(new Event("testComplete"));
}
/**
* Determines which set of steps (setup, body, cleanup) to run next
*/
private function pauseHandler():void
{
if (UnitTester.playbackControl == "step")
{
UnitTester.playbackControl = "pause";
runMoreTests();
}
else if (UnitTester.playbackControl == "play")
runMoreTests();
else
UnitTester.callback = pauseHandler;
}
private function RTEHandler(event:Event):void
{
var s:String = RTESocket.readUTFBytes(RTESocket.bytesAvailable);
TestOutput.logResult("Exception caught by RTE Monitor.");
var tr:TestResult = currentTest.testResult;
tr.doFail (s);
event.stopImmediatePropagation();
}
private static function RTEDefaultHandler(event:Event):void
{
var s:String = RTESocket.readUTFBytes(RTESocket.bytesAvailable);
TestOutput.logResult("Exception caught by RTE Monitor when no tests running.");
TestOutput.logResult(s);
}
private function callLaterErrorHandler(event:Event):void
{
var o:Object = event;
var s:String = o["error"].getStackTrace();
TestOutput.logResult("Exception caught by CallLater Monitor.");
var tr:TestResult = currentTest.testResult;
tr.doFail (s);
event.stopImmediatePropagation();
var appdom:ApplicationDomain = _root["info"]().currentDomain;
if (!appdom)
appdom = ApplicationDomain.currentDomain;
var g:Class = appdom.hasDefinition("mx.core.UIComponentGlobals") ?
Class(appdom.getDefinition("mx.core.UIComponentGlobals")) : null;
if (g)
{
o = g[layoutManager];
while (true)
{
try
{
o.validateNow();
break;
}
catch (e:Error)
{
}
}
}
}
protected function addMXMLChildren(comps:Array):void
{
}
protected function generateMXMLObject(document:Object, data:Array):Object
{
var i:int = 0;
var cls:Class = data[i++];
var comp:Object = new cls();
var m:int;
var j:int;
var name:String;
var simple:*;
var value:Object;
var id:String;
m = data[i++]; // num props
for (j = 0; j < m; j++)
{
name = data[i++];
simple = data[i++];
value = data[i++];
if (simple == null)
value = generateMXMLArray(document, value as Array);
else if (simple == false)
value = generateMXMLObject(document, value as Array);
if (name == "id")
{
document[value] = comp;
id = value as String;
}
else if (name == "_id")
{
document[value] = comp;
id = value as String;
continue; // skip assignment to comp
}
comp[name] = value;
}
if (comp is IMXMLObject)
comp.initialized(document, id);
return comp;
}
public function generateMXMLArray(document:Object, data:Array, recursive:Boolean = true):Array
{
var comps:Array = [];
var n:int = data.length;
var i:int = 0;
while (i < n)
{
var cls:Class = data[i++];
var comp:Object = new cls();
var m:int;
var j:int;
var name:String;
var simple:*;
var value:Object;
var id:String = null;
m = data[i++]; // num props
for (j = 0; j < m; j++)
{
name = data[i++];
simple = data[i++];
value = data[i++];
if (simple == null)
value = generateMXMLArray(document, value as Array, recursive);
else if (simple == false)
value = generateMXMLObject(document, value as Array);
if (name == "id")
id = value as String;
if (name == "document" && !comp.document)
comp.document = document;
else if (name == "_id")
id = value as String; // and don't assign to comp
else
comp[name] = value;
}
m = data[i++]; // num styles
for (j = 0; j < m; j++)
{
name = data[i++];
simple = data[i++];
value = data[i++];
if (simple == null)
value = generateMXMLArray(document, value as Array, recursive);
else if (simple == false)
value = generateMXMLObject(document, value as Array);
//comp.setStyle(name, value);
}
m = data[i++]; // num effects
for (j = 0; j < m; j++)
{
name = data[i++];
simple = data[i++];
value = data[i++];
if (simple == null)
value = generateMXMLArray(document, value as Array, recursive);
else if (simple == false)
value = generateMXMLObject(document, value as Array);
//comp.setStyle(name, value);
}
m = data[i++]; // num events
for (j = 0; j < m; j++)
{
name = data[i++];
value = data[i++];
comp.addEventListener(name, value);
}
var children:Array = data[i++];
if (children)
{
if (recursive)
comp.generateMXMLInstances(document, children, recursive);
else
comp.setMXMLDescriptor(children);
}
if (id)
{
document[id] = comp;
mx.binding.BindingManager.executeBindings(document, id, comp);
}
if (comp is IMXMLObject)
comp.initialized(document, id);
comps.push(comp);
}
return comps;
}
protected function generateMXMLInstances(document:Object, data:Array, recursive:Boolean = true):void
{
var comps:Array = generateMXMLArray(document, data, recursive);
addMXMLChildren(comps);
}
protected function generateMXMLAttributes(data:Array):void
{
var i:int = 0;
var m:int;
var j:int;
var name:String;
var simple:*;
var value:Object;
var id:String = null;
m = data[i++]; // num props
for (j = 0; j < m; j++)
{
name = data[i++];
simple = data[i++];
value = data[i++];
if (simple == null)
value = generateMXMLArray(this, value as Array, false);
else if (simple == false)
value = generateMXMLObject(this, value as Array);
if (name == "id")
id = value as String;
if (name == "_id")
id = value as String; // and don't assign
else
this[name] = value;
}
m = data[i++]; // num styles
for (j = 0; j < m; j++)
{
name = data[i++];
simple = data[i++];
value = data[i++];
if (simple == null)
value = generateMXMLArray(this, value as Array, false);
else if (simple == false)
value = generateMXMLObject(this, value as Array);
// this.setStyle(name, value);
}
m = data[i++]; // num effects
for (j = 0; j < m; j++)
{
name = data[i++];
simple = data[i++];
value = data[i++];
if (simple == null)
value = generateMXMLArray(this, value as Array, false);
else if (simple == false)
value = generateMXMLObject(this, value as Array);
// this.setStyle(name, value);
}
m = data[i++]; // num events
for (j = 0; j < m; j++)
{
name = data[i++];
value = data[i++];
this.addEventListener(name, value as Function);
}
}
mx_internal function setupBindings(bindingData:Array):void
{
var fieldWatcher:Object;
var n:int = bindingData[0];
var bindings:Array = [];
var i:int;
var index:int = 1;
for (i = 0; i < n; i++)
{
var source:Object = bindingData[index++];
var destFunc:Object = bindingData[index++];
var destStr:Object = bindingData[index++];
var binding:Binding = new Binding(this,
(source is Function) ? source as Function : null,
(destFunc is Function) ? destFunc as Function : null,
(destStr is String) ? destStr as String : destStr.join("."),
(source is Function) ? null : (source is String) ? source as String : source.join("."));
bindings.push(binding);
}
var watchers:Object = decodeWatcher(this, bindingData.slice(index), bindings);
this["_bindings"] = bindings;
this["_watchers"] = watchers;
}
private function decodeWatcher(target:Object, bindingData:Array, bindings:Array):Array
{
var watcherMap:Object = {};
var watchers:Array = [];
var n:int = bindingData.length;
var index:int = 0;
var watcherData:Object;
var theBindings:Array;
var bindingIndices:Array;
var bindingIndex:int;
var propertyName:String;
var eventNames:Array;
var eventName:String;
var eventObject:Object;
var getterFunction:Function;
var value:*;
var w:Watcher;
while (index < n)
{
var watcherIndex:int = bindingData[index++];
var type:int = bindingData[index++];
switch (type)
{
case 0:
{
var functionName:String = bindingData[index++];
var paramFunction:Function = bindingData[index++];
value = bindingData[index++];
if (value is String)
eventNames = [ value ];
else
eventNames = value;
eventObject = {};
for each (eventName in eventNames)
eventObject[eventName] = true;
value = bindingData[index++];
if (value is Array)
bindingIndices = value;
else
bindingIndices = [ value ];
theBindings = [];
for each (bindingIndex in bindingIndices)
theBindings.push(bindings[bindingIndex]);
w = new FunctionReturnWatcher(functionName,
this,
paramFunction,
eventObject,
theBindings);
break;
}
case 1:
{
propertyName = bindingData[index++];
value = bindingData[index++];
if (value is String)
eventNames = [ value ];
else
eventNames = value;
eventObject = {};
for each (eventName in eventNames)
eventObject[eventName] = true;
value = bindingData[index++];
if (value is Array)
bindingIndices = value;
else
bindingIndices = [ value ];
theBindings = [];
for each (bindingIndex in bindingIndices)
theBindings.push(bindings[bindingIndex]);
getterFunction = bindingData[index++];
w = new StaticPropertyWatcher(propertyName,
eventObject, theBindings, getterFunction);
break;
}
case 2:
{
propertyName = bindingData[index++];
value = bindingData[index++];
if (value is String)
eventNames = [ value ];
else
eventNames = value;
eventObject = {};
for each (eventName in eventNames)
eventObject[eventName] = true;
value = bindingData[index++];
if (value is Array)
bindingIndices = value;
else
bindingIndices = [ value ];
theBindings = [];
for each (bindingIndex in bindingIndices)
theBindings.push(bindings[bindingIndex]);
getterFunction = bindingData[index++];
w = new PropertyWatcher(propertyName,
eventObject, theBindings, getterFunction);
break;
}
case 3:
{
propertyName = bindingData[index++];
value = bindingData[index++];
if (value is Array)
bindingIndices = value;
else
bindingIndices = [ value ];
theBindings = [];
for each (bindingIndex in bindingIndices)
theBindings.push(bindings[bindingIndex]);
w = new XMLWatcher(propertyName, theBindings);
break;
}
}
watchers.push(w);
w.updateParent(target);
if (target is Watcher)
{
if (w is FunctionReturnWatcher)
FunctionReturnWatcher(w).parentWatcher = Watcher(target);
Watcher(target).addChild(w);
}
var children:Array = bindingData[index++];
if (children != null)
{
children = decodeWatcher(w, children, bindings);
}
}
return watchers;
}
private static function callLaterErrorDefaultHandler(event:Event):void
{
var o:Object = event;
var s:String = o["error"].getStackTrace();
TestOutput.logResult("Exception caught by CallLater Monitor when no tests running.");
TestOutput.logResult(s);
}
private static function RTEIOErrorHandler(event:Event):void
{
}
private static var typeInfoCache:Dictionary;
/**
* Helper used for object introspection.
*/
public function getTypeInfo(object:*):TypeInfo
{
if (UnitTester.typeInfoCache == null)
{
UnitTester.typeInfoCache = new Dictionary();
}
var className:String = flash.utils.getQualifiedClassName(object);
var typeInfo:TypeInfo = UnitTester.typeInfoCache[className];
if (typeInfo == null)
{
typeInfo = new TypeInfo(className);
UnitTester.typeInfoCache[className] = typeInfo;
}
return typeInfo;
}
/**
* Socket used by RTE monitor
*/
public static var RTESocket:Socket;
/**
* Socket used by RTE monitor
*/
public static var RTESocketAddress:String;
}
}