http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/UnitTester.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/UnitTester.as 
b/mustella/src/main/flex/UnitTester.as
new file mode 100644
index 0000000..206b211
--- /dev/null
+++ b/mustella/src/main/flex/UnitTester.as
@@ -0,0 +1,2617 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+COMPILE::SWF
+{
+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;
+*/
+
+COMPILE::JS
+{
+import org.apache.flex.events.EventDispatcher;
+import org.apache.flex.geom.Point;
+}
+
+[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.
+       **/
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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().getTime();
+
+                       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;
+                       }
+               }       
+       }
+
+    COMPILE::JS
+    public static function init(root:Object):void
+    {
+        
+    }
+
+       /**
+        *  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
+        */
+    COMPILE::SWF
+       public static var reader:URLLoader;
+
+       /**
+        *  the URL loader for the run id
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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;
+
+
+    COMPILE::SWF
+    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();
+               }
+
+       }
+
+    COMPILE::SWF
+       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;
+
+       }
+
+
+
+    COMPILE::SWF
+    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);
+               }
+
+    }
+
+    COMPILE::SWF
+    private static function readErrorHandler(event:Event):void
+    {
+               trace ("runid.properties ERROR handler with: " + event);
+               run_id_loaded=true;
+    }
+
+
+    COMPILE::SWF
+    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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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)
+                               {
+                               }
+                       }
+               }
+       }
+    
+    COMPILE::JS
+    public static function setMouseXY(stagePt:Point):void
+    {
+        
+    }
+
+       /**
+        *      ask other sandboxes for objects underneath them
+        */
+    COMPILE::SWF
+       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;
+       }
+
+    COMPILE::SWF
+       private static var effectsInEffect:QName = new QName(mx_internal, 
"effectsInEffect");
+    COMPILE::SWF
+       private static var activeTweens:QName = new QName(mx_internal, 
"activeTweens");
+
+       /**
+        *      ask other sandboxes if they are running effects
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       public static function hideSandboxes():void
+       {
+               for (var p:* in swfLoaders)
+               {
+                       var swfLoader:Object = p;
+                       if (swfLoader)
+                       {
+                               swfLoader.removeChildAt(0);
+                       }
+               }
+       }
+
+       /**
+        *      hide all sandboxed SWFLoaders
+        */
+    COMPILE::SWF
+       public static function showSandboxes():void
+       {
+               for (var p:* in swfLoaders)
+               {
+                       var swfLoader:Object = p;
+                       if (swfLoader)
+                       {
+                               swfLoader.addChildAt(swfLoader.contentHolder, 
0);
+                       }
+               }
+       }
+
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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;
+
+    COMPILE::SWF
+       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.
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       private static function scriptsCompleteHandler(event:Event):void
+       {
+               cleanUpAndExit();
+       }
+       
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       private static function windowCompleteHandler(event:Event):void
+       {
+               callback = waitForWindowScripts;
+               event.target.removeEventListener("windowComplete", 
windowCompleteHandler);
+               originalRoot = _root;
+               _root = event.target["systemManager"];
+       }
+
+    COMPILE::SWF
+       public static var exit:Function = function ():void  { fscommand 
("quit"); };
+
+    COMPILE::SWF
+       private static var layoutManager:QName = new QName(mx_internal, 
"layoutManager");
+    COMPILE::SWF
+       private static var getTextField:QName = new QName(mx_internal, 
"getTextField");
+    COMPILE::SWF
+       private static var getTextInput:QName = new QName(mx_internal, 
"getTextInput");
+    COMPILE::SWF
+       private static var getLabel:QName = new QName(mx_internal, "getLabel");
+
+    COMPILE::SWF
+       private static var mouseX:QName = new QName(mx_internal, "_mouseX");
+    COMPILE::SWF
+       private static var mouseY:QName = new QName(mx_internal, "_mouseY");
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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)
+        */
+    COMPILE::SWF
+       public static var _root:DisplayObject;
+    COMPILE::JS
+    public static var _root:Object;
+
+    /**
+     *  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();
+        COMPILE::SWF
+        {
+               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
+       {
+        COMPILE::SWF
+        {
+        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)
+                               {
+                    COMPILE::SWF
+                    {
+                                       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
+        */
+       public var valueChanged:Boolean;
+
+       /**
+        *  Whether or not the value changed
+        */
+       public function resetValue():void
+       {
+               valueChanged = false;
+               _value = null;
+
+       }
+
+       /**
+        *  The set of display objects at the start of the script.
+        *  Used to clean up by ResetComponent
+        */
+    COMPILE::SWF
+       public var knownDisplayObjects:Dictionary = new Dictionary(true);
+
+       /**
+        *  A timer used by TestCases to know when to give up waiting for 
something
+        */
+    COMPILE::SWF
+       private var timer:Timer;
+
+       /**
+        *  Create a timer that can check every second to see
+        *  if we're hung, and then run the test cases
+        */
+    COMPILE::SWF
+       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.
+        */
+    COMPILE::SWF
+       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().getTime();
+                               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
+        */
+    COMPILE::SWF
+       private function runCompleteHandler(event:Event):void
+       {
+
+               var tr:TestResult = currentTest.testResult;
+               if (!tr.hasStatus())
+                       tr.result = TestResult.PASS;
+               tr.endTime = new Date().getTime();
+               TestOutput.logResult (tr.toString());
+               currentIndex++;
+               if (UnitTester.playbackControl == "play")
+                       UnitTester.callback = runMoreTests;
+               else
+                       UnitTester.callback = pauseHandler;
+       }
+
+    COMPILE::SWF
+       private function runMoreTests():void
+       {
+               if (runTests())
+                       testComplete();
+       }
+
+       /**
+        *  called when test script is finished
+        */
+    COMPILE::SWF
+       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
+        */
+    COMPILE::SWF
+       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
+       {
+               
+       }
+    */
+    
+    COMPILE::SWF
+    private static var typeInfoCache:Dictionary;
+    
+    /**
+     * Helper used for object introspection.
+     */
+    COMPILE::SWF
+    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
+        */
+    COMPILE::SWF
+       public static var RTESocket:Socket;
+
+       /**
+        *  Socket used by RTE monitor
+        */
+    COMPILE::SWF
+       public static var RTESocketAddress:String;
+
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/Util.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/Util.as b/mustella/src/main/flex/Util.as
new file mode 100644
index 0000000..2c22f8a
--- /dev/null
+++ b/mustella/src/main/flex/Util.as
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.text.*;
+
+       /**
+        * This is a class to store utility methods.
+        **/
+       public class Util{
+               /**
+                * Rounds to 160, 240, 320 or 480 using Flex's code from 
RuntimeDPIProvider.as.
+                **/
+               public static function roundDeviceDensity( dpi:int ):int{
+                       if ( dpi < 200 )
+                           return 160;
+                       
+                       if (dpi <= 280)
+                           return 240;
+                       
+                       if (dpi <= 400)
+                               return 320;
+                                               
+                       return 480;
+               }
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/VerboseMode.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/VerboseMode.as 
b/mustella/src/main/flex/VerboseMode.as
new file mode 100644
index 0000000..343d692
--- /dev/null
+++ b/mustella/src/main/flex/VerboseMode.as
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.net.*;
+import flash.events.Event;
+
+[Mixin]
+/**
+ *  A "marker" class that causes test scripts to write out
+ *  bitmaps to the urls instead of reading and comparing
+ *  so that baselines/reference-points can be created for
+ *  future comparing.
+ */
+public class VerboseMode
+{
+
+       /**
+        *  Mixin callback that gets everything ready to go.
+        *  The UnitTester waits for an event before starting
+        */
+       public static function init(root:DisplayObject):void
+       {
+               UnitTester.verboseMode = true;
+       }
+
+
+}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/WaitForCondition.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/WaitForCondition.as 
b/mustella/src/main/flex/WaitForCondition.as
new file mode 100755
index 0000000..067d615
--- /dev/null
+++ b/mustella/src/main/flex/WaitForCondition.as
@@ -0,0 +1,150 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.events.EventDispatcher;
+import flash.utils.getTimer;
+
+import mx.core.mx_internal;
+use namespace mx_internal;
+
+/**
+ *  Instead of a property, we use an event so the MXML
+ *  compiler will wrap the code in a function for us
+ */
+[Event(name="condition", type="flash.events.Event")]
+
+/**
+ *  Vector of conditionalValue objects.
+ */
+[DefaultProperty("conditionalValues")]
+
+/**
+ *  WaitForCondition - waits for a condition to become true before continuing.
+ * 
+ *  MXML attributes:
+ *  target (not used)
+ *  condition
+ *  timeout (optional)
+ */
+public class WaitForCondition extends TestStep
+{
+    public var conditionalValues:Vector.<ConditionalValue> = null;
+
+    /**
+     *  The value the method should return
+     */
+    public var value:Boolean;
+    
+    private var dispatcher:EventDispatcher;
+    
+       /**
+        *  Test the value of a property, log result if failure.
+        */
+       override public function execute(root:DisplayObject, 
context:UnitTester, testCase:TestCase, testResult:TestResult):Boolean
+       {
+        super.execute(root, context, testCase, testResult);
+
+        var cv:ConditionalValue = null;
+        
+        context.resetValue();
+        dispatcher = this;
+        
+        // Use MultiResult to determine the proper value (or valueExpression, 
below).
+        if(conditionalValues){
+            cv = new MultiResult().chooseCV(conditionalValues);
+            if(cv){
+                value = cv.value;
+                dispatcher = cv;
+            }
+        }
+
+        if (checkCondition())
+               {
+                       testCase.setExpirationTime(getTimer() + timeout);
+                       return false;
+               } 
+               else 
+               {
+                       testCase.setExpirationTime(0);
+                       stepComplete();
+                       return true;
+               }
+
+       }
+
+       /**
+        *  customize string representation
+        */
+       override public function toString():String
+       {
+               var s:String = "WaitForCondition (condition cannot be shown) ";
+               return s;
+       }
+
+    /**
+     *  Called by the test case if you time out
+     */
+    override public function timeoutCallback():void
+    {
+        testResult.doFail("Timeout waiting for condition: (condition cannot be 
shown) " );
+        stepComplete();
+    }
+
+    /**
+     *  Evaluate the condition.
+     *  
+     *  @return The value of the condition, true or false.
+     */
+    private function checkCondition():Boolean
+    {
+        // Execute the method.
+        try
+        {
+            dispatchEvent(new RunCodeEvent("condition", root["document"], 
context, testCase, testResult));
+        }
+        catch (e:Error)
+        {
+            TestOutput.logResult("Exception thrown executing method.");
+            testResult.doFail (e.getStackTrace());
+            return false;
+        }
+
+        if (!context.valueChanged)
+            TestOutput.logResult("WARNING: value was not set by method.  
'value=' missing from expression?");
+
+        var methodValue:Object = context.value;
+        
+        if (!methodValue)
+        {
+            UnitTester.callback = checkCondition;
+            return true;
+        }
+        else
+        {
+            testCase.setExpirationTime(0);
+            stepComplete();                
+            return false;
+        }
+
+    }
+    
+}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/WaitForEffectsToEnd.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/WaitForEffectsToEnd.as 
b/mustella/src/main/flex/WaitForEffectsToEnd.as
new file mode 100644
index 0000000..6737aea
--- /dev/null
+++ b/mustella/src/main/flex/WaitForEffectsToEnd.as
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.system.ApplicationDomain;
+import flash.utils.getTimer;
+
+import mx.core.mx_internal;
+use namespace mx_internal;
+
+/**
+ *  The test step that sets a property to some value
+ *  MXML attributes:
+ *  timeout (optional)
+ */
+public class WaitForEffectsToEnd extends TestStep
+{
+       private static var effectsInEffect:QName = new QName(mx_internal, 
"effectsInEffect");
+       private static var activeTweens:QName = new QName(mx_internal, 
"activeTweens");
+
+       /**
+        *  @private
+        */
+       override public function execute(root:DisplayObject, 
context:UnitTester, testCase:TestCase, testResult:TestResult):Boolean
+       {
+               super.execute(root, context, testCase, testResult);
+
+               var appDom:ApplicationDomain = 
root["topLevelSystemManager"]["info"]().currentDomain;
+               if (!appDom)
+                       appDom = ApplicationDomain.currentDomain;
+
+               var effects:Boolean = false;
+
+               var effectMgr:Class = 
Class(appDom.getDefinition("mx.effects.EffectManager"));
+               if (effectMgr)
+               {
+                       effects = effectMgr[effectsInEffect]();
+               }
+               if (!effects)
+               {
+                       effectMgr = 
Class(appDom.getDefinition("mx.effects.Tween"));
+                       if (effectMgr)
+                       {
+                               effects = effectMgr[activeTweens].length > 0;
+                       }
+               }
+               if (!effects)
+                       effects = UnitTester.getSandboxedEffects();
+
+               if (effects)
+               {
+                       UnitTester.callback = checkEffects;
+                       testCase.setExpirationTime(getTimer() + timeout);
+               }
+               return !effects;
+       }
+
+       /**
+        *  Set the target's property to the specified value
+        */
+       private function checkEffects():void
+       {
+               var effects:Boolean = false;
+
+               var appDom:ApplicationDomain = 
root["topLevelSystemManager"]["info"]().currentDomain;
+               if (!appDom)
+                       appDom = ApplicationDomain.currentDomain;
+
+               var effectMgr:Class = 
Class(appDom.getDefinition("mx.effects.EffectManager"));
+               if (effectMgr)
+               {
+                       effects = effectMgr[effectsInEffect]();
+               }
+               if (!effects)
+               {
+                       effectMgr = 
Class(appDom.getDefinition("mx.effects.Tween"));
+                       if (effectMgr)
+                       {
+                               effects = effectMgr[activeTweens].length > 0;
+                       }
+               }
+               if (!effects)
+                       effects = UnitTester.getSandboxedEffects();
+
+               if (effects)
+               {
+                       UnitTester.callback = checkEffects;
+                       testCase.setExpirationTime(getTimer() + timeout);
+               }
+               else
+               {
+                       testCase.setExpirationTime(0);
+                       stepComplete();
+               }
+
+       }
+
+       /**
+        *  customize string representation
+        */
+       override public function toString():String
+       {
+               var s:String = "WaitForEffectsToEnd";
+               return s;
+       }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/WaitForEvent.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/WaitForEvent.as 
b/mustella/src/main/flex/WaitForEvent.as
new file mode 100644
index 0000000..d22602a
--- /dev/null
+++ b/mustella/src/main/flex/WaitForEvent.as
@@ -0,0 +1,211 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.display.IBitmapDrawable;
+import flash.utils.*;
+import flash.net.*;
+import flash.events.*;
+import flash.display.*;
+import flash.geom.Matrix;
+
+/**
+ *  WaitForEvent - waits for an event before continuing
+ *  MXML attributes:
+ *  target
+ *  eventName
+ *  numExpectedEvents
+ *  timeout (optional)
+ */
+public class WaitForEvent extends TestStep
+{
+
+       private var eventListenerListening:Boolean = false;
+
+
+       /**
+        *  Test the value of a property, log result if failure.
+        */
+       override public function execute(root:DisplayObject, 
context:UnitTester, testCase:TestCase, testResult:TestResult):Boolean
+       {
+
+               this.root = root;
+               this.context = context;
+               this.testCase = testCase;
+               this.testResult = testResult;
+
+               waitEvent = eventName;
+               waitTarget = target;
+
+
+               var actualTarget:Object = context.stringToObject(target);
+               if (actualTarget)
+               {
+                       actualTarget.addEventListener(eventName, eventListener);
+                       eventListenerListening = true;
+               } 
+               
+
+               if (numEvents < numExpectedEvents )
+               {
+                       
+                       testCase.setExpirationTime (getTimer() + timeout);
+                       if (!eventListenerListening)
+                       {
+                               actualTarget.addEventListener(eventName, 
eventListener);
+                               eventListenerListening = true;
+                               testCase.cleanupAsserts.push(this);
+                       }
+                       doStep();
+                       waitEvent = eventName;
+                       waitTarget = target;
+                       return false;
+               } 
+               else 
+               {
+                       testCase.setExpirationTime (0);
+                       stepComplete();
+                       return true;
+               }
+
+               return super.execute(root, context, testCase, testResult);
+       }
+
+
+       /**
+        *  Test the value of a property, log result if failure.
+        */
+       override protected function doStep():void
+       {
+               var actualTarget:Object = context.stringToObject(target);
+               if (!actualTarget)
+               {
+                       testResult.doFail("Target " + target + " not found");
+                       return;
+               }
+
+       }
+
+       public function doTimeout ():void 
+       {
+               testResult.doFail("Timeout waiting for " + waitEvent + " on " + 
target);
+
+       }
+
+       /**
+        *  The object to set a property on
+        */
+       public var target:String;
+
+
+       /**
+        *  The name of the event to watch for
+        */
+       public var eventName:String;
+
+       /**
+        *  The class of the event, e.g. mx.events.DataGridEvent
+        */
+       public var eventClass:String;
+
+       /**
+        *  Storage for numEvents
+        */
+       protected var numEvents:int = 0;
+
+       /**
+        *  Number of expected events (must be > 0), use AssertNoEvent for 0.
+        *  Set to -1 if you want to see at least one event and don't care if 
there's more.
+        */
+       public var numExpectedEvents:int = 1;
+
+       /**
+        *  The event object
+        */
+       private var lastEvent:Event;
+
+       /**
+        *      The event listener
+        */
+       protected function eventListener(event:Event):void
+       {
+               testCase.setExpirationTime(0);
+
+               lastEvent = event;
+               numEvents++;
+
+               waitEventHandler (event);
+
+       }
+
+       /**
+        *  Test the value of a property, log result if failure.
+        */
+       public function cleanup():void
+       {
+               var actualTarget:Object = context.stringToObject(target);
+               if (actualTarget)       // might be null if object was killed
+                       actualTarget.removeEventListener(eventName, 
eventListener);
+       }
+
+       /**
+        *  customize string representation
+        */
+       override public function toString():String
+       {
+               var s:String = "WaitForEvent";
+               if (target)
+                       s += ": target = " + target;
+               if (eventName)
+                       s += ", eventName = " + eventName;
+               return s;
+       }
+
+       /**
+        *  The method that gets called back when the event we're waiting on 
fires
+        */
+       override protected function waitEventHandler(event:Event):void
+       {
+
+               // we can rely on eventListener to update lastEvent and 
numEvents
+
+               // keep waiting if there aren't enough events
+               if (numExpectedEvents != -1 && numEvents < numExpectedEvents)
+               {
+                       return;
+               }
+
+               if (numExpectedEvents == numEvents) 
+               {
+                       cleanup();
+                       testCase.setExpirationTime (0);
+                       stepComplete();
+                       return;
+
+               }
+
+               waitEvent = eventName;
+               waitTarget = target;
+               super.waitEventHandler(event);
+
+       }
+}
+
+}

Reply via email to