Added:
trunk/run/MiscPhpTest.hxml
trunk/src/haxelib.xml
trunk/src/tests/utest/TestAssert.hx
Modified:
trunk/src/ (props changed)
trunk/src/tests/ (props changed)
trunk/src/utest/Assert.hx
trunk/src/utest/ui/text/TraceReport.hx
Log:
added Assert.same() to recursively compare any kind of value
added tests for the Assert class
Added: trunk/run/MiscPhpTest.hxml
==============================================================================
--- (empty file)
+++ trunk/run/MiscPhpTest.hxml Tue Oct 7 04:36:16 2008
@@ -0,0 +1,6 @@
+# PHP
+-php ../bin/www/tests
+-main tests.native.php.MiscPhpTest
+-debug
+-cp ../src
+--php-front main.php
Added: trunk/src/haxelib.xml
==============================================================================
--- (empty file)
+++ trunk/src/haxelib.xml Tue Oct 7 04:36:16 2008
@@ -0,0 +1,5 @@
+<project name="utest" url="http://code.google.com/p/utest" license="LGPL">
+ <user name="fponticelli"/>
+ <description>Unit Testing System for haXe</description>
+ <version name="0.1.0">initial release</version>
+</project>
Added: trunk/src/tests/utest/TestAssert.hx
==============================================================================
--- (empty file)
+++ trunk/src/tests/utest/TestAssert.hx Tue Oct 7 04:36:16 2008
@@ -0,0 +1,329 @@
+package tests.utest;
+
+import utest.Assert;
+import utest.Assertation;
+
+class TestAssert {
+ public function new();
+
+ var resultsbypass : List<Assertation>;
+ var results : List<Assertation>;
+ public function bypass() {
+ resultsbypass = Assert.results;
+ Assert.results = new List();
+ }
+
+ public function restore() {
+ results = Assert.results;
+ Assert.results = resultsbypass;
+ }
+
+ public function testBooleans() {
+ bypass();
+ Assert.isTrue(true);
+ Assert.isTrue(false);
+ Assert.isFalse(true);
+ Assert.isFalse(false);
+ restore();
+ expect(2, 2);
+ }
+
+ public function testNullity() {
+ bypass();
+ Assert.isNull(null);
+ Assert.isNull(0);
+ Assert.isNull(0.0);
+ Assert.isNull(0.1);
+ Assert.isNull(1);
+ Assert.isNull("");
+ Assert.isNull("a");
+ Assert.isNull(Math.NaN);
+ Assert.isNull(Math.POSITIVE_INFINITY);
+ Assert.isNull(true);
+ Assert.isNull(false);
+ restore();
+ expect(1, 10);
+ }
+
+ public function testNoNullity() {
+ bypass();
+ Assert.notNull(null);
+ Assert.notNull(0);
+ Assert.notNull(0.0);
+ Assert.notNull(0.1);
+ Assert.notNull(1);
+ Assert.notNull("");
+ Assert.notNull("a");
+ Assert.notNull(Math.NaN);
+ Assert.notNull(Math.POSITIVE_INFINITY);
+ Assert.notNull(true);
+ Assert.notNull(false);
+ restore();
+ expect(10, 1);
+ }
+
+ public function testRaises() {
+ bypass();
+ var errors : Array<Dynamic> = ["e", 1, 0.1, new TestAssert(),
{}, [1]];
+ var types : Array<Dynamic> = [String, Int, Float, TestAssert,
Dynamic, Array];
+ var i = 0;
+ var expectedsuccess = 12;
+ for(error in errors)
+ for(type in types) {
+ i++;
+ Assert.raises(function() throw error, type);
+ }
+ restore();
+ expect(expectedsuccess, i-expectedsuccess);
+ }
+
+ public function testIs() {
+ bypass();
+ var values : Array<Dynamic> = ["e", 1, 0.1, new TestAssert(),
{}, [1]];
+ var types : Array<Dynamic> = [String, Int, Float, TestAssert,
Dynamic, Array];
+ var i = 0;
+ var expectedsuccess = 12;
+ for(value in values)
+ for(type in types) {
+ i++;
+ Assert.is(value, type);
+ }
+ restore();
+ expect(expectedsuccess, i-expectedsuccess);
+ }
+
+ public function testLikePrimitive() {
+ bypass();
+ Assert.same(null, 1);
+ Assert.same(1, 1);
+ Assert.same(1, "1");
+ Assert.same("a", "a");
+ Assert.same(null, "");
+ Assert.same(new Date(2000, 0, 1, 0, 0, 0), null);
+ Assert.same(new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 0, 0,
0));
+
+ restore();
+ expect(3, 4);
+ }
+
+ public function testLikeType() {
+ bypass();
+ Assert.same(null, {});
+ Assert.same(null, null);
+ Assert.same({}, null);
+ Assert.same({}, 1);
+ Assert.same({}, []);
+ Assert.same(null, None);
+ Assert.same(None, null);
+
+ restore();
+ expect(1, 6);
+ }
+
+ public function testLikeArray() {
+ bypass();
+ Assert.same([], []);
+ Assert.same([1], ["1"]);
+ Assert.same([1,2,3], [1,2,3]);
+ Assert.same([1,2,3], [1,2]);
+ Assert.same([1,2], [1,2,3]);
+ Assert.same([1,[1,2]], [1,[1,2]]);
+ Assert.same([1,[1,2]], [1,[]], false);
+ Assert.same([1,[1,2]], [1,[]], true);
+
+ restore();
+ expect(4, 4);
+ }
+
+ public function testLikeObject() {
+ bypass();
+ Assert.same({}, {});
+ Assert.same({a:1}, {a:"1"});
+ Assert.same({a:1,b:"c"}, {a:1,b:"c"});
+ Assert.same({a:1,b:"c"}, {a:1,c:"c"});
+ Assert.same({a:1,b:"c"}, {a:1});
+ Assert.same({a:1,b:{a:1,c:"c"}}, {a:1,b:{a:1,c:"c"}});
+ Assert.same({a:1,b:{a:1,c:"c"}}, {a:1,b:{}}, false);
+ Assert.same({a:1,b:{a:1,c:"c"}}, {a:1,b:{}}, true);
+
+ restore();
+ expect(4, 4);
+ }
+
+ public var value : String;
+ public var sub : TestAssert;
+ public function testLikeInstance() {
+ var c1 = new TestAssert();
+ c1.value = "a";
+ var c2 = new TestAssert();
+ c2.value = "a";
+ var c3 = new TestAssert();
+
+ var r1 = new TestAssert();
+ r1.sub = c1;
+ var r2 = new TestAssert();
+ r2.sub = c2;
+ var r3 = new TestAssert();
+ r3.sub = c3;
+
+
+ bypass();
+ Assert.same(c1, c1);
+ Assert.same(c1, c2);
+ Assert.same(c1, c3);
+
+ Assert.same(r1, r2);
+ Assert.same(r1, r3, false);
+ Assert.same(r1, r3, true);
+
+ restore();
+ expect(4, 2);
+ }
+
+ public function testLikeIterable() {
+ var list1 = new List<Dynamic>();
+ list1.add("a");
+ list1.add(1);
+ var s1 = new List();
+ s1.add(2);
+ list1.add(s1);
+ var list2 = new List<Dynamic>();
+ list2.add("a");
+ list2.add(1);
+ list2.add(s1);
+ var list3 = new List<Dynamic>();
+ list3.add("a");
+ list3.add(1);
+ list3.add(new List());
+
+ bypass();
+ Assert.same(list1, list2);
+ Assert.same(list1, list3, false);
+ Assert.same(list1, list3, true);
+
+ Assert.same(new IntIter(0,3), new IntIter(0,3));
+ Assert.same(new IntIter(0,3), new IntIter(0,4));
+
+ restore();
+ expect(3, 2);
+ }
+
+ public function testLikeHash() {
+ var h1 = new Hash();
+ h1.set('a', 'b');
+ h1.set('c', 'd');
+ var h2 = new Hash();
+ h2.set('a', 'b');
+ h2.set('c', 'd');
+ var h3 = new Hash();
+ var h4 = new Hash();
+ h4.set('c', 'd');
+
+ var i1 = new IntHash();
+ i1.set(2, 'b');
+ var i2 = new IntHash();
+ i2.set(2, 'b');
+
+ bypass();
+
+ Assert.same(h1, h2);
+ Assert.same(h1, h3);
+ Assert.same(h1, h4);
+ Assert.same(i1, i2);
+
+ restore();
+ expect(2, 2);
+ }
+
+ public function testLikeEnums() {
+ bypass();
+
+ Assert.same(None, None);
+ Assert.same(Some("a"), Some("a"));
+ Assert.same(Some("a"), Some("b"), true);
+ Assert.same(Some("a"), Some("b"), false);
+ Assert.same(Some("a"), None);
+ Assert.same(Rec(Rec(Some("a"))), Rec(Rec(Some("a"))));
+ Assert.same(Rec(Rec(Some("a"))), Rec(None), true);
+// TODO: something goes wrong here with flash6, haXe/Flash6 bug?
+#if !flash6
+ Assert.same(Rec(Rec(Some("a"))), Rec(None), false);
+#end
+
+ restore();
+#if flash6
+ expect(3, 4);
+#else
+ expect(4, 4);
+#end
+ }
+
+ public function testEquals() {
+ bypass();
+ var values : Array<Dynamic> = ["e", 1, 0.1, {}];
+ var expecteds : Array<Dynamic> = ["e", 1, 0.1, {}];
+ var i = 0;
+ var expectedsuccess = 3;
+ for(expected in expecteds)
+ for(value in values) {
+ i++;
+ Assert.equals(expected, value);
+ }
+ restore();
+ expect(expectedsuccess, i-expectedsuccess);
+ }
+
+ public function testFloatEquals() {
+ bypass();
+ var values : Array<Float> = [1, 0.1,
0.000000000000000000000000000011, 0.11];
+ var expecteds : Array<Float> = [1, 0.1,
0.000000000000000000000000000012, 0.12];
+ var i = 0;
+ var expectedsuccess = 3;
+ for(expected in expecteds)
+ for(value in values) {
+ i++;
+ Assert.floatEquals(expected, value);
+ }
+ restore();
+ expect(expectedsuccess, i-expectedsuccess);
+ }
+
+ public function testFail() {
+ bypass();
+ Assert.fail();
+ restore();
+ expect(0, 1);
+ }
+
+ public function testWarn() {
+ bypass();
+ Assert.warn("");
+ restore();
+ expect(0, 0, 1);
+ }
+
+ public function expect(esuccesses : Int, efailures : Int, eothers = 0) {
+ var successes = 0;
+ var failures = 0;
+ var others = 0;
+ for(result in results) {
+ switch(result) {
+ case Success(_):
+ successes++;
+ case Failure(_,_):
+ failures++;
+ default:
+ others++;
+ }
+ }
+ Assert.equals(eothers, others, "expected "+eothers+" other results but
were "+others);
+ Assert.equals(esuccesses, successes, "expected "+esuccesses+" successes
but were "+successes);
+ Assert.equals(efailures, failures, "expected "+efailures+" failures but
were "+failures);
+ }
+}
+
+private enum Sample {
+ None;
+ Some(s : String);
+ Rec(s : Sample);
+}
\ No newline at end of file
Modified: trunk/src/utest/Assert.hx
==============================================================================
--- trunk/src/utest/Assert.hx (original)
+++ trunk/src/utest/Assert.hx Tue Oct 7 04:36:16 2008
@@ -41,33 +41,210 @@
isTrue(Math.abs(value-expected) < 1e-5, msg, pos);
}
+ static function getTypeName(v : Dynamic) {
+ try {
+ if(v == null) return null;
+ if(Std.is(v, Bool)) return "Bool";
+ if(Std.is(v, Int)) return "Int";
+ if(Std.is(v, Float)) return "Float";
+ if(Std.is(v, String)) return "String";
+ var s = Type.getClassName(Type.getClass(v));
+ if(s != null) return s;
+ if(Reflect.isObject(v)) return "{}";
+ return Type.getEnumName(Type.getEnum(v));
+ } catch(e : Dynamic) {
+ trace("ERROR: "+v + " (" + Type.typeof(v) + ")");
+ return null;
+ }
+ }
+
+ static function isIterable(v : Dynamic, isAnonym : Bool) {
+ var fields = isAnonym ? Reflect.fields(v) :
Type.getInstanceFields(Type.getClass(v));
+ if(!Lambda.has(fields, "iterator")) return false;
+ return Reflect.isFunction(Reflect.field(v, "iterator"));
+ }
+
+ static function isIterator(v : Dynamic, isAnonym : Bool) {
+ var fields = isAnonym ? Reflect.fields(v) :
Type.getInstanceFields(Type.getClass(v));
+ if(!Lambda.has(fields, "next") || !Lambda.has(fields, "hasNext")) return
false;
+ return Reflect.isFunction(Reflect.field(v, "next")) &&
Reflect.isFunction(Reflect.field(v, "hasNext"));
+ }
+
+ static function sameAs(expected : Dynamic, value : Dynamic, status :
LikeStatus) {
+ var texpected = getTypeName(expected);
+ var tvalue = getTypeName(value);
+ var isanonym = texpected == '{}';
+
+ if(texpected != tvalue) {
+ status.error = "expected type "+texpected+" but it is " + tvalue +
(status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+
+ // null
+ if(expected == null) {
+ if(value != null) {
+ status.error = "expected null but it is " + value + (status.path
== '' ? '' : ' at '+status.path);
+ return false;
+ }
+ return true;
+ }
+
+ // bool, int, float, string
+ if(Std.is(expected, Bool) || Std.is(expected, Int) || Std.is(expected,
Float) || Std.is(expected, String)) {
+ if(expected != value) {
+ status.error = "expected "+expected+" but it is " + value +
(status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+ return true;
+ }
+
+ // date
+ if(Std.is(expected, Date)) {
+ if(expected.getTime() != value.getTime()) {
+ status.error = "expected "+expected+" but it is " + value +
(status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+ return true;
+ }
+
+ // enums
+ if(Type.getEnum(expected) != null) {
+ if(status.recursive || status.path == '') {
+ var ename = Type.enumIndex(expected);
+ var vname = Type.enumIndex(value);
+ if(ename != vname) {
+ status.error = "expected "+ename+" constructor but is "+vname +
(status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+ var eparams = Type.enumParameters(expected);
+ var vparams = Type.enumParameters(value);
+ var path = status.path;
+ for(i in 0...eparams.length) {
+ status.path = path == '' ? 'enum['+i+']' : path + '['+i+']';
+ if(!sameAs(eparams[i], vparams[i], status))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // arrays
+ if(Std.is(expected, Array)) {
+ if(status.recursive || status.path == '') {
+ if(expected.length != value.length) {
+ status.error = "expected "+expected.length+" elements but they
were "+value.length + (status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+ var path = status.path;
+ for(i in 0...expected.length) {
+ status.path = path == '' ? 'array['+i+']' : path + '['+i+']';
+ if(!sameAs(expected[i], value[i], status))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // hash, inthash
+ if(Std.is(expected, Hash) || Std.is(expected, IntHash)) {
+ if(status.recursive || status.path == '') {
+ var keys = Lambda.array({ iterator : function() return
expected.keys() });
+ var vkeys = Lambda.array({ iterator : function() return value.keys()
});
+ if(keys.length != vkeys.length) {
+ status.error = "expected "+keys.length+" keys but they
were "+vkeys.length + (status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+ var path = status.path;
+ for(key in keys) {
+ status.path = path == '' ? 'hash['+key+']' : path + '['+key+']';
+ if(!sameAs(expected.get(key), value.get(key), status))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // iterator
+ if(isIterator(expected, isanonym)) {
+ if(isanonym && !(isIterator(value, true))) {
+ status.error = "expected Iterable but it is not " + (status.path
== '' ? '' : ' at '+status.path);
+ return false;
+ }
+ if(status.recursive || status.path == '') {
+ var evalues = Lambda.array({ iterator : function() return expected });
+ var vvalues = Lambda.array({ iterator : function() return value });
+ if(evalues.length != vvalues.length) {
+ status.error = "expected "+evalues.length+" values in Iterator but
they were "+vvalues.length + (status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+ var path = status.path;
+ for(i in 0...evalues.length) {
+ status.path = path == '' ? 'iterator['+i+']' : path + '['+i+']';
+ if(!sameAs(evalues[i], vvalues[i], status))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // iterable
+ if(isIterable(expected, isanonym)) {
+ if(isanonym && !(isIterable(value, true))) {
+ status.error = "expected Iterator but it is not " + (status.path
== '' ? '' : ' at '+status.path);
+ return false;
+ }
+ if(status.recursive || status.path == '') {
+ var evalues = Lambda.array(expected);
+ var vvalues = Lambda.array(value);
+ if(evalues.length != vvalues.length) {
+ status.error = "expected "+evalues.length+" values in Iterable but
they were "+vvalues.length + (status.path == '' ? '' : ' at '+status.path);
+ return false;
+ }
+ var path = status.path;
+ for(i in 0...evalues.length) {
+ status.path = path == '' ? 'iterable['+i+']' : path + '['+i+']';
+ if(!sameAs(evalues[i], vvalues[i], status))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // objects
+ if(Reflect.isObject(expected)) {
+ if(status.recursive || status.path == '') {
+ var fields = texpected == "{}" ? Reflect.fields(expected) :
Type.getInstanceFields(Type.getClass(expected));
+ var path = status.path;
+ for(field in fields) {
+ status.path = path == '' ? field : path+'.'+field;
+ if(texpected == "{}" && !Reflect.hasField(value, field)) {
+ status.error = "expected field "+status.path+" does not exist in " +
value;
+ return false;
+ }
+ var e = Reflect.field(expected, field);
+ if(Reflect.isFunction(e)) continue;
+ var v = Reflect.field(value, field);
+ if(!sameAs(e, v, status))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return throw "Unable to compare values: " +expected+" and " + value;
+ }
+
/**
- * Check that value is an object and contains at least the same fields and
values found in expcted.
+ * Check that value is an object with the same fields and values found in
expected.
* The default behavior is to check nested objects in fields recursively.
*/
- public static function like(expected : Dynamic, value : Dynamic,
recursive = true, ?path = '', ?msg : String, ?pos : PosInfos) {
- if(expected == null && value == null) {
- isTrue(true, msg, pos);
- return;
- }
- var fields = Reflect.fields(expected);
- for(field in fields) {
- if(!Reflect.hasField(value, field)) {
- Assert.fail("value doesn't have the expected field '"+path+field+"'");
- return;
- }
- var e = Reflect.field(expected, field);
- var v = Reflect.field(value, field);
- if(Reflect.isObject(e) && recursive) {
- like(e, v, true, field+'.', msg, pos);
- } else {
- if(e != v) {
- Assert.fail("the expected value for the field '"+path+field+"'
was '"+e+"' but it is '"+v+"'");
- return;
- }
- }
+ public static function same(expected : Dynamic, value : Dynamic,
recursive : Null<Bool> = true, ?msg : String, ?pos : PosInfos) {
+ var status = { recursive : recursive, path : '', error : null };
+ if(sameAs(expected, value, status)) {
+ Assert.isTrue(true, msg, pos);
+ } else {
+ Assert.fail(msg == null ? status.error : msg, pos);
}
- Assert.isTrue(true, msg, pos);
}
public static function raises(method:Void -> Void,
type:Class<Dynamic>, ?msg : String , ?pos : PosInfos) {
@@ -93,4 +270,10 @@
public static dynamic function createEvent<EventArg>(f :
EventArg->Void, ?timeout : Int) {
return function(e){};
}
-}
\ No newline at end of file
+}
+
+private typedef LikeStatus = {
+ recursive : Bool,
+ path : String,
+ error : String
+};
\ No newline at end of file
Modified: trunk/src/utest/ui/text/TraceReport.hx
==============================================================================
--- trunk/src/utest/ui/text/TraceReport.hx (original)
+++ trunk/src/utest/ui/text/TraceReport.hx Tue Oct 7 04:36:16 2008
@@ -14,6 +14,7 @@
var indent : String;
public function new(runner : Runner) {
aggregator = new ResultAggregator(runner, true);
+ runner.onStart.add(start);
aggregator.onComplete.add(complete);
#if php
if(php.Lib.isCli()) {
@@ -37,6 +38,11 @@
#end
}
+ var startTime : Float;
+ function start(e) {
+ startTime = haxe.Timer.stamp();
+ }
+
function indents(c : Int) {
var s = '';
for(_ in 0...c)
@@ -45,14 +51,23 @@
}
function complete(result : PackageResult) {
+ var end = haxe.Timer.stamp();
+#if php
+ var scripttime = Std.int(php.Sys.cpuTime()*1000)/1000;
+#end
+ var time = Std.int((end-startTime)*1000)/1000;
var buf = new StringBuf();
buf.add("results: " + (result.stats.isOk ? "ALL TESTS OK" : "SOME TESTS
FAILURES")+newline+" "+newline);
- buf.add("assertations: " + result.stats.assertations+newline);
- buf.add("successes: " + result.stats.successes+newline);
- buf.add("errors: " + result.stats.errors+newline);
- buf.add("failures: " + result.stats.failures+newline);
- buf.add("warnings: " + result.stats.warnings+newline);
+ buf.add("assertations: " + result.stats.assertations+newline);
+ buf.add("successes: " + result.stats.successes+newline);
+ buf.add("errors: " + result.stats.errors+newline);
+ buf.add("failures: " + result.stats.failures+newline);
+ buf.add("warnings: " + result.stats.warnings+newline);
+ buf.add("execution time: " + time+newline);
+#if php
+ buf.add("script time: " + scripttime+newline);
+#end
buf.add(newline);