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.