Scripting language for automated testing

I’m working on this project because I ran into a requirement of systematically performing various usability automatic tests on Flash applications. The tools I found to do the job were either not invented here, or plain bad / would require things I couldn’t provide (like large amounts of money or special operating system to run them). And so, I’ve decided to make my own tools :slight_smile: They aren’t even half as good as those I described before as unworthy, but I like them because I made them myself :slight_smile: Of course, I only started the project, so, hopefully, if I won’t give it up, it’ll grow, and maybe, some day, t will become really useful.

Rationale:
Any non-trivial application which is intended to receive users’ input has too many logical paths a single person (the programmer) may check in reasonable time, therefore, if you need your software to be somewhat stable you must have some automation tools that would try to identify potential bugs by trying to perform all those things users may do with your application. Of course these tests are not a panacea, but, if you have a huge old buggy application to maintain, they may give a chance for your creativity, and after all are fun to make!

So, the goals and the purpose:
[list]
[]A simple language one can use to script any Flash application with minimum (better none) required modifications to the Flash application itself.
[
]A set of tools that would allow to perform timed tasks, imitate user interaction and processes that could’ve been initiated by the program itself
[*]A way to communicate to external logging tools, format and serialize the gathered info[/list]

About the language (for now it’s called ZinScript, please don’t ask me why). It uses similar to Lisp syntax, but I don’t know (yet) Lisp good enough to implement it just like it (however, at some point I’d really like to!). It is a functional language with a whole bunch of problems both inherent to functional languages and acquired from mixing them with OO languages. It is at a very early development stage, however it can already do some things.
The way you may use it: Just evaluate the string. You can get stings anywhere, load them, assemble at run time - you choose. In long-time perspective there going to be added a simple socket server written in Common Lisp and reading AMF format (that’s actually work in the progress, and some progress has been made already).
Below is an example and the link to my Googlecode repository. Sorry, for now there’s no documentation available, however, I will also use this at where I work, and that place requires that I put documentation, so, in the future there will be some.

package examples
{
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.Dictionary;
	
	import mx.controls.Text;
	
	import org.wvxvws.automation.ParensParser;
	import org.wvxvws.automation.language.ParensPackage;
	
	public class TestZinScript extends Sprite
	{
		private const _parser:ParensParser = new ParensParser();
		
		private const _buttons:Dictionary = new Dictionary();
		
		public function TestZinScript()
		{
			super();
			this.makeButtons();
			this.test();
		}
		
		private function test():void
		{
			// Makes all public methods accessible by names so
			// we don't need to use utils:slot-value all the time.
			this._parser.pushContext(this, "test-runner");
			this._parser.read(
<![CDATA[
				(test-runner:testMethod)
				(test-runner:testMethodWithParameters 100 "Hello from outer space")
				
				(utils:print "Running ZinScript, highly experimental version...")
				
				(lang:defvar "test-var" 42)
				
				; (utils:dotimes (math:- 2 1) 
				; 	(lang:getvar "time:interval") ; This is not ideal, will need to pass a list instead
				; 	(lang:getvar "test-runner:testMethodWithParameters") 1000 
				; 		(lang:setvar "test-var" (math:++ (lang:getvar "test-var"))) "Hello...")
				
				; (utils:dotimes (math:+ 0 2 -10 9) 
				; 	(lang:getvar "time:timeout") ; Just same as above, need body form for loops and functions
				; 	(lang:getvar "test-runner:testMethod") 1000 5)
				
				(bool:if (math:> (math:random 10) 5)
					(utils:print "Executing true condition")
					(utils:print "Executing false condition"))
				
				; This is line comment
				
				(utils:print (string:+ "foo" "|" "bar"))
				
				(lang:defun "test-function" (lang:arguments "param1" "param2")
					(lang:body-form '(utils:print param1)
						'(utils:print param2)))
				
				(utils:print (lang:getvar "test-function"))
				
				(test-function "hello" "world")
				
				; (lang:defpackage "tests")
				; (lang:inpackage "tests")
				; (lang:defvar "test-sprite" (lang:new (utils:resolve-class "flash.display.Sprite")))
				; (lang:defvar "graphics" (utils:slot-value (lang:getvar "test-sprite") "graphics"))
				; (utils:funcall (utils:slot-value (lang:getvar "graphics") "beginFill") 255)
				; (utils:funcall (utils:slot-value (lang:getvar "graphics") "drawRect") 0 0 100 200)
				; (utils:funcall (utils:slot-value (lang:getvar "graphics") "endFill"))
				; (test-runner:addChild (lang:getvar "test-sprite"))
				; (utils:print "debugging..." (lang:package) (utils:slot-value (lang:package) "name"))
				; (utils:print "moar..." (lang:getvar "test-sprite") (lang:getvar "tests:test-sprite"))
				; (lang:extern "test-sprite")
				
				(lang:defun "test-handler" (lang:arguments "event")
					(lang:body-form '(utils:print "I am an enterFrame handler" event)
						'(utils:set-slot (lang:getvar "tests:test-sprite") "x" 
							(math:++ (utils:slot-value (lang:getvar "tests:test-sprite") "x")))))
				; (test-runner:addEventListener "enterFrame" (lang:getvar "test-handler"))
				; (lang:inpackage "test-runner")
				
				; Imitating erratic clicking on the screen once each second...
				(lang:defun "test-click" (lang:arguments)
					(lang:body-form ; Need special define for local variables, like "let"
						'(lang:defvar "x" (math:random 220))
						'(lang:defvar "y" (math:random 550))
						'(utils:print "x" (lang:getvar "x") "y" (lang:getvar "y"))
						'(display:click (lang:getvar "x") (lang:getvar "y")))))
				
				(time:interval (lang:getvar "test-click") 1000)
				(display:init (utils:slot-value (lang:getvar "test-runner:this") "stage"))
]]>.toString());
		}
		
		public function testMethod():void
		{
			trace("I am testMethod");
		}
		
		public function testMethodWithParameters(foo:int, bar:String):void
		{
			trace("I am testMethodWithParameters", foo, bar);
		}
		
		private function makeButtons():void
		{
			var button:Sprite;
			
			for (var i:int; i < 10; i++)
			{
				button = this.makeButton();
				button.x = int(i / 5) * 110;
				button.y = (i % 5) * 110;
				this._buttons[button] = i;
			}
		}
		
		private function makeButton():Sprite
		{
			var button:Sprite = new Sprite();
			var graph:Graphics = button.graphics;
			graph.beginFill(Math.random() * 0xFFFFFF);
			graph.drawRect(0, 0, 100, 100);
			button.addEventListener(MouseEvent.CLICK, this.clickHandler);
			return super.addChild(button) as Sprite;
		}
		
		private function clickHandler(event:MouseEvent):void
		{
			trace("clicked on", this._buttons[event.currentTarget], 
				event.localX, event.localY);
		}
	}
}

Semicolon is used for line comments, you may uncomment some pieces of code to see what it does. The uncommented code will, as the comment says, click erratically on the buttons and print some info on where it clicked. If you tried to test it, and you want to know what the output should be like, so, you have to see 10 differently colored squares and this or similar output:

I am testMethod
I am testMethodWithParameters 100 Hello from outer space
Running ZinScript, highly experimental version...
Executing false condition
foo|bar
function Function() {}
hello
world
x 104 y 143
[SWF] /home/wvxvw/Projects/e4xu/bin-debug/TestZinScript.swf - 39,881 bytes after decompression
x 61 y 515
clicked on 4 61 75
x 206 y 109
x 104 y 445
x 73 y 252
clicked on 2 73 32
x 192 y 309
clicked on 7 82 89

Sources: http://code.google.com/p/e4xu/source/browse/#svn%2Ftrunk%2Fsrc%2Forg%2Fwvxvws%2Fautomation