http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/SnifferRemoteClient.mxml
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/SnifferRemoteClient.mxml 
b/mustella/src/main/flex/SnifferRemoteClient.mxml
new file mode 100644
index 0000000..d73ec02
--- /dev/null
+++ b/mustella/src/main/flex/SnifferRemoteClient.mxml
@@ -0,0 +1,514 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:Application 
+            xmlns:fx="http://ns.adobe.com/mxml/2009";
+            xmlns:s="library://ns.adobe.com/flex/spark"
+            xmlns:mx="library://ns.adobe.com/flex/mx"
+            width="100%" height="800"
+            initialize="initApp()">
+
+    <fx:Script>
+    <![CDATA[
+
+        import mx.core.UIComponent;
+        import mx.collections.*;
+        import mx.core.mx_internal;
+        use namespace mx_internal;
+
+
+        [Bindable] private var theList:ArrayCollection = new ArrayCollection();
+        [Bindable] private var theListView:ListCollectionView = new 
ListCollectionView (theList);
+        private var iterator:int = 0;
+        private var connection:LocalConnection;
+        private var commandconnection:LocalConnection;
+        private var pixelconnection:LocalConnection;
+        private var pixelcommandconnection:LocalConnection;
+        private var mouseconnection:LocalConnection;
+        private var mousecommandconnection:LocalConnection;
+        private var objectconnection:LocalConnection;
+        private var objectcommandconnection:LocalConnection;
+        private var playbackconnection:LocalConnection;
+        private var playbackcommandconnection:LocalConnection;
+
+        private function initApp():void
+        {
+
+            theListView.filterFunction = filterData;
+
+            connection = new LocalConnection();
+            connection.allowDomain("*");
+            connection.client = this;
+
+            commandconnection = new LocalConnection();
+            commandconnection.allowDomain("*");
+            commandconnection.addEventListener(StatusEvent.STATUS, 
statusHandler);
+
+            pixelconnection = new LocalConnection();
+            pixelconnection.allowDomain("*");
+            pixelconnection.client = this;
+
+            pixelcommandconnection = new LocalConnection();
+            pixelcommandconnection.allowDomain("*");
+            pixelcommandconnection.addEventListener(StatusEvent.STATUS, 
statusHandler);
+
+            mouseconnection = new LocalConnection();
+            mouseconnection.allowDomain("*");
+            mouseconnection.client = this;
+
+            mousecommandconnection = new LocalConnection();
+            mousecommandconnection.allowDomain("*");
+            mousecommandconnection.addEventListener(StatusEvent.STATUS, 
statusHandler);
+
+            objectconnection = new LocalConnection();
+            objectconnection.allowDomain("*");
+            objectconnection.client = this;
+
+            objectcommandconnection = new LocalConnection();
+            objectcommandconnection.allowDomain("*");
+            objectcommandconnection.addEventListener(StatusEvent.STATUS, 
statusHandler);
+
+            playbackconnection = new LocalConnection();
+            playbackconnection.allowDomain("*");
+            playbackconnection.client = this;
+
+            playbackcommandconnection = new LocalConnection();
+            playbackcommandconnection.allowDomain("*");
+            playbackcommandconnection.addEventListener(StatusEvent.STATUS, 
statusHandler);
+
+            connect();
+            
+            toggleSniffersEnabled();
+        }
+
+        private function connect():void
+        {
+            try
+            {
+                connection.connect("_EventSniffer");
+            }
+            catch (e:Error)
+            {
+                appendLog("connection failed");
+            }
+
+            try
+            {
+                pixelconnection.connect("_PixelSniffer");
+            }
+            catch (e:Error)
+            {
+                appendLog("pixel connection failed");
+            }
+
+            try
+            {
+                mouseconnection.connect("_MouseSniffer");
+            }
+            catch (e:Error)
+            {
+                appendLog("mouse connection failed");
+            }
+
+            try
+            {
+                objectconnection.connect("_ObjectSniffer");
+            }
+            catch (e:Error)
+            {
+                appendLog("object connection failed");
+            }
+
+            try
+            {
+                playbackconnection.connect("_PlaybackSniffer");
+            }
+            catch (e:Error)
+            {
+                appendLog("playback connection failed");
+            }
+        }
+
+        private function statusHandler(event:Event):void
+        {
+        }
+
+        [Bindable]
+        public var enableLogging:Boolean = true;
+
+        public function enableSniffer():void
+        {
+            enableLogging = true;
+        }
+
+        public function disableSniffer():void
+        {
+            enableLogging = false;
+        }
+
+        /**
+        * Called by the sniffer.
+        **/
+        public function appendLog(info:Object):void
+        {
+            if (!enableLogging)
+                return;
+
+            /**
+                if (eventName==null) 
+                {
+                    eventName=" ";
+                }
+                if (event==null) 
+                {
+                    event=" ";
+                }
+            **/
+
+            iterator++;
+
+/**
+            trace ("RECEIVED item " + iterator + ":");
+            trace ("    dataSource: " + info.dataSource); // Event, Object, 
Mouse...
+            trace ("    target: " + info.target);
+            trace ("    eventName: " + info.eventName);
+            trace ("    event: " + info.event);
+**/
+                        
+            theList.addItem ({sequence:iterator, dataSource:info.dataSource, 
target:info.target, event:info.event, eventName:info.eventName});
+            /// call refresh
+            theListView.refresh();
+        }
+
+        public function filterData (o:Object):Boolean { 
+
+            // Do the easy event check before the more expensive ones.
+            if (o.dataSource == "Event"){
+                if (!eventsList.selected)
+                    return false;
+
+                if (rbIncludeEvents.selected && (eventMap[o.eventName] != 1 && 
!match(o.eventName, eventWildCards)))
+                    return false;
+    
+                if (rbExcludeEvents.selected && (eventMap[o.eventName] == 1 || 
match(o.eventName, eventWildCards)))
+                    return false;
+            }
+
+            if (!mouseList.selected && (o.dataSource == "Mouse" || 
o.dataSource == "Pixel" ))
+                return false;
+
+            if (!mustellaOutputList.selected && o.dataSource == 
"Mustella_Output")
+                return false;
+
+            if (!objectList.selected && o.dataSource == "Object")
+                return false;
+
+            if (rbIncludeTargets.selected && (targetMap[o.target] != 1 && 
!match(o.target, targetWildCards)))
+                return false;
+
+            if (rbExcludeTargets.selected && (targetMap[o.target] == 1 || 
match(o.target, targetWildCards)))
+                return false;
+
+            return true;
+        }
+
+        /**
+        * Enable or disable sniffers based on the checkboxes.
+        * This is called locally when the checkboxes are changed,
+        * and remotely by other sniffers when the SWF under test
+        * starts up.
+        **/
+        public function toggleSniffersEnabled():void{
+
+            if(eventsList.selected == true){
+                commandconnection.send("_EventSnifferCommands", 
"enableSniffer");
+            }else{
+                commandconnection.send("_EventSnifferCommands", 
"disableSniffer");
+            }
+
+            if(mouseList.selected == true){
+                mousecommandconnection.send("_MouseSnifferCommands", 
"enableSniffer");
+                pixelcommandconnection.send("_PixelSnifferCommands", 
"enableSniffer");
+            }else{
+                mousecommandconnection.send("_MouseSnifferCommands", 
"disableSniffer");
+                pixelcommandconnection.send("_PixelSnifferCommands", 
"disableSniffer");
+            }
+        }
+
+        private function scrollLog():void
+        {
+            if (log.maxVerticalScrollPosition != log.verticalScrollPosition)
+                log.verticalScrollPosition = log.maxVerticalScrollPosition;
+        }
+
+        private var _targetList:String;
+        private var targetListChanged:Boolean = false;
+
+        public function get targetList():String
+        {
+            var s:String = targets.text;
+            s = s.replace("\r", ",");
+            return s;
+        }
+
+        public function set targetList(s:String):void
+        {
+            _targetList = s;
+            targetListChanged = true;
+            invalidateProperties();
+        }
+
+        private var _eventList:String;
+        private var eventListChanged:Boolean = false;
+
+        public function get eventList():String
+        {
+            var s:String = events.text;
+            s = s.replace("\r", ",");
+            return s;
+        }
+
+        public function set eventList(s:String):void
+        {
+            _eventList = s;
+            eventListChanged = true;
+            invalidateProperties();
+        }
+
+        private var filterChanged:Boolean = false;
+
+        /**
+        * If the target or event include/exclude lists
+        * changed, process the new settings.
+        **/
+        override protected function commitProperties():void
+        {
+            var s:String;
+            var n:int;
+            var i:int;
+
+            if (eventListChanged)
+            {
+                eventListChanged = false;
+                s = _eventList.replace(",", "\r");
+                events.text = s;
+                filterChanged = true;
+            }
+
+            if (targetListChanged)
+            {
+                targetListChanged = false;
+                s = _targetList.replace(",", "\r");
+                targets.text = s;
+                filterChanged = true;
+            }
+
+            if (filterChanged)
+            {
+                filterChanged = false;
+
+                s = events.text;
+                var names:Array = s.split("\r");
+                n = names.length;
+                eventMap = {};
+                eventWildCards = [];
+                for (i = 0; i < n; i++)
+                {
+                    s = names[i];
+                    if (s.indexOf('*') >= 0)
+                        eventWildCards.push(s);
+                    else
+                        eventMap[names[i]] = 1;
+                }
+
+                s = targets.text;
+                names = s.split("\r");
+                n = names.length;
+                targetMap = {};
+                targetWildCards = [];
+                for (i = 0; i < n; i++)
+                {
+                    s = names[i];
+                    if (s.indexOf('*') >= 0)
+                        targetWildCards.push(new RegExp(s.replace("*", ".*")));
+                    else
+                        targetMap[names[i]] = 1;
+                }
+
+            }
+
+            super.commitProperties();
+            theListView.refresh();
+        }
+
+        private var eventMap:Object = {};
+        private var targetMap:Object = {};
+
+        private var eventWildCards:Array = [];
+        private var targetWildCards:Array = [];
+
+        private function changeFilter():void
+        {
+            filterChanged = true;
+            invalidateProperties();
+        }
+
+        private function match(target:String, wildCards:Array):Boolean
+        {
+            var n:int = wildCards.length;
+            for (var i:int = 0; i < n; i++)
+            {
+                if (RegExp(wildCards[i]).test(target))
+                    return true;
+            }
+            return false;
+        }
+
+        private function getAProperty():void
+        {
+            objectcommandconnection.send("_ObjectSnifferCommands", 
"dumpObject", target.text);
+        }
+
+        private function listAllProperties():void
+        {
+            objectcommandconnection.send("_ObjectSnifferCommands", 
"listProperties", target.text);
+        }
+
+        [Bindable]
+        public var paused:Boolean = true;
+
+        public function getPausedState():void
+        {
+            if (paused)            
+                pausePlayback();
+        }                
+
+        public function pausePlayback():void
+        {
+            paused = true;
+            playbackcommandconnection.send("_PlaybackCommands", "pause")
+        }
+
+        public function playback():void
+        {
+            paused = false;
+            playbackcommandconnection.send("_PlaybackCommands", "playback")
+        }
+
+        public function stepit():void
+        {
+            playbackcommandconnection.send("_PlaybackCommands", "step")
+        }
+
+        /**
+        * Someone has changed the Events, Mouse Stuff,
+        * Test Output, or Objects checkbox(es).
+        **/
+        private function handleSnifferListChange(e:Event):void{
+            theListView.refresh();
+            toggleSniffersEnabled();            
+        }
+    ]]>
+    </fx:Script>
+
+    <s:layout>
+        <s:VerticalLayout />
+    </s:layout>
+
+       <mx:Spacer width="1024" />
+
+    <!-- Each row of data looks like this: {sequence, dataSource, target, 
event, eventName} -->
+    <mx:DataGrid id="log" dataProvider="{theListView}" rowCount="20" 
width="100%" updateComplete="scrollLog()" > 
+           <mx:columns>
+                   <mx:DataGridColumn dataField="sequence" 
dataTipField="sequence" headerText="#" width="80" editable="false"/>
+                   <mx:DataGridColumn dataField="target" dataTipField="target" 
headerText="Target" width="1000" editable="false" 
itemRenderer="mx.controls.Label"/>
+                   <mx:DataGridColumn dataField="eventName" 
dataTipField="eventName" headerText="Event Name" width="1000" editable="false" 
itemRenderer="SnifferCellRenderer">
+            </mx:DataGridColumn>
+           </mx:columns>
+    </mx:DataGrid>
+
+    <mx:HBox width="100%" defaultButton="{events}" borderStyle="solid" 
paddingTop="1" paddingBottom="1" paddingLeft="1" paddingRight="1">
+        <mx:Label text="Filter by Type: "/>
+        <mx:CheckBox id="eventsList" selected="true" label="Events" 
change="handleSnifferListChange(event)" />
+        <mx:CheckBox id="mustellaOutputList" selected="true" label="Test 
Output"  change="handleSnifferListChange(event)" />
+        <mx:CheckBox id="objectList" selected="true" label="Objects"  
change="handleSnifferListChange(event)" />
+        <mx:CheckBox id="mouseList" selected="false" label="Mouse Stuff" 
change="handleSnifferListChange(event)" />
+    </mx:HBox>
+
+    <mx:Box width="100%" borderStyle="solid" paddingTop="1" paddingBottom="1"
+                            paddingLeft="1" paddingRight="1" 
enabled="{eventsList.selected}" >
+        <mx:Label text="Filter by Target/Event: " />
+        <mx:HBox width="100%" >
+            <mx:HBox width="50%" borderStyle="solid" paddingTop="1" 
paddingBottom="1" paddingLeft="1" paddingRight="1">
+                <mx:Box width="20%">
+                <mx:RadioButton id="rbIncludeTargets" groupName="targetRB" 
label="include targets"/>
+                <mx:RadioButton id="rbExcludeTargets" groupName="targetRB" 
label="exclude targets" selected="true" />
+                </mx:Box>
+                <mx:Box width="80%">
+                <mx:TextArea id="targets" width="100%" height="100" 
focusOut="changeFilter()"/>
+                </mx:Box>
+            </mx:HBox>
+            <mx:HBox width="50%" borderStyle="solid" paddingTop="1" 
paddingBottom="1" paddingLeft="1" paddingRight="1">
+                <mx:Box width="20%">
+                <mx:RadioButton id="rbIncludeEvents" groupName="eventRB" 
label="include events"/>
+                <mx:RadioButton id="rbExcludeEvents" groupName="eventRB" 
label="exclude events" selected="true"/>
+                </mx:Box>
+                <mx:Box width="80%">
+                <mx:TextArea id="events" width="100%" height="100" 
focusOut="changeFilter()"/>
+                </mx:Box>
+            </mx:HBox>
+        </mx:HBox>
+    
+        <mx:Button label="Apply Filters"  click="changeFilter()" />    
+    </mx:Box>
+
+    <mx:HBox width="100%" defaultButton="{getProperty}" 
enabled="{objectList.selected}" >
+        <mx:TextInput width="100%" id="target"/>
+        <mx:Button id="getProperty" label="Get Property" 
click="getAProperty()" />
+        <mx:Button id="listProperties" label="List Properties" 
click="listAllProperties()" />
+    </mx:HBox>
+
+    <s:Group width="100%">
+        <s:Rect id="headerBackground" left="0" right="0" top="0" height="24">
+            <s:fill>
+                <s:SolidColor color="0xCCCCCC" />
+            </s:fill>
+        </s:Rect>
+
+        <s:HGroup width="100%">
+            <s:HGroup width="100%" paddingLeft="5">
+                <s:Label text="Playback Control:" paddingTop="5"/>
+                <s:Button id="pause" label="Pause" enabled="{!paused}" 
click="pausePlayback()" skinClass="skins.pauseButtonSkin" />
+                <s:Button id="play" label="Play" enabled="{paused}" 
click="playback()" skinClass="skins.playButtonSkin" />
+                <s:Button id="step" label="Step" enabled="{paused}" 
click="stepit()" skinClass="skins.stepButtonSkin" />
+            </s:HGroup>
+
+            <s:HGroup width="100%" >
+                <s:Label text="Logging:" paddingTop="5" />
+                <s:Button label="start" enabled="{!enableLogging}" 
click="enableSniffer()" />
+                <s:Button label="stop" enabled="{enableLogging}" 
click="disableSniffer()" />
+                <s:Button label="mark" click="appendLog({dataSource:'Marker', 
target:'------------ ' + getTimer() + ' -------------', event:'', 
eventName:''})" />
+                <s:Button label="clear" click="theList.removeAll(); 
iterator=0;" />
+                <s:Label id="pixel" text="Current Pixel Color" visible="false" 
/>
+            </s:HGroup>
+    
+        </s:HGroup>
+    </s:Group>
+
+</s:Application>

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/SocketAddress.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/SocketAddress.as 
b/mustella/src/main/flex/SocketAddress.as
new file mode 100644
index 0000000..6b19dac
--- /dev/null
+++ b/mustella/src/main/flex/SocketAddress.as
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 mx.core.UIComponentGlobals;
+import mx.core.mx_internal;
+
+use namespace mx_internal;
+
+[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 SocketAddress 
+{
+
+       /**
+        *  Mixin callback that gets everything ready to go.
+        *  The UnitTester waits for an event before starting
+        */
+       public static function init(root:DisplayObject):void
+       {
+       }
+
+       function SocketAddress()
+       {
+               UnitTester.RTESocketAddress = "127.0.0.1";
+        // UnitTester.RTESocketAddress = "10.132.64.64";
+       }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/TargetConfigurations.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/TargetConfigurations.as 
b/mustella/src/main/flex/TargetConfigurations.as
new file mode 100644
index 0000000..711d0aa
--- /dev/null
+++ b/mustella/src/main/flex/TargetConfigurations.as
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+
+/**
+ *  Configurations of environments where tests are run
+ *  are mapped to IDs.
+ *  
+ *  This can't be database stuff because we don't know
+ *  some of this info. until we are actually running.
+ *
+ *  Most tests will only differ with deviceDensity and OS, with a few 
differing by screenDPI.
+ *  We aren't embedding in this release, so the same deviceDensity basesline 
will differ across
+ *  OSs.  But that will probably change in Ultra, which is why the DPI is used 
in the config ID.
+ *
+ *  The color depth is just theoretical, and the osVersion has not been needed.
+ *
+ *  DEVICES:
+ *  
+ *  Device            deviceDensity    os        screenDPI    widthxheight    
color    osVersion
+ *  win               160              win       160          320x455         
-        -
+ *  Playbook          160              qnx       170          1024x600        
-        -
+ *  win               240              win       240          480x762         
-        -
+ *  Droid 2:          240              android   240          480x854         
-        -
+ *  Droid X:          240              android   240          480x816         
-        -
+ *  Nexus One:        240              android   240          480x762         
-        -
+ *  Desire:           240              android   240          480x762         
-        -
+ *  Nexus S:          240              android   240          480x762         
-        -
+ *  win               320              win       320          640x960         
-        -
+ *  iPodTouch4G:      320              ios       326          640x960         
-        -
+ *
+ */
+
+import mx.core.FlexGlobals;
+
+public class TargetConfigurations
+{
+       //TODO add some larger screens?
+       public static var configs:Array = [
+               { configID: "160_01", deviceDensity: 160, os: DeviceNames.WIN, 
screenDPI: 160, deviceWidth: 320, deviceHeight: 455, color: null, osVersion: 
null},
+               { configID: "160_02", deviceDensity: 160, os: DeviceNames.MAC, 
screenDPI: 160, deviceWidth: 320, deviceHeight: 455, color: null, osVersion: 
null},              
+               { configID: "160_03", deviceDensity: 160, os: DeviceNames.QNX, 
screenDPI: 170, deviceWidth: 1024, deviceHeight: 600, color: null, osVersion: 
null},
+               { configID: "240_01", deviceDensity: 240, os: DeviceNames.WIN, 
screenDPI: 240, deviceWidth: 480, deviceHeight: 762, color: null, osVersion: 
null},
+               { configID: "240_02", deviceDensity: 240, os: DeviceNames.MAC, 
screenDPI: 240, deviceWidth: 480, deviceHeight: 762, color: null, osVersion: 
null},              
+               { configID: "240_03", deviceDensity: 240, os: 
DeviceNames.ANDROID, screenDPI: 240, deviceWidth: 480, deviceHeight: 816, 
color: null, osVersion: null},
+               { configID: "240_04", deviceDensity: 240, os: 
DeviceNames.ANDROID, screenDPI: 240, deviceWidth: 480, deviceHeight: 854, 
color: null, osVersion: null},
+               { configID: "240_05", deviceDensity: 240, os: 
DeviceNames.ANDROID, screenDPI: 240, deviceWidth: 480, deviceHeight: 762, 
color: null, osVersion: null},
+               { configID: "320_01", deviceDensity: 320, os: DeviceNames.WIN, 
screenDPI: 320, deviceWidth: 640, deviceHeight: 960, color: null, osVersion: 
null},
+               { configID: "320_02", deviceDensity: 320, os: DeviceNames.MAC, 
screenDPI: 320, deviceWidth: 640, deviceHeight: 960, color: null, osVersion: 
null},              
+               { configID: "320_03", deviceDensity: 320, os: DeviceNames.IOS, 
screenDPI: 326, deviceWidth: 640, deviceHeight: 960, color: null, osVersion: 
null},
+               { configID: "480_01", deviceDensity: 480, os: 
DeviceNames.ANDROID, screenDPI: 441, deviceWidth: 1080, deviceHeight: 1920, 
color: null, osVersion: null}
+       ];
+       
+       /**
+        * Returns the config ID which best matches the given ConditionalValue.
+        * This config ID will get added to baselines.  e.g. 
MyGroovyTest_160_01.png
+        **/
+       public static function getTargetConfigID( cv:ConditionalValue ):String{
+               var i:int = 0;
+
+               if( cv == null ){
+                       return null;
+               }
+               
+               // Get these when a test is actually running.
+               if( cv.deviceWidth == -1 ){
+                       cv.deviceWidth = FlexGlobals.topLevelApplication.width;
+               }
+       
+               if( cv.deviceHeight == -1 ){
+                       cv.deviceHeight = 
FlexGlobals.topLevelApplication.height;
+               }
+               
+               for( i = 0; i < configs.length; ++i ){
+                       if( cv.deviceDensity == configs[ i ].deviceDensity &&
+                           cv.os == configs[ i ].os &&
+                           cv.screenDPI == configs[ i ].screenDPI &&
+                           cv.deviceWidth == configs[ i ].deviceWidth &&
+                           cv.deviceHeight == configs[ i ].deviceHeight
+                           ){
+                               return configs[ i ].configID;
+                       }else{
+                               //trace( "*********This did not 
match:**********" );
+                               //trace( "deviceDensity: " + configs[ i 
].deviceDensity + "!=" + cv.deviceDensity );
+                               //trace( "os: " + configs[ i ].os + "!=" + 
cv.os );
+                               //trace( "screenDPI: " + configs[ i ].screenDPI 
+ "!=" + cv.screenDPI );
+                               //trace( "deviceWidth: " + configs[ i 
].deviceWidth + "!=" + cv.deviceWidth );
+                               //trace( "deviceHeight: " + configs[ i 
].deviceHeight + "!=" + cv.deviceHeight );                               
+                       }
+               }
+               
+               // No matches.
+               return null;
+
+       }
+} // end TargetConfigurations class
+} // end package

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/TestCase.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/TestCase.as 
b/mustella/src/main/flex/TestCase.as
new file mode 100644
index 0000000..7a063f1
--- /dev/null
+++ b/mustella/src/main/flex/TestCase.as
@@ -0,0 +1,525 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.events.Event;
+import flash.events.EventDispatcher;
+import flash.events.IEventDispatcher;
+import flash.utils.getQualifiedClassName;
+import flash.utils.getTimer;
+import flash.utils.Timer;
+}
+COMPILE::JS
+{
+    import org.apache.flex.events.EventDispatcher;
+    import org.apache.flex.utils.Timer;
+}
+/**
+ *  A Test.  A Test script comprises several of these
+ *  TestCases.  Each TestCase consists of a
+ *  setup, body and cleanup section which are sets
+ *  of TestStep-derived classes like SetProperty,
+ *  RunCode, AssertPropertyValue, etc.
+ *
+ *  MXML Properties (not attributes)
+ *  setup
+ *  body
+ *  cleanup
+ */
+public class TestCase extends EventDispatcher
+{
+       /**
+        *  The history of bugs that this test case has encountered
+        */
+       public var bugs:Array;
+
+       /**
+        *  The sequence of TestSteps that comprise the test setup
+        */
+       public var setup:Array;
+
+       /**
+        *  The sequence of TestSteps that comprise the test
+        */
+       public var body:Array;
+
+       /**
+        *  The sequence of TestSteps that restore the test environment
+        */
+       public var cleanup:Array;
+
+       /**
+        *  An identifier for the test
+        */
+       public var testID:String;
+
+       /**
+        *  A description of the test 
+        */
+       public var description:String;
+
+       /**
+        *  keywords, summarizing the tests
+        */
+       public var keywords:String;
+
+       /**
+        *  frequency, an estimate of the tests's intersection with real-world
+        *  usage. 1 = most frequent usage; 2 somewhat common; 3 = unusual
+        */
+       public var frequency:String;
+
+       /**
+        *  The current set of steps (setup, body, cleanup) we are executing
+        */
+       private var currentSteps:Array;
+
+       /**
+        *  Which step we're currently executing (or waiting on an event for)
+        */
+       private var currentIndex:int = 0;
+
+       /**
+        *  Number of steps in currentSteps
+        */
+       private var numSteps:int = 0;
+
+       /**
+        *  The root of the SWF (SystemManager)
+        */
+    COMPILE::SWF
+       private var root:DisplayObject;
+
+       /**
+        *  The shared timer we listen to
+        */
+       private var timer:Timer;
+
+       /**
+        *  The unit tester
+        */
+       private var context:UnitTester;
+
+       /**
+        *  Whether we need to emit a runComplete event or not
+        */
+       private var needCompleteEvent:Boolean = false;
+
+       /**
+        *  If non-zero, the time when we'll give up on waiting
+        */
+       public var expirationTime:int;
+
+       /**
+        *  the last expected Error thrown by SetProperty
+        */
+       public var lastError:Error;
+
+       /**
+        *      Called by test steps looking for a timeout indicator
+        */
+       public function setExpirationTime(time:int):void
+       {
+               expirationTime = (time > 0) ? time + UnitTester.timeout_plus : 
0;
+       }
+
+       /**
+        *  Storage for the cleanupAsserts
+        */
+       private var _cleanupAsserts:Array;
+
+       /**
+        *  Steps we have to review at the end of the body to see if
+        *  they failed or not.  These steps monitor activity like
+        *  checking for duplicate events or making sure unwanted events
+        *  don't fire.
+        */
+       public function get cleanupAsserts():Array
+       {
+               return _cleanupAsserts;
+       }
+
+       /**
+        *  Storage for this tests's result
+        */
+       private var _testResult:TestResult;
+
+       /**
+        *  This tests's result
+        */
+       public function get testResult():TestResult 
+       { 
+               _testResult.testID = testID;
+               return _testResult;
+       }
+
+       /**
+        * Constructor. Create the TestResult associated with this TestCase
+        */
+       public function TestCase() {
+
+               _testResult = new TestResult();
+               _testResult.testID = testID;
+
+               _cleanupAsserts = [];
+       }
+
+       /**
+        *  Called by the UnitTester when it is time to execute
+        *  this test case.
+        *
+        *  @param root The SystemManager
+        *  @param timer The shared Timer;
+        *  @param context the UnitTester that contains these tests
+        */
+       public function runTest(root:Object, timer:Timer, 
context:UnitTester):Boolean
+       {
+               COMPILE::SWF
+        {
+               _testResult.beginTime = new Date().time;
+               _testResult.context = context;
+               this.timer = timer;
+               this.timer.addEventListener("timer", timerHandler);
+               this.root = root;
+               this.context = context;
+
+               if (UnitTester.hasRTE) 
+               { 
+                       return true;
+
+               }
+
+               // do a sanity test here
+               if (UnitTester.verboseMode)
+               {
+                       var needWaitEvent:Boolean = false;
+                       var i:int;
+                       if (setup)
+                       {
+                               for (i = 0; i < setup.length; i++)
+                               {
+                                       if (setup[i] is ResetComponent)
+                                               needWaitEvent = true;
+                                       if (needWaitEvent)
+                                       {
+                                               if (setup[i].waitEvent)
+                                               {
+                                                       needWaitEvent = false;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (needWaitEvent)
+                                       TestOutput.logResult("WARNING: Test " + 
getQualifiedClassName(context) + "." + testID + " ResetComponent used without 
any setup steps using a waitEvent\n");
+                       }
+                       var allAsserts:Boolean = true;
+                       needWaitEvent = false;
+                       for (i = 0; i < body.length; i++)
+                       {
+                               if (body[i] is SetProperty || body[i] is 
SetStyle)
+                                       needWaitEvent = true;
+                               if (!(body[i] is Assert))
+                                       allAsserts = false;
+                               if (allAsserts && body[i] is AssertEvent)
+                               {
+                                       TestOutput.logResult("WARNING: Test " + 
getQualifiedClassName(context) + "." + testID + " no non-Assert steps in body 
before AssertEvent\n");
+                               }
+                               if (needWaitEvent)
+                               {
+                                       if (body[i].waitEvent)
+                                       {
+                                               needWaitEvent = false;
+                                               break;
+                                       }
+                               }
+                       }
+                       if (needWaitEvent)
+                               TestOutput.logResult("WARNING: Test " + 
getQualifiedClassName(context) + "." + testID + " tests setting values without 
a waitEvent\n");
+                       
+               }
+
+               return runSetup();
+        }
+        COMPILE::JS
+        {
+            return true;
+        }
+       }
+
+       /**
+        *  Execute the setup portion of the test
+        *
+        *  @param continuing If true, don't reset the counter to zero
+        *  and just continue on with the test at the currentIndex
+        */
+    COMPILE::SWF
+       private function runSetup(continuing:Boolean = false):Boolean
+       {
+               if (!testResult.hasStatus()) 
+               { 
+                       if (setup)
+                       {
+                               testResult.phase = TestResult.SETUP;
+                               currentSteps = setup;
+                               if (!continuing)
+                                       currentIndex = 0;
+                               numSteps = setup.length;
+                               // return if we need to wait for something
+                               if (!runSteps())
+                                       return false;
+
+                       }
+               }
+               return runBody();
+       }
+
+       /**
+        *  Execute the body portion of the test
+        *
+        *  @param continuing If true, don't reset the counter to zero
+        *  and just continue on with the test at the currentIndex
+        */
+    COMPILE::SWF
+       private function runBody(continuing:Boolean = false):Boolean
+       {
+               if (!testResult.hasStatus()) 
+               { 
+                       if (body)
+                       {
+                               testResult.phase = TestResult.BODY;
+                               currentSteps = body;
+                               if (!continuing)
+                                       currentIndex = 0;
+                               numSteps = body.length;
+                               // return if we need to wait for something
+                               if (!runSteps())
+                                       return false;
+
+                       }
+               }
+               return runCleanup();
+       }
+
+       /**
+        *  Execute the cleanup portion of the test
+        *
+        *  @param continuing If true, don't reset the counter to zero
+        *  and just continue on with the test at the currentIndex
+        */
+    COMPILE::SWF
+       private function runCleanup(continuing:Boolean = false):Boolean
+       {
+               if (!testResult.hasStatus()) 
+               { 
+                       if (cleanup)
+                       {
+                               testResult.phase = TestResult.CLEANUP;
+                               currentSteps = cleanup;
+                               if (!continuing)
+                                       currentIndex = 0;
+                               numSteps = cleanup.length;
+                               // return if we need to wait for something
+                               if (!runSteps())
+                                       return false;
+
+                       }
+               }
+               return runComplete();
+       }
+
+       /**
+        *  Clean up when all three phases are done.  Sends an event
+        *  to the UnitTester harness to tell it that it can run
+        *  the next test case.
+        */
+    COMPILE::SWF
+       private function runComplete():Boolean
+       {
+               var n:int = cleanupAsserts.length;
+               for (var i:int = 0; i < n; i++)
+               {
+                       var assert:Assert = cleanupAsserts[i];
+                       assert.cleanup();
+               }
+
+               timer.removeEventListener("timer", timerHandler);
+               if (needCompleteEvent)
+                       dispatchEvent(new Event("runComplete"));
+               return true;
+       }
+
+       /**
+        *  Go through the currentSteps, executing each one.
+        *  Returns true if no test steps required waiting.
+        *  Returns false if we have to wait for an event before
+        *  continuing.
+        */
+    COMPILE::SWF
+       private function runSteps():Boolean
+       {
+               while (currentIndex < numSteps)
+               {
+                       // return if a step failed
+                       if (testResult.hasStatus()) 
+                               return true;
+                                                       
+                       /* 
+                           The following lets you step each step but makes 
tests
+                           more sensitive to which step actually sends the 
event.
+                               For example, cb.mouseDown/mouseUp, the tween 
starts on
+                               mousedown and fires its event before you step.  
Another
+                               example is asserting with 
waitEvent=updateComplete.  It
+                               could have fired a long time before you step
+                       */
+                       if (UnitTester.playbackControl == "pause")
+                       {
+                               UnitTester.callback = pauseHandler;
+                               needCompleteEvent = true;
+                               return false;
+                       }
+                       else if (UnitTester.playbackControl == "step")
+                               UnitTester.playbackControl = "pause";
+
+                       var step:TestStep = currentSteps[currentIndex];
+                       if (!(step is Assert))
+                       {
+                               // look at subsequent steps for Asserts and set 
them up early
+                               for (var j:int = currentIndex + 1; j < 
numSteps; j++)
+                               {
+                                       // scan following asserts for 
AssertEvents and set them up early
+                                       var nextStep:TestStep = currentSteps[j];
+                                       if (nextStep is Assert)
+                                       {
+                                               Assert(nextStep).preview(root, 
context, this, testResult);
+                                               // do a check to be sure folks 
are using AssertEventPropertyValue correctly
+                                               if (nextStep is 
AssertEventPropertyValue)
+                                               {
+                                                       // AEPV must follow an 
AssertEvent or another AEPV
+                                                       if (j == 0 || 
!(currentSteps[j-1] is AssertEvent || currentSteps[j-1] is 
AssertEventPropertyValue))
+                                                               
TestOutput.logResult("WARNING: AssertEventPropertyValue may be missing 
preceding AssertEvent");
+                                               }
+                                               else if (nextStep is 
AssertError)
+                                               {
+                                                       if (step is SetProperty)
+                                                               
SetProperty(step).expectError = true;
+                                               }
+                                       }
+                                       else
+                                               break;
+                               }
+                       }
+                       if (UnitTester.verboseMode)
+                       {
+                               TestOutput.logResult(step.toString());
+                       }
+                       UnitTester.lastStep = step;
+                       UnitTester.lastStepLine = currentIndex;
+                       step.addEventListener("stepComplete", 
stepCompleteHandler);
+                       if (!step.execute(root, context, this, testResult))
+                       {
+                               needCompleteEvent = true;
+                               return false;
+                       }
+                       step.removeEventListener("stepComplete", 
stepCompleteHandler);
+                       currentIndex++;
+               }
+               return true;
+       }
+
+    COMPILE::SWF
+       private function stepCompleteHandler(event:Event):void
+       {
+               var step:TestStep = currentSteps[currentIndex];
+               step.removeEventListener("stepComplete", stepCompleteHandler);
+               if (UnitTester.playbackControl == "play")
+                       UnitTester.callback = runNextSteps;
+               else
+               {
+                       currentIndex++;
+                       UnitTester.callback = pauseHandler;
+               }
+       }
+
+
+       /**
+        *  Handler for timer events to see if we've waited too long
+        */
+    COMPILE::SWF
+       private function timerHandler(event:Event):void
+       {
+               if (expirationTime > 0)
+                       if (expirationTime < getTimer())
+                       {
+                               var step:TestStep = currentSteps[currentIndex];
+                               step.timeoutCallback();
+                       }
+       }
+
+       /**
+        *  Determines which set of steps (setup, body, cleanup) to run next
+        */
+    COMPILE::SWF
+       private function runNextSteps():void
+       {
+               currentIndex++;
+               if (currentSteps == setup)
+                       runSetup(true);
+               else if (currentSteps == body)
+                       runBody(true);
+               else if (currentSteps == cleanup)
+                       runCleanup(true);
+               else
+                       runComplete();
+       }
+
+       /**
+        *  Determines which set of steps (setup, body, cleanup) to run next
+        */
+    COMPILE::SWF
+       private function continueSteps():void
+       {
+               if (currentSteps == setup)
+                       runSetup(true);
+               else if (currentSteps == body)
+                       runBody(true);
+               else if (currentSteps == cleanup)
+                       runCleanup(true);
+               else
+                       runComplete();
+       }
+
+       /**
+        *  Determines which set of steps (setup, body, cleanup) to run next
+        */
+    COMPILE::SWF
+       private function pauseHandler():void
+       {
+               if (UnitTester.playbackControl == "step")
+                       continueSteps();
+               else if (UnitTester.playbackControl == "play")
+                       continueSteps();
+               else
+                       UnitTester.callback = pauseHandler;
+       }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/TestOutput.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/TestOutput.as 
b/mustella/src/main/flex/TestOutput.as
new file mode 100644
index 0000000..2fb69a6
--- /dev/null
+++ b/mustella/src/main/flex/TestOutput.as
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.events.EventDispatcher;
+import flash.events.Event;
+import flash.events.IEventDispatcher;
+}
+COMPILE::JS
+{
+    import org.apache.flex.events.EventDispatcher;
+}
+/**
+ *  The class that handles redirectable output
+ *  The default here is trace, but you can
+ *  set the output handler to something else
+ */
+public class TestOutput extends EventDispatcher
+{
+       /** 
+        *  Send the string to the output handler
+        */
+       public static function logResult(result:String):void
+       {
+        COMPILE::SWF
+        {
+               getInstance().dispatchEvent(new MustellaLogEvent ("result", 
result));
+        }
+       }
+
+
+       private static var theInst:TestOutput = null;
+
+    COMPILE::SWF
+       public static function getInstance():TestOutput
+       {
+               if (theInst == null) {
+                       theInst = new TestOutput();
+                       trace ("Created new test output instance");
+               }
+
+               return theInst;
+               
+       }
+
+       
+
+       /**
+        *  By default, here, send the output to trace. Mixin something else, 
and 
+        *  with its own outputHandler function to override this.
+        */
+    COMPILE::SWF
+       public static var outputHandler:Function = function(s:String):void 
{trace(s)};
+
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/TestResult.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/TestResult.as 
b/mustella/src/main/flex/TestResult.as
new file mode 100644
index 0000000..389abde
--- /dev/null
+++ b/mustella/src/main/flex/TestResult.as
@@ -0,0 +1,164 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.utils.*;
+}
+
+/**
+ *  The class that collects TestResults for a TestCase
+ */
+public class TestResult 
+{
+
+       public static const PASS:int = 0;
+       public static const FAIL:int = 1;
+       public static const ERROR:int = 2;
+
+       public static const SETUP:int=0;
+       public static const BODY:int=1;
+       public static const CLEANUP:int=2;
+
+       /** 
+        *  testID
+        */
+       public var testID:String;
+
+       /** 
+        *  begin time
+        */
+       public var beginTime:Number;
+
+       /** 
+        *  end time
+        */
+       public var endTime:Number;
+
+       /** 
+        *  hang on to the context, ie, the script
+        */
+       public var context:UnitTester;
+
+       /** 
+        *  result
+        */
+       public var result:int = -1;  // "pass", "fail", "error"
+
+       /** 
+        *  message. Failures often have messages
+        */
+       public var message:String = "";
+
+       /** 
+        *  extraInfo: failures may have a file associated 
+        */
+       public var extraInfo:String = "";
+
+       /** 
+        *  Name of the Script associated with this result
+        */
+       public var scriptName:String = "";
+
+       /** 
+        *  phase. how far the test finished. setup, body, cleanup
+        */
+       public var phase:int = -1;
+
+       /** 
+        *  get printable version of phase
+        */
+    COMPILE::SWF
+       public static function getPhaseString(val:int):String { 
+               if (val == CLEANUP) {
+                       return "cleanup";
+               }else if (val == BODY) {
+                       return "body";
+               }else if (val == SETUP) {
+                       return "setup";
+
+               }
+               return "no phase set";
+       }
+
+       /** 
+        *  get printable version of result
+        */
+    COMPILE::SWF
+       public static function getResultString(val:int):String { 
+               if (val == PASS) {
+                       return "pass";
+               }else if (val == FAIL) {
+                       return "fail";
+               }else if (val == ERROR) {
+                       return "error";
+               }
+               return null;
+       }
+
+
+       /** 
+        *  default output look
+        */
+    COMPILE::SWF
+       public function toString():String 
+       { 
+
+               var className:String =  getQualifiedClassName (context);
+
+               return "RESULT: scriptName="+context.testDir + className+" 
id="+ testID + " result=" + getResultString(result)  + " elapsed=" + 
(endTime-beginTime) + " phase=" + getPhaseString(phase) + " started=" + 
beginTime + " extraInfo=" + extraInfo + " msg=" + message ;
+       }
+
+
+       public function hasStatus():Boolean { 
+               return (result != -1);
+       }
+
+       public function doFail (msg:String, extraInfo:String=null):void { 
+        COMPILE::SWF
+        {
+               // first failure is the one we keep
+               if (UnitTester.noFail)
+               {
+                       return; 
+
+               }
+               if (this.result != FAIL)
+               {
+                       this.result = FAIL;
+                       // this.message = msg;
+                       if (UnitTester.run_id == "-1")
+                       { 
+                               var tmp:String = 
UnitTester.lastStep.toString().substr(0,UnitTester.lastStep.toString().indexOf 
(":")) + "(" +getPhaseString(phase) + ":step " + (UnitTester.lastStepLine+1) + 
") ";
+                               this.message = tmp + " " + msg;
+                       } 
+                       else 
+                       {
+                               this.message = msg;
+                       }
+                       this.extraInfo = extraInfo;
+               }
+        }
+       }
+               
+
+}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/TestStep.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/TestStep.as 
b/mustella/src/main/flex/TestStep.as
new file mode 100644
index 0000000..1733758
--- /dev/null
+++ b/mustella/src/main/flex/TestStep.as
@@ -0,0 +1,191 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.DisplayObject;
+import flash.events.Event;
+import flash.events.EventDispatcher;
+import flash.events.IEventDispatcher;
+import flash.utils.getTimer;
+}
+COMPILE::JS
+{
+    import org.apache.flex.events.Event;
+    import org.apache.flex.events.EventDispatcher;
+    import org.apache.flex.events.IEventDispatcher;
+}
+/**
+ *  The abstract base class for all steps in a test case.  TestStep
+ *  cannot be used directly, instead its subclasses must be used
+ *  such as SetProperty, RunCode, Assert, etc.
+ */
+public class TestStep extends EventDispatcher
+{
+       /**
+        *  Called by the TestCase when it is time to start this step
+        *  The default implementation checks for a wait event and
+        *  returns true if there isn't one and false if there is.
+        */
+       public function execute(root:Object, context:UnitTester, 
testCase:TestCase, testResult:TestResult):Boolean
+       {
+        COMPILE::SWF
+        {
+               var tryLater:Boolean = false;
+
+               this.root = root;
+               this.context = context;
+               this.testCase = testCase;
+               this.testResult = testResult;
+
+               if (waitEvent)
+               {
+                       var actualTarget:IEventDispatcher = 
context.stringToObject(waitTarget) as IEventDispatcher;
+                       if (!actualTarget)
+                       {
+                               // its ok if the target isn't here yet, it may 
be created during this step
+                               tryLater = true;
+                       }
+                       else
+                       {
+                UnitTester.waitEvent = waitEvent;
+                               actualTarget.addEventListener(waitEvent, 
waitEventHandler);
+                               testCase.setExpirationTime(getTimer() + 
timeout);
+                       }
+               }
+
+               if (!UnitTester.hasRTE)
+                       doStep();
+
+               // if test failed, don't bother waiting, just bail
+               if (testResult.hasStatus() || UnitTester.hasRTE)
+               {
+                       if (UnitTester.hasRTE)
+                       { 
+                               testResult.result = 1;
+                               testResult.message = UnitTester.RTEMsg;
+                               dispatchEvent(new Event("runComplete"));
+                               return true;    
+                       }
+
+                       if (waitEvent)
+                       {
+                UnitTester.waitEvent = null;
+                               actualTarget = 
context.stringToObject(waitTarget) as IEventDispatcher;
+                               actualTarget.removeEventListener(waitEvent, 
waitEventHandler);
+                               testCase.setExpirationTime(0);
+                       }
+                       return true;
+               }
+
+               if (tryLater && waitEvent)
+               {
+                       actualTarget = context.stringToObject(waitTarget) as 
IEventDispatcher;
+                       if (!actualTarget)
+                       {
+                               testResult.doFail("Target " + waitTarget + " 
not found");
+                               return true;
+                       }
+            UnitTester.waitEvent = waitEvent;
+                       actualTarget.addEventListener(waitEvent, 
waitEventHandler);
+                       testCase.setExpirationTime(getTimer() + timeout);
+               }
+        }
+               return (waitEvent == null);
+       }
+
+       /**
+        *  The name of the object to listen for an event we're waiting on
+        */
+       public var waitTarget:String;
+
+       /**
+        *  The name of the event to listen for on the waitTarget
+        */
+       public var waitEvent:String;
+
+       /**
+        *  The number of milliseconds to wait before giving up
+        */
+       public var timeout:int = 3000;
+
+       /**
+        *  The TestResult for this TestCase
+        */
+       protected var testResult:TestResult;
+
+       /**
+        *  The TestCase that this step belongs to
+        */
+       protected var testCase:TestCase;
+
+       /**
+        *  The UnitTester that this step belongs to
+        */
+       protected var context:UnitTester;
+
+       /**
+        *  The root for the SWF
+        */
+       protected var root:Object;
+
+       /**
+        *  The method that gets called when it is time to perform the work in 
the step.
+        */
+       protected function doStep():void
+       {
+       }
+
+       /**
+        *  The method that gets called back when the event we're waiting on 
fires
+        */
+       protected function waitEventHandler(event:Event):void
+       {
+               stepComplete();
+       }
+
+       /**
+        *  The method that gets called when it is time to clean up the step.
+        */
+       protected function stepComplete():void
+       {
+               if (waitEvent)
+               {
+            UnitTester.waitEvent = null;
+                       var actualTarget:IEventDispatcher = 
context.stringToObject(waitTarget) as IEventDispatcher;
+                       if (actualTarget)       // can be null if object killed 
during step
+                               actualTarget.removeEventListener(waitEvent, 
waitEventHandler);
+                       testCase.setExpirationTime(0);
+               }
+               dispatchEvent(new Event("stepComplete"));
+       }
+
+       /**
+        *  Called by the test case if you time out
+        */
+       public function timeoutCallback():void
+       {
+               testResult.doFail("Timeout waiting for " + waitEvent + " from " 
+ waitTarget);
+               stepComplete();
+       }
+
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/TypeInfo.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/TypeInfo.as 
b/mustella/src/main/flex/TypeInfo.as
new file mode 100644
index 0000000..064c116
--- /dev/null
+++ b/mustella/src/main/flex/TypeInfo.as
@@ -0,0 +1,100 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.utils.Dictionary;
+}
+    
+    /**
+     *  Helper class useful for runtime object introspection.
+     *  This object is generally cached by the TypeInfoCache
+     *  and reused once constructed.  Right now we only care about
+     *  base types and interfaces but the class could easily be 
+     *  extended to properties and methods if desired.
+     */
+    public class TypeInfo
+    {
+        COMPILE::SWF
+        private var baseTypes:Dictionary = new Dictionary();
+        COMPILE::SWF
+        private var interfaces:Dictionary = new Dictionary();
+        public var className:String;
+        
+        /**
+         * Constructor
+         */
+        public function TypeInfo(className:String):void
+        {
+            COMPILE::SWF
+            {
+            this.className = className;
+            describeType(className);
+            }
+        }
+        
+        /** 
+         * Initialization method used to populate the base types and the
+         * implemented interfaces for our type.
+         */
+        COMPILE::SWF
+        private function describeType(className:String):void
+        {
+            try
+            {
+                var definition:Object = 
flash.utils.getDefinitionByName(className);
+            }
+            catch(e:Error)
+            { 
+                return;
+            }
+            
+            var typeInfo:XMLList = 
flash.utils.describeType(definition).child("factory");
+            
+            var types:XMLList = 
typeInfo.child("extendsClass").attribute("type");
+            for each (var name:String in types)
+                baseTypes[name] = true;
+            
+            var interfaces:XMLList = 
typeInfo.child("implementsInterface").attribute("type");
+            for each (name in interfaces)
+                this.interfaces[name] = true;
+        }
+        
+        /**
+         * Returns true if our type implements the given fully qualified 
interface.
+         */
+        COMPILE::SWF
+        public function implementsInterface(interfaceName:String):Boolean
+        {
+           return (interfaces[interfaceName] == true) ? true : false;
+        }
+        
+        /**
+         * Returns true if our type is assignable to the type provided.
+         */
+        COMPILE::SWF
+        public function isAssignableTo(typeName:String):Boolean
+        {
+           return (interfaces[typeName] == true || 
+                   baseTypes[typeName]  == true ||
+                   typeName == className) ? true : false;
+        }
+    }
+}
\ No newline at end of file

Reply via email to