| <?xml version="1.0" encoding="utf-8"?> |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" |
| implements="flexunit.flexui.IFlexWriter" creationComplete="onCreationComplete()" xmlns:geom="flash.geom.*" currentState="StackTraceView"> |
| <mx:Script> |
| <![CDATA[ |
| |
| import mx.collections.ArrayCollection; |
| |
| import flexunit.framework.TestCase; |
| import flexunit.framework.Test; |
| import flexunit.framework.AssertionFailedError; |
| import flexunit.flexui.TestRunner; |
| import flexunit.flexui.IFlexWriter; |
| |
| import mx.collections.ListCollectionView; |
| |
| import flash.system.*; |
| |
| public var beforeTest:Function; |
| public var afterTest:Function; |
| |
| public var suiteMetaData:Object; |
| |
| private static const END_OF_TEST_RUN : String = "<endOfTestRun/>"; |
| private static const END_OF_TEST_ACK : String ="<endOfTestRunAck/>"; |
| |
| private var _totalTests:uint = 0; |
| private var _totalErrors:uint = 0; |
| private var _totalFailures:uint = 0; |
| private var _numTestsRun:uint = 0; |
| |
| public var test:Test; |
| |
| private var reports : Object = new Object(); |
| private var socket : XMLSocket; |
| private var connectionTries : int = 0; |
| private var connectionTryMax : int = 10; |
| |
| public var reportXML : Boolean = true; |
| [Inspectable] |
| public var port : uint = 1024; |
| [Inspectable] |
| public var server : String = "127.0.0.1"; |
| |
| public function onCreationComplete():void |
| { |
| suiteMetaData = new Object(); |
| } |
| |
| public function startTest():void |
| { |
| |
| flexunit.flexui.TestRunner.afterTest = afterTest; |
| flexunit.flexui.TestRunner.beforeTest = beforeTest; |
| |
| if( test != null ) |
| { |
| _totalTests = test.countTestCases(); |
| |
| progressBar.minimum = 0; |
| testFailures.dataProvider = new Array(); |
| allTestsList.dataProvider = new Array(); |
| |
| updateLabels(); |
| |
| flexunit.flexui.TestRunner.run( test, this ); |
| } |
| } |
| |
| private function updateLabels():void |
| { |
| runLabel.htmlText = "<b>Run:</b> "+_numTestsRun.toString()+"/"+_totalTests.toString(); |
| errorsLabel.htmlText = "<b>Errors:</b> "+_totalErrors.toString(); |
| failuresLabel.htmlText = "<b>Failures:</b> "+_totalFailures.toString(); |
| } |
| |
| private function updateProgress():void |
| { |
| progressBar.setProgress( _numTestsRun, _totalTests ); |
| |
| if( _totalErrors > 0 || _totalFailures > 0 ) |
| progressBar.setStyle("barColor",0xFF0000); |
| } |
| |
| private function addFailureToList( test:Test, error:Error ):void |
| { |
| var t:TestCase = test as TestCase; |
| if( t != null ) |
| { |
| ListCollectionView(testFailures.dataProvider).addItem( {label: t.toString(), error:error} ); |
| testFailures.selectedIndex = testFailures.dataProvider.length; |
| testFailures.verticalScrollPosition = testFailures.maxVerticalScrollPosition; |
| onTestSelected(); |
| } |
| } |
| |
| private function onTestSelected():void |
| { |
| var list:List = (testTabs.selectedIndex == 0) ? testFailures : allTestsList; |
| var errorString:String; |
| |
| if( list.selectedItem != null ) |
| if( list.selectedItem.error != null ) |
| { |
| this.currentState = "StackTraceView"; |
| errorString = list.selectedItem.error.getStackTrace(); |
| if (errorString == null) |
| { |
| errorString = list.selectedItem.error.message; |
| } |
| stackTrace.text = errorString; |
| testDetails.text = "Stack Trace"; |
| } |
| else |
| { |
| this.currentState = "ResultsView"; |
| //testDetails.text = "Test Details"; |
| var dp_TestResults:ArrayCollection = new ArrayCollection(); |
| var dp_MemUsage:ArrayCollection = new ArrayCollection(); |
| resultsGrid.dataProvider = dp_TestResults; |
| rawMemGrid.dataProvider = dp_MemUsage; |
| var setUpDuration : String = list.selectedItem.setUpDuration.toFixed(3); |
| var middleDuration : String = list.selectedItem.middleDuration.toFixed(3); |
| var tearDownDuration : String = list.selectedItem.tearDownDuration.toFixed(3); |
| dp_TestResults.addItem({stage: "SetUp", duration: setUpDuration, memUsage: list.selectedItem.setUpMemFinal - list.selectedItem.setUpMemInitial}); |
| dp_TestResults.addItem({stage: "Middle", duration: middleDuration, memUsage: list.selectedItem.middleMemFinal - list.selectedItem.middleMemInitial}); |
| dp_TestResults.addItem({stage: "TearDown", duration: tearDownDuration, memUsage: list.selectedItem.tearDownMemFinal - list.selectedItem.tearDownMemInitial}); |
| dp_MemUsage.addItem({initialMem: list.selectedItem.setUpMemInitial, finalMem: list.selectedItem.tearDownMemFinal, memDifference: (list.selectedItem.tearDownMemFinal - list.selectedItem.setUpMemInitial)}); |
| dp_TestResults = null; |
| dp_MemUsage = null; |
| } |
| } |
| |
| private function addTestToList( success:Boolean, test:Test, error:Error = null ):void |
| { |
| var t:TestCase = test as TestCase; |
| if( t != null ) |
| { |
| var label:String = ( success ) ? "[PASS] " : "[FAIL] "; |
| // get mem data from test here - don't add it to the data provider |
| ListCollectionView(allTestsList.dataProvider).addItem( {label:label+t.toString(), |
| error:error, |
| setUpDuration:t.setUpDuration, |
| middleDuration:t.middleDuration, |
| tearDownDuration:t.tearDownDuration, |
| setUpMemInitial:t.setUpMemInitial, |
| setUpMemFinal:t.setUpMemFinal, |
| middleMemInitial:t.middleMemInitial, |
| middleMemFinal:t.middleMemFinal, |
| tearDownMemInitial:t.tearDownMemInitial, |
| tearDownMemFinal:t.tearDownMemFinal |
| } ); |
| allTestsList.selectedIndex = allTestsList.dataProvider.length; |
| allTestsList.verticalScrollPosition = allTestsList.maxVerticalScrollPosition; |
| onTestSelected(); |
| } |
| } |
| |
| //--------------------------------------------------------------------- |
| // IFlexWriter Methods |
| //--------------------------------------------------------------------- |
| |
| public function onTestStart( test:Test ) : void |
| { |
| titlePanel.title = "Test Runner (running: " + test.toString() + ")"; |
| if (reportXML) |
| addMethod( test ); |
| } |
| |
| public function onTestEnd( test:Test ) : void |
| { |
| //only run if reportXML is enabled |
| if (reportXML) |
| { |
| if (test is TestCase) |
| { |
| var time : Number = TestCase(test).setUpDuration + TestCase(test).middleDuration + TestCase(test).tearDownDuration; |
| |
| // Add time to the method. |
| var methodObject : Object = getMethod( test ); |
| methodObject.time = time; |
| methodObject.setuptime = TestCase(test).setUpDuration; |
| methodObject.setupmeminitial = TestCase(test).setUpMemInitial; |
| methodObject.setupmemfinal = TestCase(test).setUpMemFinal; |
| methodObject.middletime = TestCase(test).middleDuration; |
| methodObject.middlememinitial = TestCase(test).middleMemInitial; |
| methodObject.middlememfinal = TestCase(test).middleMemFinal; |
| methodObject.teardowntime = TestCase(test).tearDownDuration; |
| methodObject.teardownmeminitial = TestCase(test).tearDownMemInitial; |
| methodObject.teardownmemfinal = TestCase(test).tearDownMemFinal; |
| } |
| |
| Security.loadPolicyFile("xmlsocket:\\" + server + ":" + port); |
| // If we have finished running all the tests send the results. |
| if ( (_numTestsRun+1) == _totalTests ) |
| { |
| sendResults(); |
| } |
| } |
| _numTestsRun++; |
| updateLabels(); |
| updateProgress(); |
| titlePanel.title = "Test Runner"; |
| } |
| |
| public function onAllTestsEnd() : void |
| { |
| progressBar.setProgress(100,100); |
| if( _totalErrors == 0 && _totalFailures == 0 ) |
| progressBar.setStyle("barColor",0x00FF00); |
| } |
| |
| public function onSuccess( test:Test ):void |
| { |
| addTestToList( true, test ); |
| } |
| |
| public function onError( test:Test, error:Error ) : void |
| { |
| _totalErrors++; |
| addFailureToList( test, error ); |
| addTestToList( false, test, error ); |
| |
| if (reportXML) |
| addError( test, error); |
| } |
| |
| public function onFailure( test:Test, error:AssertionFailedError ) : void |
| { |
| _totalFailures++; |
| addFailureToList( test, error ); |
| addTestToList( false, test, error ); |
| |
| if (reportXML) |
| addFailure( test, AssertionFailedError(error)); |
| } |
| |
| //--------------------------------------------------------------------- |
| // JUnitRunner Methods |
| //--------------------------------------------------------------------- |
| |
| /** |
| * Add the currently executing method on a Test to the internal report |
| * model. |
| * @param test the Test. |
| */ |
| private function addMethod( test : Test ) : void |
| { |
| var reportObject : Object = getReport( test ); |
| reportObject.tests++; |
| |
| var methodName : String = TestCase(test).toString(); |
| var methodsObject : Object = reportObject.methods; |
| |
| var methodObject : Object = new Object(); |
| methodsObject[ methodName ] = methodObject; |
| |
| methodObject.classname = test.className; |
| methodObject.metaData = TestCase(test).metaData; |
| methodObject.name = methodName; |
| methodObject.time = 0.0; |
| methodObject.setuptime = 0.0; |
| methodObject.setupmeminitial = 0.0; |
| methodObject.setupmemfinal = 0.0; |
| methodObject.middletime = 0.0; |
| methodObject.middlememinitial = 0.0; |
| methodObject.middlememfinal = 0.0; |
| methodObject.teardowntime = 0.0; |
| methodObject.teardownmeminitial = 0.0; |
| methodObject.teardownmemfinal = 0.0; |
| |
| } |
| |
| /** |
| * Called when an error occurs. |
| * @param test the Test that generated the error. |
| * @param error the Error. |
| */ |
| public function addError( test : Test, error : Error ) : void |
| { |
| // Increment error count. |
| var report : Object = getReport( test ); |
| report.errors++; |
| |
| // Add the error to the method. |
| var methodObject : Object = getMethod( test ); |
| |
| var errorObject : Object = new Object(); |
| methodObject.error = errorObject; |
| |
| errorObject.type = getClassName( error ); |
| errorObject.message = error.message; |
| } |
| |
| /** |
| * Called when a failure occurs. |
| * @param test the Test that generated the failure. |
| * @param error the failure. |
| */ |
| public function addFailure( test : Test, error : AssertionFailedError ) : void |
| { |
| // Increment failure count. |
| var report : Object = getReport( test ); |
| report.failures++; |
| |
| // Add the failure to the method. |
| var methodObject : Object = getMethod( test ); |
| |
| var failureObject : Object = new Object(); |
| methodObject.failure = failureObject; |
| |
| failureObject.type = getClassName( error ); |
| failureObject.message = error.message; |
| } |
| |
| /** |
| * Return the fully qualified class name for an Object. |
| * @param obj the Object. |
| * @return the class name. |
| */ |
| private function getClassName( obj : Object ) : String |
| { |
| var description : XML = describeType( obj ); |
| var className : Object = description.@name; |
| |
| return className[ 0 ]; |
| } |
| |
| /** |
| * Return the method Object from the internal report model for the |
| * currently executing method on a Test. |
| * @param test the Test. |
| * @return the method Object. |
| */ |
| private function getMethod( test : Test ) : Object |
| { |
| var reportObject : Object = getReport( test ); |
| var methodsObject : Object = reportObject.methods; |
| |
| var methodName : String = TestCase(test).toString() |
| |
| return methodsObject[ methodName ]; |
| } |
| |
| /** |
| * Return the report Object from the internal report model for the |
| * currently executing Test. |
| * @param Test the test. |
| */ |
| private function getReport( test : Test ) : Object |
| { |
| var reportObject : Object; |
| var className : String = test.className; |
| |
| // Check we have a report Object for the executing Test, if not |
| // create a new one. |
| if ( reports[ className ] ) |
| { |
| reportObject = reports[ className ]; |
| } |
| else |
| { |
| reportObject = new Object(); |
| reportObject.name = className; |
| reportObject.errors = 0; |
| reportObject.failures = 0; |
| reportObject.tests = 0; |
| reportObject.time = getTimer()/1000; |
| reportObject.methods = new Object(); |
| |
| reports[ className ] = reportObject; |
| } |
| |
| return reportObject; |
| } |
| |
| /** |
| * Sends the results. This sends the reports back to the controlling Ant |
| * task using an XMLSocket. |
| */ |
| public function sendResults() : void |
| { |
| // Open an XML socket. |
| socket = new XMLSocket(); |
| socket.addEventListener( Event.CONNECT, handleConnect ); |
| socket.addEventListener( DataEvent.DATA, dataHandler ); |
| socket.addEventListener( IOErrorEvent.IO_ERROR, handleIOError ); |
| socket.addEventListener( SecurityErrorEvent.SECURITY_ERROR, handleSecurityError ); |
| socket.connect( server, port ); |
| } |
| |
| private function handleSecurityError( event : Event ) : void |
| { |
| var e:SecurityErrorEvent = event as SecurityErrorEvent; |
| throw new Error("SecurityErrorEvent on Connect-> " + e.target + ": " + |
| e.type + ". " + e.text + "."); |
| } |
| |
| private function handleIOError( event : Event ) : void |
| { |
| if(connectionTries <= connectionTryMax){ |
| connectionTries++; |
| sendResults(); |
| }else{ |
| var e:IOErrorEvent = event as IOErrorEvent; |
| throw new Error("IOErrorEvent on Connect-> " + e.target + ": " + |
| e.type + ". " + e.text + ". " + socket.connected + " " + server + ":" + port); |
| } |
| } |
| |
| /** |
| * Event listener to handle data received on the socket. |
| * @param event the DataEvent. |
| */ |
| private function dataHandler( event : DataEvent ) : void |
| { |
| var data : String = event.data; |
| |
| // If we received an acknowledgement finish-up. |
| if ( data == END_OF_TEST_ACK ) |
| { |
| exit(); |
| } |
| } |
| |
| private function handleConnect( event : Event ) : void |
| { |
| for ( var className : String in reports ) |
| { |
| // Create the XML report. |
| var xml : XML = createXMLReport( reports[ className ] ); |
| |
| // Send the XML report. |
| socket.send( xml.toXMLString() ); |
| } |
| |
| // Send the end of reports terminator. |
| socket.send( END_OF_TEST_RUN ); |
| } |
| |
| /** |
| * Create the XML report. |
| * @param obj the report Object. |
| * @return the XML report. |
| */ |
| private function createXMLReport( obj : Object ) : XML |
| { |
| // Create the test suite element. |
| var testSuite : XML = createTestSuite( obj ); |
| |
| // Create the test case elements. |
| var methodsObject : Object = obj.methods; |
| for ( var methodName : String in methodsObject ) |
| { |
| var methodObject : Object = methodsObject[ methodName ]; |
| var testCase : XML = createTestCase( methodObject ); |
| |
| // Create the failure element. |
| if ( methodObject.failure ) |
| { |
| var failureObject : Object = methodObject.failure; |
| var failure : XML = createFailure( failureObject ); |
| |
| testCase = testCase.appendChild( failure ); |
| } |
| |
| // Create the error element. |
| if ( methodObject.error ) |
| { |
| var errorObject : Object = methodObject.error; |
| var error : XML = createError( errorObject ); |
| |
| testCase = testCase.appendChild( error ); |
| } |
| |
| testSuite = testSuite.appendChild( testCase ); |
| } |
| |
| return testSuite; |
| } |
| |
| /** |
| * Create the test suite XML. |
| * @return the XML. |
| */ |
| private function createTestSuite( obj : Object ) : XML |
| { |
| var name : String = obj.name; |
| var errors : uint = obj.errors; |
| var failures : uint = obj.failures; |
| var tests : uint = obj.tests; |
| var time : int = getTimer()/1000 - obj.time; |
| var methods : Object = obj.methods; |
| var flashVersion : String = Capabilities.version; |
| var flashLanguage : String = Capabilities.language; |
| var flashPlayerType : String = Capabilities.playerType; |
| var flashConfig : String; |
| |
| setDebugFlag(); |
| if (debugMode) |
| { |
| flashConfig = "release debugger"; |
| } |
| else |
| { |
| flashConfig = "release"; |
| } |
| |
| var xml : XML = |
| <testsuite |
| errors={ errors } |
| failures={ failures } |
| name={ formatQualifiedClassName( name ) } |
| tests={ tests } |
| time={ time } |
| flashversion={ flashVersion } |
| flashlanguage={ flashLanguage } |
| flashconfig={ flashConfig } |
| flashplayertype={ flashPlayerType }/>; |
| |
| // Add any other meta-data |
| for (var attrib:Object in suiteMetaData) |
| { |
| xml.@[attrib] = suiteMetaData[attrib]; |
| } |
| |
| return xml; |
| } |
| |
| /** |
| * Create the test case XML. |
| * @return the XML. |
| */ |
| private function createTestCase( obj : Object ) : XML |
| { |
| var classname : String = obj.classname; |
| var name : String = obj.name; |
| var time : Number = obj.time; |
| var setuptime : Number = obj.setuptime; |
| var setupmeminitial : Number = obj.setupmeminitial; |
| var setupmemfinal : Number = obj.setupmemfinal; |
| var middletime : Number = obj.middletime; |
| var middlememinitial : Number = obj.middlememinitial; |
| var middlememfinal : Number = obj.middlememfinal; |
| var teardowntime : Number = obj.teardowntime; |
| var teardownmeminitial : Number = obj.teardownmeminitial; |
| var teardownmemfinal : Number = obj.teardownmemfinal; |
| |
| var xml : XML = |
| <testcase |
| classname={ formatQualifiedClassName( classname ) } |
| name={ name } |
| time={ time.toFixed(3) } |
| setuptime={ setuptime.toFixed(3) } |
| setupmeminitial={ setupmeminitial } |
| setupmemfinal={ setupmemfinal } |
| middletime={ middletime.toFixed(3) } |
| middlememinitial={ middlememinitial } |
| middlememfinal={ middlememfinal } |
| teardowntime={ teardowntime.toFixed(3) } |
| teardownmeminitial={ teardownmeminitial } |
| teardownmemfinal={ teardownmemfinal }/>; |
| |
| // Add any other meta-data |
| for (var attrib:Object in obj.metaData) |
| { |
| xml.@[attrib] = obj.metaData[attrib]; |
| } |
| |
| return xml; |
| } |
| |
| /** |
| * Create the failure XML. |
| * @return the XML. |
| */ |
| private function createFailure( obj : Object ) : XML |
| { |
| var type : String = obj.type; |
| var message : String = obj.message; |
| |
| var xml : XML = |
| <failure type={ formatQualifiedClassName( type ) }> |
| { message } |
| </failure>; |
| |
| return xml; |
| } |
| |
| /** |
| * Create the test error XML. |
| * @return the XML. |
| */ |
| private function createError( obj : Object ) : XML |
| { |
| var type : String = obj.type; |
| var message : String = obj.message; |
| |
| var xml : XML = |
| <error type={ formatQualifiedClassName( type ) }> |
| { message } |
| </error>; |
| |
| return xml; |
| } |
| |
| /** |
| * Exit the test runner and close the player. |
| */ |
| private function exit() : void |
| { |
| // Close the socket. |
| socket.close(); |
| } |
| |
| private function formatQualifiedClassName( className : String ) : String |
| { |
| var pattern : RegExp = /::/; |
| |
| return className.replace( pattern, "." ); |
| } |
| |
| private function setDebugFlag(): void |
| { |
| var e:Error = new Error(); |
| var s:String = e.getStackTrace(); |
| try |
| { |
| var i:int = s.indexOf("setDebugFlag"); |
| if (s.charAt(i + 14) == '[') |
| debugMode = true; |
| } |
| catch (err:Error) // error is thrown in release player |
| { |
| debugMode = false; |
| } |
| } |
| |
| private var debugMode : Boolean = false; |
| |
| ]]> |
| </mx:Script> |
| <mx:states> |
| <mx:State name="StackTraceView" basedOn=""> |
| <mx:AddChild relativeTo="{stackTrace}" position="lastChild" creationPolicy="all" /> |
| </mx:State> |
| <mx:State name="ResultsView" basedOn=""> |
| <mx:RemoveChild target="{stackTrace}" /> |
| <mx:AddChild relativeTo="{vbox1}" position="lastChild"> |
| <mx:DataGrid id="resultsGrid" rowCount="3" width="100%" draggableColumns="false" sortableColumns="false"> |
| <mx:columns> |
| <mx:DataGridColumn headerText="Stage" dataField="stage"/> |
| <mx:DataGridColumn headerText="Duration (s)" dataField="duration"/> |
| <mx:DataGridColumn headerText="Memory Usage (MB)" dataField="memUsage"/> |
| </mx:columns> |
| </mx:DataGrid> |
| </mx:AddChild> |
| <mx:AddChild relativeTo="{vbox1}" position="lastChild"> |
| <mx:DataGrid id="rawMemGrid" editable="false" width="100%" rowCount="1" draggableColumns="false" sortableColumns="false"> |
| <mx:columns> |
| <mx:DataGridColumn headerText="Initial Memory (MB)" dataField="initialMem"/> |
| <mx:DataGridColumn headerText="Final Memory (MB)" dataField="finalMem"/> |
| <mx:DataGridColumn headerText="Total Usage (MB)" dataField="memDifference"/> |
| </mx:columns> |
| </mx:DataGrid> |
| </mx:AddChild> |
| </mx:State> |
| </mx:states> |
| <mx:Canvas width="100%" height="100%"> |
| <mx:Panel id="titlePanel" backgroundAlpha="0.4" title="Test Runner" paddingBottom="10" width="100%" height="100%"> |
| <mx:HBox width="100%"> |
| <mx:HBox width="50%" height="20" horizontalAlign="left" paddingLeft="0" paddingRight="10"> |
| <mx:ProgressBar width="100%" trackHeight="12" id="progressBar" labelPlacement="left" label="Running..." mode="manual" /> |
| </mx:HBox> |
| <mx:HBox width="50%" height="20" horizontalAlign="right"> |
| <mx:Label id="runLabel" paddingRight="10" /> |
| <mx:Label id="errorsLabel" text="Errors: 0" paddingRight="10" /> |
| <mx:Label id="failuresLabel" text="Failures: 0" /> |
| </mx:HBox> |
| </mx:HBox> |
| <mx:HDividedBox width="100%" height="100%"> |
| <mx:TabNavigator id="testTabs" change="onTestSelected()" width="50%" |
| height="100%" paddingBottom="0" paddingLeft="0" paddingRight="0" |
| paddingTop="0" creationPolicy="all" > |
| <mx:Canvas label="Failures" width="100%" height="100%"> |
| <mx:List id="testFailures" width="100%" height="100%" borderStyle="none" editable="true" |
| change="onTestSelected()" /> |
| </mx:Canvas> |
| <mx:Canvas label="All Tests" width="100%" height="100%"> |
| <mx:List id="allTestsList" width="100%" height="100%" borderStyle="none" |
| change="onTestSelected()" /> |
| </mx:Canvas> |
| </mx:TabNavigator> |
| <mx:VBox width="50%" height="100%" id="vbox1"> |
| <mx:HBox width="100%" height="15" paddingBottom="0" |
| paddingLeft="0" paddingRight="0" paddingTop="0"> |
| <mx:Label id="testDetails"></mx:Label> |
| </mx:HBox> |
| <mx:TextArea id="stackTrace" width="100%" height="100%" borderStyle="none" wordWrap="false" /> |
| </mx:VBox> |
| </mx:HDividedBox> |
| </mx:Panel> |
| </mx:Canvas> |
| </mx:VBox> |