You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by jo...@apache.org on 2019/09/18 22:02:45 UTC
[royale-asjs] branch develop updated: RoyaleUnit: Started
implementing async tests
This is an automated email from the ASF dual-hosted git repository.
joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
The following commit(s) were added to refs/heads/develop by this push:
new 542446f RoyaleUnit: Started implementing async tests
542446f is described below
commit 542446fadb47e9b8374ac109ea191f0c920592de
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Wed Sep 18 15:02:36 2019 -0700
RoyaleUnit: Started implementing async tests
Tests must be defined with [Test(async)] metadata.
Can use Async.delayCall() to call a function with asserts later. Other helper functions still need to be implemented.
---
.../src/main/royale/RoyaleUnitClasses.as | 3 +
.../royale/org/apache/royale/test/async/Async.as | 35 ++++++
.../org/apache/royale/test/async/AsyncLocator.as | 107 ++++++++++++++++
.../org/apache/royale/test/async/IAsyncHandler.as | 33 +++++
.../apache/royale/test/runners/MetadataRunner.as | 134 +++++++++++++++++----
.../RoyaleUnit/src/test/royale/tests/AsyncTests.as | 85 ++++++++++++-
6 files changed, 370 insertions(+), 27 deletions(-)
diff --git a/frameworks/projects/RoyaleUnit/src/main/royale/RoyaleUnitClasses.as b/frameworks/projects/RoyaleUnit/src/main/royale/RoyaleUnitClasses.as
index 0088dcc..b406d94 100644
--- a/frameworks/projects/RoyaleUnit/src/main/royale/RoyaleUnitClasses.as
+++ b/frameworks/projects/RoyaleUnit/src/main/royale/RoyaleUnitClasses.as
@@ -38,6 +38,9 @@ internal class RoyaleUnitClasses
import org.apache.royale.test.asserts.assertStrictlyEquals;assertStrictlyEquals;
import org.apache.royale.test.asserts.assertTrue;assertTrue;
import org.apache.royale.test.asserts.fail;fail;
+ import org.apache.royale.test.async.Async;Async;
+ import org.apache.royale.test.async.AsyncLocator;AsyncLocator;
+ import org.apache.royale.test.async.IAsyncHandler;IAsyncHandler;
import org.apache.royale.test.listeners.CIListener;CIListener;
import org.apache.royale.test.listeners.FailureListener;FailureListener;
import org.apache.royale.test.listeners.TraceListener;TraceListener
diff --git a/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/Async.as b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/Async.as
new file mode 100644
index 0000000..206d330
--- /dev/null
+++ b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/Async.as
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.test.async
+{
+ /**
+ * Helper functions for tests marked with <code>[Test(async)]</code> metadata.
+ */
+ public class Async
+ {
+ public static function delayCall(testCase:Object, delayedFunction:Function, delayMS:int):void
+ {
+ var handler:IAsyncHandler = AsyncLocator.getAsyncHandlerForTest(testCase);
+ handler.asyncHandler(function():void
+ {
+ delayedFunction();
+ }, delayMS);
+ }
+ }
+}
\ No newline at end of file
diff --git a/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/AsyncLocator.as b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/AsyncLocator.as
new file mode 100644
index 0000000..aa0721f
--- /dev/null
+++ b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/AsyncLocator.as
@@ -0,0 +1,107 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.test.async
+{
+ import org.apache.royale.test.AssertionError;
+
+ COMPILE::SWF
+ {
+ import flash.utils.Dictionary;
+ }
+
+ /**
+ * Used for tests with <code>[Test(async)]</code> metadata.
+ */
+ COMPILE::SWF
+ public class AsyncLocator
+ {
+ private static var handlers:Dictionary;
+
+ public static function setAsyncHandlerForTest(test:Object, handler:IAsyncHandler):void
+ {
+ if(!handlers)
+ {
+ handlers = new Dictionary()
+ }
+ handlers[test] = handler;
+ }
+
+ public static function getAsyncHandlerForTest(test:Object):IAsyncHandler
+ {
+ if(!handlers || !hasAsyncHandlerForTest(test))
+ {
+ throw new AssertionError(MISSING_ASYNC_ERROR_MESSAGE);
+ }
+ return handlers[test];
+ }
+
+ public static function clearAsyncHandlerForTest(test:Object):void
+ {
+ if(!handlers || !hasAsyncHandlerForTest(test))
+ {
+ return;
+ }
+ delete handlers[test];
+ }
+
+ public static function hasAsyncHandlerForTest(test:Object):Boolean
+ {
+ return test in handlers;
+ }
+ }
+
+ /**
+ * Used for tests with <code>[Test(async)]</code> metadata.
+ */
+ COMPILE::JS
+ public class AsyncLocator
+ {
+ private static var handlers:Map = new Map();
+
+ public static function setAsyncHandlerForTest(test:Object, handler:IAsyncHandler):void
+ {
+ handlers.set(test, handler);
+ }
+
+ public static function getAsyncHandlerForTest(test:Object):IAsyncHandler
+ {
+ if(!handlers || !hasAsyncHandlerForTest(test))
+ {
+ throw new AssertionError(MISSING_ASYNC_ERROR_MESSAGE);
+ }
+ return IAsyncHandler(handlers.get(test));
+ }
+
+ public static function clearAsyncHandlerForTest(test:Object):void
+ {
+ if(!handlers || !hasAsyncHandlerForTest(test))
+ {
+ return;
+ }
+ handlers.delete(test);
+ }
+
+ public static function hasAsyncHandlerForTest(test:Object):Boolean
+ {
+ return handlers.has(test);
+ }
+ }
+}
+
+const MISSING_ASYNC_ERROR_MESSAGE:String = "Cannot add asynchronous functionality to methods defined by Test, Before, or After that are not marked async"
\ No newline at end of file
diff --git a/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/IAsyncHandler.as b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/IAsyncHandler.as
new file mode 100644
index 0000000..86652b1
--- /dev/null
+++ b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/async/IAsyncHandler.as
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.test.async
+{
+ /**
+ * Used for tests with <code>[Test(async)]</code> metadata.
+ */
+ public interface IAsyncHandler
+ {
+ /**
+ * Indicates if the body of the test function is currently executing.
+ */
+ function get bodyExecuting():Boolean;
+
+ function asyncHandler(eventListener:Function, delay:int):void;
+ }
+}
\ No newline at end of file
diff --git a/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/runners/MetadataRunner.as b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/runners/MetadataRunner.as
index 8197465..8bb3d19 100644
--- a/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/runners/MetadataRunner.as
+++ b/frameworks/projects/RoyaleUnit/src/main/royale/org/apache/royale/test/runners/MetadataRunner.as
@@ -25,6 +25,8 @@ package org.apache.royale.test.runners
import org.apache.royale.reflection.describeType;
import org.apache.royale.reflection.getQualifiedClassName;
import org.apache.royale.test.AssertionError;
+ import org.apache.royale.test.async.AsyncLocator;
+ import org.apache.royale.test.async.IAsyncHandler;
import org.apache.royale.test.runners.notification.Failure;
import org.apache.royale.test.runners.notification.IRunListener;
import org.apache.royale.test.runners.notification.IRunNotifier;
@@ -149,6 +151,11 @@ package org.apache.royale.test.runners
public function pleaseStop():void
{
_stopRequested = true;
+ if(_timer)
+ {
+ _timer.stop();
+ cleanupAsync();
+ }
}
/**
@@ -234,12 +241,20 @@ package org.apache.royale.test.runners
{
_before.apply(_target);
}
+ var asyncHandler:AsyncHandler = null;
+ if(test.asyncTimeout > 0)
+ {
+ asyncHandler = new AsyncHandler();
+ AsyncLocator.setAsyncHandlerForTest(_target, asyncHandler);
+ asyncHandler.bodyExecuting = true;
+ }
test.reference.apply(_target);
- if(test.async)
+ if(asyncHandler)
{
- var timeout:int = getTimeout(test);
- _timer = new Timer(timeout, 1);
+ asyncHandler.bodyExecuting = false;
+ _timer = new Timer(test.asyncTimeout, 1);
_timer.addEventListener(Timer.TIMER, timer_timerHandler);
+ _timer.start();
return false;
}
}
@@ -333,6 +348,7 @@ package org.apache.royale.test.runners
var testFunction:Function = null;
var ignore:Boolean = false;
var async:Boolean = false;
+ var asyncTimeout:int = 0;
var testMetadata:Array = method.retrieveMetaDataByName(TestMetadata.TEST);
if(testMetadata.length > 0)
@@ -349,7 +365,18 @@ package org.apache.royale.test.runners
qualifiedName += lastPart;
testName = qualifiedName + "." + method.name;
testFunction = _target[method.name];
- async = testTag.getArgsByKey("async").length > 0;
+ if(testTag.getArgsByKey("async").length > 0)
+ {
+ var timeoutArgs:Array = testTag.getArgsByKey("timeout");
+ if(timeoutArgs.length > 0)
+ {
+ asyncTimeout = parseFloat(timeoutArgs[0].value);
+ }
+ else
+ {
+ asyncTimeout = DEFAULT_ASYNC_TIMEOUT;
+ }
+ }
}
var ignoreMetadata:Array = method.retrieveMetaDataByName(TestMetadata.IGNORE);
if(ignoreMetadata.length > 0)
@@ -358,7 +385,7 @@ package org.apache.royale.test.runners
}
if(testName !== null)
{
- _collectedTests.push(new TestInfo(testName, testFunction, ignore, async));
+ _collectedTests.push(new TestInfo(testName, testFunction, ignore, asyncTimeout));
}
}
}
@@ -366,18 +393,12 @@ package org.apache.royale.test.runners
/**
* @private
*/
- protected function getTimeout(test:TestInfo):int
- {
- return DEFAULT_ASYNC_TIMEOUT;
- }
-
- /**
- * @private
- */
- protected function cleanupTimer():void
+ protected function cleanupAsync():void
{
_timer.removeEventListener(Timer.TIMER, timer_timerHandler);
_timer = null;
+
+ AsyncLocator.clearAsyncHandlerForTest(_target);
}
/**
@@ -385,33 +406,98 @@ package org.apache.royale.test.runners
*/
protected function timer_timerHandler(event:Event):void
{
- cleanupTimer();
+ var asyncHandler:AsyncHandler = AsyncHandler(AsyncLocator.getAsyncHandlerForTest(_target));
+ cleanupAsync();
var test:TestInfo = _collectedTests[_currentIndex];
- var timeout:int = getTimeout(test);
- _failures = true;
- _notifier.fireTestFailure(new Failure(test.description, new AssertionError("Test did not complete within specified timeout " + timeout + "ms")));
+ if(asyncHandler.error)
+ {
+ _failures = true;
+ _notifier.fireTestFailure(new Failure(test.description, asyncHandler.error));
+ }
+ if(asyncHandler.pendingCount > 0)
+ {
+ _failures = true;
+ _notifier.fireTestFailure(new Failure(test.description, new AssertionError("Test did not complete within specified timeout " + test.asyncTimeout + "ms")));
+ }
- _notifier.fireTestFinished(test.description);
- _currentIndex++;
-
+ afterTest(test);
continueAll();
}
}
}
+import org.apache.royale.events.Event;
+import org.apache.royale.test.async.IAsyncHandler;
+import org.apache.royale.test.runners.notification.Failure;
+import org.apache.royale.utils.Timer;
+
class TestInfo
{
- public function TestInfo(name:String, reference:Function, ignore:Boolean, async:Boolean)
+ public function TestInfo(name:String, reference:Function, ignore:Boolean, asyncTimeout:int)
{
this.description = name;
this.reference = reference;
this.ignore = ignore;
- this.async = async;
+ this.asyncTimeout = asyncTimeout;
}
public var description:String;
public var reference:Function;
public var ignore:Boolean;
- public var async:Boolean;
+ public var asyncTimeout:int;
+}
+
+class AsyncHandler implements IAsyncHandler
+{
+ private var _bodyExecuting:Boolean = false;
+
+ public function get bodyExecuting():Boolean
+ {
+ return _bodyExecuting;
+ }
+
+ public function set bodyExecuting(value:Boolean):void
+ {
+ _bodyExecuting = value;
+ }
+
+ private var _error:Error = null;
+
+ public function get error():Error
+ {
+ return _error;
+ }
+
+ public function get pendingCount():int
+ {
+ return _timers.length;
+ }
+
+ private var _timers:Vector.<Timer> = new <Timer>[];
+
+ public function asyncHandler(callback:Function, delayMS:int):void
+ {
+ var timerIndex:int = -1;
+ var timer:Timer = new Timer(delayMS, 1);
+ timer.addEventListener(Timer.TIMER, function(event:Event):void
+ {
+ timer.removeEventListener(Timer.TIMER, arguments["callee"]);
+ var index:int = _timers.indexOf(timer);
+ if(index !== -1)
+ {
+ _timers.splice(index, 1);
+ }
+ try
+ {
+ callback();
+ }
+ catch(error:Error)
+ {
+ _error = error;
+ }
+ });
+ _timers.push(timer);
+ timer.start();
+ }
}
\ No newline at end of file
diff --git a/frameworks/projects/RoyaleUnit/src/test/royale/tests/AsyncTests.as b/frameworks/projects/RoyaleUnit/src/test/royale/tests/AsyncTests.as
index 4ff89df..3d817c7 100644
--- a/frameworks/projects/RoyaleUnit/src/test/royale/tests/AsyncTests.as
+++ b/frameworks/projects/RoyaleUnit/src/test/royale/tests/AsyncTests.as
@@ -22,6 +22,7 @@ package tests
import org.apache.royale.test.runners.MetadataRunner;
import org.apache.royale.test.runners.notification.RunNotifier;
import org.apache.royale.test.runners.notification.IRunListener;
+ import org.apache.royale.test.async.Async;
public class AsyncTests
{
@@ -48,18 +49,75 @@ package tests
_runner.run(notifier);
Assert.assertTrue(listener.runStarted);
Assert.assertFalse(listener.runFinished);
- Assert.assertStrictlyEquals(listener.finishedCount, 1);
+ //we can't check finished count because the order of tests cannot be
+ //determined. the async test may be run first.
Assert.assertStrictlyEquals(listener.failureCount, 0);
Assert.assertStrictlyEquals(listener.ignoreCount, 0);
- Assert.assertEquals(listener.active, "tests.AsyncTests::AsyncFixture.test2");
+ Assert.assertTrue(listener.active.indexOf("AsyncTests::AsyncFixture.test2") > 0);
_runner.pleaseStop();
}
+
+ [Test(async)]
+ public function testRunFinishesAsynchronously():void
+ {
+ var notifier:RunNotifier = new RunNotifier();
+ var listener:AsyncListener = new AsyncListener();
+ notifier.addListener(listener);
+ _runner = new MetadataRunner(AsyncFixture);
+ _runner.run(notifier);
+ Async.delayCall(this, function():void
+ {
+ Assert.assertTrue(listener.runStarted);
+ Assert.assertTrue(listener.runFinished);
+ Assert.assertStrictlyEquals(listener.finishedCount, 2);
+ Assert.assertStrictlyEquals(listener.failureCount, 0);
+ Assert.assertStrictlyEquals(listener.ignoreCount, 0);
+ Assert.assertNull(listener.active);
+ }, 400);
+ }
+
+ [Test(async)]
+ public function testAsyncTestWithSynchronousFail():void
+ {
+ var notifier:RunNotifier = new RunNotifier();
+ var listener:AsyncListener = new AsyncListener();
+ notifier.addListener(listener);
+ _runner = new MetadataRunner(AsyncFailFixture);
+ _runner.run(notifier);
+ Assert.assertTrue(listener.runStarted);
+ Assert.assertTrue(listener.runFinished);
+ Assert.assertStrictlyEquals(listener.finishedCount, 1);
+ Assert.assertStrictlyEquals(listener.failureCount, 1);
+ Assert.assertStrictlyEquals(listener.ignoreCount, 0);
+ Assert.assertNull(listener.active);
+ }
+
+ [Test(async)]
+ public function testAsyncTestWithFailInsideDelayCall():void
+ {
+ var notifier:RunNotifier = new RunNotifier();
+ var listener:AsyncListener = new AsyncListener();
+ notifier.addListener(listener);
+ _runner = new MetadataRunner(AsyncFailWithDelayCallFixture);
+ _runner.run(notifier);
+ Async.delayCall(this, function():void
+ {
+ Assert.assertTrue(listener.runStarted);
+ Assert.assertTrue(listener.runFinished);
+ Assert.assertStrictlyEquals(listener.finishedCount, 1);
+ Assert.assertStrictlyEquals(listener.failureCount, 1);
+ Assert.assertStrictlyEquals(listener.ignoreCount, 0);
+ Assert.assertNull(listener.active);
+ }, 400);
+ }
}
}
import org.apache.royale.test.runners.notification.IRunListener;
import org.apache.royale.test.runners.notification.Failure;
import org.apache.royale.test.runners.notification.Result;
+import org.apache.royale.test.Assert;
+import org.apache.royale.test.async.Async;
class AsyncFixture
{
@@ -68,12 +126,33 @@ class AsyncFixture
{
}
- [Test(async)]
+ [Test(async,timeout="100")]
public function test2():void
{
}
}
+class AsyncFailFixture
+{
+ [Test(async,timeout="100")]
+ public function test1():void
+ {
+ Assert.fail();
+ }
+}
+
+class AsyncFailWithDelayCallFixture
+{
+ [Test(async,timeout="200")]
+ public function test1():void
+ {
+ Async.delayCall(this, function():void
+ {
+ Assert.fail();
+ }, 100);
+ }
+}
+
class AsyncListener implements IRunListener
{
public var runStarted:Boolean = false;