Created
May 17, 2010 22:31
-
-
Save wolever/404321 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package utils.testlisteners { | |
import flash.external.ExternalInterface; | |
import mx.events.DynamicEvent; | |
/** | |
* Reports the results of a TextTestListener into the browser, using HTML | |
* instad of Flash. | |
*/ | |
public class HTMLTestReporter { | |
protected static var bootstrapJavascript:String = (<![CDATA[ | |
(function() { | |
// Note: The Flash player will restart if it's resized or moved, | |
// which will break the connection to the debugger and other | |
// unhappy things. | |
// So, instead of trying to move it, we just slap this <pre> | |
// over top of it. | |
var logTarget = document.createElement("pre"); | |
logTarget.id = "logTarget"; | |
logTarget.style.position = "fixed"; | |
logTarget.style.left = 0; | |
logTarget.style.top = 0; | |
logTarget.style.margin = 0; | |
logTarget.style.width = "100%"; | |
logTarget.style.height = "100%"; | |
logTarget.style.overflow = "auto"; | |
document.body.appendChild(logTarget); | |
window.writeText = function(message) { | |
logTarget.textContent += message; | |
logTarget.scrollTop = logTarget.scrollHeight; | |
}; | |
})(); | |
]]>); | |
public static function setup(listener:TextTestListener):void { | |
ExternalInterface.call("eval", bootstrapJavascript); | |
listener.addEventListener("gotText", | |
function(event:DynamicEvent):void { | |
ExternalInterface.call("writeText", event.text); | |
}); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" | |
xmlns:adobe="http://www.adobe.com/2009/flexUnitUIRunner" | |
creationComplete="runTests();" | |
layout="absolute" xmlns:testlisteners="utils.testlisteners.*"> | |
<mx:Script><![CDATA[ | |
import tests.UtilsTestSuite; | |
import org.flexunit.runner.FlexUnitCore; | |
import utils.logger.Log; | |
import utils.logger.LogEvent; | |
import utils.testlisteners.HTMLTestReporter; | |
import utils.testlisteners.TextTestListener; | |
public function runTests():void { | |
var listener:TextTestListener = new TextTestListener(); | |
HTMLTestReporter.setup(listener); | |
Log.addListener(function(event:LogEvent):void { | |
var msg:String = event.levelString + ": " + event.message + "\n"; | |
listener.injectLogMessage(msg); | |
}); | |
var core:FlexUnitCore; | |
core = new FlexUnitCore(); | |
core.addListener(listener); | |
core.run(UtilsTestSuite); | |
} | |
]]></mx:Script> | |
</mx:Application> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package utils.testlisteners { | |
import flash.events.EventDispatcher; | |
import flex.lang.reflect.metadata.MetaDataAnnotation; | |
import mx.events.DynamicEvent; | |
import org.flexunit.runner.IDescription; | |
import org.flexunit.runner.Result; | |
import org.flexunit.runner.notification.Failure; | |
import org.flexunit.runner.notification.IRunListener; | |
import utils.create; | |
import utils.filter; | |
import utils.forEach; | |
import utils.printf; | |
/** | |
* A FlexUnit RunListener which emits plain text describing the status | |
* of the test run. | |
* Note: See the 'injectLogMessage' method, which allows log messages | |
* to be injected into the test run then displated if a test fails. | |
*/ | |
[Event(name="gotText", type="mx.events.DynamicEvent")] | |
public class TextTestListener extends EventDispatcher implements IRunListener { | |
public static const QUIET:int = 0; | |
public static const VERBOSE:int = 1; | |
public var verbosity:int = VERBOSE; | |
public var stacktraceLimit:int = 20; | |
public var stacktraceShortenPaths:Boolean = true; | |
public var stacktraceShortenPathRe:RegExp = /\[.*([\/\\].*?[\/\\].*?)\]/ | |
protected var startTime:Date; | |
protected var failures:Array; | |
protected var ignored:Array; | |
protected var logMessages:Array; | |
public function injectLogMessage(message:String):void { | |
// Just incase we get log messages before the tests start | |
if (logMessages) | |
logMessages.push(message); | |
} | |
protected function gotText(txtVerbosityOrTxt:*, ...args):void { | |
var txtVerbosity:int = verbosity; | |
var text:String; | |
if (txtVerbosityOrTxt is int) { | |
txtVerbosity = txtVerbosityOrTxt; | |
text = args[0]; | |
args.splice(0, 1); | |
} else { | |
text = txtVerbosityOrTxt; | |
} | |
if (verbosity >= txtVerbosity) { | |
text = printf.apply(null, [text].concat(args)); | |
dispatchEvent(create(DynamicEvent, "gotText", { text: text })); | |
} | |
} | |
/** | |
* Called before any tests have been run and the test run is starting. | |
* | |
* @param description The <code>IDescription</code> of the top most | |
* <code>IRunner</code>. | |
*/ | |
public function testRunStarted(description:IDescription):void { | |
startTime = new Date(); | |
failures = []; | |
ignored = []; | |
trace("ASDFSDF"); | |
gotText(VERBOSE, "Starting tests...\n"); | |
} | |
protected static function getMetadata(description:IDescription, | |
name:String):MetaDataAnnotation { | |
for each (var metadata:MetaDataAnnotation in description.getAllMetadata()) { | |
if (metadata.name == name) | |
return metadata; | |
} | |
throw Error("Metadata with name '" + name + "' not found."); | |
} | |
protected function showIgnored():void { | |
if (ignored.length <= 0) | |
return; | |
gotText("===================================" + | |
"===================================\n"); | |
gotText("IGNORED\n"); | |
gotText("-----------------------------------" + | |
"-----------------------------------\n"); | |
for each (var description:IDescription in ignored) { | |
var ignore:MetaDataAnnotation = getMetadata(description, "Ignore"); | |
var desc:String = ""; | |
if (ignore.arguments.length > 0) | |
desc = ": " + ignore.arguments[0].key; | |
gotText(description.displayName + desc + "\n"); | |
} | |
gotText("\n"); | |
} | |
protected function showFailures():void { | |
if (failures.length <= 0) | |
return; | |
for each (var failureObj:Object in failures) { | |
var failure:Failure = failureObj.failure; | |
gotText("===================================" + | |
"===================================\n"); | |
gotText("ERROR: %s\n", failure.description.displayName); | |
gotText("-----------------------------------" + | |
"-----------------------------------\n"); | |
// Fix up the stack trace | |
// Shorten it up a bit... | |
var stackTrace:Array = failure.stackTrace.split("\n"); | |
if (stackTrace.length > stacktraceLimit) { | |
var removed:int = stackTrace.length - stacktraceLimit; | |
stackTrace = stackTrace.slice(0, stacktraceLimit); | |
stackTrace.push("... and " + removed + " more ..."); | |
} | |
// Shorten the paths | |
if (stacktraceShortenPaths) { | |
stackTrace = stackTrace.map(function(line:String, ..._):String { | |
return line.replace(stacktraceShortenPathRe, "[...$1]"); | |
}); | |
} | |
// Split out the error portion | |
stackTrace.reverse(); | |
var _inTrace:Boolean = true; | |
// Find the error second to reduce the chance that the error | |
// will look like a stack trace. | |
var error:Array = filter(function(line:String):Boolean { | |
if (_inTrace && !line.match(/^\tat /)) | |
_inTrace = false; | |
return !_inTrace; | |
}, stackTrace.slice(1)); | |
stackTrace.splice(stackTrace.length - error.length, error.length); | |
error.reverse(); | |
// Send out the stack trace | |
gotText(stackTrace.join("\n") + "\n"); | |
gotText(error.join("\n") + "\n"); | |
// Print any log messages associated with this failure | |
if (failureObj.logMessages.length > 0) { | |
gotText("---------------------- >> " + | |
"begin captured log" + | |
" << ----------------------\n"); | |
forEach(failureObj.logMessages, gotText); | |
gotText("----------------------- >> " + | |
"end captured log" + | |
" << -----------------------\n"); | |
} | |
gotText("\n"); | |
} | |
} | |
/** | |
* Called when all tests have finished and the test run is done. | |
* | |
* @param result The <code>Result</code> of the test run, including all the tests | |
* that have failed. | |
*/ | |
public function testRunFinished(result:Result):void { | |
showIgnored(); | |
showFailures(); | |
// Since 'quiet' just spits out a string of periods, we've got to | |
// manually stick a newline after it. | |
if (verbosity == QUIET) | |
gotText("\n"); | |
gotText("-----------------------------------" + | |
"-----------------------------------\n"); | |
var runTime:Number = ((new Date()).time - startTime.time)/1000; | |
gotText("Ran %s tests in %0.3fs\n\n", result.runCount, runTime); | |
var summary:Array = [] | |
if (result.failureCount) { | |
summary.push(printf("failures=%s", result.failureCount)); | |
} | |
if (result.ignoreCount) { | |
summary.push(printf("ignored=%s", result.ignoreCount)); | |
} | |
var summaryStr:String = ""; | |
if (summary.length > 0) { | |
summaryStr = printf(" (%s)", summary.join(", ")); | |
} | |
if (result.failureCount > 0) { | |
gotText("FAILED%s\n", summaryStr); | |
} else { | |
gotText("OK%s\n", summaryStr); | |
} | |
} | |
/** | |
* Called when an atomic test is about to be begin. | |
* | |
* @param description The <code>IDescription</code> of the test that is about | |
* to be run (generally a class and method name). | |
*/ | |
public function testStarted(description:IDescription):void { | |
logMessages = []; | |
gotText(VERBOSE, description.displayName + "... "); | |
} | |
/** | |
* Called when an atomic test has finished, whether the test succeeds or fails. | |
* | |
* @param description The <code>IDescription</code> of the test that finished. | |
*/ | |
protected var _lastDidntSucceed:Boolean = false; | |
public function testFinished(description:IDescription):void { | |
if (_lastDidntSucceed) { | |
_lastDidntSucceed = false; | |
return; | |
} | |
if (verbosity >= VERBOSE) | |
gotText("ok\n"); | |
else | |
gotText("."); | |
} | |
/** | |
* Called when an atomic test fails. | |
* | |
* @param failure The <code>Failure</code> indicating why the test has failed. | |
*/ | |
public function testFailure(failure:Failure):void { | |
failures.push({ failure: failure, logMessages: logMessages }); | |
_lastDidntSucceed = true; | |
if (verbosity >= VERBOSE) | |
gotText("FAILED\n"); | |
else | |
gotText("F"); | |
} | |
/** | |
* Called when an atomic test flags that it assumes a condition that is false. | |
* | |
* @param failure The <code>Failure</code> indicating the | |
* <code>AssumptionViolatedException</code> that was thrown. | |
*/ | |
public function testAssumptionFailure(failure:Failure):void { | |
// do nothing - I don't like assumptions | |
} | |
/** | |
* Called when a test will not be run, generally because a test method is annotated | |
* with the <code>Ignore</code> tag. | |
* | |
* @param description The <code>IDescription</code> of the test that will be | |
* ignored. | |
*/ | |
public function testIgnored(description:IDescription):void { | |
ignored.push(description); | |
_lastDidntSucceed = false; | |
if (verbosity >= VERBOSE) | |
gotText(description.displayName + "... IGNORED\n"); | |
else | |
gotText("I"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment