From 049daf4b0d4777b4e173a9831d1a11826f9e9e4b Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Tue, 23 Apr 2024 06:28:28 +0200 Subject: [PATCH] Added more tests + benchmark works --- tests/src/HScriptRunner.hx | 36 +++++++++++ tests/src/Main.hx | 4 +- tests/src/Util.hx | 27 ++++++--- tests/src/benchmark/Benchmark.hx | 75 ++++++++++++++++------- tests/src/benchmark/TestBenchmark.hx | 24 ++++++++ tests/src/tests/BinOpCase.hx | 89 ++++++++++++++++++++++++++++ tests/src/tests/SwitchCase.hx | 33 +++++++++++ tests/src/tests/TestCase.hx | 34 +---------- 8 files changed, 262 insertions(+), 60 deletions(-) create mode 100644 tests/src/HScriptRunner.hx create mode 100644 tests/src/benchmark/TestBenchmark.hx create mode 100644 tests/src/tests/SwitchCase.hx diff --git a/tests/src/HScriptRunner.hx b/tests/src/HScriptRunner.hx new file mode 100644 index 0000000..1056615 --- /dev/null +++ b/tests/src/HScriptRunner.hx @@ -0,0 +1,36 @@ +package; + +@:access(hscript.Interp) +@:access(hscript.Parser) +class HScriptRunner { + public var interp:Interp; + public var headerCode:String = ""; + public var tailCode:String = ""; + + public var clearPrevious:Bool = true; + + public function new() {} + + public function getNewInterp() { + var interp = Util.getInterp(); + interp.errorHandler = Util.errorHandler; + return interp; + } + + public function setup() { + interp = getNewInterp(); + } + public function run() {} + public function teardown() {} + + public function execute(script:String):Dynamic { + var interp = clearPrevious ? getNewInterp() : this.interp; + var expr = Util.parse(headerCode + script + tailCode); + if(expr == null) + return "ERROR"; + if (clearPrevious) + return interp.execute(expr); + else + return interp.exprReturn(expr); + } +} \ No newline at end of file diff --git a/tests/src/Main.hx b/tests/src/Main.hx index 5448bbe..c4276bf 100644 --- a/tests/src/Main.hx +++ b/tests/src/Main.hx @@ -11,7 +11,8 @@ class Main { #if BENCHMARK Sys.println("Running benchmark"); - new Benchmark("Test", 1000); + //new Benchmark("Test", 1000); + new TestBenchmark(); #else Sys.println("Beginning tests"); if(!Main.SHOW_KNOWN_BUGS) { @@ -34,6 +35,7 @@ class Main { runTest("String", new StringCase()); runTest("StringBuf", new StringBufCase()); runTest("StringTools", new StringToolsCase()); + runTest("SwitchStatement", new SwitchCase()); // TODO: UnicodeCase.hx? // TODO: UnicodeStringCase.hx? Util.printTestResults(); diff --git a/tests/src/Util.hx b/tests/src/Util.hx index 05e3ca0..69847a9 100644 --- a/tests/src/Util.hx +++ b/tests/src/Util.hx @@ -75,14 +75,14 @@ class Util { } public static function printTestResults() { - Sys.println("-----------------------------RESULTS-----------------------------"); + Sys.println(Util.getTitle('RESULTS')); Sys.println("Total Units: " + totalTestUnits); Sys.println("Total tests passed: " + passedTestUnits); Sys.println("Total tests failed: " + failedTestUnits); if(skippedKnownBugs > 0) Sys.println("Skipped known bugs: " + skippedKnownBugs); //Sys.println("Finished unit tests " + currentTestPath.pop()); - Sys.println("-----------------------------RESULTS-----------------------------"); + Sys.println(Util.getTitle('RESULTS')); } public static function parse(str:String) { @@ -162,9 +162,8 @@ class Util { } public static function deepCompareArrays(a1: Array, a2: Array): Bool { - if (a1.length != a2.length) { + if (a1.length != a2.length) return false; - } for (i in 0...a1.length) { var item1 = a1[i]; @@ -173,11 +172,25 @@ class Util { if (Std.isOfType(item1, Array) && Std.isOfType(item2, Array)) { if (!deepCompareArrays(item1, item2)) return false; - } else if (item1 != item2) { + } else if (item1 != item2) return false; - } } - return true; } + + public static function roundDecimal(Value:Float, Precision:Int):Float { + var mult:Float = 1; + for (i in 0...Precision) + mult *= 10; + return Math.fround(Value * mult) / mult; + } + + public inline static function roundWith(Value:Float, Mult:Int):Float { + return Math.fround(Value * Mult) / Mult; + } + + public static function getTitle(title:String, ?dashsLen:Int = 46) { + var l = StringTools.lpad("", "-", Std.int((dashsLen - title.length - 2)/2)); + return l + ' $title ' + l; + } } \ No newline at end of file diff --git a/tests/src/benchmark/Benchmark.hx b/tests/src/benchmark/Benchmark.hx index 1bb7959..77ac4e3 100644 --- a/tests/src/benchmark/Benchmark.hx +++ b/tests/src/benchmark/Benchmark.hx @@ -1,36 +1,71 @@ package benchmark; -class Benchmark { - public var times:Array = []; +import hscript.Expr; + +class Benchmark extends HScriptRunner { + public var haxeTimes:Array = []; + public var haxeTotalTime:Float = 0; + + public var hscriptTimes:Array = []; + public var hscriptTotalTime:Float = 0; public function new(name:String, iterations:Int) { - times = cast new haxe.ds.Vector(iterations); - + super(); + + haxeTimes = cast new haxe.ds.Vector(iterations); + hscriptTimes = cast new haxe.ds.Vector(iterations); + for (i in 0...iterations) { - var startTime:Float = Util.getTime(); - benchmark(); - var endTime:Float = Util.getTime(); - times[i] = endTime-startTime; + reset(); + var haxeStartTime:Float = Util.getTime(); + haxeBenchmark(); + var haxeEndTime:Float = Util.getTime(); + haxeTimes[i] = haxeEndTime-haxeStartTime; + + reset(); + var hscriptStartTime:Float = Util.getTime(); + hscriptBenchmark(); + var hscriptEndTime:Float = Util.getTime(); + hscriptTimes[i] = hscriptEndTime-hscriptStartTime; } - times.sort(Reflect.compare); - var total:Float = 0; - for (time in times) - total += time; + haxeTimes.sort(Reflect.compare); + hscriptTimes.sort(Reflect.compare); - Sys.println('--------------------$name BENCHMARK--------------------'); + for (time in haxeTimes) haxeTotalTime += time; + for (time in hscriptTimes) hscriptTotalTime += time; - if (total == 0 || times.length <= 0) { + Sys.println(Util.getTitle('$name BENCHMARK')); + + if ((haxeTotalTime == 0 || hscriptTotalTime == 0) || (haxeTimes.length <= 0 || hscriptTimes.length <= 0)) { Sys.println('BENCHMARK UNNOTICABLE, TOOK 0 SECONDS'); } else { - Sys.println('Running $iterations times...'); - Sys.println('Lowest Time: ${Util.convertToReadableTime(times[0])}'); - Sys.println('Average Time: ${Util.convertToReadableTime(total/iterations)}'); - Sys.println('Highest Time: ${Util.convertToReadableTime(times[times.length-1])}'); + var haxeWon = hscriptTotalTime > haxeTotalTime; + Sys.println('${haxeWon ? "Haxe" : "Hscript"} was faster overall (Faster by: ${Util.roundWith((haxeWon ? hscriptTotalTime/haxeTotalTime : haxeTotalTime/hscriptTotalTime), 100)}x)'); + Sys.println('Haxe average time: ${Util.convertToReadableTime(haxeTotalTime/iterations)} (Highest: ${haxeTimes[haxeTimes.length-1]})'); + Sys.println('Hscript average time: ${Util.convertToReadableTime(hscriptTotalTime/iterations)} (Highest: ${hscriptTimes[haxeTimes.length-1]})'); } - Sys.println('--------------------$name BENCHMARK--------------------'); + Sys.println(Util.getTitle('$name BENCHMARK')); } - public function benchmark() {} + public function reset() {} + + public var expr:Expr; + + public inline function cacheExpr(script:String) { + interp = getNewInterp(); + interp.variables.set("self", this); + interp.scriptObject = this; + expr = Util.parse(headerCode + script + tailCode); + } + + public override function execute(script:String):Dynamic { + if (expr == null) + cacheExpr(script); + return interp.execute(expr); + } + + public function haxeBenchmark() {} + public function hscriptBenchmark() {} } \ No newline at end of file diff --git a/tests/src/benchmark/TestBenchmark.hx b/tests/src/benchmark/TestBenchmark.hx new file mode 100644 index 0000000..ce45984 --- /dev/null +++ b/tests/src/benchmark/TestBenchmark.hx @@ -0,0 +1,24 @@ +package benchmark; + +@:dce(off) +class TestBenchmark extends Benchmark { + public function new() { + super("Test", 5000); + } + + public override function reset() { + super.reset(); + a = []; + if(interp != null) + interp.variables.remove("a"); + } + + public var a:Array = []; + public override function haxeBenchmark() { + for (i in 0...1000) a.push(i); + } + + public override function hscriptBenchmark() { + execute("var a:Array = []; for (i in 0...1000) a.push(i);"); + } +} \ No newline at end of file diff --git a/tests/src/tests/BinOpCase.hx b/tests/src/tests/BinOpCase.hx index 8510200..55d0d9c 100644 --- a/tests/src/tests/BinOpCase.hx +++ b/tests/src/tests/BinOpCase.hx @@ -64,6 +64,95 @@ var c = 3; assertEq("a << b >> c", a << b >> c); assertEq("a >>> b << c", a >>> b << c); + + headerCode = " + var a = null; + var b = 'hello'; + var c = 'world'; + var d = '!'; + "; + + var a = null; + var b = 'hello'; + var c = 'world'; + var d = '!'; + + assertEq("a + b", a + b); + + #if (haxe >= "4.3.0") + assertEq("a ?? b", a ?? b); + assertEq("a ?? b ?? c", a ?? b ?? c); + assertEq("a ?? b ?? c ?? d", a ?? b ?? c ?? d); + #else + Sys.println("Tests are inaccurate with Haxe 4.2.x"); + assertEq("a ?? b", nc(a, b)); + assertEq("a ?? b ?? c", nc(a, nc(b, c))); + assertEq("a ?? b ?? c ?? d", nc(a, nc(b, nc(c, d)))); + #end + + var tests:Array> = [ + [null, "null"], + [{}, "{}"], + [{a: 1}, "{a: 1}"], + [{a: {b: 1}}, "{a: {b: 1}}"], + [{a: {b: {c: 1}}}, "{a: {b: {c: 1}}}"], + [{a: {b: {c: {d: 1}}}}, "{a: {b: {c: {d: 1}}}}"] + ]; + + for(code in tests) { + headerCode = 'var a = ${code[1]};'; + var a = code[0]; + + #if (haxe >= "4.3.0") + assertEq("a?.b", a?.b); + assertEq("a?.b?.c", a?.b?.c); + assertEq("a?.b?.c?.d", a?.b?.c?.d); + #else + assertEq("a?.b", nf(a, b)); + assertEq("a?.b?.c", nf(nf(a, b), c)); + assertEq("a?.b?.c?.d", nf(nf(nf(a, b), c), d)); + #end + } + + headerCode = " + var a = null; + var b = 'hello'; + "; + + var a = null; + var b = 'hello'; + + #if (haxe >= "4.3.0") + a ??= b; + assertEq("a ?"+"?= b; a", a); + #else + if(a == null) a = b; + assertEq("a ?"+"?= b; a", a); + #end + + headerCode = " + var a = 'test'; + var b = 'hello'; + "; + + var a = 'test'; + var b = 'hello'; + + #if (haxe >= "4.3.0") + a ??= b; + assertEq("a ?"+"?= b; a", a); + #else + if(a == null) a = b; + assertEq("a ?"+"?= b; a", a); + #end + } + + inline function nc(a:Dynamic, b:Dynamic):Dynamic { + return (a == null) ? b : a; + } + + inline function nf(a:Dynamic, b:String):Dynamic { + return (a == null) ? null : Reflect.field(a, b); } override function teardown() { diff --git a/tests/src/tests/SwitchCase.hx b/tests/src/tests/SwitchCase.hx new file mode 100644 index 0000000..6851c8f --- /dev/null +++ b/tests/src/tests/SwitchCase.hx @@ -0,0 +1,33 @@ +package tests; + +class SwitchCase extends TestCase { + override function setup() { + super.setup(); + } + + override function run() { + assertEq("switch(1) { case 1: 1; case 2: 2; default: -1; }", switch(1) { case 1: 1; case 2: 2; default: -1; }); + assertEq("switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }); + assertEq("switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }); + assertEq("switch(6) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(6) { case 1: 1; case 2: 2; case 3: 3; default: -1; }); + + // TODO: add https://haxe.org/manual/lf-pattern-matching-enums.html + // TODO: add https://haxe.org/manual/lf-pattern-matching-variable-capture.html (including case _) + // TODO: add https://haxe.org/manual/lf-pattern-matching-structure.html + // TODO: add https://haxe.org/manual/lf-pattern-matching-array.html + // TODO: add https://haxe.org/manual/lf-pattern-matching-guards.html + // TODO: add https://haxe.org/manual/lf-pattern-matching-tuples.html + // TODO: add https://haxe.org/manual/lf-pattern-matching-extractors.html + // TODO: detect https://haxe.org/manual/lf-pattern-matching-exhaustiveness.html + // TODO: maybe? https://haxe.org/manual/lf-pattern-matching-unused.html + // TODO: add https://haxe.org/manual/lf-pattern-matching-single.html + + assertEq("switch(5) { case 1|4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1|4: 'error'; case 5: 0; default: -1; }); + assertEq("switch(5) { case 1|4: 'error'; default: -1; }", switch(5) { case 1|4: 'error'; default: -1; }); + assertEq("switch(5) { case 1,4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1,4: 'error'; case 5: 0; default: -1; }); + } + + override function teardown() { + super.teardown(); + } +} \ No newline at end of file diff --git a/tests/src/tests/TestCase.hx b/tests/src/tests/TestCase.hx index bc0f41f..c54b525 100644 --- a/tests/src/tests/TestCase.hx +++ b/tests/src/tests/TestCase.hx @@ -2,46 +2,16 @@ package tests; @:access(hscript.Interp) @:access(hscript.Parser) -class TestCase { - public var interp:Interp; - public var headerCode:String = ""; - public var tailCode:String = ""; - - public var clearPrevious:Bool = true; - - public function new() {} - - public function getNewInterp() { - var interp = Util.getInterp(); - interp.errorHandler = Util.errorHandler; - return interp; - } - - public function setup() { - interp = getNewInterp(); - } - public function run() {} - public function teardown() {} - +class TestCase extends HScriptRunner { public function assertEq(script:String, expected:Dynamic, ?message:String) { if(message == null) message = script; Util.assertEq(execute(headerCode + script + tailCode), expected, message); } + public function assertNeq(script:String, expected:Dynamic, ?message:String) { if(message == null) message = script; Util.assertNeq(execute(headerCode + script + tailCode), expected, message); } - - public function execute(script:String):Dynamic { - var interp = clearPrevious ? getNewInterp() : this.interp; - var expr = Util.parse(headerCode + script + tailCode); - if(expr == null) - return "ERROR"; - if (clearPrevious) - return interp.execute(expr); - else - return interp.exprReturn(expr); - } } \ No newline at end of file