Test Harness
There are other test harnesses out there, but today I thought I’d share one of my own.
The goals of my test harness are as follows:
- Get to writing code as fast as possible
- Minimal overhead
- Framerate counter
- Logging support without external tools
To that end I have a one file test harness to share with you:
package { import flash.display.*; import flash.events.*; import flash.text.*; import flash.utils.*; [SWF(backgroundColor=0xEEEADB)] /** * A simple harness for an AS3 test * @author Jackson Dunstan */ public class SimpleTestHarness extends Sprite { /** Radius of the particle */ private static const PARTICLE_RADIUS:Number = 50; /** Sprite we're going to animate around */ private var __particle:Sprite; /** The particle's velocity in the X */ private var __velocityX:Number; /** The particle's velocity in the Y */ private var __velocityY:Number; /** * Initialize the application */ private function init(): void { // Test out timing beginTiming(); var sum:uint = 0; for (var i:uint = 0; i < 1000000; ++i) { sum += i; } endTiming("Time to sum the first million positive integers"); // Test out something to update __particle = new Sprite(); __particle.graphics.beginFill(0x261c13); __particle.graphics.drawCircle(0, 0, PARTICLE_RADIUS); __particle.graphics.endFill(); addChild(__particle); launchParticle(); } /** * Update based on elapsed time * @param elapsed Number of milliseconds elapsed since the last update */ private function update(elapsed:int): void { var pixelsToMove:Number = (200 * elapsed) / 1000; __particle.x += __velocityX * pixelsToMove; __particle.y += __velocityY * pixelsToMove; if (__particle.x < -PARTICLE_RADIUS || __particle.y < -PARTICLE_RADIUS || __particle.x > stage.stageWidth+PARTICLE_RADIUS || __particle.y > stage.stageHeight+PARTICLE_RADIUS) { log("resetting particle"); launchParticle(); } } /** * Launch the particle */ private function launchParticle(): void { __particle.x = stage.stageWidth / 2; __particle.y = stage.stageWidth / 2; __velocityX = Math.random() - 1; __velocityY = Math.random() - 1; } /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// // Test harness code is below. You shouldn't have to change it for most // tests you do. /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// /** Last time we updated FPS */ private var __lastFPSUpdateTime:uint; /** Last time we entered a frame */ private var __lastFrameTime:uint; /** Count of frames since the last time we updated the FPS */ private var __frameCount:uint; /** Framerate display */ private var __framerate:TextField; /** Text field to show log messages */ private var __logger:TextField; /** Time when we started timing */ private var __beginTime:int; /** * Application entry point. You shouldn't have to change this function * for most tests. */ public function SimpleTestHarness() { // Setup stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; var format:TextFormat = new TextFormat(); format.font = "_sans"; // Setup framerate display __framerate = new TextField(); __framerate.autoSize = TextFieldAutoSize.LEFT; __framerate.background = true; __framerate.backgroundColor = 0xEEEADB; __framerate.selectable = false; __framerate.text = "Gathering FPS data..."; __framerate.setTextFormat(format); __framerate.defaultTextFormat = format; addChild(__framerate); addEventListener(Event.ENTER_FRAME, onEnterFrame); // Setup logger __logger = new TextField(); __logger.y = __framerate.height; __logger.width = stage.stageWidth; __logger.height = stage.stageHeight - __logger.y; __logger.setTextFormat(format); __logger.defaultTextFormat = format; addChild(__logger); init(); } /** * Start timing */ private function beginTiming(): void { __beginTime = getTimer(); } /** * End timing * @param taskName Name of the task that was timed */ private function endTiming(taskName:String): void { var elapsed:int = getTimer() - __beginTime; log(taskName + ": " + elapsed); } /** * Log a message * @param msg Message to log */ private function log(msg:*): void { __logger.appendText(msg + "\n"); __logger.scrollV = __logger.maxScrollV; } /** * Callback for when a frame is entered. You shouldn't have to change * this function for most tests. * @param ev ENTER_FRAME event */ private function onEnterFrame(ev:Event): void { __frameCount++; var now:int = getTimer(); var dTime:int = now - __lastFrameTime; var elapsed:int = now - __lastFPSUpdateTime; if (elapsed > 1000) { var framerateValue:Number = 1000 / (elapsed / __frameCount); if (__framerate.visible) { __framerate.text = "FPS: " + framerateValue.toFixed(4); } __lastFPSUpdateTime = now; __frameCount = 0; } __lastFrameTime = now; update(dTime); } } }
You’ll see that all of the code for your test goes at the top of the file and all of the test harness code goes at the bottom with a big comment dividing the two. The goal is that you should rarely need to edit anything below that big comment block and spend your time actually creating the test at the top of the file. In my example, I setup a speed test and a real time test in the init() and then did the real time work in the update(). My variables are simply declared as fields of the class. So let’s see if the goals are met:
- Get to writing code as fast as possible
Simply fill out init() and update() with your test code. - Minimal overhead
One file solution that doesn’t rely on any libraries. - Framerate counter
Persistent, averaging framerate display that is always readable. - Logging support without external tools
Messages can be printed directly to a scrolling, selectable text field that is always readable.
I hope this test harness will be useful to you. Even if you don’t use it, consider using a test harness of some sort as it’ll really save you time getting up and running with a test. If your barrier to testing is low, you’ll find yourself doing more and more tests and learning more and more about AS3 and Flash.