You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by pi...@apache.org on 2014/10/12 23:58:10 UTC

[5/9] Add FactoryImportTest.as and FlowModelTest.as

http://git-wip-us.apache.org/repos/asf/flex-tlf/blob/7961f332/automation_tests/src/UnitTest/Tests/FlowModelTest.as
----------------------------------------------------------------------
diff --git a/automation_tests/src/UnitTest/Tests/FlowModelTest.as b/automation_tests/src/UnitTest/Tests/FlowModelTest.as
index ea0d482..0af28df 100644
--- a/automation_tests/src/UnitTest/Tests/FlowModelTest.as
+++ b/automation_tests/src/UnitTest/Tests/FlowModelTest.as
@@ -18,967 +18,985 @@
 ////////////////////////////////////////////////////////////////////////////////
 package UnitTest.Tests
 {
-	import UnitTest.ExtendedClasses.TestDescriptor;
-	import UnitTest.ExtendedClasses.TestSuiteExtended;
-	import UnitTest.ExtendedClasses.VellumTestCase;
-	import UnitTest.Fixtures.TestConfig;
-	
-	import flash.display.Bitmap;
-	import flash.display.BitmapData;
-	import flash.display.DisplayObjectContainer;
-	import flash.display.Sprite;
-	import flash.geom.Rectangle;
-	import flash.text.engine.FontDescription;
-	import flash.text.engine.FontWeight;
-	import flash.text.engine.TextLine;
-	import flash.utils.ByteArray;
-	import flash.utils.getTimer;
-	
-	import flashx.textLayout.compose.StandardFlowComposer;
-	import flashx.textLayout.compose.TextFlowLine;
-	import flashx.textLayout.container.ContainerController;
-	import flashx.textLayout.edit.EditManager;
-	import flashx.textLayout.edit.IEditManager;
-	import flashx.textLayout.elements.Configuration;
-	import flashx.textLayout.elements.DivElement;
-	import flashx.textLayout.elements.FlowElement;
-	import flashx.textLayout.elements.FlowLeafElement;
-	import flashx.textLayout.elements.GlobalSettings;
-	import flashx.textLayout.elements.InlineGraphicElement;
-	import flashx.textLayout.elements.LinkElement;
-	import flashx.textLayout.elements.ListElement;
-	import flashx.textLayout.elements.ListItemElement;
-	import flashx.textLayout.elements.ParagraphElement;
-	import flashx.textLayout.elements.SpanElement;
-	import flashx.textLayout.elements.TCYElement;
-	import flashx.textLayout.elements.TextFlow;
-	import flashx.textLayout.formats.BlockProgression;
-	import flashx.textLayout.formats.Direction;
-	import flashx.textLayout.formats.FormatValue;
-	import flashx.textLayout.formats.ITextLayoutFormat;
-	import flashx.textLayout.formats.ListStyleType;
-	import flashx.textLayout.formats.TextAlign;
-	import flashx.textLayout.formats.TextLayoutFormat;
-	import flashx.textLayout.property.*;
-	import flashx.textLayout.tlf_internal;
-	import flashx.undo.IUndoManager;
-	import flashx.undo.UndoManager;
-	
-	import mx.core.FTETextField;
-	import mx.utils.LoaderUtil;
+    import UnitTest.ExtendedClasses.TestDescriptor;
+    import UnitTest.ExtendedClasses.TestSuiteExtended;
+    import UnitTest.ExtendedClasses.VellumTestCase;
+    import UnitTest.Fixtures.TestConfig;
+
+    import flash.display.Bitmap;
+    import flash.display.BitmapData;
+    import flash.display.DisplayObjectContainer;
+    import flash.display.Sprite;
+    import flash.geom.Rectangle;
+    import flash.text.engine.FontDescription;
+    import flash.text.engine.FontWeight;
+    import flash.text.engine.TextLine;
+    import flash.utils.ByteArray;
+    import flash.utils.getTimer;
+
+    import flashx.textLayout.compose.StandardFlowComposer;
+    import flashx.textLayout.compose.TextFlowLine;
+    import flashx.textLayout.container.ContainerController;
+    import flashx.textLayout.edit.EditManager;
+    import flashx.textLayout.edit.IEditManager;
+    import flashx.textLayout.elements.Configuration;
+    import flashx.textLayout.elements.DivElement;
+    import flashx.textLayout.elements.FlowElement;
+    import flashx.textLayout.elements.FlowLeafElement;
+    import flashx.textLayout.elements.GlobalSettings;
+    import flashx.textLayout.elements.InlineGraphicElement;
+    import flashx.textLayout.elements.LinkElement;
+    import flashx.textLayout.elements.ListElement;
+    import flashx.textLayout.elements.ListItemElement;
+    import flashx.textLayout.elements.ParagraphElement;
+    import flashx.textLayout.elements.SpanElement;
+    import flashx.textLayout.elements.TCYElement;
+    import flashx.textLayout.elements.TextFlow;
+    import flashx.textLayout.formats.BlockProgression;
+    import flashx.textLayout.formats.Direction;
+    import flashx.textLayout.formats.FormatValue;
+    import flashx.textLayout.formats.ITextLayoutFormat;
+    import flashx.textLayout.formats.ListStyleType;
+    import flashx.textLayout.formats.TextAlign;
+    import flashx.textLayout.formats.TextLayoutFormat;
+    import flashx.textLayout.property.*;
+    import flashx.textLayout.tlf_internal;
+    import flashx.undo.IUndoManager;
+    import flashx.undo.UndoManager;
+
+    import mx.core.FTETextField;
+    import mx.utils.LoaderUtil;
 
     import org.flexunit.asserts.assertTrue;
 
     use namespace tlf_internal;
 
-	public class FlowModelTest extends VellumTestCase
-	{
-		public function FlowModelTest(methodName:String, testID:String, testConfig:TestConfig, testXML:XML = null)
-		{
-			super(methodName, testID, testConfig);
-
-			// Note: These must correspond to a Watson product area (case-sensitive)
-			metaData.productArea = "Text Container";
-			metaData.productSubArea = "Flow";
-		}
-
-		public static function suite(testConfig:TestConfig, ts:TestSuiteExtended):void
-		{
-			if (testConfig.writingDirection[0] == BlockProgression.TB && testConfig.writingDirection[1] == Direction.LTR)
-			{
-	   			ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "basicAPITest1", testConfig ) );
-	   			ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "basicAPITest2", testConfig ) );
-	   			ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "basicAPITest3", testConfig ) );
-	   			ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "basicAPITest4", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "basicAPITest5", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "textFlowHostCharacterFormat", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "iterateParagraphForward", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "iterateParagraphBackward", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "cascadeValidation", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "elementMovingTest", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "emptyTextFlowTests", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "emptyElementCopyTests", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "fontMappingTest", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "insertLinkNoUpdateAPI", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "insertLinkNoUpdateViaEditManager", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "testUndo", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "testFindControllerIndexAtPosition", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "testFTETextField", testConfig ) );
-				
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "listItemInsertion", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "replaceChildrenTest", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "resolveFontLookupTest", testConfig ) );
-				ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "bindableSpan", testConfig ) );
-				//ts.addTestDescriptor (new TestDescriptor (FlowModelTest, "softKeyboardFlagTest", testConfig ) );
-			}
-   		}
-
-   		private var beginTime:int;
-   		private function beginAPITest():void
-   		{
-   			beginTime = getTimer();
-			SelManager.selectRange(-1,-1);
-   		}
-   		private function endAPITest():void
-   		{
-			SelManager.selectRange(0,0);
-			SelManager.textFlow.flowComposer.updateAllControllers();
-   		}
-
-		/**
-		 * Direct usage of the flow APIs. No validation - just look for crashes.
-		 * This test changes the color of the entire text.
-		 */
-		public function basicAPITest1():void
-		{
-			// set the color on the entire flow
-			beginAPITest();
-			SelManager.textFlow.color = 0xff0000;
-			endAPITest();
-		}
-
-		/**
-		 * Direct usage of the flow APIs. No validation - just look for crashes.
-		 * This test deletes every other character in the entire text.
-		 */
-		public function basicAPITest2():void
-		{
-			// delete every other character in the textflow
-			beginAPITest();
-
-			var idx:int = 0;
-			while (idx < SelManager.textFlow.textLength)
-			{
-				var span:SpanElement = SelManager.textFlow.findLeaf(idx) as SpanElement;
-				if (span)
-				{
-					var spanStart:int = span.getAbsoluteStart();
-					span.replaceText(idx-spanStart,idx-spanStart+1,null);
-				}
-				idx++;
-
-			}
-			endAPITest();
-		}
-
-		/**
-		 * Direct usage of the flow APIs. No validation - just look for crashes.
-		 * This test adds another paragraph to the flow containing the text "Hello World"
-		 * in 24 point font.
-		 */
-		public function basicAPITest3():void
-		{
-			// add a paragraph
-			beginAPITest();
-			var p:ParagraphElement = new ParagraphElement();
-			var s:SpanElement = new SpanElement();
-			s.text = "Hello world";
-			s.fontSize = 24;
-			p.replaceChildren(0,0,s);
-			SelManager.textFlow.replaceChildren(SelManager.textFlow.numChildren,SelManager.textFlow.numChildren,p);
-			endAPITest();
-		}
-
-		/**
-		 * This test performs a series of changes and validates the changes after they're performed.
-		 */
-		public function basicAPITest4():void
-		{
-			// more comprehensive set of tests - several manipulations to the flow and then display it
-
-			// generic begin to an API test
-			beginAPITest();
-
-			// get the textflow
-			var textFlow:TextFlow = SelManager.textFlow;
-
-			// remove all the textflow children
-			textFlow.replaceChildren(0,textFlow.numChildren);
-			assertTrue("expected no elements on the flow, but found " + textFlow.numChildren,
-						textFlow.numChildren == 0);
-
-			// create a paragraph
-			var p:ParagraphElement = new ParagraphElement();
-
-			// create a span
-			var s:SpanElement = new SpanElement();
-			s.text = "Hello world. ";
-			s.fontSize = 24;
-
-			// split the span
-			var nextSpan:SpanElement = s.splitAtPosition(6) as SpanElement;
-			// set the color - color can be a string or an unsigned integer
-			nextSpan.color = "0xff";
-			assertTrue("expected that the color would be 255, but found " + uint(nextSpan.format.color),
-						uint(nextSpan.format.color) == 255);
-
-			// put the two spans in the paragraph
-			p.replaceChildren(0,0,s);
-			/*CONFIG::debug { assertTrue("debugCheckFlowElement() failed after adding one span",
-										p.debugCheckFlowElement() == 0); }     */
-			p.replaceChildren(1,1,nextSpan);
-			/*CONFIG::debug { assertTrue("debugCheckFlowElement() failed after adding second span",
-										p.debugCheckFlowElement() == 0); }  */
-			assertTrue("expected the element count to be 2, but it was " + p.numChildren,
-						p.numChildren == 2);
-
-			// add another span
-			s = new SpanElement();
-			s.text = "Start:"
-			p.replaceChildren(0,0,s);
-		/*	CONFIG::debug { assertTrue("debugCheckFlowElement() failed after adding third span",
-										p.debugCheckFlowElement() == 0); }      */
-
-			// put the paragraph in the TextFlow
-			textFlow.replaceChildren(0,0,p);
-			assertTrue("text flow should have one element but has " + textFlow.numChildren,
-						textFlow.numChildren == 1);
-		/*	CONFIG::debug { assertTrue("debugCheckFlowElement() failed after adding para to flow",
-										textFlow.debugCheckFlowElement() == 0) }   */
-
-			// make another paragraph
-			p = new ParagraphElement();
-			s = new SpanElement();
-			p.replaceChildren(0,0,s);
-			s.text="NEW FIRST PARAGRAPH";
-
-			// set the paragraph attributes directly
-			p.textIndent = 20;
-			// set the paragraph attributes via clone and set
-			var pa:TextLayoutFormat = new TextLayoutFormat(p.format);
-			pa.textAlign = TextAlign.RIGHT;
-			p.format = pa;
-
-			// into the textFlow at the beginning
-			textFlow.replaceChildren(0,0,p);
-			/*CONFIG::debug {assertTrue("debugCheckFlowElement() failed after adding para to beginning",
-										textFlow.debugCheckFlowElement() == 0); }     */
-
-			// generic end to an API test
-			endAPITest();
-		}
-
-		/**
-		 * This test inserts an FE in the middle of a paragraph
-		 */
-		public function basicAPITest5():void
-		{
-			// more comprehensive set of tests - several manipulations to the flow and then display it
-
-			// generic begin to an API test
-			beginAPITest();
-
-			// get the textflow
-			var textFlow:TextFlow = SelManager.textFlow;
-
-			// create a paragraph empty the flow and insert it
-			var p:ParagraphElement = new ParagraphElement();
-			textFlow.replaceChildren(0,textFlow.numChildren,p);
-
-			// create a span
-			var s:SpanElement = new SpanElement();
-			s.text = "Hello world. ";
-			s.fontSize = 24;
-
-			// put it in the paragraph
-			p.replaceChildren(0,0,s);
-
-			// split the span
-			var nextSpanElement:SpanElement = s.splitAtPosition(6) as SpanElement;
-			assertTrue("Incorrect elementCount after split", p.numChildren == 2);
-
-			// create and insert an image between the spans
-			var image:InlineGraphicElement = new InlineGraphicElement();
-			image.width = 19;
-			image.height = 19;
-			image.source = LoaderUtil.createAbsoluteURL(baseURL,"../../test/testFiles/assets/surprised.png");
-			p.replaceChildren(1,1,image);
-
-			assertTrue("failed length on new InlineGraphicElement", image.textLength == 1);
-			assertTrue("failed elementCount after image insert", p.numChildren == 3);
-			assertTrue("bad first child",p.getChildAt(0) is SpanElement);
-			assertTrue("bad first child",p.getChildAt(1) is InlineGraphicElement);
-			assertTrue("bad first child",p.getChildAt(2) is SpanElement);
-			
-			// set a userStyle on a ContainerController
-			var saveFormat:ITextLayoutFormat = ContainerController.containerControllerInitialFormat;
-			try
-			{
-				ContainerController.containerControllerInitialFormat = null;
-				var controller:ContainerController = new ContainerController(new Sprite());
-				controller.setStyle("foo","blah");
-			}
-			catch (e:Error)
-			{
-				ContainerController.containerControllerInitialFormat = saveFormat;
-				throw(e);			
-			}
-			ContainerController.containerControllerInitialFormat = saveFormat;
-
-			// generic end to an API test
-			endAPITest();
-		}
-
-		/** Test setting hostCharacterFormat on the TextFlow */
-		public function textFlowHostCharacterFormat():void
-		{
-			// generic begin to an API test
-			beginAPITest();
-
-			// get the textflow
-			var textFlow:TextFlow = SelManager.textFlow;
-			var leaf:FlowLeafElement = textFlow.getFirstLeaf();
-
-			// make it red
-			var cf:TextLayoutFormat = new TextLayoutFormat();
-			cf.color = 0xff0000;
-			textFlow.hostFormat = cf;
-			assertTrue("host character format set color failed",leaf.computedFormat.color == 0xff0000);
-
-			// make it blue
-			textFlow.color = 0xff;
-			assertTrue("textFlow character format color override failed",leaf.computedFormat.color == 0xff);
-
-			// clear the blue
-			textFlow.color = undefined;
-			assertTrue("textFlow color clear failed",leaf.computedFormat.color == 0xff0000);
-
-			// clear the hostCharacterFormat
-			textFlow.hostFormat = null;
-			assertTrue("host character format clear failed",leaf.computedFormat.color == 0);
-
-			endAPITest();
-		}
-
-		private function initTextFlowAAA():TextFlow
-		{
-			var textFlow:TextFlow = new TextFlow();
-			var p:ParagraphElement = new ParagraphElement();
-			var span:SpanElement = new SpanElement();
-			span.text = "aaa";
-			p.replaceChildren(0, 0, span);
-			textFlow.replaceChildren(0, 0, p);
-			return textFlow;
-		}
-
-		// Insert a link to a paragraph that hasn't ever been updated.
-		public function insertLinkNoUpdateAPI():void
-		{
-			var textFlow:TextFlow = new TextFlow();
-			var p:ParagraphElement = new ParagraphElement();
-			var link:LinkElement = new LinkElement();
-			link.href = "http://www.cnn.com";
-			link.target = "_self";
-			var span:SpanElement = new SpanElement();
-			span.text = "aaa";
-			link.replaceChildren(0, 0, span);
-			p.replaceChildren(0, 0, link);
-			textFlow.replaceChildren(0, 0, p);
-		}
-
-		// Insert a link to a paragraph that hasn't ever been updated.
-		public function insertLinkNoUpdateViaEditManager():void
-		{
-			var textFlow:TextFlow = initTextFlowAAA();
-			var editManager:IEditManager = new EditManager();
-			textFlow.interactionManager = editManager;
-
-			editManager.selectRange(1,2);
-			editManager.applyLink("http://livedocs.adobe.com/", "_self", true);
-		}
-
-		// undo in a flow that has no controllers
-		public function testUndo():void
-		{
-			var textFlow:TextFlow = initTextFlowAAA();
-			var undoManager:IUndoManager = new UndoManager();
-			var editManager:IEditManager = new EditManager(undoManager);
-			textFlow.interactionManager = editManager;
-
-			editManager.selectRange(1,2);
-			var format:TextLayoutFormat = new TextLayoutFormat();
-			format.fontWeight = FontWeight.BOLD;
-			editManager.applyLeafFormat(format);
-
-			undoManager.undo();
-		}
-
-		private function createParaIterationModel():TextFlow
-		{
-			// Creates 3 divs, each have 4 paras
-			const paraTotal:int = 4;
-			var paraCount:int;
-			const divTotal:int = 3;
-			var divCount:int;
-
-			var flow:TextFlow = new TextFlow();
-			for (var j:int = 0; j < divTotal; j++)
-			{
-				var div:DivElement = new DivElement();
-				for (var i:int = 0; i < paraTotal; i++)
-				{
-					var para:ParagraphElement = new ParagraphElement();
-					var span:SpanElement = new SpanElement();
-					span.text = paraCount.toString();
-					para.addChild(span);
-					div.addChild(para);
-					paraCount++;
-				}
-				flow.addChild(div);
-			}
-			return flow;
-		}
-
-		public function iterateParagraphForward():void
-		{
-			var flow:TextFlow = createParaIterationModel();
-			var para:ParagraphElement = flow.getFirstLeaf().getParagraph();
-			var i:int = 0;
-			while (para != null)
-			{
-				var cStr:String = para.getText();
-				assertTrue("Text not as expected", int(cStr) == i);
-				para = para.getNextParagraph();
-				i++;
-			}
-			assertTrue("Unexpected paragraph count", i == 12);
-		}
-
-		public function iterateParagraphBackward():void
-		{
-			//const kParaTerminator:String = '\u2029';
-
-			var flow:TextFlow = createParaIterationModel();
-			var para:ParagraphElement = flow.getLastLeaf().getParagraph();
-			var i:int = 11;
-			//These two tests are no longer valid due to PARB changes to removed the terminator
-			//parameter from getText on a paragraph element.
-			//
-			//var terminatorTestStr:String = para.getText("\n");
-			//assertTrue("Should have newline as terminator", terminatorTestStr.substr(terminatorTestStr.length - 1, 1) == '\n');
-			//terminatorTestStr = para.getText();
-			//assertTrue("Should have paragraph terminator as terminator", terminatorTestStr.substr(terminatorTestStr.length - 1, 1) == kParaTerminator);
-			while (para != null)
-			{
-				var cStr:String = para.getText();
-				assertTrue("Text not as expected", int(cStr) == i);
-				para = para.getPreviousParagraph();
-				i--;
-			}
-			assertTrue("Unexpected paragraph count", i == -1);
-		}
-		public function cascadeValidation():void
-		{
-			var flow:TextFlow = new TextFlow();
-			var para:ParagraphElement = new ParagraphElement();
-			var span:SpanElement = new SpanElement();
-			flow.addChild(para);
-			para.addChild(span);
-			flow.backgroundColor = 0xff;
-			assertTrue("backgroundColor should not inherit",TextLayoutFormat.backgroundColorProperty.inherited == false);
-			assertTrue("bad flow backGroundColor", flow.computedFormat.backgroundColor == 0xff);
-			assertTrue("bad para backGroundColor", para.computedFormat.backgroundColor == TextLayoutFormat.backgroundColorProperty.defaultValue);
-			assertTrue("bad span backGroundColor", span.computedFormat.backgroundColor == TextLayoutFormat.backgroundColorProperty.defaultValue);
-		}
-
-		// tests api change to automatically remove a flowelements children when using replaceChildren
-		public function elementMovingTest():void
-		{
-			var lengthBefore:int;
-
-			// this flow should have two paragraphs as children
-			var flow:TextFlow = SelManager.textFlow.deepCopy() as TextFlow;	// clone the flow
-
-			var firstPara:FlowElement = flow.getChildAt(0);
-			lengthBefore = flow.textLength;
-			//firstPara.parent.removeChild(firstPara);	// soon to no longer be needed
-			//assertTrue("elementMovingTest: removing para incorrect lengths",flow.textLength == lengthBefore-firstPara.textLength);
-
-			lengthBefore = SelManager.textFlow.textLength;
-			SelManager.textFlow.addChild(firstPara);
-			assertTrue("elementMovingTest: adding para incorrect lengths",SelManager.textFlow.textLength == lengthBefore+firstPara.textLength);
-
-			var lastLeaf:FlowElement = flow.getLastLeaf();
-			var lastLeafLength:int = lastLeaf.textLength;
-			//lastLeaf.parent.removeChild(lastLeaf);	// soon to no longer be needed
-			lengthBefore = SelManager.textFlow.textLength;
-			SelManager.textFlow.getLastLeaf().parent.addChild(lastLeaf);
-			assertTrue("elementMovingTest: adding span incorrect lengths",SelManager.textFlow.textLength == lengthBefore+lastLeafLength-1);
-		}
-
-		// Empty Flow Tests - verify that empty/partially empty TextFlow's are normalized correctly
-		public function emptyTextFlowTests():void
-		{
-			var tf:TextFlow;
-
-			// just an empty TextFlow
-			tf = new TextFlow();
-			tf.flowComposer.addController(new ContainerController(new Sprite()));
-			tf.flowComposer.updateAllControllers();
-
-			// empty TextFlow with paragraph
-			tf = new TextFlow();
-			tf.addChild(new ParagraphElement());
-			tf.flowComposer.addController(new ContainerController(new Sprite()));
-			tf.flowComposer.updateAllControllers();
-
-			// empty TextFlow with paragraph and ILG
-			tf = new TextFlow();
-			var p:ParagraphElement = new ParagraphElement();
-			p.addChild(new InlineGraphicElement());
-			tf.addChild(p);
-			tf.flowComposer.addController(new ContainerController(new Sprite()));
-			tf.flowComposer.updateAllControllers();
-
-			// empty TextFlow with paragraph and empty LinkElement
-			tf = new TextFlow();
-			p = new ParagraphElement();
-			p.addChild(new LinkElement());
-			tf.addChild(p);
-			tf.flowComposer.addController(new ContainerController(new Sprite()));
-			tf.flowComposer.updateAllControllers();
-
-			// empty TextFlow with paragraph and empty TCYElement
-			tf = new TextFlow();
-			p = new ParagraphElement();
-			p.addChild(new TCYElement());
-			tf.addChild(p);
-			tf.flowComposer.addController(new ContainerController(new Sprite()));
-			tf.flowComposer.updateAllControllers();
-
-			// a more complex example
-			tf = new TextFlow();
-			p = new ParagraphElement();
-			var tcy:TCYElement = new TCYElement();
-			tcy.addChild(new InlineGraphicElement());
-			tcy.addChild(new SpanElement());
-			p.addChild(tcy);
-			tf.addChild(p);
-			tcy.removeChildAt(1);
-			tf.flowComposer.addController(new ContainerController(new Sprite()));
-			tf.flowComposer.updateAllControllers();
-
-			// several empty spans
-			tf = new TextFlow();
-			p = new ParagraphElement();
-			tf.addChild(p);
-			p.addChild(new SpanElement());
-			p.addChild(new SpanElement());
-			p.addChild(new SpanElement());
-			tf.flowComposer.addController(new ContainerController(new Sprite()));
-			tf.flowComposer.updateAllControllers();
-		}
-		
-		public function emptyElementCopyTests():void
-		{
-			var elemList:Array = GeneralFunctionsTest.childParentTable[0];
-			for (var idx:int = 1; idx < elemList.length; idx++)
-			{
-				var elem:FlowElement = new elemList[idx];
-				elem.shallowCopy();
-				elem.deepCopy();
-			}
-		}
-
-		private function validateBitmap(actual:Bitmap, expected:Bitmap):Boolean
-		{
-			actual.bitmapData.draw(expected, null, null, "difference");
-			var bounds:Rectangle = new Rectangle(0, 0, actual.width, actual.height);
-			var diffPixels:ByteArray = actual.bitmapData.getPixels(bounds);
-			diffPixels.position = 0;
-			while (diffPixels.bytesAvailable > 0)
-			{
-				if (diffPixels.readByte() > 0)
-					return false;
-			}
-
-			return true;
-		}
-
-		private function myFontMapper(fd:FontDescription):void
-		{
-			if (fd.fontName == "Arial Black")
-			{
-				fd.fontName = "Arial";
-				fd.fontWeight = FontWeight.BOLD;
-			}
-		}
-
-		/** Tests fontMapping */
-		public function fontMappingTest():void
-		{
-			var textFlow:TextFlow = SelManager.textFlow;
-			var container:DisplayObjectContainer = DisplayObjectContainer(textFlow.flowComposer.getControllerAt(0).container);
-
-			textFlow.fontFamily = "Arial";
-			textFlow.fontWeight = FontWeight.BOLD;
-			textFlow.flowComposer.updateAllControllers();
-
-			var arialBoldBits:BitmapData = new BitmapData(container.width,container.height);
-			arialBoldBits.draw(container as DisplayObjectContainer);
-			var arialBoldData:Bitmap = new Bitmap(arialBoldBits);
-
-			textFlow.fontFamily = "Arial Black";
-			textFlow.fontWeight = undefined;
-			textFlow.flowComposer.updateAllControllers();
-
-			var arialBlackBits:BitmapData = new BitmapData(container.width,container.height);
-			arialBlackBits.draw(container as DisplayObjectContainer);
-			var arialBlackData:Bitmap = new Bitmap(arialBlackBits);
-
-			GlobalSettings.fontMapperFunction = myFontMapper;
-			textFlow.invalidateAllFormats();
-
-			try
-			{
-				textFlow.flowComposer.updateAllControllers();
-			}
-			finally
-			{
-				GlobalSettings.fontMapperFunction = null;
-				textFlow.invalidateAllFormats();
-			}
-
-			var mappedBits:BitmapData = new BitmapData(container.width,container.height);
-			mappedBits.draw(container as DisplayObjectContainer);
-			var mappedData:Bitmap = new Bitmap(mappedBits);
-
-			assertTrue("font mapping failed", validateBitmap(mappedData, arialBoldData));
-
-			textFlow.flowComposer.updateAllControllers();
-
-			var mappingClearedBits:BitmapData = new BitmapData(container.width,container.height);
-			mappingClearedBits.draw(container as DisplayObjectContainer);
-			var mappingClearedData:Bitmap = new Bitmap(mappingClearedBits);
-
-			assertTrue("clearing font mapping failed", validateBitmap(mappingClearedData, arialBlackData));
-
-		}
-
-		/** test the binary search algorithm which in findControllerIndexAtPosition - tricky bits wrt handling of zero length containers */
-		public function testFindControllerIndexAtPosition():void
-		{
-			var s:Sprite = new Sprite();	// scratch
-			var controller:ContainerController;	// scratch
-			var composer:StandardFlowComposer = new StandardFlowComposer();
-			// ideally shouldn't need TextFlow but because containercontrollers find their owning composer via the textflow its needed
-			var textFlow:TextFlow = new TextFlow();
-			textFlow.flowComposer = composer;
-			textFlow.mxmlChildren = [ "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678" ];
-
-			controller = new ContainerController(s);
-			controller.verticalScrollPolicy = "off";
-			composer.addController(controller);
-			controller.setTextLength(100);
-			controller.verticalScrollPolicy = "off";
-
-			assertTrue("Bad result in findControllerIndexAtPosition 9",composer.findControllerIndexAtPosition(0)== 0);
-			assertTrue("Bad result in findControllerIndexAtPosition 10",composer.findControllerIndexAtPosition(100,true)== 0);
-			assertTrue("Bad result in findControllerIndexAtPosition 11",composer.findControllerIndexAtPosition(100,false)== -1);
-
-			for (var idx1:int  = 0; idx1 < 4; idx1++)
-			{
-				for (var idx2:int = 0; idx2 < 4 ; idx2++)
-				{
-					var idx:int;
-
-					composer = new StandardFlowComposer();
-					textFlow = new TextFlow();
-					textFlow.flowComposer = composer;
-					textFlow.mxmlChildren = [ "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678" ];
-
-					// add some empties
-					for (idx = 0; idx < idx1; idx++)
-						composer.addController(new ContainerController(s));
-					// add one of length one
-					controller = new ContainerController(s);
-					controller.verticalScrollPolicy = "off";	// scrolling confuses it
-					composer.addController(controller);
-					controller.setTextLength(100);				// internal API
-					// add some empties
-					for (idx = 0; idx < idx2; idx++)
-						composer.addController(new ContainerController(s));
-					assertTrue("Bad result in findControllerIndexAtPosition 1",composer.findControllerIndexAtPosition(0)== 0);
-					assertTrue("Bad result in findControllerIndexAtPosition 2",composer.findControllerIndexAtPosition(0,true)== 0);
-					assertTrue("Bad result in findControllerIndexAtPosition 3",composer.findControllerIndexAtPosition(100,true)== idx1);
-					assertTrue("Bad result in findControllerIndexAtPosition 4",composer.findControllerIndexAtPosition(100,false)== -1);
-					// add one with some length
-					textFlow.mxmlChildren = [ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678" ];
-					controller.setTextLength(100);
-					controller = new ContainerController(s);
-					controller.verticalScrollPolicy = "off";	// scrolling confuses it
-					composer.addController(controller);
-					controller.setTextLength(100);				// internal API
-					assertTrue("Bad result in findControllerIndexAtPosition 5",composer.findControllerIndexAtPosition(100,true)== idx1);
-					assertTrue("Bad result in findControllerIndexAtPosition 6",composer.findControllerIndexAtPosition(100,false)== idx1+1);
-					assertTrue("Bad result in findControllerIndexAtPosition 7",composer.findControllerIndexAtPosition(200,true)== idx1+idx2+1);
-					assertTrue("Bad result in findControllerIndexAtPosition 8",composer.findControllerIndexAtPosition(200,false)== -1);
-				}
-			}
-
-		}
-
-		
-		private static function checkListLines(textFlow:TextFlow,numLines:int,prefix:String):void
-		{
-			for (var idx:int = 0; idx < numLines; idx++)
-			{
-				var tfl:TextFlowLine = textFlow.flowComposer.getLineAt(idx);
-				assertTrue(prefix+": Missing TextFlowLine: "+idx,tfl != null);
-				var textLine:TextLine = tfl.getTextLine();
-				assertTrue(prefix+": Missing TextLine: "+idx,textLine != null);
-				var numberLine:TextLine = textLine.getChildAt(0) as TextLine;
-				assertTrue(prefix+": Missing NumberLine: "+idx,numberLine != null);
-				/* var numberString:String = numberLine.userData as String;
-				var expectedString:String = (idx+1).toString() + ".";	// for numeric lists
-				assertTrue(prefix+": NumberLine missing userData: "+idx,numberString != null);
-				assertTrue(prefix+": Incorrect NumberLine userData: "+idx,numberLine.userData as String == expectedString); 
-				assertTrue(prefix+": Incorrect NumberLine rawTextLength: "+idx,numberString.length+1 == numberLine.rawTextLength); */
-			}
-		}
-		
-		public function listItemInsertion():void
-		{
-			// some validations that ensure ListElement is correctly setup for list processing
-			
-			// every ListStyleType must have an entry in ListElement.listSuffixes
-			var handler:EnumPropertyHandler = TextLayoutFormat.listStyleTypeProperty.findHandler(EnumPropertyHandler) as EnumPropertyHandler;
-			assertTrue("listItemInsertion: missing handler for ListStyleType", handler != null && handler.range != null);
-
-			var	range:Object = handler.range;
-			var value:String;
-			
-			var numberedListStyles:Object = { };
-			for (value in ListElement.algorithmicListStyles)
-			{
-				assertTrue("listItemInsertion: table entry duplicated",numberedListStyles[value] === undefined);
-				numberedListStyles[value] = ListElement.algorithmicListStyles[value];
-			}
-			for (value in ListElement.numericListStyles)
-			{
-				assertTrue("listItemInsertion: table entry duplicated",numberedListStyles[value] === undefined);
-				numberedListStyles[value] = ListElement.numericListStyles[value];
-			}
-			for (value in ListElement.alphabeticListStyles)
-			{
-				assertTrue("listItemInsertion: table entry duplicated",numberedListStyles[value] === undefined);
-				numberedListStyles[value] = ListElement.alphabeticListStyles[value];
-			}
-			
-			for (value in range)
-			{
-				if (value != FormatValue.INHERIT)
-				{
-					// must be a numbered list or an unnumbered list but not both
-					assertTrue("listItemInsertion: listStyleType must be numbered or unnumbered but not both: "+value,
-						numberedListStyles[value] !== undefined && ListElement.constantListStyles[value] === undefined
-						|| numberedListStyles[value] === undefined && ListElement.constantListStyles[value] !== undefined)
-					// numbered lists must have a suffix
-					if ( ListElement.constantListStyles[value] === undefined)
-						assertTrue("listItemInsertion: missing suffix property: " + value,ListElement.listSuffixes[value] !== undefined);
-				}
-			}
-			
-			// verify that all constantListStyles are in range
-			for (value in ListElement.constantListStyles)
-				assertTrue("listItemInsertion: invalid value in constantListStyles: " + value, range[value] !== undefined);
-			// verify that all numberedListStyles are in range
-			for (value in numberedListStyles)
-				assertTrue("listItemInsertion: invalid value in numberedListStyles: " + value, range[value] !== undefined);
-			// verify that all listSuffixes are in range
-			for (value in ListElement.listSuffixes)
-				assertTrue("listItemInsertion: invalid value in listSuffixes: " + value, range[value] !== undefined);			
-				
-			SelManager.selectRange(0,0);
-
-			// remove all the children and put in a list
-			var textFlow:TextFlow = SelManager.textFlow;
-			
-			textFlow.replaceChildren(0,textFlow.numChildren);
-			
-			var list:ListElement = new ListElement();
-			list.listStyleType = ListStyleType.DECIMAL;
-			
-			textFlow.addChild(list);
-			var item:ListItemElement = new ListItemElement();
-			list.addChild(item);
-			
-			textFlow.flowComposer.updateAllControllers();
-			
-			assertTrue("listItemInsertion: incorrect normalize",textFlow.findAbsoluteParagraph(0).parent == item);
-			
-			// append two items and compose
-			list.addChild(new ListItemElement());
-			list.addChild(new ListItemElement());
-			textFlow.flowComposer.updateAllControllers();
-			
-			// check the three textlines
-			checkListLines(textFlow,3,"listItemInsertion1");
-			
-			// now insert a brand new ListItem at the head and verify
-			list.replaceChildren(0,0,new ListItemElement());
-			textFlow.flowComposer.updateAllControllers();
-			
-			// check four textlines
-			checkListLines(textFlow,4,"listItemInsertion2");
-			
-			// remove a list item
-			list.removeChildAt(1);
-			textFlow.flowComposer.updateAllControllers();
-			
-			// check three textLines
-			checkListLines(textFlow,3,"listItemInsertion3");
-			
-			// add another list in the first ListItemElement
-			item = list.getChildAt(0) as ListItemElement;
-			var newList:ListElement = new ListElement();
-			item.addChild(newList);
-			textFlow.flowComposer.updateAllControllers();
-			
-			// assert the empty list is deleted
-			assertTrue("listItemInsertion: newList not normalized",newList.numChildren == 1);
-
-		}
-		
-		public function testFTETextField():void
-		{
-			// use the TextFlow's container
-			var fieldParent:Sprite = SelManager.textFlow.flowComposer.getControllerAt(0).container;
-			// remove the controller so the the textFlow isn't displayed in it
-			SelManager.textFlow.flowComposer.removeControllerAt(0);
-			
-			var field:FTETextField = new FTETextField();
-			field.htmlText = "Hello world";
-			fieldParent.addChild(field);
-		}
-		
-		public function replaceChildrenTest():void
-		{
-			SelManager.selectRange(0,0);
-			
-			// remove all the children 
-			var textFlow:TextFlow = SelManager.textFlow;
-			textFlow.replaceChildren(0,textFlow.numChildren);
-			
-			var p:ParagraphElement = new ParagraphElement();
-			textFlow.addChild(p);
-			
-			var link:LinkElement = new LinkElement();
-			link.href = "XXX";
-			p.addChild(link);
-			var span:SpanElement = new SpanElement();
-			span.text = "Hello, ";
-			span.fontSize = 24;
-			link.addChild(span);
-			
-			var link2:LinkElement = new LinkElement();
-			link2.href = "YYY";
-			p.addChild(link2);
-			
-			var span2:SpanElement = new SpanElement();
-			span2.text = "world ";
-			span2.fontSize = 24;
-			link2.addChild(span2);
-			
-			textFlow.flowComposer.updateAllControllers();
-			
-			link.replaceChildren(link2.numChildren,link2.numChildren,link2.mxmlChildren);
-			p.removeChild(link2);
-			textFlow.flowComposer.updateAllControllers();
-			
-			assertTrue("replaceChildrenTest: extra line - look for extra terminator",textFlow.flowComposer.numLines == 1);
-		}
-		
-		private function myFontLookup(context:mySwfContext, tlf:ITextLayoutFormat):Function
-		{
-			return myFontLookup;
-		}
-		
-		public function resolveFontLookupTest():void
-		{
-			var textFlow:TextFlow = SelManager.textFlow;
-			
-			textFlow.fontFamily = "Arial";
-			textFlow.fontWeight = FontWeight.BOLD;
-			textFlow.fontLookup = "device";
-			textFlow.flowComposer.updateAllControllers();
-			
-			var swfContext:mySwfContext = new mySwfContext();
-			try
-			{
-				GlobalSettings.resolveFontLookupFunction = myFontLookup(swfContext, textFlow.format);
-				textFlow.flowComposer.updateAllControllers();
-				assertTrue ("fontLookup not matched.", textFlow.fontLookup = swfContext.myFontlookup);
-			}
-			finally
-			{
-				GlobalSettings.resolveFontLookupFunction = null;
-			}
-		}
-		
-		public function bindableSpan():void
-		{
-			// Bindable span should not lose its formatting
-			var textFlow:TextFlow = new TextFlow();
-			var paragraph:ParagraphElement =  new ParagraphElement();
-			var span1:SpanElement = new SpanElement();
-			var span2:SpanElement = new SpanElement();
-			var format:TextLayoutFormat = new TextLayoutFormat();
-			format.fontWeight = FontWeight.BOLD;
-			span2.format = format;
-			paragraph.mxmlChildren = [ span1, span2 ];
-			textFlow.mxmlChildren = [ paragraph ];
-			textFlow.flowComposer.addController(new ContainerController(new Sprite()));
-			textFlow.flowComposer.compose();		// force normalize
-			assertTrue("Spans should not be merged!", span2.parent == span1.parent && paragraph.numChildren == 2);
-			assertTrue("Formatting on second span should be preserved!", span2.fontWeight == FontWeight.BOLD);
-		}
-		
-		// This test does not work in our current build environment, since playerEnablesSpicyFeatures will always be false.
-		// Once we have a method of compiling VellumUnit as a 10.2 swf, this test should be enabled.
-		public function softKeyboardFlagTest():void
-		{
-			if (Configuration.playerEnablesSpicyFeatures)	// only run the rest of the test if we're in 10.2 or higher
-			{
-				// test 1 - add controller, then interaction manager
-				var sprite:Sprite = new Sprite();
-				var textFlow:TextFlow = new TextFlow();
-				textFlow.flowComposer.addController(new ContainerController(sprite));
-				textFlow.interactionManager = new EditManager();
-				assertTrue("needsSoftKeyboard should be true after adding EditManager", sprite["needsSoftKeyboard"] == true);
-				// test 2 - add another controller
-				var sprite1:Sprite = new Sprite();
-				textFlow.flowComposer.addController(new ContainerController(sprite1));
-				assertTrue("needsSoftKeyboard should be true for a second container", sprite1["needsSoftKeyboard"] == true);		
-
-				// test 3 - add interaction manager, then controller
-				var sprite2:Sprite = new Sprite();
-				var textFlow2:TextFlow = new TextFlow();
-				textFlow2.interactionManager = new EditManager();
-				textFlow2.flowComposer.addController(new ContainerController(sprite2));
-				assertTrue("needsSoftKeyboard should be true after adding controller", sprite2["needsSoftKeyboard"] == true);
-			}
-		}
-	}
+    public class FlowModelTest extends VellumTestCase
+    {
+        private var beginTime:int;
+
+        public function FlowModelTest()
+        {
+            super("", "FlowModelTest", TestConfig.getInstance());
+
+            metaData = {};
+            // Note: These must correspond to a Watson product area (case-sensitive)
+            metaData.productArea = "Text Container";
+            metaData.productSubArea = "Flow";
+        }
+
+        [Before]
+        override public function setUpTest():void
+        {
+            super.setUpTest();
+        }
+
+        [After]
+        override public function tearDownTest():void
+        {
+            super.tearDownTest();
+        }
+
+        [Test]
+        /**
+         * Direct usage of the flow APIs. No validation - just look for crashes.
+         * This test changes the color of the entire text.
+         */
+        public function basicAPITest1():void
+        {
+            // set the color on the entire flow
+            beginAPITest();
+            SelManager.textFlow.color = 0xff0000;
+            endAPITest();
+        }
+
+        [Test]
+        /**
+         * Direct usage of the flow APIs. No validation - just look for crashes.
+         * This test deletes every other character in the entire text.
+         */
+        public function basicAPITest2():void
+        {
+            // delete every other character in the textflow
+            beginAPITest();
+
+            var idx:int = 0;
+            while (idx < SelManager.textFlow.textLength)
+            {
+                var span:SpanElement = SelManager.textFlow.findLeaf(idx) as SpanElement;
+                if (span)
+                {
+                    var spanStart:int = span.getAbsoluteStart();
+                    span.replaceText(idx - spanStart, idx - spanStart + 1, null);
+                }
+                idx++;
+
+            }
+            endAPITest();
+        }
+
+        [Test]
+        /**
+         * Direct usage of the flow APIs. No validation - just look for crashes.
+         * This test adds another paragraph to the flow containing the text "Hello World"
+         * in 24 point font.
+         */
+        public function basicAPITest3():void
+        {
+            // add a paragraph
+            beginAPITest();
+            var p:ParagraphElement = new ParagraphElement();
+            var s:SpanElement = new SpanElement();
+            s.text = "Hello world";
+            s.fontSize = 24;
+            p.replaceChildren(0, 0, s);
+            SelManager.textFlow.replaceChildren(SelManager.textFlow.numChildren, SelManager.textFlow.numChildren, p);
+            endAPITest();
+        }
+
+        [Test]
+        /**
+         * This test performs a series of changes and validates the changes after they're performed.
+         */
+        public function basicAPITest4():void
+        {
+            // more comprehensive set of tests - several manipulations to the flow and then display it
+
+            // generic begin to an API test
+            beginAPITest();
+
+            // get the textflow
+            var textFlow:TextFlow = SelManager.textFlow;
+
+            // remove all the textflow children
+            textFlow.replaceChildren(0, textFlow.numChildren);
+            assertTrue("expected no elements on the flow, but found " + textFlow.numChildren,
+                    textFlow.numChildren == 0);
+
+            // create a paragraph
+            var p:ParagraphElement = new ParagraphElement();
+
+            // create a span
+            var s:SpanElement = new SpanElement();
+            s.text = "Hello world. ";
+            s.fontSize = 24;
+
+            // split the span
+            var nextSpan:SpanElement = s.splitAtPosition(6) as SpanElement;
+            // set the color - color can be a string or an unsigned integer
+            nextSpan.color = "0xff";
+            assertTrue("expected that the color would be 255, but found " + uint(nextSpan.format.color),
+                    uint(nextSpan.format.color) == 255);
+
+            // put the two spans in the paragraph
+            p.replaceChildren(0, 0, s);
+            p.replaceChildren(1, 1, nextSpan);
+
+            assertTrue("expected the element count to be 2, but it was " + p.numChildren,
+                    p.numChildren == 2);
+
+            // add another span
+            s = new SpanElement();
+            s.text = "Start:"
+            p.replaceChildren(0, 0, s);
+
+            // put the paragraph in the TextFlow
+            textFlow.replaceChildren(0, 0, p);
+            assertTrue("text flow should have one element but has " + textFlow.numChildren,
+                    textFlow.numChildren == 1);
+
+            // make another paragraph
+            p = new ParagraphElement();
+            s = new SpanElement();
+            p.replaceChildren(0, 0, s);
+            s.text = "NEW FIRST PARAGRAPH";
+
+            // set the paragraph attributes directly
+            p.textIndent = 20;
+            // set the paragraph attributes via clone and set
+            var pa:TextLayoutFormat = new TextLayoutFormat(p.format);
+            pa.textAlign = TextAlign.RIGHT;
+            p.format = pa;
+
+            // into the textFlow at the beginning
+            textFlow.replaceChildren(0, 0, p);
+
+            // generic end to an API test
+            endAPITest();
+        }
+
+        [Test]
+        /**
+         * This test inserts an FE in the middle of a paragraph
+         */
+        public function basicAPITest5():void
+        {
+            // more comprehensive set of tests - several manipulations to the flow and then display it
+
+            // generic begin to an API test
+            beginAPITest();
+
+            // get the textflow
+            var textFlow:TextFlow = SelManager.textFlow;
+
+            // create a paragraph empty the flow and insert it
+            var p:ParagraphElement = new ParagraphElement();
+            textFlow.replaceChildren(0, textFlow.numChildren, p);
+
+            // create a span
+            var s:SpanElement = new SpanElement();
+            s.text = "Hello world. ";
+            s.fontSize = 24;
+
+            // put it in the paragraph
+            p.replaceChildren(0, 0, s);
+
+            // split the span
+            var nextSpanElement:SpanElement = s.splitAtPosition(6) as SpanElement;
+            assertTrue("Incorrect elementCount after split", p.numChildren == 2);
+
+            // create and insert an image between the spans
+            var image:InlineGraphicElement = new InlineGraphicElement();
+            image.width = 19;
+            image.height = 19;
+            image.source = LoaderUtil.createAbsoluteURL(baseURL, "../../test/testFiles/assets/surprised.png");
+            p.replaceChildren(1, 1, image);
+
+            assertTrue("failed length on new InlineGraphicElement", image.textLength == 1);
+            assertTrue("failed elementCount after image insert", p.numChildren == 3);
+            assertTrue("bad first child", p.getChildAt(0) is SpanElement);
+            assertTrue("bad first child", p.getChildAt(1) is InlineGraphicElement);
+            assertTrue("bad first child", p.getChildAt(2) is SpanElement);
+
+            // set a userStyle on a ContainerController
+            var saveFormat:ITextLayoutFormat = ContainerController.containerControllerInitialFormat;
+            try
+            {
+                ContainerController.containerControllerInitialFormat = null;
+                var controller:ContainerController = new ContainerController(new Sprite());
+                controller.setStyle("foo", "blah");
+            }
+            catch (e:Error)
+            {
+                ContainerController.containerControllerInitialFormat = saveFormat;
+                throw(e);
+            }
+            ContainerController.containerControllerInitialFormat = saveFormat;
+
+            // generic end to an API test
+            endAPITest();
+        }
+
+        [Test]
+        /**
+         * Test setting hostCharacterFormat on the TextFlow
+         */
+        public function textFlowHostCharacterFormat():void
+        {
+            // generic begin to an API test
+            beginAPITest();
+
+            // get the textflow
+            var textFlow:TextFlow = SelManager.textFlow;
+            var leaf:FlowLeafElement = textFlow.getFirstLeaf();
+
+            // make it red
+            var cf:TextLayoutFormat = new TextLayoutFormat();
+            cf.color = 0xff0000;
+            textFlow.hostFormat = cf;
+            assertTrue("host character format set color failed", leaf.computedFormat.color == 0xff0000);
+
+            // make it blue
+            textFlow.color = 0xff;
+            assertTrue("textFlow character format color override failed", leaf.computedFormat.color == 0xff);
+
+            // clear the blue
+            textFlow.color = undefined;
+            assertTrue("textFlow color clear failed", leaf.computedFormat.color == 0xff0000);
+
+            // clear the hostCharacterFormat
+            textFlow.hostFormat = null;
+            assertTrue("host character format clear failed", leaf.computedFormat.color == 0);
+
+            endAPITest();
+        }
+
+        [Test]
+        /**
+         * Insert a link to a paragraph that hasn't ever been updated.
+         */
+        public function insertLinkNoUpdateAPI():void
+        {
+            var textFlow:TextFlow = new TextFlow();
+            var p:ParagraphElement = new ParagraphElement();
+            var link:LinkElement = new LinkElement();
+            link.href = "http://www.cnn.com";
+            link.target = "_self";
+            var span:SpanElement = new SpanElement();
+            span.text = "aaa";
+            link.replaceChildren(0, 0, span);
+            p.replaceChildren(0, 0, link);
+            textFlow.replaceChildren(0, 0, p);
+        }
+
+        [Test]
+        /**
+         *
+         */
+        public function insertLinkNoUpdateViaEditManager():void
+        {
+            var textFlow:TextFlow = initTextFlowAAA();
+            var editManager:IEditManager = new EditManager();
+            textFlow.interactionManager = editManager;
+
+            editManager.selectRange(1, 2);
+            editManager.applyLink("http://livedocs.adobe.com/", "_self", true);
+        }
+
+        [Test]
+        /**
+         * undo in a flow that has no controllers
+         */
+        public function testUndo():void
+        {
+            var textFlow:TextFlow = initTextFlowAAA();
+            var undoManager:IUndoManager = new UndoManager();
+            var editManager:IEditManager = new EditManager(undoManager);
+            textFlow.interactionManager = editManager;
+
+            editManager.selectRange(1, 2);
+            var format:TextLayoutFormat = new TextLayoutFormat();
+            format.fontWeight = FontWeight.BOLD;
+            editManager.applyLeafFormat(format);
+
+            undoManager.undo();
+        }
+
+        [Test]
+        public function iterateParagraphForward():void
+        {
+            var flow:TextFlow = createParaIterationModel();
+            var para:ParagraphElement = flow.getFirstLeaf().getParagraph();
+            var i:int = 0;
+            while (para != null)
+            {
+                var cStr:String = para.getText();
+                assertTrue("Text not as expected", int(cStr) == i);
+                para = para.getNextParagraph();
+                i++;
+            }
+            assertTrue("Unexpected paragraph count", i == 12);
+        }
+
+        [Test]
+        public function iterateParagraphBackward():void
+        {
+            //const kParaTerminator:String = '\u2029';
+
+            var flow:TextFlow = createParaIterationModel();
+            var para:ParagraphElement = flow.getLastLeaf().getParagraph();
+            var i:int = 11;
+            //These two tests are no longer valid due to PARB changes to removed the terminator
+            //parameter from getText on a paragraph element.
+            //
+            //var terminatorTestStr:String = para.getText("\n");
+            //assertTrue("Should have newline as terminator", terminatorTestStr.substr(terminatorTestStr.length - 1, 1) == '\n');
+            //terminatorTestStr = para.getText();
+            //assertTrue("Should have paragraph terminator as terminator", terminatorTestStr.substr(terminatorTestStr.length - 1, 1) == kParaTerminator);
+            while (para != null)
+            {
+                var cStr:String = para.getText();
+                assertTrue("Text not as expected", int(cStr) == i);
+                para = para.getPreviousParagraph();
+                i--;
+            }
+            assertTrue("Unexpected paragraph count", i == -1);
+        }
+
+        [Test]
+        public function cascadeValidation():void
+        {
+            var flow:TextFlow = new TextFlow();
+            var para:ParagraphElement = new ParagraphElement();
+            var span:SpanElement = new SpanElement();
+            flow.addChild(para);
+            para.addChild(span);
+            flow.backgroundColor = 0xff;
+            assertTrue("backgroundColor should not inherit", TextLayoutFormat.backgroundColorProperty.inherited == false);
+            assertTrue("bad flow backGroundColor", flow.computedFormat.backgroundColor == 0xff);
+            assertTrue("bad para backGroundColor", para.computedFormat.backgroundColor == TextLayoutFormat.backgroundColorProperty.defaultValue);
+            assertTrue("bad span backGroundColor", span.computedFormat.backgroundColor == TextLayoutFormat.backgroundColorProperty.defaultValue);
+        }
+
+        [Test]
+        /**
+         * tests api change to automatically remove a flowelements children when using replaceChildren
+         */
+        public function elementMovingTest():void
+        {
+            var lengthBefore:int;
+
+            // this flow should have two paragraphs as children
+            var flow:TextFlow = SelManager.textFlow.deepCopy() as TextFlow;	// clone the flow
+
+            var firstPara:FlowElement = flow.getChildAt(0);
+            lengthBefore = flow.textLength;
+            //firstPara.parent.removeChild(firstPara);	// soon to no longer be needed
+            //assertTrue("elementMovingTest: removing para incorrect lengths",flow.textLength == lengthBefore-firstPara.textLength);
+
+            lengthBefore = SelManager.textFlow.textLength;
+            SelManager.textFlow.addChild(firstPara);
+            assertTrue("elementMovingTest: adding para incorrect lengths", SelManager.textFlow.textLength == lengthBefore + firstPara.textLength);
+
+            var lastLeaf:FlowElement = flow.getLastLeaf();
+            var lastLeafLength:int = lastLeaf.textLength;
+            //lastLeaf.parent.removeChild(lastLeaf);	// soon to no longer be needed
+            lengthBefore = SelManager.textFlow.textLength;
+            SelManager.textFlow.getLastLeaf().parent.addChild(lastLeaf);
+            assertTrue("elementMovingTest: adding span incorrect lengths", SelManager.textFlow.textLength == lengthBefore + lastLeafLength - 1);
+        }
+
+        [Test]
+        /**
+         * Empty Flow Tests - verify that empty/partially empty TextFlow's are normalized correctly
+         */
+        public function emptyTextFlowTests():void
+        {
+            var tf:TextFlow;
+
+            // just an empty TextFlow
+            tf = new TextFlow();
+            tf.flowComposer.addController(new ContainerController(new Sprite()));
+            tf.flowComposer.updateAllControllers();
+
+            // empty TextFlow with paragraph
+            tf = new TextFlow();
+            tf.addChild(new ParagraphElement());
+            tf.flowComposer.addController(new ContainerController(new Sprite()));
+            tf.flowComposer.updateAllControllers();
+
+            // empty TextFlow with paragraph and ILG
+            tf = new TextFlow();
+            var p:ParagraphElement = new ParagraphElement();
+            p.addChild(new InlineGraphicElement());
+            tf.addChild(p);
+            tf.flowComposer.addController(new ContainerController(new Sprite()));
+            tf.flowComposer.updateAllControllers();
+
+            // empty TextFlow with paragraph and empty LinkElement
+            tf = new TextFlow();
+            p = new ParagraphElement();
+            p.addChild(new LinkElement());
+            tf.addChild(p);
+            tf.flowComposer.addController(new ContainerController(new Sprite()));
+            tf.flowComposer.updateAllControllers();
+
+            // empty TextFlow with paragraph and empty TCYElement
+            tf = new TextFlow();
+            p = new ParagraphElement();
+            p.addChild(new TCYElement());
+            tf.addChild(p);
+            tf.flowComposer.addController(new ContainerController(new Sprite()));
+            tf.flowComposer.updateAllControllers();
+
+            // a more complex example
+            tf = new TextFlow();
+            p = new ParagraphElement();
+            var tcy:TCYElement = new TCYElement();
+            tcy.addChild(new InlineGraphicElement());
+            tcy.addChild(new SpanElement());
+            p.addChild(tcy);
+            tf.addChild(p);
+            tcy.removeChildAt(1);
+            tf.flowComposer.addController(new ContainerController(new Sprite()));
+            tf.flowComposer.updateAllControllers();
+
+            // several empty spans
+            tf = new TextFlow();
+            p = new ParagraphElement();
+            tf.addChild(p);
+            p.addChild(new SpanElement());
+            p.addChild(new SpanElement());
+            p.addChild(new SpanElement());
+            tf.flowComposer.addController(new ContainerController(new Sprite()));
+            tf.flowComposer.updateAllControllers();
+        }
+
+        [Test]
+        public function emptyElementCopyTests():void
+        {
+            var elemList:Array = GeneralFunctionsTest.childParentTable[0];
+            for (var idx:int = 1; idx < elemList.length; idx++)
+            {
+                var elem:FlowElement = new elemList[idx];
+                elem.shallowCopy();
+                elem.deepCopy();
+            }
+        }
+
+        [Test]
+        /**
+         * Tests fontMapping
+         */
+        public function fontMappingTest():void
+        {
+            var textFlow:TextFlow = SelManager.textFlow;
+            var container:DisplayObjectContainer = DisplayObjectContainer(textFlow.flowComposer.getControllerAt(0).container);
+
+            textFlow.fontFamily = "Arial";
+            textFlow.fontWeight = FontWeight.BOLD;
+            textFlow.flowComposer.updateAllControllers();
+
+            var arialBoldBits:BitmapData = new BitmapData(container.width, container.height);
+            arialBoldBits.draw(container as DisplayObjectContainer);
+            var arialBoldData:Bitmap = new Bitmap(arialBoldBits);
+
+            textFlow.fontFamily = "Arial Black";
+            textFlow.fontWeight = undefined;
+            textFlow.flowComposer.updateAllControllers();
+
+            var arialBlackBits:BitmapData = new BitmapData(container.width, container.height);
+            arialBlackBits.draw(container as DisplayObjectContainer);
+            var arialBlackData:Bitmap = new Bitmap(arialBlackBits);
+
+            GlobalSettings.fontMapperFunction = myFontMapper;
+            textFlow.invalidateAllFormats();
+
+            try
+            {
+                textFlow.flowComposer.updateAllControllers();
+            }
+            finally
+            {
+                GlobalSettings.fontMapperFunction = null;
+                textFlow.invalidateAllFormats();
+            }
+
+            var mappedBits:BitmapData = new BitmapData(container.width, container.height);
+            mappedBits.draw(container as DisplayObjectContainer);
+            var mappedData:Bitmap = new Bitmap(mappedBits);
+
+            assertTrue("font mapping failed", validateBitmap(mappedData, arialBoldData));
+
+            textFlow.flowComposer.updateAllControllers();
+
+            var mappingClearedBits:BitmapData = new BitmapData(container.width, container.height);
+            mappingClearedBits.draw(container as DisplayObjectContainer);
+            var mappingClearedData:Bitmap = new Bitmap(mappingClearedBits);
+
+            assertTrue("clearing font mapping failed", validateBitmap(mappingClearedData, arialBlackData));
+
+        }
+
+        [Test]
+        /**
+         * test the binary search algorithm which in findControllerIndexAtPosition - tricky bits wrt handling of zero length containers
+         */
+        public function testFindControllerIndexAtPosition():void
+        {
+            var s:Sprite = new Sprite();	// scratch
+            var controller:ContainerController;	// scratch
+            var composer:StandardFlowComposer = new StandardFlowComposer();
+            // ideally shouldn't need TextFlow but because containercontrollers find their owning composer via the textflow its needed
+            var textFlow:TextFlow = new TextFlow();
+            textFlow.flowComposer = composer;
+            textFlow.mxmlChildren = [ "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678" ];
+
+            controller = new ContainerController(s);
+            controller.verticalScrollPolicy = "off";
+            composer.addController(controller);
+            controller.setTextLength(100);
+            controller.verticalScrollPolicy = "off";
+
+            assertTrue("Bad result in findControllerIndexAtPosition 9", composer.findControllerIndexAtPosition(0) == 0);
+            assertTrue("Bad result in findControllerIndexAtPosition 10", composer.findControllerIndexAtPosition(100, true) == 0);
+            assertTrue("Bad result in findControllerIndexAtPosition 11", composer.findControllerIndexAtPosition(100, false) == -1);
+
+            for (var idx1:int = 0; idx1 < 4; idx1++)
+            {
+                for (var idx2:int = 0; idx2 < 4; idx2++)
+                {
+                    var idx:int;
+
+                    composer = new StandardFlowComposer();
+                    textFlow = new TextFlow();
+                    textFlow.flowComposer = composer;
+                    textFlow.mxmlChildren = [ "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678" ];
+
+                    // add some empties
+                    for (idx = 0; idx < idx1; idx++)
+                        composer.addController(new ContainerController(s));
+                    // add one of length one
+                    controller = new ContainerController(s);
+                    controller.verticalScrollPolicy = "off";	// scrolling confuses it
+                    composer.addController(controller);
+                    controller.setTextLength(100);				// internal API
+                    // add some empties
+                    for (idx = 0; idx < idx2; idx++)
+                        composer.addController(new ContainerController(s));
+                    assertTrue("Bad result in findControllerIndexAtPosition 1", composer.findControllerIndexAtPosition(0) == 0);
+                    assertTrue("Bad result in findControllerIndexAtPosition 2", composer.findControllerIndexAtPosition(0, true) == 0);
+                    assertTrue("Bad result in findControllerIndexAtPosition 3", composer.findControllerIndexAtPosition(100, true) == idx1);
+                    assertTrue("Bad result in findControllerIndexAtPosition 4", composer.findControllerIndexAtPosition(100, false) == -1);
+                    // add one with some length
+                    textFlow.mxmlChildren = [ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678" ];
+                    controller.setTextLength(100);
+                    controller = new ContainerController(s);
+                    controller.verticalScrollPolicy = "off";	// scrolling confuses it
+                    composer.addController(controller);
+                    controller.setTextLength(100);				// internal API
+                    assertTrue("Bad result in findControllerIndexAtPosition 5", composer.findControllerIndexAtPosition(100, true) == idx1);
+                    assertTrue("Bad result in findControllerIndexAtPosition 6", composer.findControllerIndexAtPosition(100, false) == idx1 + 1);
+                    assertTrue("Bad result in findControllerIndexAtPosition 7", composer.findControllerIndexAtPosition(200, true) == idx1 + idx2 + 1);
+                    assertTrue("Bad result in findControllerIndexAtPosition 8", composer.findControllerIndexAtPosition(200, false) == -1);
+                }
+            }
+
+        }
+
+        [Test]
+        public function listItemInsertion():void
+        {
+            // some validations that ensure ListElement is correctly setup for list processing
+
+            // every ListStyleType must have an entry in ListElement.listSuffixes
+            var handler:EnumPropertyHandler = TextLayoutFormat.listStyleTypeProperty.findHandler(EnumPropertyHandler) as EnumPropertyHandler;
+            assertTrue("listItemInsertion: missing handler for ListStyleType", handler != null && handler.range != null);
+
+            var range:Object = handler.range;
+            var value:String;
+
+            var numberedListStyles:Object = { };
+            for (value in ListElement.algorithmicListStyles)
+            {
+                assertTrue("listItemInsertion: table entry duplicated", numberedListStyles[value] === undefined);
+                numberedListStyles[value] = ListElement.algorithmicListStyles[value];
+            }
+            for (value in ListElement.numericListStyles)
+            {
+                assertTrue("listItemInsertion: table entry duplicated", numberedListStyles[value] === undefined);
+                numberedListStyles[value] = ListElement.numericListStyles[value];
+            }
+            for (value in ListElement.alphabeticListStyles)
+            {
+                assertTrue("listItemInsertion: table entry duplicated", numberedListStyles[value] === undefined);
+                numberedListStyles[value] = ListElement.alphabeticListStyles[value];
+            }
+
+            for (value in range)
+            {
+                if (value != FormatValue.INHERIT)
+                {
+                    // must be a numbered list or an unnumbered list but not both
+                    assertTrue("listItemInsertion: listStyleType must be numbered or unnumbered but not both: " + value,
+                            numberedListStyles[value] !== undefined && ListElement.constantListStyles[value] === undefined
+                                    || numberedListStyles[value] === undefined && ListElement.constantListStyles[value] !== undefined)
+                    // numbered lists must have a suffix
+                    if (ListElement.constantListStyles[value] === undefined)
+                        assertTrue("listItemInsertion: missing suffix property: " + value, ListElement.listSuffixes[value] !== undefined);
+                }
+            }
+
+            // verify that all constantListStyles are in range
+            for (value in ListElement.constantListStyles)
+                assertTrue("listItemInsertion: invalid value in constantListStyles: " + value, range[value] !== undefined);
+            // verify that all numberedListStyles are in range
+            for (value in numberedListStyles)
+                assertTrue("listItemInsertion: invalid value in numberedListStyles: " + value, range[value] !== undefined);
+            // verify that all listSuffixes are in range
+            for (value in ListElement.listSuffixes)
+                assertTrue("listItemInsertion: invalid value in listSuffixes: " + value, range[value] !== undefined);
+
+            SelManager.selectRange(0, 0);
+
+            // remove all the children and put in a list
+            var textFlow:TextFlow = SelManager.textFlow;
+
+            textFlow.replaceChildren(0, textFlow.numChildren);
+
+            var list:ListElement = new ListElement();
+            list.listStyleType = ListStyleType.DECIMAL;
+
+            textFlow.addChild(list);
+            var item:ListItemElement = new ListItemElement();
+            list.addChild(item);
+
+            textFlow.flowComposer.updateAllControllers();
+
+            assertTrue("listItemInsertion: incorrect normalize", textFlow.findAbsoluteParagraph(0).parent == item);
+
+            // append two items and compose
+            list.addChild(new ListItemElement());
+            list.addChild(new ListItemElement());
+            textFlow.flowComposer.updateAllControllers();
+
+            // check the three textlines
+            checkListLines(textFlow, 3, "listItemInsertion1");
+
+            // now insert a brand new ListItem at the head and verify
+            list.replaceChildren(0, 0, new ListItemElement());
+            textFlow.flowComposer.updateAllControllers();
+
+            // check four textlines
+            checkListLines(textFlow, 4, "listItemInsertion2");
+
+            // remove a list item
+            list.removeChildAt(1);
+            textFlow.flowComposer.updateAllControllers();
+
+            // check three textLines
+            checkListLines(textFlow, 3, "listItemInsertion3");
+
+            // add another list in the first ListItemElement
+            item = list.getChildAt(0) as ListItemElement;
+            var newList:ListElement = new ListElement();
+            item.addChild(newList);
+            textFlow.flowComposer.updateAllControllers();
+
+            // assert the empty list is deleted
+            assertTrue("listItemInsertion: newList not normalized", newList.numChildren == 1);
+
+        }
+
+        [Test]
+        public function testFTETextField():void
+        {
+            // use the TextFlow's container
+            var fieldParent:Sprite = SelManager.textFlow.flowComposer.getControllerAt(0).container;
+            // remove the controller so the the textFlow isn't displayed in it
+            SelManager.textFlow.flowComposer.removeControllerAt(0);
+
+            var field:FTETextField = new FTETextField();
+            field.htmlText = "Hello world";
+            fieldParent.addChild(field);
+        }
+
+        [Test]
+        public function replaceChildrenTest():void
+        {
+            SelManager.selectRange(0, 0);
+
+            // remove all the children
+            var textFlow:TextFlow = SelManager.textFlow;
+            textFlow.replaceChildren(0, textFlow.numChildren);
+
+            var p:ParagraphElement = new ParagraphElement();
+            textFlow.addChild(p);
+
+            var link:LinkElement = new LinkElement();
+            link.href = "XXX";
+            p.addChild(link);
+            var span:SpanElement = new SpanElement();
+            span.text = "Hello, ";
+            span.fontSize = 24;
+            link.addChild(span);
+
+            var link2:LinkElement = new LinkElement();
+            link2.href = "YYY";
+            p.addChild(link2);
+
+            var span2:SpanElement = new SpanElement();
+            span2.text = "world ";
+            span2.fontSize = 24;
+            link2.addChild(span2);
+
+            textFlow.flowComposer.updateAllControllers();
+
+            link.replaceChildren(link2.numChildren, link2.numChildren, link2.mxmlChildren);
+            p.removeChild(link2);
+            textFlow.flowComposer.updateAllControllers();
+
+            assertTrue("replaceChildrenTest: extra line - look for extra terminator", textFlow.flowComposer.numLines == 1);
+        }
+
+        [Test]
+        public function resolveFontLookupTest():void
+        {
+            var textFlow:TextFlow = SelManager.textFlow;
+
+            textFlow.fontFamily = "Arial";
+            textFlow.fontWeight = FontWeight.BOLD;
+            textFlow.fontLookup = "device";
+            textFlow.flowComposer.updateAllControllers();
+
+            var swfContext:mySwfContext = new mySwfContext();
+            try
+            {
+                GlobalSettings.resolveFontLookupFunction = myFontLookup(swfContext, textFlow.format);
+                textFlow.flowComposer.updateAllControllers();
+                assertTrue("fontLookup not matched.", textFlow.fontLookup = swfContext.myFontlookup);
+            }
+            finally
+            {
+                GlobalSettings.resolveFontLookupFunction = null;
+            }
+        }
+
+        [Test]
+        public function bindableSpan():void
+        {
+            // Bindable span should not lose its formatting
+            var textFlow:TextFlow = new TextFlow();
+            var paragraph:ParagraphElement = new ParagraphElement();
+            var span1:SpanElement = new SpanElement();
+            var span2:SpanElement = new SpanElement();
+            var format:TextLayoutFormat = new TextLayoutFormat();
+            format.fontWeight = FontWeight.BOLD;
+            span2.format = format;
+            paragraph.mxmlChildren = [ span1, span2 ];
+            textFlow.mxmlChildren = [ paragraph ];
+            textFlow.flowComposer.addController(new ContainerController(new Sprite()));
+            textFlow.flowComposer.compose();		// force normalize
+            assertTrue("Spans should not be merged!", span2.parent == span1.parent && paragraph.numChildren == 2);
+            assertTrue("Formatting on second span should be preserved!", span2.fontWeight == FontWeight.BOLD);
+        }
+
+        [Test]
+        /**
+         *  This test does not work in our current build environment, since playerEnablesSpicyFeatures will always be false.
+         *  Once we have a method of compiling VellumUnit as a 10.2 swf, this test should be enabled.
+         */
+        public function softKeyboardFlagTest():void
+        {
+            if (Configuration.playerEnablesSpicyFeatures)	// only run the rest of the test if we're in 10.2 or higher
+            {
+                // test 1 - add controller, then interaction manager
+                var sprite:Sprite = new Sprite();
+                var textFlow:TextFlow = new TextFlow();
+                textFlow.flowComposer.addController(new ContainerController(sprite));
+                textFlow.interactionManager = new EditManager();
+                assertTrue("needsSoftKeyboard should be true after adding EditManager", sprite["needsSoftKeyboard"] == true);
+                // test 2 - add another controller
+                var sprite1:Sprite = new Sprite();
+                textFlow.flowComposer.addController(new ContainerController(sprite1));
+                assertTrue("needsSoftKeyboard should be true for a second container", sprite1["needsSoftKeyboard"] == true);
+
+                // test 3 - add interaction manager, then controller
+                var sprite2:Sprite = new Sprite();
+                var textFlow2:TextFlow = new TextFlow();
+                textFlow2.interactionManager = new EditManager();
+                textFlow2.flowComposer.addController(new ContainerController(sprite2));
+                assertTrue("needsSoftKeyboard should be true after adding controller", sprite2["needsSoftKeyboard"] == true);
+            }
+        }
+
+
+        private function beginAPITest():void
+        {
+            beginTime = getTimer();
+            SelManager.selectRange(-1, -1);
+        }
+
+        private function endAPITest():void
+        {
+            SelManager.selectRange(0, 0);
+            SelManager.textFlow.flowComposer.updateAllControllers();
+        }
+
+        private function initTextFlowAAA():TextFlow
+        {
+            var textFlow:TextFlow = new TextFlow();
+            var p:ParagraphElement = new ParagraphElement();
+            var span:SpanElement = new SpanElement();
+            span.text = "aaa";
+            p.replaceChildren(0, 0, span);
+            textFlow.replaceChildren(0, 0, p);
+            return textFlow;
+        }
+
+        private function createParaIterationModel():TextFlow
+        {
+            // Creates 3 divs, each have 4 paras
+            const paraTotal:int = 4;
+            var paraCount:int;
+            const divTotal:int = 3;
+            var divCount:int;
+
+            var flow:TextFlow = new TextFlow();
+            for (var j:int = 0; j < divTotal; j++)
+            {
+                var div:DivElement = new DivElement();
+                for (var i:int = 0; i < paraTotal; i++)
+                {
+                    var para:ParagraphElement = new ParagraphElement();
+                    var span:SpanElement = new SpanElement();
+                    span.text = paraCount.toString();
+                    para.addChild(span);
+                    div.addChild(para);
+                    paraCount++;
+                }
+                flow.addChild(div);
+            }
+            return flow;
+        }
+
+        private function myFontLookup(context:mySwfContext, tlf:ITextLayoutFormat):Function
+        {
+            return myFontLookup;
+        }
+
+
+        private static function checkListLines(textFlow:TextFlow, numLines:int, prefix:String):void
+        {
+            for (var idx:int = 0; idx < numLines; idx++)
+            {
+                var tfl:TextFlowLine = textFlow.flowComposer.getLineAt(idx);
+                assertTrue(prefix + ": Missing TextFlowLine: " + idx, tfl != null);
+                var textLine:TextLine = tfl.getTextLine();
+                assertTrue(prefix + ": Missing TextLine: " + idx, textLine != null);
+                var numberLine:TextLine = textLine.getChildAt(0) as TextLine;
+                assertTrue(prefix + ": Missing NumberLine: " + idx, numberLine != null);
+                /* var numberString:String = numberLine.userData as String;
+                 var expectedString:String = (idx+1).toString() + ".";	// for numeric lists
+                 assertTrue(prefix+": NumberLine missing userData: "+idx,numberString != null);
+                 assertTrue(prefix+": Incorrect NumberLine userData: "+idx,numberLine.userData as String == expectedString);
+                 assertTrue(prefix+": Incorrect NumberLine rawTextLength: "+idx,numberString.length+1 == numberLine.rawTextLength); */
+            }
+        }
+
+
+        private function validateBitmap(actual:Bitmap, expected:Bitmap):Boolean
+        {
+            actual.bitmapData.draw(expected, null, null, "difference");
+            var bounds:Rectangle = new Rectangle(0, 0, actual.width, actual.height);
+            var diffPixels:ByteArray = actual.bitmapData.getPixels(bounds);
+            diffPixels.position = 0;
+            while (diffPixels.bytesAvailable > 0)
+            {
+                if (diffPixels.readByte() > 0)
+                    return false;
+            }
+
+            return true;
+        }
+
+        private function myFontMapper(fd:FontDescription):void
+        {
+            if (fd.fontName == "Arial Black")
+            {
+                fd.fontName = "Arial";
+                fd.fontWeight = FontWeight.BOLD;
+            }
+        }
+    }
 }
 
 import flash.text.engine.FontLookup;
+
 import flashx.textLayout.compose.ISWFContext;
-import flashx.textLayout.elements.FlowElement;
 import flashx.textLayout.elements.TextFlow;
-import flashx.textLayout.formats.TextLayoutFormat;
 
 class mySwfContext implements ISWFContext
-{	
-	public var myFontlookup:String = FontLookup.EMBEDDED_CFF;
-	
-	public function callInContext(fn:Function, thisArg:Object, argsArray:Array, returns:Boolean=true):*
-	{
-		var tf:TextFlow = thisArg as TextFlow;
-		tf.fontLookup = FontLookup.EMBEDDED_CFF;
-		if (returns)
-			return fn.apply(thisArg, argsArray);
-		fn.apply(thisArg, argsArray);
-	}
-	
+{
+    public var myFontlookup:String = FontLookup.EMBEDDED_CFF;
+
+    public function callInContext(fn:Function, thisArg:Object, argsArray:Array, returns:Boolean = true):*
+    {
+        var tf:TextFlow = thisArg as TextFlow;
+        tf.fontLookup = FontLookup.EMBEDDED_CFF;
+        if (returns)
+            return fn.apply(thisArg, argsArray);
+        fn.apply(thisArg, argsArray);
+    }
+
 }