You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ah...@apache.org on 2017/09/22 05:47:51 UTC

[20/24] git commit: [flex-asjs] [refs/heads/packaging] - our own copy of mustella. Only some have been fully ported to FlexJS. See list in compile-js-config.xml

our own copy of mustella.  Only some have been fully ported to FlexJS.  See list in compile-js-config.xml


Project: http://git-wip-us.apache.org/repos/asf/flex-asjs/repo
Commit: http://git-wip-us.apache.org/repos/asf/flex-asjs/commit/27eb06f5
Tree: http://git-wip-us.apache.org/repos/asf/flex-asjs/tree/27eb06f5
Diff: http://git-wip-us.apache.org/repos/asf/flex-asjs/diff/27eb06f5

Branch: refs/heads/packaging
Commit: 27eb06f5a01cd0e796bb067a7950ac83c6e134eb
Parents: d9e8503
Author: Alex Harui <ah...@apache.org>
Authored: Thu Sep 21 11:46:09 2017 -0700
Committer: Alex Harui <ah...@apache.org>
Committed: Thu Sep 21 11:46:09 2017 -0700

----------------------------------------------------------------------
 mustella/build.xml                              |  114 +
 mustella/src/main/config/compile-js-config.xml  |  163 ++
 mustella/src/main/config/compile-swf-config.xml |  145 +
 mustella/src/main/flex/AIR/CompareBitmap.as     | 1260 +++++++++
 mustella/src/main/flex/ApolloFilePath.as        |  129 +
 mustella/src/main/flex/Assert.as                |  185 ++
 mustella/src/main/flex/AssertError.as           |   90 +
 mustella/src/main/flex/AssertEvent.as           |  219 ++
 .../src/main/flex/AssertEventPropertyValue.as   |  112 +
 mustella/src/main/flex/AssertMethodValue.as     |  143 +
 mustella/src/main/flex/AssertNoEvent.as         |   80 +
 mustella/src/main/flex/AssertPixelValue.as      |  150 +
 mustella/src/main/flex/AssertPropertyValue.as   |  142 +
 mustella/src/main/flex/AssertStyleValue.as      |  124 +
 mustella/src/main/flex/AssertTitle.as           |   70 +
 mustella/src/main/flex/AssertType.as            |  111 +
 mustella/src/main/flex/AssertURL.as             |   79 +
 mustella/src/main/flex/Bug.as                   |   29 +
 mustella/src/main/flex/CBTester.mxml            |  305 ++
 mustella/src/main/flex/ChangeState.as           |   96 +
 mustella/src/main/flex/CheckEmbeddedFonts.as    |   44 +
 mustella/src/main/flex/CompareBitmap.as         | 1603 +++++++++++
 mustella/src/main/flex/ConditionalValue.as      |  356 +++
 .../ContinueAfterRuntimeException.ICO           |  Bin 0 -> 1078 bytes
 .../ContinueAfterRuntimeException.cpp           |  362 +++
 .../ContinueAfterRuntimeException.dsp           |  140 +
 .../ContinueAfterRuntimeException.dsw           |   29 +
 .../ContinueAfterRuntimeException.h             |   30 +
 .../ContinueAfterRuntimeException.rc            |  140 +
 .../ContinueAfterRuntimeException/SMALL.ICO     |  Bin 0 -> 318 bytes
 .../ContinueAfterRuntimeException/StdAfx.cpp    |   26 +
 .../flex/ContinueAfterRuntimeException/StdAfx.h |   52 +
 .../ContinueAfterRuntimeException/resource.h    |   45 +
 mustella/src/main/flex/CoverageTimeout.as       |   43 +
 .../src/main/flex/CreateBitmapReferences.as     |   46 +
 mustella/src/main/flex/DesktopMacSettings.as    |   40 +
 mustella/src/main/flex/DesktopWinSettings.as    |   40 +
 mustella/src/main/flex/DeviceNames.as           |   66 +
 mustella/src/main/flex/DispatchEvent.as         |  141 +
 mustella/src/main/flex/DispatchKeyEvent.as      |  673 +++++
 .../src/main/flex/DispatchMouseClickEvent.as    |  298 ++
 mustella/src/main/flex/DispatchMouseEvent.as    |  306 ++
 mustella/src/main/flex/DragAndDropMain.mxml     |   41 +
 mustella/src/main/flex/DragAndDropScript.mxml   |   66 +
 mustella/src/main/flex/EffectTesting.as         |  645 +++++
 mustella/src/main/flex/EnableRemoteImageDiff.as |   46 +
 mustella/src/main/flex/ErrorArray.as            |   41 +
 mustella/src/main/flex/EventSnifferRemote.as    |  154 ++
 mustella/src/main/flex/ExcludeFileLocation.as   |  226 ++
 .../src/main/flex/ExcludeFileLocationApollo.as  |  300 ++
 mustella/src/main/flex/ExcludeList.as           |   44 +
 mustella/src/main/flex/ExcludeList.txt          |    1 +
 mustella/src/main/flex/ExcludeListTextFile.as   |   71 +
 mustella/src/main/flex/ExitWhenDone.as          |   49 +
 mustella/src/main/flex/FakeSoftKeyboard.as      |  129 +
 mustella/src/main/flex/IncludeFileLocation.as   |  100 +
 mustella/src/main/flex/IncludeList.as           |   45 +
 mustella/src/main/flex/IncludeList.txt          |    1 +
 mustella/src/main/flex/IncludeListTextFile.as   |   71 +
 mustella/src/main/flex/Localhost8080.as         |   46 +
 mustella/src/main/flex/MobileConfig.as          |   30 +
 mustella/src/main/flex/MouseSnifferRemote.as    |  282 ++
 mustella/src/main/flex/MultiResult.as           |  193 ++
 mustella/src/main/flex/MustellaLogEvent.as      |   44 +
 mustella/src/main/flex/MustellaPNGEncoder.as    |  183 ++
 .../src/main/flex/MustellaRealTimeLogger.as     |   68 +
 mustella/src/main/flex/MustellaSandboxEvent.as  |   53 +
 mustella/src/main/flex/NoFail.as                |   41 +
 mustella/src/main/flex/ObjectSnifferRemote.as   |  333 +++
 mustella/src/main/flex/Pause.as                 |   66 +
 mustella/src/main/flex/PixelSnifferRemote.as    |  205 ++
 mustella/src/main/flex/PlaybackControl.as       |   91 +
 mustella/src/main/flex/README                   |  271 ++
 mustella/src/main/flex/ResetComponent.as        |  476 ++++
 mustella/src/main/flex/RunCode.as               |   77 +
 mustella/src/main/flex/RunCodeEvent.as          |   68 +
 mustella/src/main/flex/RunnerPortAlt.as         |   46 +
 mustella/src/main/flex/SaveBitmapFailures.as    |   62 +
 .../main/flex/SaveBitmapFailuresDistServer.as   |   62 +
 mustella/src/main/flex/ScriptRunner.as          |   53 +
 .../src/main/flex/SendFormattedResultsToLog.as  |  121 +
 mustella/src/main/flex/SendResultsToRunner.as   |  192 ++
 .../src/main/flex/SendResultsToSnifferClient.as |   83 +
 mustella/src/main/flex/SetProperty.as           |  144 +
 mustella/src/main/flex/SetShowRTE.as            |   43 +
 mustella/src/main/flex/SetStyle.as              |  118 +
 mustella/src/main/flex/SetURL.as                |   95 +
 mustella/src/main/flex/SetVettingRun.as         |   44 +
 mustella/src/main/flex/SnifferCellRenderer.mxml |   75 +
 mustella/src/main/flex/SnifferRemoteClient.mxml |  514 ++++
 mustella/src/main/flex/SocketAddress.as         |   52 +
 mustella/src/main/flex/TargetConfigurations.as  |  114 +
 mustella/src/main/flex/TestCase.as              |  525 ++++
 mustella/src/main/flex/TestOutput.as            |   76 +
 mustella/src/main/flex/TestResult.as            |  164 ++
 mustella/src/main/flex/TestStep.as              |  191 ++
 mustella/src/main/flex/TypeInfo.as              |  100 +
 mustella/src/main/flex/UnitTester.as            | 2617 ++++++++++++++++++
 mustella/src/main/flex/Util.as                  |   43 +
 mustella/src/main/flex/VerboseMode.as           |   46 +
 mustella/src/main/flex/WaitForCondition.as      |  150 +
 mustella/src/main/flex/WaitForEffectsToEnd.as   |  125 +
 mustella/src/main/flex/WaitForEvent.as          |  211 ++
 mustella/src/main/flex/WaitForLayoutManager.as  |  205 ++
 mustella/src/main/flex/WaitForSandboxApp.as     |  106 +
 mustella/src/main/flex/WaitForWindow.as         |   46 +
 mustella/src/main/flex/imageDiff.mxml           |  444 +++
 mustella/src/main/flex/main.mxml                |   35 +
 mustella/src/main/flex/myBitmap.png             |  Bin 0 -> 622 bytes
 mustella/src/main/flex/myTest4.png              |  Bin 0 -> 622 bytes
 .../src/main/flex/skins/pauseButtonSkin.mxml    |  176 ++
 mustella/src/main/flex/skins/pauseIcon.png      |  Bin 0 -> 369 bytes
 .../src/main/flex/skins/playButtonSkin.mxml     |  177 ++
 mustella/src/main/flex/skins/playIcon.png       |  Bin 0 -> 605 bytes
 .../src/main/flex/skins/stepButtonSkin.mxml     |  177 ++
 mustella/src/main/flex/skins/stepIcon.png       |  Bin 0 -> 413 bytes
 116 files changed, 20215 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/build.xml
----------------------------------------------------------------------
diff --git a/mustella/build.xml b/mustella/build.xml
new file mode 100644
index 0000000..891d4c6
--- /dev/null
+++ b/mustella/build.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0"?>
+<!--
+
+  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.
+
+-->
+
+
+<project name="mustella" default="main" basedir=".">
+    <property name="FLEXJS_HOME" location=".."/>
+    
+    <property file="${FLEXJS_HOME}/env.properties"/>
+    <property environment="env"/>
+    <property file="${FLEXJS_HOME}/build.properties"/>
+    <property name="FLEX_HOME" value="${FLEXJS_HOME}"/>
+    
+    <property name="target.name" value="${ant.project.name}.swc" />
+    
+    <target name="main" depends="clean,check-compiler,compile,compile-js" description="Full build of ${ant.project.name}.swc">
+    </target>
+    
+    <target name="compile-js" unless="env.AIR_HOME">
+        <echo message="Compiling ${ant.project.name}.swc"/>
+        <echo message="FLEX_HOME: ${FLEX_HOME}"/>
+        <echo message="FALCON_HOME: ${FALCON_HOME}"/>
+        <echo message="FALCONJX_HOME: ${FALCONJX_HOME}"/>
+        
+        <java jar="${FALCONJX_HOME}/lib/compc.jar" fork="true" >
+            <jvmarg value="-Xmx384m" />
+            <jvmarg value="-Dsun.io.useCanonCaches=false" />
+            <jvmarg value="-Dflexlib=${FLEX_HOME}/frameworks" />
+            <arg value="+flexlib=${FLEX_HOME}/frameworks" />
+            <arg value="+playerglobal.version=${playerglobal.version}" />
+            <arg value="-compiler.strict-xml=true" />
+            <arg value="-compiler.targets=SWF,JSFlex" />
+            <arg value="-output=${basedir}/${target.name}" />
+            <arg value="-load-config=${FLEX_HOME}/frameworks/js-config.xml" />
+            <arg value="-load-config+=${basedir}/src/main/config/compile-js-config.xml" />
+            <arg value="-js-load-config=${FLEX_HOME}/frameworks/js-config.xml" />
+            <arg value="-js-load-config+=${basedir}/src/main/config/compile-js-config.xml" />
+        </java>
+    </target>
+    
+    <target name="clean">
+        <delete file="${target.name}" failonerror="false" />
+    </target>
+    
+    <target name="compile" description="Compiles .as files into .swc" if="env.AIR_HOME">
+        <echo message="Compiling ${ant.project.name}.swc"/>
+        <echo message="FLEX_HOME: ${FLEX_HOME}"/>
+        <echo message="FALCON_HOME: ${FALCON_HOME}"/>
+        <echo message="FALCONJX_HOME: ${FALCONJX_HOME}"/>
+        
+        <java jar="${FALCONJX_HOME}/lib/compc.jar" fork="true" >
+            <jvmarg value="-Xmx384m" />
+            <jvmarg value="-Dsun.io.useCanonCaches=false" />
+            <jvmarg value="-Dflexlib=${FLEX_HOME}/frameworks" />
+            <arg value="+flexlib=${FLEX_HOME}/frameworks" />
+            <arg value="+playerglobal.version=${playerglobal.version}" />
+            <arg value="+env.AIR_HOME=${env.AIR_HOME}" />
+            <arg value="-compiler.strict-xml=true" />
+            <arg value="-compiler.targets=SWF,JSFlex" />
+            <arg value="-output=${basedir}/${target.name}" />
+            <arg value="-load-config=${basedir}/src/main/config/compile-swf-config.xml" />
+            <arg value="-js-load-config=${FLEX_HOME}/frameworks/js-config.xml" />
+            <arg value="-js-load-config+=${basedir}/src/main/config/compile-js-config.xml" />
+        </java>
+    </target>
+    
+    <target name="check-compiler" depends="check-falcon-home,check-falconjx-home">
+        <path id="lib.path">
+            <fileset dir="${FALCONJX_HOME}/lib" includes="falcon-flexTasks.jar"/>
+        </path>
+        <taskdef resource="flexTasks.tasks" classpathref="lib.path"/>
+    </target>
+    
+    <target name="check-falcon-home"
+        description="Set FALCON_HOME to point at the compiler.">
+        
+        <available file="${FLEXJS_HOME}/lib/falcon-mxmlc.jar"
+        type="file"
+        property="FALCON_HOME"
+        value="${FLEXJS_HOME}"/>
+        
+        <fail message="FALCON_HOME must be set to a folder with a lib sub-folder containing falcon-mxmlc.jar such as the compiler folder in flex-falcon repo or the root of a FlexJS SDK"
+        unless="FALCON_HOME"/>
+    </target>
+    
+    <target name="check-falconjx-home"
+        description="Set FALCONJX_HOME to point at the cross-compiler.">
+        
+        <available file="${FLEXJS_HOME}/js/lib/jsc.jar"
+        type="file"
+        property="FALCONJX_HOME"
+        value="${FLEXJS_HOME}/js"/>
+        
+        <fail message="FALCONJX_HOME must be set to a folder with a lib sub-folder containing jsc.jar such as the compiler-jx folder in flex-falcon repo or the js folder of a FlexJS SDK"
+        unless="FALCONJX_HOME"/>
+    </target>
+    
+</project>

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/config/compile-js-config.xml
----------------------------------------------------------------------
diff --git a/mustella/src/main/config/compile-js-config.xml b/mustella/src/main/config/compile-js-config.xml
new file mode 100644
index 0000000..21b61f8
--- /dev/null
+++ b/mustella/src/main/config/compile-js-config.xml
@@ -0,0 +1,163 @@
+<!--
+
+  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.
+
+-->
+<flex-config>
+
+    <compiler>
+        <accessible>false</accessible>
+
+        <!-- build both SWF and JS. -->
+        <targets>
+            <target>SWF</target>
+            <target>JSFlex</target>
+        </targets>
+        <strict-xml>true</strict-xml>
+
+		<mxml>
+			<children-as-data>true</children-as-data>
+		</mxml>
+		<binding-value-change-event>org.apache.flex.events.ValueChangeEvent</binding-value-change-event>
+		<binding-value-change-event-kind>org.apache.flex.events.ValueChangeEvent</binding-value-change-event-kind>
+		<binding-value-change-event-type>valueChange</binding-value-change-event-type>
+
+        <define>
+            <name>COMPILE::SWF</name>
+            <value>false</value>
+        </define>
+        <define>
+            <name>COMPILE::JS</name>
+            <value>true</value>
+        </define>
+
+        <keep-as3-metadata>
+          <name>Bindable</name>
+          <name>Managed</name>
+          <name>ChangeEvent</name>
+          <name>NonCommittingChangeEvent</name>
+          <name>Transient</name>
+        </keep-as3-metadata>
+	  
+        <locale/>
+        
+        <namespaces>
+            <!--
+            <namespace>
+                <uri>library://ns.apache.org/flexjs/basic</uri>
+                <manifest>../../../../../../../projects/Core/src/main/resources/basic-manifest.xml</manifest>
+            </namespace>
+             -->
+        </namespaces>
+
+        <!-- overwrite the default library-path setting -->
+        <library-path>
+          <path-element>../../../../js/libs/GCL.swc</path-element>
+          <path-element>../../../../frameworks/js/FlexJS/libs/CoreJS.swc</path-element>
+        </library-path>
+        
+        <source-path>
+            <path-element>../flex</path-element>
+        </source-path>
+        
+        <warn-no-constructor>false</warn-no-constructor>
+        
+        <!-- Use of the instanceof operator. -->
+        <warn-instance-of-changes>false</warn-instance-of-changes>
+    </compiler>
+    
+    <include-file>
+    </include-file>
+
+    <include-sources>
+    </include-sources>
+    
+    <include-classes>
+        <!--
+        <class>ApolloFilePath</class>
+         -->
+        <class>Assert</class>
+        <!--
+        <class>AssertError</class>
+        <class>AssertEvent</class>
+        <class>AssertEventPropertyValue</class>
+        <class>AssertMethodValue</class>
+        <class>AssertNoEvent</class>
+        <class>AssertPixelValue</class>
+         -->
+        <class>AssertPropertyValue</class>
+        <!--
+        <class>AssertStyleValue</class>
+        <class>AssertTitle</class>
+        <class>AssertType</class>
+        <class>AssertURL</class>
+        <class>CheckEmbeddedFonts</class>
+        <class>ChangeState</class>
+        <class>CompareBitmap</class>
+        <class>CreateBitmapReferences</class>
+         -->
+        <class>DispatchEvent</class>
+        <class>DispatchKeyEvent</class>
+        <class>DispatchMouseClickEvent</class>
+        <class>DispatchMouseEvent</class>
+        <!--
+        <class>ExcludeFileLocation</class>
+        <class>ExcludeFileLocationApollo</class>
+         -->
+        <class>ExitWhenDone</class>
+        <!--
+        <class>IncludeListTextFile</class>
+        <class>IncludeFileLocation</class>
+        <class>Localhost8080</class>
+        <class>MustellaPNGEncoder</class>
+        <class>Pause</class>
+        <class>ResetComponent</class>
+         -->
+        <class>RunCode</class>
+        <!--
+        <class>RunCodeEvent</class>
+        <class>SaveBitmapFailures</class>
+        <class>ScriptRunner</class>
+         -->
+        <class>SendFormattedResultsToLog</class>
+        <class>SendResultsToRunner</class>
+        <class>SetProperty</class>
+        <!--
+        <class>SetStyle</class>
+        <class>SetShowRTE</class>
+        <class>SetURL</class>
+        <class>SocketAddress</class>
+        -->
+        <class>TestCase</class>
+        <class>TestOutput</class>
+        <class>TestResult</class>
+        <class>TestStep</class>
+        <!--
+        <class>TypeInfo</class>
+         -->
+        <class>UnitTester</class>
+        <!--
+        <class>VerboseMode</class>
+        <class>WaitForCondition</class>
+        <class>WaitForEffectsToEnd</class>
+        <class>WaitForEvent</class>
+        <class>WaitForLayoutManager</class>
+        <class>WaitForSandboxApp</class>
+        <class>WaitForWindow</class>
+         -->
+    </include-classes>
+    
+</flex-config>

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/config/compile-swf-config.xml
----------------------------------------------------------------------
diff --git a/mustella/src/main/config/compile-swf-config.xml b/mustella/src/main/config/compile-swf-config.xml
new file mode 100644
index 0000000..5c87682
--- /dev/null
+++ b/mustella/src/main/config/compile-swf-config.xml
@@ -0,0 +1,145 @@
+<!--
+
+  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.
+
+-->
+<flex-config>
+
+    <compiler>
+        <accessible>false</accessible>
+        
+        <!-- build both SWF and JS. -->
+        <targets>
+            <target>SWF</target>
+            <target>JSFlex</target>
+        </targets>
+        <strict-xml>true</strict-xml>
+
+        <external-library-path>
+            <path-element>${env.AIR_HOME}/frameworks/libs/air/airglobal.swc</path-element>
+        </external-library-path>
+        
+        <allow-subclass-overrides>true</allow-subclass-overrides>
+        
+		<mxml>
+			<children-as-data>true</children-as-data>
+		</mxml>
+		<binding-value-change-event>org.apache.flex.events.ValueChangeEvent</binding-value-change-event>
+		<binding-value-change-event-kind>org.apache.flex.events.ValueChangeEvent</binding-value-change-event-kind>
+		<binding-value-change-event-type>valueChange</binding-value-change-event-type>
+        
+        <define>
+            <name>COMPILE::SWF</name>
+            <value>true</value>
+        </define>
+        <define>
+            <name>COMPILE::JS</name>
+            <value>false</value>
+        </define>
+
+        <keep-as3-metadata>
+          <name>Bindable</name>
+          <name>Managed</name>
+          <name>ChangeEvent</name>
+          <name>NonCommittingChangeEvent</name>
+          <name>Transient</name>
+          <name>SWFOverride</name>
+        </keep-as3-metadata>
+	  
+        <locale/>
+        
+        <library-path/>
+
+        <namespaces>
+            <!--
+            <namespace>
+                <uri>library://ns.apache.org/flexjs/basic</uri>
+                <manifest>../resources/basic-manifest.xml</manifest>
+            </namespace>
+             -->
+        </namespaces>
+        
+        <source-path>
+            <path-element>../flex</path-element>
+        </source-path>
+        
+        <warn-no-constructor>false</warn-no-constructor>
+        
+        <!-- Use of the instanceof operator. -->
+        <warn-instance-of-changes>false</warn-instance-of-changes>
+    </compiler>
+    
+    <include-classes>
+        <class>ApolloFilePath</class>
+        <class>Assert</class>
+        <class>AssertError</class>
+        <class>AssertEvent</class>
+        <class>AssertEventPropertyValue</class>
+        <class>AssertMethodValue</class>
+        <class>AssertNoEvent</class>
+        <class>AssertPixelValue</class>
+        <class>AssertPropertyValue</class>
+        <class>AssertStyleValue</class>
+        <class>AssertTitle</class>
+        <class>AssertType</class>
+        <class>AssertURL</class>
+        <class>CheckEmbeddedFonts</class>
+        <class>ChangeState</class>
+        <class>CompareBitmap</class>
+        <class>CreateBitmapReferences</class>
+        <class>DispatchEvent</class>
+        <class>DispatchKeyEvent</class>
+        <class>DispatchMouseClickEvent</class>
+        <class>DispatchMouseEvent</class>
+        <class>ExcludeFileLocation</class>
+        <class>ExcludeFileLocationApollo</class>
+        <class>ExitWhenDone</class>
+        <class>IncludeListTextFile</class>
+        <class>IncludeFileLocation</class>
+        <class>Localhost8080</class>
+        <class>MustellaPNGEncoder</class>
+        <class>Pause</class>
+        <class>ResetComponent</class>
+        <class>RunCode</class>
+        <class>RunCodeEvent</class>
+        <class>SaveBitmapFailures</class>
+        <class>ScriptRunner</class>
+        <class>SendFormattedResultsToLog</class>
+        <class>SendResultsToRunner</class>
+        <class>SetProperty</class>
+        <class>SetStyle</class>
+        <class>SetShowRTE</class>
+        <class>SetURL</class>
+        <class>SocketAddress</class>
+        <class>TestCase</class>
+        <class>TestOutput</class>
+        <class>TestResult</class>
+        <class>TestStep</class>
+        <class>TypeInfo</class>
+        <class>UnitTester</class>
+        <class>VerboseMode</class>
+        <class>WaitForCondition</class>
+        <class>WaitForEffectsToEnd</class>
+        <class>WaitForEvent</class>
+        <class>WaitForLayoutManager</class>
+        <class>WaitForSandboxApp</class>
+        <class>WaitForWindow</class>
+    </include-classes>
+    
+    <target-player>20.0</target-player>
+	
+
+</flex-config>

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/AIR/CompareBitmap.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/AIR/CompareBitmap.as b/mustella/src/main/flex/AIR/CompareBitmap.as
new file mode 100644
index 0000000..e96da6f
--- /dev/null
+++ b/mustella/src/main/flex/AIR/CompareBitmap.as
@@ -0,0 +1,1260 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 AIR { 
+
+import flash.display.*;
+import flash.events.*;
+import flash.filesystem.*;
+import flash.net.*;
+import flash.text.*;
+import flash.text.engine.*;
+import flash.geom.ColorTransform;
+import flash.utils.*;
+import flash.filesystem.*;
+import flash.geom.*;
+import mx.core.IChildList;
+import mx.core.IRawChildrenContainer;
+import mx.core.mx_internal;
+use namespace mx_internal;
+
+/**
+*  Vector of conditionalValue objects.
+**/
+[DefaultProperty("conditionalValues")]
+
+/**
+ *  The test step that compares a bitmap against a reference bitmap
+ *  MXML attributes:
+ *  target
+ *  url
+ *  timeout (optional);
+ *  stageText (optional)
+ *  maxColorVariance
+ *  numColorVariances
+ *  waitTarget Do Not Use
+ *  waitEvent Do Not Use
+ *
+ *  Do not set waitEvent or waitTarget on this step.  They are set internally
+ *  to manage the loading of the reference bitmap.  The step prior to this
+ *  step must wait for the system to synch up.
+ *  
+ *  CompareBitmap will parse the url attribute for $testID, replacing with the current testID
+ */
+public class CompareBitmap extends Assert
+{ 
+	public static var useRemoteDiffer:Boolean = false;
+
+	private static var identityMatrix:String = new Matrix().toString();
+	private static var identityColorTransform:String = new ColorTransform().toString();
+	
+	public static var DEFAULT_MAX_COLOR_VARIANCE:int = 0;
+	public static var DEFAULT_NUM_COLOR_VARIANCES:int = 0;
+
+	// This is the default property.
+	public var conditionalValues:Vector.<ConditionalValue> = null;
+	
+	/**
+	 *  The url of the file to read. If UnitTester.createBitmapReferences = true,
+	 *  the url to store the bitmap
+	 */
+	public var url:String;
+
+
+	/**
+	 *  If the user tells us it's a stageText, we'll try to grab the bitmapdata 
+	 *  via mx_internal call 
+	 */
+	public var stageText:Boolean
+
+	private var _maxColorVariance:int = 0;
+	/**
+	 *  The maximum color variation allowed in a bitmap compare.
+	 *  Some machines render slightly differently and thus we have
+	 *  to allow the the compare to not be exact.
+	 */
+	public function get maxColorVariance():int
+	{
+		if (_maxColorVariance)
+			return _maxColorVariance;
+
+		return DEFAULT_MAX_COLOR_VARIANCE;
+	}
+	public function set maxColorVariance(value:int):void
+	{
+		_maxColorVariance = value;
+	}
+
+	private var _ignoreMaxColorVariance:Boolean = false;
+	
+	/**
+	 * Sometimes you have numColorVariance defined and you don't really care by how much the pixel really differ.
+	 * as long as the number of mismatching pixels is &lt;= numColorVariance
+	 * Setting this to true will skip the maxColorVariance check (and take the guess work out of picture). default is false
+	 */
+	public function get ignoreMaxColorVariance():Boolean
+	{
+		return _ignoreMaxColorVariance;
+	}
+	
+	public function set ignoreMaxColorVariance(value:Boolean):void
+	{
+		_ignoreMaxColorVariance = value;
+	}
+
+
+	private var _numColorVariances:int = 0;
+	/**
+	 *  The number of color variation allowed in a bitmap compare.
+	 *  Some machines render slightly differently and thus we have
+	 *  to allow the the compare to not be exact.
+	 */
+	public function get numColorVariances():int
+	{
+		if (_numColorVariances)
+			return _numColorVariances;
+
+		return DEFAULT_NUM_COLOR_VARIANCES;
+	}
+	public function set numColorVariances(value:int):void
+	{
+		_numColorVariances = value;
+	}
+
+	/**
+	 *  Suffix to add to the file being written out (the case of a compare failure)
+	 */
+	public static var fileSuffix:String = "";
+
+	private var reader:Loader;
+	private var xmlreader:URLLoader;
+	private var writer:URLLoader;
+
+	private static var connection:LocalConnection;
+	private static var commandconnection:LocalConnection;
+	
+	private function statusHandler(event:Event):void
+	{
+	}
+
+	/**
+	 *  Constructor
+	 */
+	public function CompareBitmap() 
+	{ 
+		if (!connection)
+		{
+			connection = new LocalConnection();
+			connection.allowDomain("*");
+			connection.addEventListener(StatusEvent.STATUS, statusHandler);
+
+			commandconnection = new LocalConnection();
+			commandconnection.allowDomain("*");
+
+			try
+			{
+				commandconnection.connect("_ImageDifferCommands");
+			}
+			catch (e:Error)
+			{
+				trace("connection failed");
+			}
+		}
+
+	}
+
+	// We override execute here.  In other TestSteps, we override doStep().
+	override public function execute(root:DisplayObject, context:UnitTester, testCase:TestCase, testResult:TestResult):Boolean
+	{
+		var cv:ConditionalValue = null;
+		var configID:String = null;
+		
+		if( conditionalValues ){			
+			// Use MultiResult to determine the proper URL.
+			cv = new MultiResult().chooseCV(conditionalValues);
+			if(cv){
+				// Use thet cv's url if it is set; otherwise, stick with the CompareBitmap's url (directory).
+				if( cv.url != null ){
+					url = cv.url;
+				}
+			}
+		}else{
+			// We do not have ConditionalValues.  If the current config is unknown, it is probably
+			// a desktop AIR run, and we should just let things take the course they always have.
+			// If a config is known, then we want to use the new config ID suffix mechanism later.
+			configID = TargetConfigurations.getTargetConfigID( UnitTester.cv );
+			if( configID ){
+				trace( "CompareBitmap: No ConditionalValues found.  configID is " + configID.toString() );
+			}else{
+				trace( "CompareBitmap: No ConditionalValues found.  configID is " + configID );
+			}
+		}
+
+		if( url == null ){
+			if( cv == null ){
+				throw new Error("Found no url on the CompareBitmap for test case " + testCase.testID);
+			}else{
+				throw new Error("Found no url on the ConditionalValue for test case " + testCase.testID + ", ConditionalValue: " + cv.toString());
+			}
+		}
+
+		// See if url ends with .png.  If not, create a file name.
+		if( url.lastIndexOf( ".png" ) != url.length - 4 ){			      
+			// Add a path separator if necessary.
+			if( url.lastIndexOf( "/" ) != url.length - 1 ){
+				url += "/";
+			}		
+
+			// Decide on a file name.
+			if( conditionalValues ){
+				// If we ended up with a matching CV, ask it to create a file name.
+				// Otherwise, go with the test ID.
+				// Keep this path alive until (if ever) ConditionalValues in CompareBitmaps have all been removed.
+				if(cv){
+					trace( "CompareBitmap: Asking the ConditionalValue to create the file name." );
+					url += cv.createFilename( testCase.testID );
+				} else {
+					trace( "CompareBitmap: Creating the file name from the testID." );
+					url += testCase.testID + ".png";
+				}
+			}else if( configID ){
+				// We have no ConditionalValues and we're running a known config,
+				// so use the config id in the suffix.
+				trace( "CompareBitmap: Creating the file name from the configID." );
+				url += testCase.testID + "@" + configID + ".png";
+			}else{
+				trace( "There is no file name, there are no Conditional Values, and there is no configID.  There's not much we can do now, is there?" );
+			}
+		}
+				
+		if (url != null && url.indexOf ("$testID") != -1) { 
+			trace ("Replacing $testID with " + UnitTester.currentTestID);
+			url = url.replace ("$testID", UnitTester.currentTestID);
+			trace ("result 2: " + url);
+		}
+
+		if (url == null)
+			trace ("URL was null at execute time");
+
+		if (commandconnection)
+			commandconnection.client = this;
+
+		var actualTarget:DisplayObject = DisplayObject(context.stringToObject(target));
+		if (!actualTarget)
+		{
+			testResult.doFail("Target " + target + " not found");
+			return true;
+		}
+
+		this.root = root;
+		this.context = context;
+		this.testResult = testResult;
+
+		if (UnitTester.createBitmapReferences)
+		{
+			if (UnitTester.checkEmbeddedFonts)
+			{
+				if (!checkEmbeddedFonts(actualTarget))
+				{
+					testResult.doFail ("Target " + actualTarget + " is using non-embedded or advanced anti-aliased fonts");	
+					return true;
+				}
+			}
+
+			writeBaselines(actualTarget);
+			return false;
+		}
+		else
+		{
+			readPNG();
+			return false;
+		}
+
+	}
+
+	private function getTargetSize(target:DisplayObject):Point
+	{
+		var width:Number;
+		var height:Number;
+
+    	try
+    	{    		
+    		width = target["getUnscaledWidth"]() * Math.abs(target.scaleX) * target.root.scaleX;
+    		height = target["getUnscaledHeight"]() * Math.abs(target.scaleY) * target.root.scaleY;
+		}
+		catch(e:ReferenceError)
+		{
+			width = target.width * target.root.scaleX;
+			height = target.height * target.root.scaleY;
+		}
+		return new Point(width, height);
+	}
+
+	// Given a displayObject, sets up the screenBits.
+	private function getScreenBits(target:DisplayObject):void{
+		try 
+		{
+			if (!stageText) {
+				var targetSize:Point = getTargetSize(target);
+				var stagePt:Point = target.localToGlobal(new Point(0, 0));
+            			var altPt:Point = target.localToGlobal(targetSize);
+            			stagePt.x = Math.min(stagePt.x, altPt.x);
+            			stagePt.y = Math.min(stagePt.y, altPt.y);
+				screenBits = new BitmapData(targetSize.x, targetSize.y);
+				screenBits.draw(target.stage, new Matrix(1, 0, 0, 1, -stagePt.x, -stagePt.y));
+			} else {
+
+				trace ("Using stagetext");
+				try { 
+					var tmpbm:BitmapData = target["textDisplay"]["captureBitmapData"]();
+					screenBits = new BitmapData(tmpbm.rect.width, tmpbm.rect.height, true, 0xFFFFFFFF);
+					screenBits.draw (tmpbm, new Matrix());
+
+				} catch (e:Error) {
+					trace ("Tried for StageText bitmap data, but it failed");
+					trace (e.getStackTrace());
+				}
+
+			}
+		}
+		catch (se:SecurityError)
+		{
+			UnitTester.hideSandboxes();
+			try
+			{
+				screenBits.draw(target.stage, new Matrix(1, 0, 0, 1, -stagePt.x, -stagePt.y));
+			}
+			catch (se2:Error)
+			{
+				try 
+				{
+					// if we got a security error and ended up here, assume we're in the
+					// genericLoader loads us scenario
+					screenBits.draw(target.root, new Matrix(1, 0, 0, 1, -stagePt.x, -stagePt.y));
+				}
+				catch (se3:Error)
+				{
+				}
+			}
+			UnitTester.showSandboxes();
+			var sb:Array = UnitTester.getSandboxBitmaps();
+			var n:int = sb.length;
+			for (var i:int = 0; i < n; i++)
+			{
+				mergeSandboxBitmap(target, stagePt, screenBits, sb[i]);
+			}
+		}
+		catch (e:Error)
+		{
+			testResult.doFail (e.getStackTrace());	
+		}
+	}
+
+	private var MAX_LC:int = 12000;
+	private var screenBits:BitmapData;
+	private var baselineBits:BitmapData;
+	
+	private var compareVal:Object;
+
+	public function comparePNG(target:DisplayObject):Boolean 
+	{ 
+		if (UnitTester.checkEmbeddedFonts)
+		{
+			if (!checkEmbeddedFonts(target))
+			{
+				testResult.doFail ("Target " + target + " is using non-embedded or advanced anti-aliased fonts");	
+				return true;
+			}
+		}
+
+		if (!reader.content)
+		{
+			testResult.doFail ("baseline image not available");
+			return true;
+		}
+
+		getScreenBits(target);
+
+		try
+		{
+			baselineBits = new BitmapData(reader.content.width, reader.content.height);
+			baselineBits.draw(reader.content, new Matrix());
+
+			compareVal = baselineBits.compare (screenBits);
+
+			if (compareVal is BitmapData && numColorVariances) {
+				compareVal = compareWithVariances(compareVal as BitmapData)
+			}
+
+			if (compareVal != 0)
+			{
+				trace ("compare returned" + compareVal);
+				
+				var req:URLRequest = new URLRequest();
+				if (UnitTester.isApollo) 
+				{
+					req.url = encodeURI2(CompareBitmap.adjustPath (url));
+				} 
+				else
+				{
+					req.url = url;
+					var base:String = normalizeURL(context.application.url);
+					base = base.substring(0, base.lastIndexOf("/"));
+					while (req.url.indexOf("../") == 0)
+					{
+						base = base.substring(0, base.lastIndexOf("/"));
+						req.url = req.url.substring(3);
+					}
+					
+					req.url = encodeURI2(base + "/" + req.url);
+				}
+								
+				req.url += ".xml";
+				xmlreader = new URLLoader();
+				xmlreader.addEventListener(Event.COMPLETE, readXMLCompleteHandler);
+				xmlreader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, readErrorHandler);
+				xmlreader.addEventListener(IOErrorEvent.IO_ERROR, readXMLIOErrorHandler);
+				xmlreader.load (req);	
+				return false;
+			}
+		}
+		catch (e:Error) 
+		{ 
+			testResult.doFail (e.getStackTrace());	
+		}
+		return true;
+	}
+	
+	private function readXMLCompleteHandler(event:Event):void
+	{
+		var actualTarget:DisplayObject = DisplayObject(context.stringToObject(target));
+		var s:String = getDisplayListXML(actualTarget).toXMLString();
+		if (s !== xmlreader.data)
+		{
+			testResult.doFail ("compare returned" + compareVal, absolutePathResult(url) + ".bad.png");
+
+			if (useRemoteDiffer)
+			{
+				sendImagesToDiffer();
+			}
+			else if (fileSuffix != "") 
+			{ 
+				writeBaselines (actualTarget);
+			}
+		else
+			stepComplete();
+	}
+
+	private function readXMLIOErrorHandler(event:Event):void
+	{
+		if (useRemoteDiffer)
+		{
+			sendImagesToDiffer();
+		} 
+		else if (fileSuffix != "") 
+		{ 
+			testResult.doFail ("compare returned" + compareVal, absolutePathResult(url) + ".bad.png");
+			var actualTarget:DisplayObject = DisplayObject(context.stringToObject(target));
+			writePNG (actualTarget);
+		}	
+	}
+		
+	private function mergeSandboxBitmap(target:DisplayObject, pt:Point, bm:BitmapData, obj:Object):void
+	{
+		var targetSize:Point = getTargetSize(target);
+		var sbm:BitmapData = new BitmapData(obj.width, obj.height);
+		var srcRect:Rectangle = new Rectangle(0, 0, obj.width, obj.height);
+		sbm.setPixels(srcRect, obj.bits);
+		var targetRect:Rectangle = new Rectangle(pt.x, pt.y, targetSize.x, targetSize.y);
+		var sbRect:Rectangle = new Rectangle(obj.x, obj.y, obj.width, obj.height);
+		var area:Rectangle = targetRect.intersection(sbRect);
+		if (area)
+			bm.copyPixels(sbm, srcRect, target.globalToLocal(area.topLeft));
+	}
+
+	private function sendImagesToDiffer():void
+	{
+		UnitTester.callback = stringifyScreen;
+	}
+
+	private var ba:ByteArray;
+	private function stringifyScreen():void
+	{
+		ba = screenBits.getPixels(screenBits.rect);
+		ba.position = 0;
+		connection.send("_ImageDiffer", "startScreenData", screenBits.width, screenBits.height, ba.length, UnitTester.currentTestID, UnitTester.currentScript);
+		UnitTester.callback = sendScreen;
+	}
+
+	private function sendScreen():void
+	{
+		if (ba.position + MAX_LC < ba.length)
+		{
+			connection.send("_ImageDiffer", "addScreenData", stringify(ba));
+			UnitTester.callback = sendScreen;
+		}
+		else
+		{
+			connection.send("_ImageDiffer", "addScreenData", stringify(ba));
+			UnitTester.callback = stringifyBase;
+		}
+	}
+
+	private function stringifyBase():void
+	{
+		ba = baselineBits.getPixels(baselineBits.rect);
+		ba.position = 0;
+		connection.send("_ImageDiffer", "startBaseData", baselineBits.width, baselineBits.height, ba.length);
+		UnitTester.callback = sendBase;
+	}
+
+	private function sendBase():void
+	{
+		if (ba.position + MAX_LC < ba.length)
+		{
+			connection.send("_ImageDiffer", "addBaseData", stringify(ba));
+			UnitTester.callback = sendBase;
+		}
+		else
+		{
+			connection.send("_ImageDiffer", "addBaseData", stringify(ba));
+			connection.send("_ImageDiffer", "compareBitmaps");
+		}
+	}
+
+	private function stringify(ba:ByteArray):String
+	{
+		var n:int = Math.min(ba.length - ba.position, MAX_LC);
+		var arr:Array = [];
+		for (var i:int = 0; i < n; i++)
+		{
+			var b:int = ba.readUnsignedByte();
+			arr.push(b.toString(16))
+		}
+		return arr.toString();
+	}
+
+	private function readCompleteHandler(event:Event):void
+	{
+		var actualTarget:DisplayObject = DisplayObject(context.stringToObject(target));
+		if (comparePNG(actualTarget))
+			stepComplete();
+	}
+
+	private function readErrorHandler(event:Event = null):void
+	{
+		var failString:String = "baseline image could not be read.";
+
+		failString += "  Creating image file as a .bad.png.";
+		var actualTarget:DisplayObject = DisplayObject(context.stringToObject(target));
+		getScreenBits(actualTarget);
+		writePNG(actualTarget);
+		testResult.doFail ( failString );
+		stepComplete();
+	}
+
+	/**
+	* Read a file and return a ByteArray that we can feed into reader (a Loader).
+	**/
+	public function loadFileBytes( file:File ):ByteArray{
+				
+		var fileStream:FileStream = new FileStream();
+		var file:File;
+		var ba:ByteArray = new ByteArray();
+		var bytesRead:int = 0;
+		var bytesAvail:int = 0;
+		
+		fileStream.open(file, FileMode.READ);
+		bytesAvail = fileStream.bytesAvailable;
+		
+		while( bytesAvail > 0 ){
+			fileStream.readBytes(ba, bytesRead, bytesAvail);
+			bytesAvail = fileStream.bytesAvailable;
+		}
+		
+		fileStream.close();
+
+		return ba;
+	}
+
+
+	public function readPNG():void
+	{
+		var req:URLRequest = new URLRequest();
+		var ba:ByteArray = null;
+		var file:File;
+		
+		// If iOS and AIR, let's try using file I/O instead of loader stuff. AIR on devices can be a pain with the url stuff.
+		if( UnitTester.isApollo && (UnitTester.cv.os.toLowerCase() == DeviceNames.IOS.toLowerCase()) ){
+			// Trim the leading ../ if we have it.
+			if ( url.indexOf ("../") == 0 ){
+				url = url.substring (3);
+			}
+			
+			file = File.documentsDirectory.resolvePath( url );
+			
+			if( !file.exists ){
+				readErrorHandler();
+				return;
+			}
+			
+			ba = loadFileBytes( file );
+			reader.loadBytes( ba );
+		}else{		
+			if (UnitTester.isApollo)
+			{
+				req.url = encodeURI2(CompareBitmap.adjustPath (url));
+			}
+			else
+			{
+				req.url = url;
+				var base:String = normalizeURL(context.application.url);
+				base = base.substring(0, base.lastIndexOf("/"));
+				while (req.url.indexOf("../") == 0)
+				{
+					base = base.substring(0, base.lastIndexOf("/"));
+					req.url = req.url.substring(3);
+				}
+				req.url = encodeURI2(base + "/" + req.url);
+			}
+		}
+		
+		reader = new Loader();
+		reader.contentLoaderInfo.addEventListener(Event.COMPLETE, readCompleteHandler);
+		reader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, readErrorHandler);
+		reader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, readErrorHandler);
+		//	req.url = encodeURI2(url);
+		// }
+		
+		trace ("readPNG:requesting url: " + req.url);			
+		reader.load (req);	
+		
+	}
+
+
+	public static var adjustPath:Function = function(url:String):String { return url; };
+
+
+
+
+    public function getPngByteArray(target:DisplayObject, bitmapData:BitmapData):ByteArray 
+	{
+		// add png headers
+		if (UnitTester.createBitmapReferences)
+		{
+			if (stageText == false) 
+			{
+				var targetSize:Point = getTargetSize(target);
+				var stagePt:Point = target.localToGlobal(new Point(0, 0));
+       	     			var altPt:Point = target.localToGlobal(targetSize);
+
+            			stagePt.x = Math.min(stagePt.x, altPt.x);
+            			stagePt.y = Math.min(stagePt.y, altPt.y);
+				bitmapData = new BitmapData(targetSize.x, targetSize.y);
+				bitmapData.draw(target.stage, new Matrix(1, 0, 0, 1, -stagePt.x, -stagePt.y));
+			} else {
+
+				trace ("stageText value: " + stageText);
+				try { 
+					bitmapData = target["textDisplay"]["captureBitmapData"]();
+					trace ("success asking StageText for bitmap data");
+				} catch (e:Error) {
+					trace ("Tried for StageText bitmap data, but it failed");
+					trace (e.getStackTrace());
+				}
+
+			}
+
+		} 
+		var png:MustellaPNGEncoder = new MustellaPNGEncoder();
+		var ba:ByteArray = png.encode (bitmapData);
+
+		return ba;
+	}
+
+
+	public function writeBaselines(target:DisplayObject, writeDisplayList:Boolean = true):void 
+	{
+		var req:URLRequest = new URLRequest();
+		writer = new URLLoader();
+		req.method = "POST";
+		
+		/**
+		 * either we got called here to write new baselines
+		 * or to save a .bad.png for investigation
+		 * in addition, with failures, we upload baseline and failure to a server
+		 */
+		if (UnitTester.createBitmapReferences) 
+		{	
+			fileSuffix = "";
+		} 
+		
+		
+		if (writeDisplayList)
+		{
+			var s:String = getDisplayListXML(target).toXMLString();
+			// request data goes on the URL Request
+			req.data = s;
+			
+			req.contentType = "text/xml";
+			if (UnitTester.isApollo) 
+			{ 
+				req.url = encodeURI2(UnitTester.bitmapServerPrefix + adjustWriteURI(adjustPath(url))) + fileSuffix + ".xml";
+			} else 
+			{
+				req.url = encodeURI2(UnitTester.bitmapServerPrefix + absolutePath(url)) + fileSuffix + ".xml";
+			}
+			trace ("writing url: " + req.url);
+			writer.addEventListener(Event.COMPLETE, writeXMLCompleteHandler);
+			writer.addEventListener(SecurityErrorEvent.SECURITY_ERROR, writeErrorHandler);
+			writer.addEventListener(IOErrorEvent.IO_ERROR, writeErrorHandler);
+			
+			writer.load (req);	
+		}
+	}
+	
+	private function writeXMLCompleteHandler(event:Event):void
+	{
+		var actualTarget:DisplayObject = DisplayObject(context.stringToObject(target));
+		writePNG(actualTarget);
+	}
+	
+	private function writePNG(target:DisplayObject):void
+	{
+		var req:URLRequest = new URLRequest();
+		writer = new URLLoader();
+		req.method = "POST";
+		
+		var ba:ByteArray = getPngByteArray(target, screenBits);			
+		trace ("image size: " + ba.length);
+
+		if( UnitTester.createBitmapReferences ){
+			fileSuffix = "";
+		}
+
+		if( UnitTester.writeBaselinesToDisk ){
+			// context.testDir: mobile/components/Button/properties/
+			// url: ../properties/baselines/button_android_test1_CustomName.png
+			var writePath:String = createWritePath( context.testDir, url + fileSuffix );
+			var deviceWritePath:String = UnitTester.mustellaWriteLocation + "/" + writePath;
+//			trace("*******deviceWritePath = " + deviceWritePath); 
+			var hostWritePath:String = writePath;
+			var file:File = new File ( deviceWritePath );
+			var fileStream:FileStream = new FileStream();
+			var hostCommand:String;
+
+			// open() opens synchronously, so we don't have to (and can't) use listeners.
+			fileStream.open(file, FileMode.WRITE);
+			fileStream.writeBytes(ba);
+			fileStream.close();
+
+			// Tell the host machine to copy the baseline image.
+			// Also, give it a URL with test info. to use when sending it to flexqa01's baseline server.
+			if( UnitTester.cv.os.toLowerCase() == DeviceNames.ANDROID.toLowerCase() ||
+				UnitTester.cv.os.toLowerCase() == DeviceNames.IOS.toLowerCase() ||
+				UnitTester.cv.os.toLowerCase() == DeviceNames.QNX.toLowerCase() ){
+				hostCommand = "CopyDeviceFile: FROM=" + deviceWritePath + " TO=" + hostWritePath;
+				
+				// If this is about creating bitmaps, skip the upload, we're done
+				if ( !UnitTester.createBitmapReferences ){
+					hostCommand += " SCREENURL=" + (UnitTester.urlAssemble ("screen", context.testDir, context.scriptName, this.testResult.testID, UnitTester.run_id));
+					hostCommand += " BASELINEURL=" + (UnitTester.urlAssemble ("baseline", context.testDir, context.scriptName, this.testResult.testID, UnitTester.run_id));
+				}
+
+				// Trace statements are parsed by the host.
+				trace( hostCommand );
+			}
+
+			if (UnitTester.createBitmapReferences){
+				stepComplete();
+			}
+
+			return;
+		}else{
+			trace("AIR CompareBitmap is called, but we're not writing to disk.  This is wrong.");
+		}
+    }
+
+	private function adjustWriteURI(url:String):String
+	{
+		var base:String = null;
+		
+		// For iOS, do something different.
+		if( UnitTester.isApollo && (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.IOS.toLowerCase()) ){
+			base = url;
+			
+			// Trim the leading ../ if we have it.
+			if ( base.indexOf ("../") == 0 ){
+				base = base.substring (3);
+			}
+			
+			base = File.documentsDirectory.resolvePath(base).nativePath;
+			
+			return base;
+		}else{
+			var pos:int = url.indexOf("file:///");
+			if (pos != 0)
+			{
+				return url;
+			}
+			url = url.substring(8);
+			pos = url.indexOf("|");
+
+			if (pos != 1)
+			{
+				return url;
+			}
+
+			var drive:String = url.substring(0, 1);
+			drive = drive.toLowerCase();
+			return drive + ":" + url.substring(2);
+		}
+	}
+
+	/**
+	 * Called by writePNG when, for example, we're testing on Android.
+	 * firstPart:	mobile/components/Button/properties/
+	 * secondPart:	../properties/baselines/button_android_test1_CustomName.png
+	 **/
+	private function createWritePath( firstPart:String, secondPart:String ):String{
+		var ret:String = "tests/" + firstPart.substring( 0, firstPart.lastIndexOf( "/" ) );
+		
+		var removeMe:String = ret.substring( ret.lastIndexOf( "/" ), ret.length ); // "properties"
+		ret += secondPart.substring( secondPart.indexOf( removeMe ) + removeMe.length, secondPart.length );
+
+		trace("createWritePath returning " + ret);
+		return ret;
+	}
+
+	private var screenDone:Boolean = false;
+	private var baselineDone:Boolean = false;
+
+	private function writeCompleteHandler(event:Event):void
+	{
+		trace("baseline write successful " + event);
+		stepComplete();
+	}
+
+	private function uploadCompleteHandler(event:Event):void
+	{
+		trace("screen image upload successful " + event);
+		screenDone = true;
+		checkForStepComplete();
+	}
+
+	private function upload2CompleteHandler(event:Event):void
+	{
+		trace("baseline image upload successful " + event);
+		baselineDone = true;
+		checkForStepComplete();
+	}
+
+	private function writeErrorHandler(event:Event):void
+	{
+		testResult.doFail ("error on baseline write: " + event);
+		trace("Image baseline write failed " + event);
+		if (UnitTester.createBitmapReferences)
+			stepComplete();
+	}
+	private function uploadErrorHandler(event:Event):void
+	{
+		testResult.doFail ("error on baseline write: " + event);
+		trace("Image screen upload failed " + event);
+		screenDone = true;
+		checkForStepComplete();
+	}
+
+	private function upload2ErrorHandler(event:Event):void
+	{
+		testResult.doFail ("error on baseline write: " + event);
+		trace("Image baseline upload failed " + event);
+		baselineDone = true;
+		checkForStepComplete();
+	}
+
+	private function checkForStepComplete():void
+	{
+
+		if (baselineDone && screenDone)
+			stepComplete();
+
+
+	}
+
+	/**
+	 *  customize string representation
+	 */
+	override public function toString():String
+	{
+		var s:String = (UnitTester.createBitmapReferences) ? "CreateBitmap: " : "CompareBitmap";
+		if (target)
+			s += ": target = " + target;
+		if (url)
+			s += ", url = " + url;
+		return s;
+	}
+
+	private function absolutePathResult(url:String):String
+	{ 
+		var base:String = null;
+
+		if( UnitTester.isApollo && (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.IOS.toLowerCase()) ){
+			base = url;
+			
+			// Trim the leading ../ if we have it.
+			if ( base.indexOf ("../") == 0 ){
+				base = base.substring (3);
+			}
+			
+			base = File.documentsDirectory.resolvePath(base).nativePath;
+			
+		} else if( UnitTester.isApollo && (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.ANDROID.toLowerCase()) ){
+
+			/// code doing what it does now: 
+				
+			var fPath:String = createWritePath( context.testDir, url + fileSuffix );
+
+			/// clean up 
+			if (fPath.indexOf ("tests/") == 0)
+				fPath = fPath.substring (5);
+
+			/// clean up 
+			/// it probably has the .bad.png suffix
+			if (fPath.indexOf (".bad.png") > 0)
+				fPath = fPath.substring (0, fPath.length-".bad.png".length);
+				
+			base = fPath;
+
+		}else{
+			if (UnitTester.isApollo) {
+				base = adjustWriteURI(adjustPath (url));
+			}else{
+				base = context.application.url;
+			}
+
+			base = normalizeURL(base);
+			base = base.substring (base.indexOf ("mustella/tests")+14);
+
+			if (!UnitTester.isApollo) {
+				base = base.substring(0, base.lastIndexOf("/"));
+			
+				var tmp:String = url;
+
+				while (tmp.indexOf("../") == 0){
+						base = base.substring(0, base.lastIndexOf("/"));
+						tmp = tmp.substring(3);
+				}
+
+				base += "/" + tmp;
+			}
+		}
+		
+		return base;
+	}
+
+	
+	private function absolutePath(url:String):String
+	{
+		var swf:String = normalizeURL(root.loaderInfo.url);
+
+		var pos:int = swf.indexOf("file:///");
+		if (pos != 0)
+		{
+			trace("WARNING: unexpected swf url format, no file:/// at offset 0");
+			return url;
+		}
+		swf = swf.substring(8);
+		pos = swf.indexOf("|");
+		if (pos != 1)
+		{
+			trace("WARNING: unexpected swf url format, no | at offset 1 in: " + swf);
+			// assume we're on a mac or other unix box, it will do no harm
+			return "/" + swf.substring(0, swf.lastIndexOf ("/")+1)  + url;
+		}
+
+		var drive:String = swf.substring(0, 1);
+		drive = drive.toLowerCase();
+		return drive + ":" + swf.substring(2, swf.lastIndexOf("/") + 1) + url;
+	}
+
+
+        public static function normalizeURL(url:String):String
+        {
+        	var results:Array = url.split("/[[DYNAMIC]]/");
+        	return results[0];
+        }
+
+
+	public function keepGoing():void
+	{
+		trace("keepgoing", url, hasEventListener("stepComplete"));
+		stepComplete();
+	}
+
+	private function encodeURI2(s:String):String
+	{
+		var pos:int = s.lastIndexOf("/");
+		if (pos != -1)
+		{
+			var fragment:String = s.substring(pos + 1);
+			s = s.substring(0, pos + 1);
+			fragment= encodeURIComponent(fragment);
+			s = s + fragment;
+		}
+		return s;
+	}
+
+	private function compareWithVariances(bm:BitmapData):Object
+	{
+
+		var allowed:int = numColorVariances * UnitTester.pixelToleranceMultiplier;
+		var n:int = bm.height;
+		var m:int = bm.width;
+
+		for (var i:int = 0; i < n; i++)
+		{
+			for (var j:int = 0; j < m; j++)
+			{
+				var pix:int = bm.getPixel(j, i);
+				if (pix)
+				{
+					if(!ignoreMaxColorVariance)
+					{
+						var red:int = pix >> 16 & 0xff;
+						var green:int = pix >> 8 & 0xff;
+						var blue:int = pix & 0xff;
+						if (red & 0x80)
+							red = 256 - red;
+						if (blue & 0x80)
+							blue = 256 - blue;
+						if (green & 0x80)
+							green = 256 - green;
+						if (red > maxColorVariance ||
+							blue > maxColorVariance ||
+							green > maxColorVariance)
+						{
+							return bm;
+						}
+					}
+					allowed--;
+					if (allowed < 0)
+					{
+						return bm;
+					}
+				}
+			}
+		}
+		return 0;
+	}
+
+	private function checkEmbeddedFonts(target:Object):Boolean
+	{
+		if ("rawChildren" in target)
+			target = target.rawChildren;
+
+		if (target is TextField)
+		{
+			if (target.embedFonts == false)
+				return false;
+			if (target.antiAliasType == "advanced")
+				return false;
+			return true;
+		}
+		else if ("numChildren" in target)
+		{
+			var n:int = target.numChildren;
+			for (var i:int = 0; i < n; i++)
+			{
+				if (!checkEmbeddedFonts(target.getChildAt(i)))
+					return false;
+			}
+		}
+		
+		return true;
+	}
+
+	override protected function stepComplete():void 
+	{ 
+
+                if (baselineBits != null)
+                        baselineBits.dispose();
+                if (screenBits != null)
+                        screenBits.dispose();
+
+
+		reader=null;
+		writer=null;
+		
+		super.stepComplete();
+
+
+	}
+	
+	/****** DisplayList Comparision ******/
+	protected function getDisplayListProperties(d:DisplayObject, noMask:Boolean = false):XML
+	{
+		var xml:XML;
+		var n:int;
+		var i:int;
+		var childXML:XML;
+		var s:String = getQualifiedClassName(d);
+		s = s.replace("::", ".");
+		xml = new XML("<" + s + "/>");
+		s = d.transform.concatenatedColorTransform.toString();
+		if (s != identityColorTransform)
+			xml.@concatenatedColorTransform = s;
+		if (d.transform.matrix)
+		{
+			s = d.transform.matrix.toString();
+			if (s != identityMatrix)
+			{
+				if (s.indexOf("(a=1, b=0, c=0, d=1, ") == -1)
+					xml.@matrix = s;
+			}
+		}
+		else
+		{
+			s = d.transform.matrix3D.rawData.toString();
+			xml.@matrix3D = s;
+		}
+		if (d.x != 0)
+			xml.@x = d.x;
+		if (d.y != 0)
+			xml.@y = d.y;
+		xml.@width = d.width;
+		xml.@height = d.height;
+		if (xml.visible == false)
+			xml.@visible = "false";
+		if (d.mask && !noMask)
+		{
+			xml.mask = <mask/>;
+			childXML = getDisplayListProperties(d.mask, true);
+			xml.mask.appendChild = childXML;
+		}
+		if (d.scrollRect)
+		{
+			s = d.scrollRect.toString();
+			xml.@scrollRect = s;
+		}
+		if (d.blendMode && d.blendMode != "normal")
+			xml.@blendMode = d.blendMode;
+		if (d.cacheAsBitmap)
+			xml.@cacheAsBitmap = "true";
+		if (d.filters && d.filters.length > 0)
+		{
+			s = d.filters.toString();
+			xml.@filters = s;
+		}
+		if (d.opaqueBackground)
+			xml.@opaqueBackground = "true";
+		if (d.scale9Grid)
+		{
+			s = d.scale9Grid.toString();
+			xml.@scale9Grid = s;
+		}
+		if (d is TextField)
+		{
+			xml.htmlText = TextField(d).htmlText;
+		}
+		if (d is Loader && Loader(d).contentLoaderInfo.contentType.indexOf("image") != -1)
+		{
+			s = Loader(d).contentLoaderInfo.url;
+			s = s.substring(s.lastIndexOf("/") + 1);
+			xml.@loaderbitmap = s;
+		}
+		if (d is TextLine)
+		{
+			var tl:TextLine = TextLine(d);
+			xml.@ascent = tl.ascent;
+			xml.@descent = tl.descent;
+			xml.@atomCount = tl.atomCount;
+			xml.@hasGraphicElement = tl.hasGraphicElement;
+			if (tl.textBlock)
+			{
+				var tb:TextBlock = TextLine(d).textBlock;
+				var ce:ContentElement = tb.content;
+				s = ce.rawText.substr(tl.textBlockBeginIndex, tl.rawTextLength);
+				xml.@text = s;
+			}
+		}
+		
+		if (d is IRawChildrenContainer)
+		{
+			var rawChildren:IChildList = IRawChildrenContainer(d).rawChildren;
+			n = rawChildren.numChildren;
+			for (i = 0; i < n; i++)
+			{
+				childXML = getDisplayListProperties(rawChildren.getChildAt(i));
+				xml.appendChild(childXML);				
+			}
+		}
+		else if (d is DisplayObjectContainer)
+		{
+			var doc:DisplayObjectContainer = d as DisplayObjectContainer;
+			n = doc.numChildren;
+			for (i = 0; i < n; i++)
+			{
+				childXML = getDisplayListProperties(doc.getChildAt(i));
+				xml.appendChild(childXML);				
+			}
+		}
+		return xml;
+	}
+	
+	// scan entire display list, but only dump objects intersecting target
+	protected function getDisplayListXML(target:DisplayObject):XML
+	{
+		var n:int;
+		var i:int;
+		var child:DisplayObject;
+		var childXML:XML;
+		
+		var doc:DisplayObjectContainer = DisplayObjectContainer(target.root);
+		var xml:XML = <DisplayList />;
+		if (doc is IRawChildrenContainer)
+		{
+			var rawChildren:IChildList = IRawChildrenContainer(doc).rawChildren;
+			n = rawChildren.numChildren;
+			for (i = 0; i < n; i++)
+			{
+				child = rawChildren.getChildAt(i);
+				if (target.hitTestObject(child))
+				{
+					childXML = getDisplayListProperties(child);
+					xml.appendChild(childXML);	
+				}
+			}
+		}
+		else 
+		{
+			n = doc.numChildren;
+			for (i = 0; i < n; i++)
+			{
+				child = doc.getChildAt(i);
+				if (target.hitTestObject(child))
+				{
+					childXML = getDisplayListProperties(child);
+					xml.appendChild(childXML);									
+				}
+			}
+		}
+		return xml;
+	}
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/ApolloFilePath.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/ApolloFilePath.as b/mustella/src/main/flex/ApolloFilePath.as
new file mode 100644
index 0000000..47a66c0
--- /dev/null
+++ b/mustella/src/main/flex/ApolloFilePath.as
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.utils.*;
+import flash.net.*;
+import flash.events.*;
+import flash.display.*;
+import flash.desktop.NativeApplication;
+import flash.geom.Matrix;
+import flash.geom.Point;
+import flash.filesystem.File;
+
+[Mixin]
+/**
+ * Apollo doesn't return anything useable from the loaderInfo.url: 
+ * it looks like this: app-context://my.swf
+ * The received url in a test, which is looks like "../Properties/baselines/my.png"
+ * won't load. Apparently, the "../" is offensive. To get the fully qualified path, 
+ * we need the Apollo File class.  That's what this Mixin supplies.
+ * Also, Apollo like soft exits, and won't re-launch after a kill (java's destroy process). 
+ * so exit by calling the window's close method. 
+ */
+public class ApolloFilePath 
+{ 
+
+	public static var _root:DisplayObject;
+
+	public static function init(root:DisplayObject):void
+	{
+		UnitTester.isApollo = true;
+		CompareBitmap.adjustPath = apolloAdjust;
+		
+		/// the exit method has to be gentler for apollo, too
+		UnitTester.exitWhenDone = true;
+		UnitTester.exit = apolloExit;
+		_root=root;
+
+	}
+
+	
+	/**
+	 * gets the url from CompareBitmap; 
+	 * creates fully qualified path using flash File class. 
+	 */
+	public static function apolloAdjust(url:String):String
+	{
+
+		var swf:String = _root.loaderInfo.url;
+		var f:File = new File (swf);
+		// clean it up: 
+		var myPattern:RegExp = /\\/g;
+		var path:String;
+		
+		if( UnitTester.cv.os == DeviceNames.ANDROID ){
+			// AIR for Android returns empty string for nativePath (on purpose).  Use url instead.
+			// See https://zerowing.corp.adobe.com/display/airlinux/Resource+Mapping.
+			path = f.url;
+		}else{
+			path = f.nativePath;
+			path = path.replace (":", "|");
+		}
+
+		path = path.replace (myPattern, "/");			
+
+		// yank off the swfs directory, which we're in
+		path = path.substr (0, path.lastIndexOf ("/")-1);
+		path = path.substr (0, path.lastIndexOf ("/"));
+
+		if (url.indexOf ("../")==0)
+			url = url.substring (2);
+
+		if (url.indexOf ("/..")==0)
+		{
+			url = url.substring (3);
+			path = path.substr (0, path.lastIndexOf ("/"));
+		}
+
+		/// create the final url
+		path = path + url;
+
+		if( UnitTester.cv.os == DeviceNames.ANDROID ){
+			// AIR for Android needs it to start with app:/, so just return at this point.
+			return path;
+		}else{
+			return "file:///" + path;		
+		}
+	}
+
+	/**
+	 * call the native window close method
+	 */
+	public static function apolloExit(): void 
+	{ 
+		/// hack around an issue that apollo seems to hang when it exits
+		/// with a socket still open. Arbitrary sleep isn't attractive
+		/// but we never received a response from Runner after sending 
+		/// ScriptDone
+		setTimeout (real_apolloExit, 1500);
+	}
+
+
+	public static function real_apolloExit(): void 
+	{ 
+
+		// Call the more general exit
+		trace ("Doing an apollo exit");
+		NativeApplication.nativeApplication.exit(1);
+	}
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/Assert.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/Assert.as b/mustella/src/main/flex/Assert.as
new file mode 100644
index 0000000..b5b5ead
--- /dev/null
+++ b/mustella/src/main/flex/Assert.as
@@ -0,0 +1,185 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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;
+}
+
+import org.apache.flex.utils.ObjectUtil;
+
+/**
+ *  The base class for all Asserts that the test
+ *  has succeeded or not.
+ *  All asserts should be derived from this class because
+ *  the timing of the wait events is different than that for
+ *  other TestSteps.  See AssertPropertyValue for an example.
+ *
+ *  Asserts don't execute their steps until after the
+ *  waitEvent has been received (if there is one) whereas
+ *  non-Asserts execute their steps and then wait for the
+ *  waitEvent.
+ */
+public class Assert extends TestStep
+{
+	// list of properties we don't examine in determining equivalence
+	private static var excludeList:Array = [
+						"stage",
+						"systemManager",
+						"parent",
+						"owner",
+						"target",
+						"currentTarget"
+			];
+
+	/**
+	 *  Called by the test case in case you need to set up before execute()
+	 */
+	public function preview(root:Object, context:UnitTester, testCase:TestCase, testResult:TestResult):void
+	{
+		this.root = root;
+		this.context = context;
+		this.testCase = testCase;
+		this.testResult = testResult;
+
+		if (waitEvent)
+		{
+			if (waitTarget == null)
+				waitTarget = target;
+			var actualTarget:Object = context.stringToObject(waitTarget);
+			// it is ok for us to not have an actualTarget during preview
+			// someone may be waiting for the object to be created.
+			if (actualTarget)
+				actualTarget.addEventListener(waitEvent, eventListener);
+		}
+	}
+
+	/**
+	 *  All subclasses should override this method
+	 */
+    COMPILE::SWF
+	override public function execute(root:Object, context:UnitTester, testCase:TestCase, testResult:TestResult):Boolean
+	{
+		this.root = root;
+		this.context = context;
+		this.testCase = testCase;
+		this.testResult = testResult;
+
+		if (waitEvent)
+		{
+			if (waitTarget == null)
+				waitTarget = target;
+
+			var actualTarget:IEventDispatcher = context.stringToObject(waitTarget) as IEventDispatcher;
+			if (!actualTarget)
+			{
+				testResult.doFail("waitTarget " + waitTarget + " not found");
+				return true;
+			}
+			actualTarget.removeEventListener(waitEvent, eventListener);
+			if (numEvents == 0)
+			{
+				actualTarget.addEventListener(waitEvent, waitEventHandler);
+				testCase.setExpirationTime(getTimer() + timeout);
+				return false;
+			}
+		}
+
+		doStep();
+		return true;
+	}
+
+	/**
+	 *  Storage for numEvents
+	 */
+	private var numEvents:int = 0;
+
+	/**
+	 *  The name of the object to test.
+	 */
+	public var target:String;
+
+	/**
+	 *  The method that gets called back when the event we're waiting on fires
+	 */
+    COMPILE::SWF
+	override protected function waitEventHandler(event:Event):void
+	{
+		doStep();
+		super.waitEventHandler(event);
+	}
+
+	/**
+	 *	The event listener
+	 */
+	private function eventListener(event:Event):void
+	{
+		testCase.setExpirationTime(0);
+
+		numEvents++;
+	}
+
+	/**
+	 *  Called by the test case in case you need to clean up after execute()
+	 */
+	public function cleanup():void
+	{
+	}
+
+	/**
+	 *  convert everything to strings (like null)
+	 */
+	protected function valueToString(value:*):String
+	{
+		if (value == null)
+			return "null";
+		var s:String;
+
+		if (value is Number)
+		{
+			if ((value is int) || (value is uint))
+				s = value.toString();
+			else
+				s = value.toFixed(6);
+		}
+		else
+			s = value.toString();
+
+		if (s == "[object Object]")
+			s = ObjectUtil._toString(value);
+		return s;
+	}
+
+    protected function contains(value:*, expectedError:ErrorArray):Boolean {
+        if (expectedError && expectedError.parts && expectedError.parts.length)
+            for (var i:uint = 0; i < expectedError.parts.length; i++) {
+                if (valueToString(value).indexOf(valueToString(expectedError.parts[i])) == -1)
+                    return false;
+            }
+
+        return true;
+    }
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/AssertError.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/AssertError.as b/mustella/src/main/flex/AssertError.as
new file mode 100644
index 0000000..59fd925
--- /dev/null
+++ b/mustella/src/main/flex/AssertError.as
@@ -0,0 +1,90 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.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="valueExpression", type="flash.events.Event")]
+
+/**
+ *  Tests that the value of a property is as expected
+ *  MXML attributes:
+ *  value
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional)
+ */
+public class AssertError extends Assert
+{
+	/**
+	 *  See if the property has the correct value
+	 */
+	override protected function doStep():void
+	{
+		if (hasEventListener("valueExpression"))
+		{
+			context.resetValue();
+			try
+			{
+				dispatchEvent(new RunCodeEvent("valueExpression", root["document"], context, testCase, testResult));
+			}
+			catch (e1:Error)
+			{
+				TestOutput.logResult("Exception thrown evaluating value expression.");
+				testResult.doFail (e1.getStackTrace());
+				return;
+			}
+			value = context.value;
+			if (!context.valueChanged)
+				TestOutput.logResult("WARNING: value was not set by valueExpression.  'value=' missing from expression?");
+		}
+
+		if (errorArray) {
+            var errors:ErrorArray = new ErrorArray(errorArray);
+			if (!contains(testCase.lastError, errors))
+				testResult.doFail("Expected Error contains " + valueToString(errors) + ", got " + valueToString(testCase.lastError));
+        } else if (valueToString(testCase.lastError) != valueToString(value))
+            testResult.doFail("Expected Error " + valueToString(value) + ", got " + valueToString(testCase.lastError));
+	}
+
+	/**
+	 *  The value the property should have.
+	 */
+	public var value:*;
+
+    public var errorArray:Array;
+
+	/**
+	 *  customize string representation
+	 */
+	override public function toString():String
+	{
+		var s:String = "AssertError";
+		return s;
+	}
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/AssertEvent.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/AssertEvent.as b/mustella/src/main/flex/AssertEvent.as
new file mode 100644
index 0000000..0abb8ce
--- /dev/null
+++ b/mustella/src/main/flex/AssertEvent.as
@@ -0,0 +1,219 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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;
+
+/**
+ *  Tests that the value of a property is as expected
+ *  MXML attributes:
+ *  target
+ *  eventName
+ *  eventType
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional)
+ */
+public class AssertEvent extends Assert
+{
+
+	private var eventListenerListening:Boolean = false;
+
+	/**
+	 *  Test the value of a property, log result if failure.
+	 */
+	override public function preview(root:DisplayObject, context:UnitTester, testCase:TestCase, testResult:TestResult):void
+	{
+		this.root = root;
+		this.context = context;
+		this.testCase = testCase;
+		this.testResult = testResult;
+
+		var actualTarget:Object = context.stringToObject(target);
+		if (actualTarget)
+		{
+			actualTarget.addEventListener(eventName, eventListener);
+			eventListenerListening = true;
+			testCase.cleanupAsserts.push(this);
+		}
+		// we don't fail during preview, we will fail later anyway
+	}
+
+	/**
+	 *  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;
+
+		if (numEvents == 1 && (numExpectedEvents == 1 || numExpectedEvents == -1))
+		{
+			doStep();
+			return true;
+		}
+
+		if ((numEvents < numExpectedEvents) || (numEvents == 0 && numExpectedEvents == -1))
+		{
+			var actualTarget:IEventDispatcher = context.stringToObject(target) as IEventDispatcher;
+			if (!actualTarget)
+			{
+				testResult.doFail("target " + target + " not found");
+				return true;
+			}
+			// if eventListener is not hooked up, make sure it is
+			if (!eventListenerListening)
+			{
+				actualTarget.addEventListener(eventName, eventListener);
+				eventListenerListening = true;
+				testCase.cleanupAsserts.push(this);
+			}
+			// don't remove eventListener because we still want to check for extra events
+			// but add this in so we can wait until we get the required number of events
+			actualTarget.addEventListener(eventName, waitEventHandler);
+			testCase.setExpirationTime(getTimer() + timeout);
+			waitEvent = eventName;
+			waitTarget = target;
+			return false;
+		}
+
+		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;
+		}
+
+		context.lastEvent = lastEvent;
+
+		if (numExpectedEvents != -1 && numEvents != numExpectedEvents)
+		{
+			testResult.doFail("Event " + eventName + " received " + numEvents + " times");
+			return;
+		}
+
+		if (getQualifiedClassName(lastEvent).indexOf(eventClass) == -1)
+			testResult.doFail("Event " + eventName + " of class " + getQualifiedClassName(lastEvent));
+	}
+
+	/**
+	 *  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++;
+
+		if (numExpectedEvents != -1 && numEvents > numExpectedEvents)
+		{
+			testResult.doFail ("Event " + eventName + " received " + numEvents + " times");	
+			return;
+		}
+	}
+
+	/**
+	 *  Test the value of a property, log result if failure.
+	 */
+	override 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 = "AssertEvent";
+		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)
+		{
+			testCase.setExpirationTime(getTimer() + timeout);
+			return;
+		}
+
+		// finish up
+		waitEvent = eventName;
+		waitTarget = target;
+		super.waitEventHandler(event);
+
+	}
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/AssertEventPropertyValue.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/AssertEventPropertyValue.as b/mustella/src/main/flex/AssertEventPropertyValue.as
new file mode 100644
index 0000000..fa732fd
--- /dev/null
+++ b/mustella/src/main/flex/AssertEventPropertyValue.as
@@ -0,0 +1,112 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.utils.*;
+
+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="valueExpression", type="flash.events.Event")]
+
+
+/**
+ *  Tests that the value of a property is as expected
+ *  MXML attributes:
+ *  target (ignored)
+ *  propertyName
+ *  value
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional)
+ */
+public class AssertEventPropertyValue extends Assert
+{
+	/**
+	 *  See if the property has the correct value
+	 */
+	override protected function doStep():void
+	{
+		if (hasEventListener("valueExpression"))
+		{
+			context.resetValue();
+			try
+			{
+				dispatchEvent(new RunCodeEvent("valueExpression", root["document"], context, testCase, testResult));
+			}
+			catch (e1:Error)
+			{
+				TestOutput.logResult("Exception thrown evaluating value expression.");
+				testResult.doFail (e1.getStackTrace());	
+				return;
+			}
+			value = context.value;
+			if (!context.valueChanged)
+				TestOutput.logResult("WARNING: value was not set by valueExpression.  'value=' missing from expression?");
+		}
+
+		var val:Object;
+		if (!context.lastEvent)
+		{
+			testResult.doFail ("No event fired prior to this step");
+			return;
+		}
+		if (!(propertyName in context.lastEvent))
+		{
+			testResult.doFail ("Event does not have property " + propertyName);
+			return;
+		}
+		val = context.lastEvent[propertyName];
+
+		if (valueToString(val) != valueToString(value))
+		{
+			testResult.doFail ( getQualifiedClassName(context.lastEvent) + "." + propertyName + " " + valueToString(val)  + " != " + valueToString(value));
+		} 
+	}
+
+	/**
+	 *  The name of the property to test
+	 */
+	public var propertyName:String;
+
+	/**
+	 *  The value the property should have
+	 */
+	public var value:Object;
+
+	/**
+	 *  customize string representation
+	 */
+	override public function toString():String
+	{
+		var s:String = "AssertEventPropertyValue";
+		if (target)
+			s += ": target = " + target;
+		if (propertyName)
+			s += ", propertyName = " + propertyName;
+		return s;
+	}
+}
+
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/27eb06f5/mustella/src/main/flex/AssertMethodValue.as
----------------------------------------------------------------------
diff --git a/mustella/src/main/flex/AssertMethodValue.as b/mustella/src/main/flex/AssertMethodValue.as
new file mode 100644
index 0000000..96b7cfb
--- /dev/null
+++ b/mustella/src/main/flex/AssertMethodValue.as
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.events.EventDispatcher;
+import flash.utils.*;
+import flash.net.*;
+import flash.events.*;
+import flash.display.*;
+import flash.geom.Matrix;
+
+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="method", type="flash.events.Event")]
+
+/**
+ *  Instead of a property, we use an event so the MXML
+ *  compiler will wrap the code in a function for us
+ */
+[Event(name="valueExpression", type="flash.events.Event")]
+
+/**
+*  Vector of conditionalValue objects.
+**/
+[DefaultProperty("conditionalValues")]
+
+/**
+ *  Tests that the value of a property is as expected
+ *  MXML attributes:
+ *  target (not used)
+ *  method
+ *  value
+ *  waitTarget (optional)
+ *  waitEvent (optional)
+ *  timeout (optional)
+ */
+public class AssertMethodValue extends Assert
+{
+	public var conditionalValues:Vector.<ConditionalValue> = null;
+
+	/**
+	 *  Test the value of a property, log result if failure.
+	 */
+	override protected function doStep():void
+	{
+		var cv:ConditionalValue = null;
+		var dispatcher:EventDispatcher = this;
+
+		context.resetValue();
+
+		// Use MultiResult to determine the proper value (or valueExpression, below).
+		if(conditionalValues){
+			cv = new MultiResult().chooseCV(conditionalValues);
+			if(cv){
+				value = cv.value;
+				dispatcher = cv;
+			}
+		}
+
+		// Execute the method.
+		try
+		{
+			dispatchEvent(new RunCodeEvent("method", root["document"], context, testCase, testResult));
+		}
+		catch (e:Error)
+		{
+			TestOutput.logResult("Exception thrown executing method.");
+			testResult.doFail (e.getStackTrace());
+			return;
+		}
+		if (!context.valueChanged)
+			TestOutput.logResult("WARNING: value was not set by method.  'value=' missing from expression?");
+		var methodValue:Object = context.value;
+
+		// Execute the valueExpression.
+		if (dispatcher.hasEventListener("valueExpression"))
+		{
+			context.resetValue();
+			try
+			{
+				dispatcher.dispatchEvent(new RunCodeEvent("valueExpression", root["document"], context, testCase, testResult));
+			}
+			catch (e1:Error)
+			{
+				TestOutput.logResult("Exception thrown evaluating value expression.");
+				testResult.doFail (e1.getStackTrace());
+				return;
+			}
+			value = context.value;
+			if (!context.valueChanged)
+				TestOutput.logResult("WARNING: value was not set by valueExpression.  'value=' missing from expression?");
+		}
+
+        if (errorArray) {
+            var errors:ErrorArray = new ErrorArray(errorArray);
+            if (!contains(methodValue, errors))
+                testResult.doFail("method returned " + valueToString(methodValue) + ", expected it contains " + valueToString(errors));
+        } else if (valueToString(methodValue) != valueToString(value))
+            testResult.doFail("method returned " + valueToString(methodValue) + ", expected " + valueToString(value));
+	}
+
+	/**
+	 *  The value the method should return
+	 */
+	public var value:*;
+
+    public var errorArray:Array;
+
+	/**
+	 *  customize string representation
+	 */
+	override public function toString():String
+	{
+		var s:String = "AssertMethodValue (method cannot be shown) ";
+		return s;
+	}
+
+}
+
+}