<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>test/flash_tests/TestApp.mxml</filename>
    </added>
    <added>
      <filename>test/flash_tests/TestAppCode.as</filename>
    </added>
    <added>
      <filename>test/flash_tests/TestBar.as</filename>
    </added>
    <added>
      <filename>test/flash_tests/TestFoo.as</filename>
    </added>
    <added>
      <filename>test/flash_tests/build.py</filename>
    </added>
    <added>
      <filename>test/flash_tests/index.html</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -17,15 +17,153 @@ Copyright 2009, Matthew Eernisse (mde@fleegix.org) and Slide, Inc.
 package org.windmill {
   import org.windmill.WMLocator;
   import org.windmill.WMLogger;
+  import flash.utils.getQualifiedClassName;
 
-  public class WMAssert {
-    public function WMAssert():void {}
+  public dynamic class WMAssert {
+
+    public static var assertTemplates:Object = {
+      assertTrue: {
+        expr: function (a:Boolean):Boolean {
+            return a === true;
+          },
+        errMsg: 'expected true but was false.'
+      },
+      assertFalse: {
+        expr: function (a:Boolean):Boolean {
+            return a === false;
+        },
+        errMsg: 'expected false but was true.'
+      },
+      assertEquals: {
+        expr: function (a:*, b:*):Boolean { return a === b; },
+        errMsg: 'expected $1 but was $2.'
+      },
+      assertNotEquals: {
+        expr: function (a:*, b:*):Boolean { return a !== b; },
+        errMsg: 'expected one of the two values not to be $1.'
+      },
+      assertGreaterThan: {
+        expr: function (a:*, b:*):Boolean { return a &gt; b; },
+        errMsg: 'expected a value greater than $2 but was $1.'
+      },
+      assertLessThan: {
+        expr: function (a:*, b:*):Boolean { return a &lt; b; },
+        errMsg: 'expected a value less than $2 but was $1.'
+      },
+      assertNull: {
+        expr: function (a:*):Boolean { return a === null; },
+        errMsg: 'expected to be null but was $1.'
+      },
+      assertNotNull: {
+        expr: function (a:*):Boolean { return a !== null; },
+        errMsg: 'expected not to be null but was null.'
+      },
+      assertUndefined: {
+        expr: function (a:*):Boolean { return typeof a == 'undefined'; },
+        errMsg: 'expected to be undefined but was $1.'
+      },
+      assertNotUndefined: {
+        expr: function (a:*):Boolean { return typeof a != 'undefined'; },
+        errMsg: 'expected not to be undefined but was undefined.'
+      },
+      assertNaN: {
+        expr: function (a:*):Boolean { return isNaN(a); },
+        errMsg: 'expected $1 to be NaN, but was not NaN.'
+      },
+      assertNotNaN: {
+        expr: function (a:*):Boolean { return !isNaN(a); },
+        errMsg: 'expected $1 not to be NaN, but was NaN.'
+      },
+      assertEvaluatesToTrue: {
+        expr: function (a:*):Boolean { return !!a; },
+        errMsg: 'value of $1 does not evaluate to true.'
+      },
+      assertEvaluatesToFalse: {
+        expr: function (a:*):Boolean { return !a; },
+        errMsg: 'value of $1 does not evaluate to false.'
+      },
+      assertContains: {
+        expr: function (a:*, b:*):Boolean {
+            if (typeof a != 'string' || typeof b != 'string') {
+              throw('Bad argument to assertContains.');
+            }
+            return (a.indexOf(b) &gt; -1);
+        },
+        errMsg: 'value of $1 does not contain $2.'
+      }
+    };
 
     private static var matchTypes:Object = {
       EXACT: 'exact',
       CONTAINS: 'contains'
     };
 
+    public static function init():void {
+      for (var p:String in WMAssert.assertTemplates) {
+        WMAssert[p] = WMAssert.createAssert(p);
+      }
+    }
+
+    private static function createAssert(meth:String):Function {
+      // Makes sure each assert is called with the right
+      // number of args
+      // -------
+      var validateArgs:Function = function(count:int,
+          args:Array):Boolean {
+        if (!(args.length == count ||
+        (args.length == count + 1 &amp;&amp; typeof(args[0]) == 'string') )) {
+          throw('Incorrect arguments passed to assert function');
+        }
+        return true;
+      }
+      // Creates error message for each assert
+      // -------
+      var createErrMsg:Function = function (msg:String, arr:Array):String {
+        var str:String = msg;
+        for (var i:int = 0; i &lt; arr.length; i++) {
+          // When calling jum functions arr is an array with a null entry
+          if (arr[i] != null){
+            var val:* = arr[i];
+            var display:String = '&lt;' + val.toString().replace(/\n/g, '') +
+              '&gt; (' + getQualifiedClassName(val) + ')';
+            str = str.replace('$' + (i + 1).toString(), display);
+          }
+        }
+        return str;
+      }
+      // Function that runs the dynamically generated asserts
+      // -------
+      var doAssert:Function = function(...args):Boolean {
+        // The actual assert method, e.g, 'equals'
+        var meth:String = args.shift();
+        // The assert object
+        var asrt:Object = WMAssert.assertTemplates[meth]; 
+        // The assert expresion
+        var expr:Function = asrt.expr;
+        // Validate the args passed
+        var valid:Boolean = validateArgs(expr.length, args);
+        // Pull off additional comment which may be first arg
+        //var comment = args.length &gt; expr.length ?
+        //  args.shift() : null;
+        // Run the assert
+        var res:Boolean = expr.apply(null, args);
+        if (res) {
+          return true;
+        }
+        else {
+          var message:String = meth + ' -- ' +
+              createErrMsg(asrt.errMsg, args);
+          throw new Error(message);
+        }
+      }
+      // Return a function for each dynamically generated
+      // assert in the assertTemplates list
+      return function (...args):Boolean {
+        args.unshift(meth);
+        return doAssert.apply(null, args);
+      }
+    }
+
     public static function assertDisplayObject(params:Object):Boolean {
       var obj:* = WMLocator.lookupDisplayObject(params);
       if (!!obj) {
@@ -38,7 +176,7 @@ package org.windmill {
     }
 
     public static function assertProperty(params:Object):Boolean {
-      return WMAssert.doAssert(params);
+      return WMAssert.doBaseAssert(params);
     }
 
     public static function assertText(params:Object):Boolean {
@@ -51,7 +189,7 @@ package org.windmill {
     
     private static function assertTextGeneric(params:Object,
         exact:Boolean):Boolean {
-      return WMAssert.doAssert(params, {
+      return WMAssert.doBaseAssert(params, {
         attrName: ['htmlText', 'label'],
         preMatchProcess: function (str:String):String {
           return str.replace(/^\s*|\s*$/g, '');
@@ -63,7 +201,7 @@ package org.windmill {
     
     // Workhorse function that does all the main work for
     // most asserts
-    private static function doAssert(params:Object,
+    private static function doBaseAssert(params:Object,
         opts:Object = null):Boolean {
       // Ref to the object to do the lookup on
       var obj:* = WMLocator.lookupDisplayObject(params);</diff>
      <filename>flash/org/windmill/WMAssert.as</filename>
    </modified>
    <modified>
      <diff>@@ -22,11 +22,10 @@ package org.windmill {
   import flash.system.LoaderContext;
 
   public class WMBootstrap {
-    public function WMBootstrap():void {}
-
+    public static var windmillLibPath:String = '/flash/org/windmill/Windmill.swf'; 
     public static function init(context:*):void {
       var loader:Loader = new Loader();
-      var url:String = '/flash/org/windmill/Windmill.swf';
+      var url:String = WMBootstrap.windmillLibPath;
       var req:URLRequest = new URLRequest(url);
       var con:LoaderContext = new LoaderContext(false,
           ApplicationDomain.currentDomain);</diff>
      <filename>flash/org/windmill/WMBootstrap.as</filename>
    </modified>
    <modified>
      <diff>@@ -18,7 +18,11 @@ package org.windmill {
   import flash.external.ExternalInterface;
 
   public class WMLogger {
-    public static var mode:String = 'browser';
+    public static var modes:Object = {
+      TRACE: 'trace',
+      BROWSER: 'browser'
+    };
+    public static var mode:String = WMLogger.modes.TRACE;
     public static function log(msg:*):void {
       if (WMLogger.mode == 'browser') {
         ExternalInterface.call(&quot;console.log&quot;, msg);</diff>
      <filename>flash/org/windmill/WMLogger.as</filename>
    </modified>
    <modified>
      <diff>@@ -83,7 +83,7 @@ package org.windmill {
       }
       context = config.context;
 
-      // Expose controller and assert methods
+      // Expose controller and non-dynamic assert methods
       // ----------------
       for (var key:String in packages) {
         // Introspect all the public packages
@@ -102,8 +102,24 @@ package org.windmill {
               genExtFunc(packages[key].packageRef[methodName]));
         }
       }
-
+      
+      // Expose dynamic asserts
+      // ----------------
+      // Create all the dynamic assert methods
+      WMAssert.init();
+      // Dynamically generated asserts -- these *will not*
+      // show up via introspection with describeType, but
+      // they *are there* -- add them manually by iterating
+      // through the same list that used to build them
+      var asserts:* = WMAssert;
+      for (methodName in asserts.assertTemplates) {
+        ExternalInterface.addCallback('wm_' + methodName,
+            genExtFunc(asserts[methodName]));
+        
+      }
+      
       // Other misc ExternalInterface methods
+      // ----------------
       var miscMethods:Object = {
         explorerStart: WMExplorer.start,
         explorerStop: WMExplorer.stop,
@@ -111,11 +127,11 @@ package org.windmill {
         recorderStop: WMRecorder.stop,
         runASTests: ASTest.run
       }
-      // Don't care what order these happen in
       for (methodName in miscMethods) {
         ExternalInterface.addCallback('wm_' + methodName,
             genExtFunc(miscMethods[methodName]));
       }
+
     }
 
     public static function getStage():Stage {</diff>
      <filename>flash/org/windmill/Windmill.as</filename>
    </modified>
    <modified>
      <diff>@@ -20,7 +20,9 @@ package org.windmill.astest {
 
   public class ASTest {
     public static var testClassList:Array = [];
-    public static var testList:Array = [];
+    private static var testListComplete:Array = [];
+    private static var testList:Array = [];
+
     public static function run(files:Array = null):void {
       //['/flash/TestFoo.swf', '/flash/TestBar.swf']
       // If we're passed some files, load 'em up first
@@ -36,18 +38,34 @@ package org.windmill.astest {
   
     public static function loadTestFiles(files:Array):void {
       ASTest.testClassList = [];
+      ASTest.testList = [];
       WMLoader.load(files);
     }
 
     public static function start():void {
-      for each (var obj:Object in ASTest.testList) {
+      ASTest.testList = ASTest.testListComplete.slice();
+      // Run recursively in a setTimeout loop so
+      // we can implement sleeps and waits
+      ASTest.runNextTest();
+    }
+
+    public static function runNextTest():void {
+      if (ASTest.testList.length == 0) {
+        // Bail
+      }
+      else {
+        var test:Object = ASTest.testList.shift();
         try {
-          WMLogger.log('Running ' + obj.className + '.' + obj.methodName);
-          obj.instance[obj.methodName].call(obj.instance);
+          WMLogger.log('Running ' + test.className + '.' + test.methodName);
+          test.instance[test.methodName].call(test.instance);
+          WMLogger.log('SUCCESS');
         }
         catch (e:Error) {
-          WMLogger.log(e.message);
+          WMLogger.log('ERROR: ' + e.message);
         }
+        setTimeout(function ():void {
+          ASTest.runNextTest.call(ASTest);
+        }, 50);
       }
     }
 
@@ -65,7 +83,6 @@ package org.windmill.astest {
       // No args -- this is being re-invoked from WMLoader
       // now that we have our tests loaded
       for each (var item:Object in ASTest.testClassList) {
-        WMLogger.log('className: ' + item.className);
         var currTestList:Array = [];
         var descr:XML;
         var hasSetup:Boolean = false;
@@ -73,10 +90,11 @@ package org.windmill.astest {
         descr = flash.utils.describeType(
             item.classDescription);
         var meth:*;
+        var methods:Object = {};
         for each (meth in descr..method) {
           var methodName:String = meth.@name.toXMLString();
           if (/^test/.test(methodName)) {
-            currTestList.push(createTestItem(item, methodName)); 
+            methods[methodName] = item;
           }
           // If there's a setup or teardown somewhere in there
           // flag them so we can prepend/append after adding all
@@ -88,6 +106,26 @@ package org.windmill.astest {
             hasTeardown = true;
           }
         }
+
+        // Normal test methods
+        // -----
+        // If there's an 'order' array defined, run any tests
+        // it contains in the defined order
+        var key:String;
+        if ('order' in item.instance) {
+          WMLogger.log('ordering ...');
+          for each (key in item.instance.order) {
+            currTestList.push(createTestItem(methods[key], key));
+            delete methods[key];
+          }
+        }
+        // Run any other methods in whatever order
+        for (key in methods) {
+          currTestList.push(createTestItem(methods[key], key));
+        }
+
+        
+        // -----
         // Prepend list with setup if one exists
         if (hasSetup) {
           currTestList.unshift(createTestItem(item, 'setup'));
@@ -98,8 +136,7 @@ package org.windmill.astest {
         }
         testList = testList.concat.apply(testList, currTestList);
       }
-      ASTest.testList = testList;
-
+      ASTest.testListComplete = testList;
     }
   }
 }</diff>
      <filename>flash/org/windmill/astest/ASTest.as</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>flash/ExampleCode.as</filename>
    </removed>
    <removed>
      <filename>flash/TestBar.as</filename>
    </removed>
    <removed>
      <filename>flash/TestFoo.as</filename>
    </removed>
    <removed>
      <filename>flash/index.html</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>9c4a760275f363b46f00b144f5d986e4c6c78239</id>
    </parent>
    <parent>
      <id>dbfb0aec3ed60b2092551330b425c4a23c0a89c3</id>
    </parent>
  </parents>
  <author>
    <name>Adam Christian</name>
    <email>adam.christian@gmail.com</email>
  </author>
  <url>http://github.com/mde/windmill/commit/9e61b787a8374c8ae9120497ef841fe4835fdc80</url>
  <id>9e61b787a8374c8ae9120497ef841fe4835fdc80</id>
  <committed-date>2009-09-01T15:28:18-07:00</committed-date>
  <authored-date>2009-09-01T15:28:18-07:00</authored-date>
  <message>Merge commit 'upstream/master'</message>
  <tree>85e98456f5866e92f9f16f3ea3c60212a2a580e5</tree>
  <committer>
    <name>Adam Christian</name>
    <email>adam.christian@gmail.com</email>
  </committer>
</commit>
