Updated Asterix SDK + Easy XDM
diff --git a/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.js b/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.js
new file mode 100644
index 0000000..1da4812
--- /dev/null
+++ b/asterix-examples/src/main/resources/js/easyXDM/tests/easyTest.js
@@ -0,0 +1,262 @@
+var easyTest = (function(){
+    var _messages;
+    var _start;
+    var MessageType = {
+        Error: 1,
+        Success: 2,
+        Info: 3
+    };
+    
+    /**
+     * Logs the message to the body
+     * @param {String} msg The message to displey
+     * @param {MessageType} type The messagetype
+     */
+    function _log(msg, type){
+        var el = _messages.appendChild(document.createElement("div"));
+        el.innerHTML = msg;
+        _messages.scrollTop = _messages.scrollHeight;
+        switch (type) {
+            case MessageType.Error:
+                el.className = "easyTest_error";
+                break;
+            case MessageType.Success:
+                el.className = "easyTest_success";
+                break;
+            default:
+                el.className = "easyTest_msg";
+                break;
+        }
+    }
+    
+    var Assert = {
+        // Type checks
+        isTypeOf: function(type, obj){
+            return typeof obj === type;
+        },
+        isInstanceOf: function(type, obj){
+            return obj instanceof type;
+        },
+        isString: function(obj){
+            return this.isTypeOf("string", obj);
+        },
+        isNumber: function(obj){
+            return this.isTypeOf("number", obj);
+        },
+        isObject: function(obj){
+            return this.isTypeOf("object", obj);
+        },
+        isBoolean: function(obj){
+            return this.isTypeOf("boolean", obj);
+        },
+        isFunction: function(obj){
+            return this.isTypeOf("function", obj);
+        },
+        // Equality
+        areEqual: function(a, b){
+            return a == b;
+        },
+        areNotEqual: function(a, b){
+            return a != b;
+        },
+        // Identical
+        areSame: function(a, b){
+            return a === b;
+        },
+        areNotSame: function(a, b){
+            return a !== b;
+        }
+        
+    };
+    
+    function Test(test, fn){
+        var _scope, _steps = test.steps, _step, _stepIndex = 0;
+        var _timer, _runStep, _startedAt, _stepStartedAt;
+        
+        /**
+         * Clean up and tear down the test.<br/>
+         * Calls back to notify that the test is complete
+         */
+        function _endTest(){
+            // Tear down the test
+            if (test.tearDown) {
+                try {
+                    test.tearDown.call(_scope);
+                } 
+                catch (ex) {
+                    _log("Teardown '" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'", MessageType.Error);
+                }
+            }
+            for (var key in _scope) {
+                if (_scope.hasOwnProperty(key)) {
+                    delete _scope[key];
+                }
+            }
+            
+            // Call back
+            fn();
+        }
+        
+        /**
+         * Used to notify the framework of the result of the test
+         * @param {String} name The name of the test
+         * @param {Boolean} result The result of the test
+         * @param {String} reason An optional reason why the test returned the result
+         */
+        function _notifyResult(name, result, reason){
+            var now = new Date().getTime();
+            var testsetTime = now - _start.getTime();
+            var testTime = now - _startedAt.getTime();
+            var stepTime = now - _stepStartedAt.getTime();
+            
+            var times = testsetTime + "ms, " + testTime + "ms, " + stepTime + "ms - ";
+            if (result) {
+                _log(times + name + " succeeded! " + (reason || ""), MessageType.Success);
+            }
+            else {
+                _log(times + name + " failed! " + (reason || ""), MessageType.Error);
+                if (test.failedMessage) {
+                    _log(test.failedMessage, MessageType.Info);
+                }
+            }
+            // Go to next step
+            if (result) {
+                _stepIndex++;
+                window.setTimeout(function(){
+                    _runStep();
+                }, 0);
+            }
+            else {
+                _endTest();
+            }
+        }
+        
+        
+        /**
+         * Runs through the test step
+         */
+        _runStep = function(){
+            if (_stepIndex < _steps.length) {
+                // We still have steps to run
+                _step = _steps[_stepIndex];
+                _stepStartedAt = new Date();
+                if (_step.timeout) {
+                    // This an asynchronous test
+                    _timer = window.setTimeout(function(){
+                        _notifyResult(_step.name, false, "Failed due to timeout.");
+                    }, _step.timeout);
+                    try {
+                        _step.run.call(_scope);
+                    } 
+                    catch (ex) {
+                        //If it fails we cancel the timeout
+                        window.clearTimeout(_timer);
+                        _notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
+                    }
+                }
+                else {
+                    // This is a synchronous test
+                    try {
+                        var result = _step.run.call(_scope);
+                        _notifyResult(_step.name, result);
+                    } 
+                    catch (ex) {
+                        _notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
+                    }
+                }
+            }
+            else {
+                _endTest();
+            }
+        };
+        
+        return {
+            /**
+             * Runs the test.
+             * Will first try to execute the setup method before continuing the steps
+             */
+            run: function(){
+                var excuse;
+                if (test.runIf) {
+                    excuse = test.runIf();
+                }
+                if (excuse) {
+                    _log("Skipping test ' " + test.name + "'. " + excuse);
+                    fn();
+                }
+                else {
+                    _log("Running test '" + test.name + "'");
+                    _scope = {
+                        Assert: Assert,
+                        log: _log,
+                        notifyResult: function(result){
+                            window.clearTimeout(_timer);
+                            _notifyResult(_step.name, result);
+                        }
+                    };
+                    if (test.setUp) {
+                        // Setup the test
+                        try {
+                            test.setUp.call(_scope);
+                            _log("Setup succeeded", MessageType.Success);
+                        } 
+                        catch (ex) {
+                            _log("Setup failed", MessageType.Error);
+                        }
+                    }
+                    _startedAt = new Date();
+                    _runStep();
+                }
+            }
+        };
+    }
+    
+    return {
+        /**
+         * Runs through all the tests
+         * @param {Array} tests The tests to run
+         */
+        test: function(testset){
+            var tests = [], testConfig, i = testset.length, test;
+            
+            // Prepare the messaging facilities
+            if (!_messages) {
+                _messages = document.createElement("div");
+                _messages.className = "easyTest_messages";
+                (document.getElementById("messages") || document.body).appendChild(_messages);
+            }
+            else {
+                _messages.innerHTML = "";
+            }
+            
+            // Convert the testset
+            while (i--) {
+                testConfig = testset[i];
+                if (!testConfig.steps) {
+                    // Convert a single step test to a proper test
+                    testConfig = {
+                        steps: testConfig
+                    };
+                }
+                
+                tests.push(new Test(testConfig, function(){
+                    // Get the next test to run
+                    test = tests.pop();
+                    if (test) {
+                        // This is needed to avoid a strange bug in Opera,
+                        window.setTimeout(function(){
+                            test.run();
+                        }, 0);
+                    }
+                    else {
+                        // No more tests to run
+                        _log("Test run complete", MessageType.Info);
+                    }
+                }));
+            }
+            // Start the first test
+            _start = new Date();
+            tests.pop().run();
+        }
+    };
+}());