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 <aha...@apache.org> Authored: Thu Sep 21 11:46:09 2017 -0700 Committer: Alex Harui <aha...@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 <= 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; + } + +} + +}