├── bin └── .gitignore ├── .gitignore ├── run.cpp.bat ├── run.flash.bat ├── run.neko.bat ├── LICENSE ├── run.cs.bat ├── run.java.bat ├── run.nodejs.bat ├── run.js.bat ├── src ├── buddy │ ├── Buddy.hx │ ├── SingleSuite.hx │ ├── reporting │ │ ├── ConsoleColorReporter.hx │ │ ├── TravisHxReporter.hx │ │ ├── Reporter.hx │ │ ├── ConsoleFileReporter.hx │ │ ├── ConsoleReporter.hx │ │ └── TraceReporter.hx │ ├── tests │ │ ├── index.html │ │ ├── buddy.phantomjs.js │ │ ├── SelfTest.hx │ │ └── AllTests.hx │ ├── internal │ │ ├── sys │ │ │ ├── NodeJs.hx │ │ │ ├── Js.hx │ │ │ └── Flash.hx │ │ ├── GenerateMain.hx │ │ └── SuiteBuilder.hx │ ├── CompilationShould.hx │ ├── tools │ │ └── AsyncTools.hx │ ├── BuddySuite.hx │ ├── SuitesRunner.hx │ └── Should.hx └── haxelib.json ├── run.phantomjs.bat ├── buddy.neko.hxml ├── buddy.php.hxml ├── buddy.cs.hxml ├── buddy.java.hxml ├── buddy.cpp.hxml ├── buddy.python.hxml ├── buddy.nodejs.hxml ├── flash-travis-run.sh ├── buddy.flash.hxml ├── buddy.js.hxml ├── flash-travis-setup.sh ├── buddy.python.hxproj ├── buddy.cpp.hxproj ├── buddy.js.hxproj ├── buddy.php.hxproj ├── buddy.cs.hxproj ├── buddy.java.hxproj ├── buddy.nodejs.hxproj ├── buddy.neko.hxproj ├── .travis.yml ├── buddy.flash.hxproj └── README.md /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /.vagrant 3 | /www 4 | -------------------------------------------------------------------------------- /run.cpp.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | bin\AllTests.exe 3 | pause 4 | -------------------------------------------------------------------------------- /run.flash.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | start /WAIT bin\buddy.swf 3 | -------------------------------------------------------------------------------- /run.neko.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | neko bin\buddy.n 3 | pause 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/buddy/master/LICENSE -------------------------------------------------------------------------------- /run.cs.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | bin\buddy\bin\AllTests.exe 3 | pause 4 | -------------------------------------------------------------------------------- /run.java.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -jar bin\AllTests.jar 3 | pause 4 | -------------------------------------------------------------------------------- /run.nodejs.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | node bin\buddy.nodejs.js 3 | pause 4 | -------------------------------------------------------------------------------- /run.js.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | copy src\buddy\tests\index.html bin 3 | start bin\index.html 4 | -------------------------------------------------------------------------------- /src/buddy/Buddy.hx: -------------------------------------------------------------------------------- 1 | package buddy; 2 | 3 | @:autoBuild(buddy.internal.GenerateMain.withSuites()) interface Buddy {} 4 | -------------------------------------------------------------------------------- /src/buddy/SingleSuite.hx: -------------------------------------------------------------------------------- 1 | package buddy; 2 | 3 | @:autoBuild(buddy.internal.GenerateMain.withSuites()) 4 | class SingleSuite extends BuddySuite 5 | { 6 | public function new() super(); 7 | } -------------------------------------------------------------------------------- /run.phantomjs.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | copy src\buddy\tests\index.html bin >nul 3 | copy src\buddy\tests\buddy.phantomjs.js bin >nul 4 | d:\program\misc\phantomjs\phantomjs bin\buddy.phantomjs.js 5 | pause 6 | -------------------------------------------------------------------------------- /src/buddy/reporting/ConsoleColorReporter.hx: -------------------------------------------------------------------------------- 1 | package buddy.reporting; 2 | 3 | class ConsoleColorReporter extends ConsoleReporter 4 | { 5 | public function new(colors = true) super(colors); 6 | } 7 | -------------------------------------------------------------------------------- /buddy.neko.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -D buddy-no-colors 8 | -main buddy.tests.AllTests 9 | -cmd echo "---------- Neko -----------" 10 | -x bin/buddy.n -------------------------------------------------------------------------------- /buddy.php.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -php bin 8 | -main buddy.tests.AllTests 9 | -cmd echo "---------- PHP ----------" 10 | -cmd php bin/index.php 11 | -------------------------------------------------------------------------------- /buddy.cs.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -cs bin 8 | -main buddy.tests.AllTests 9 | -cmd echo "---------- C# ----------" 10 | -cmd mono bin/bin/AllTests.exe 11 | -------------------------------------------------------------------------------- /buddy.java.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -java bin 8 | -main buddy.tests.AllTests 9 | -cmd echo "---------- JAVA ----------" 10 | -cmd java -jar bin/AllTests.jar 11 | -------------------------------------------------------------------------------- /buddy.cpp.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -lib hxcpp 8 | -cpp bin 9 | -main buddy.tests.AllTests 10 | -cmd echo "---------- CPP ----------" 11 | -cmd bin/AllTests 12 | # -D HXCPP_M64 13 | -------------------------------------------------------------------------------- /buddy.python.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -python bin/buddy.python.py 8 | -main buddy.tests.AllTests 9 | -cmd echo "---------- Python ----------" 10 | -cmd python3 bin/buddy.python.py 11 | -------------------------------------------------------------------------------- /src/buddy/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | buddy.js 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /buddy.nodejs.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -D nodejs 8 | -js bin/buddy.nodejs.js 9 | -main buddy.tests.AllTests 10 | -cmd echo "---------- Node.js ----------" 11 | -cmd node bin/buddy.nodejs.js 12 | -------------------------------------------------------------------------------- /flash-travis-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # The flash player has some issues with unexplained crashes, 4 | # but if it runs about 7 times, it should succeed one of those. 5 | for i in 1 2 3 4 5 6 7 6 | do 7 | xvfb-run ~/flashplayerdebugger bin/buddy.swf 8 | test $? -eq 0 && exit 0 9 | done 10 | exit 1 11 | -------------------------------------------------------------------------------- /src/buddy/internal/sys/NodeJs.hx: -------------------------------------------------------------------------------- 1 | package buddy.internal.sys; 2 | 3 | #if nodejs 4 | class NodeJs 5 | { 6 | public static function print(s : String) 7 | { 8 | untyped __js__("process.stdout.write(s)"); 9 | } 10 | 11 | public static function println(s : String) 12 | { 13 | untyped __js__("console.log(s)"); 14 | } 15 | } 16 | #end 17 | -------------------------------------------------------------------------------- /buddy.flash.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -swf bin/buddy.swf 8 | -main buddy.tests.AllTests 9 | --times 10 | -D reporter=buddy.reporting.TraceReporter 11 | -D flash-exit 12 | -D macro-times 13 | -cmd echo "---------- Flash (xvfb-run) ----------" 14 | -cmd chmod +x flash-travis-run.sh && ./flash-travis-run.sh 15 | -------------------------------------------------------------------------------- /src/haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buddy", 3 | "description": "Your friendly BDD testing library for Haxe!", 4 | "url" : "https://github.com/ciscoheat/buddy", 5 | "license": "MIT", 6 | "tags": ["cross", "bdd", "testing"], 7 | "version": "2.5.2", 8 | "releasenote": "Travix output fix.", 9 | "contributors": ["ciscoheat"], 10 | "dependencies": { "promhx": "", "asynctools": "" } 11 | } -------------------------------------------------------------------------------- /buddy.js.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -lib asynctools 3 | -lib promhx 4 | -lib utest 5 | -lib slambda 6 | -lib tink_await 7 | -js bin/buddy.js 8 | -main buddy.tests.AllTests 9 | -D reporter=buddy.reporting.TraceReporter 10 | -D buddy-no-colors 11 | -cmd echo "---------- JS (PhantomJS) ----------" 12 | -cmd cp src/buddy/tests/index.html bin 13 | -cmd cp src/buddy/tests/buddy.phantomjs.js bin 14 | -cmd phantomjs bin/buddy.phantomjs.js -------------------------------------------------------------------------------- /src/buddy/reporting/TravisHxReporter.hx: -------------------------------------------------------------------------------- 1 | package buddy.reporting; 2 | 3 | import buddy.BuddySuite.Suite; 4 | import buddy.reporting.TraceReporter; 5 | 6 | /** 7 | * For usage together with travis-hx: https://github.com/waneck/travis-hx 8 | * @author deep 9 | */ 10 | class TravisHxReporter extends TraceReporter 11 | { 12 | override public function done(suites:Iterable, status : Bool) 13 | { 14 | var res = super.done(suites, status); 15 | println('success: ${status}'); 16 | return res; 17 | } 18 | } -------------------------------------------------------------------------------- /src/buddy/tests/buddy.phantomjs.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | 3 | page.open('bin/index.html', function(status) { 4 | if (status !== 'success') { 5 | console.log('Error: Unable to access network!'); 6 | phantom.exit(1); 7 | } else { 8 | setInterval(function() { 9 | var result = page.evaluate(function() { return document.body.textContent; }); 10 | if(!/\d+ specs, \d+ failures, \d+ pending/.test(result)) return; 11 | 12 | console.log(result); 13 | 14 | var failed = /\d+ specs, [1-9]\d* failures, \d+ pending/.test(result); 15 | phantom.exit(failed ? 1 : 0); 16 | }, 100); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/buddy/tests/SelfTest.hx: -------------------------------------------------------------------------------- 1 | package buddy.tests; 2 | 3 | import buddy.BuddySuite.Failure; 4 | import buddy.BuddySuite.Spec; 5 | import buddy.BuddySuite.Suite; 6 | import buddy.BuddySuite.SpecStatus; 7 | 8 | /** 9 | * Used for Buddy to test itself 10 | */ 11 | class SelfTest { 12 | public static var lastSpec : Spec; 13 | public static var lastSuite : Suite; 14 | 15 | public static function passLastSpecIf(expr : Bool, failReason : String) { 16 | if (expr) { 17 | setLastSpec(Passed); 18 | } 19 | else { 20 | setLastSpec(Failed); 21 | lastSpec.failures.push(new Failure(failReason, [])); 22 | } 23 | } 24 | 25 | public static function setLastSpec(status : SpecStatus) { 26 | Reflect.setProperty(lastSpec, "status", status); 27 | } 28 | } -------------------------------------------------------------------------------- /flash-travis-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Some xvfb settings 4 | export DISPLAY=:99.0 5 | export AUDIODEV=null 6 | 7 | # Required libs for the flash player 8 | sudo apt-get -y -q install libcurl3:i386 libglib2.0-0:i386 libx11-6:i386 libxext6:i386 libxt6:i386 libxcursor1:i386 libnss3:i386 libgtk2.0-0:i386 9 | 10 | # Download and unzip the flash player 11 | wget http://fpdownload.macromedia.com/pub/flashplayer/updaters/11/flashplayer_11_sa_debug.i386.tar.gz 12 | tar -xf flashplayer_11_sa_debug.i386.tar.gz -C ~ 13 | 14 | # Create a configuration file so the trace log is enabled 15 | echo -e "ErrorReportingEnable=1\nTraceOutputFileEnable=1" > ~/mm.cfg 16 | 17 | # Add the current directory as trusted, so exit() can be used 18 | mkdir -p ~/.macromedia/Flash_Player/#Security/FlashPlayerTrust 19 | echo "`pwd`" > ~/.macromedia/Flash_Player/#Security/FlashPlayerTrust/buddy.cfg 20 | -------------------------------------------------------------------------------- /src/buddy/reporting/Reporter.hx: -------------------------------------------------------------------------------- 1 | package buddy.reporting ; 2 | import buddy.BuddySuite; 3 | import promhx.Promise; 4 | 5 | interface Reporter 6 | { 7 | /** 8 | * Called just before tests are run. If promise is resolved with "false", 9 | * testing will immediately exit with status 1. 10 | */ 11 | public function start() : Promise; 12 | 13 | /** 14 | * Called for every Spec. Can be used to display realtime notifications. 15 | * Resolve with the same spec as the parameter. 16 | */ 17 | public function progress(spec : Spec) : Promise; 18 | 19 | /** 20 | * Called after the last spec is run. Useful for displaying a test summary. 21 | * Resolve with the same iterable as the parameter. 22 | * Status is true if all tests passed, otherwise false. 23 | */ 24 | public function done(suites : Iterable, status : Bool) : Promise>; 25 | } 26 | -------------------------------------------------------------------------------- /src/buddy/internal/sys/Js.hx: -------------------------------------------------------------------------------- 1 | package buddy.internal.sys; 2 | 3 | #if js 4 | import js.Browser; 5 | using StringTools; 6 | 7 | class Js 8 | { 9 | static var completed = ~/^\d+ specs, (\d+) failures, (\d+) pending$/; 10 | 11 | public static function print(s : String) { 12 | #if travix 13 | travix.Logger.print(s); 14 | #end 15 | } 16 | 17 | public static function println(s : String) { 18 | #if travix 19 | travix.Logger.println(s); 20 | #else 21 | var log = Browser.window.console; 22 | if (completed.match(s)) { 23 | switch Std.parseInt(completed.matched(1)) { 24 | case 0: 25 | switch Std.parseInt(completed.matched(2)) { 26 | case 0: log.info('%c$s', 'color: green'); 27 | case _: log.warn('%c$s', 'color: green'); 28 | } 29 | 30 | case _: log.error(s); 31 | } 32 | } else 33 | log.log(s); 34 | #end 35 | } 36 | } 37 | #end -------------------------------------------------------------------------------- /src/buddy/internal/sys/Flash.hx: -------------------------------------------------------------------------------- 1 | package buddy.internal.sys; 2 | #if flash 3 | import flash.events.Event; 4 | import flash.text.TextField; 5 | import flash.Lib; 6 | 7 | class Flash 8 | { 9 | private static var tf : TextField; 10 | 11 | private static function init() 12 | { 13 | tf = new TextField(); 14 | var stage = Lib.current.stage; 15 | 16 | stage.scaleMode = flash.display.StageScaleMode.NO_SCALE; 17 | stage.addEventListener(Event.RESIZE, function(_) { 18 | tf.width = stage.stageWidth; 19 | tf.height = stage.stageHeight; 20 | }); 21 | 22 | Lib.current.addChild(tf); 23 | stage.dispatchEvent(new Event(Event.RESIZE)); 24 | } 25 | 26 | public static function print(s : String) 27 | { 28 | if (tf == null) init(); 29 | tf.text += s; 30 | } 31 | 32 | public static function println(s : String) 33 | { 34 | if (tf == null) init(); 35 | tf.text += s + "\n"; 36 | } 37 | } 38 | #end -------------------------------------------------------------------------------- /src/buddy/reporting/ConsoleFileReporter.hx: -------------------------------------------------------------------------------- 1 | package buddy.reporting; 2 | 3 | import buddy.BuddySuite.Spec; 4 | 5 | class ConsoleFileReporter extends ConsoleReporter 6 | { 7 | var lastFileName : String = null; 8 | 9 | public function new(colors = false) { 10 | super(colors); 11 | } 12 | 13 | override public function progress(spec : Spec) 14 | { 15 | if(lastFileName != spec.fileName) { 16 | if(lastFileName != null) { 17 | progressString += "\n"; 18 | println(""); 19 | } 20 | 21 | progressString += spec.fileName + ": "; 22 | print(spec.fileName + ": "); 23 | 24 | lastFileName = spec.fileName; 25 | } 26 | 27 | var status = switch(spec.status) { 28 | case Failed: strCol(Red) + "X"; 29 | case Passed: strCol(Green) + "."; 30 | case Pending: strCol(Yellow) + "P"; 31 | case Unknown: strCol(Yellow) + "?"; 32 | } 33 | 34 | progressString += status; 35 | print(status + strCol(Default)); 36 | 37 | return resolveImmediately(spec); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/buddy/CompilationShould.hx: -------------------------------------------------------------------------------- 1 | package buddy; 2 | 3 | import haxe.macro.Context; 4 | import haxe.macro.Expr; 5 | 6 | using haxe.macro.ExprTools; 7 | 8 | class CompilationShould 9 | { 10 | // Thanks to back2dos: https://github.com/back2dos/travix/issues/19#issuecomment-222100034 11 | macro public static function failFor(e : Expr) { 12 | var exception : String; 13 | var status = try { 14 | Context.typeof(e); 15 | false; 16 | } catch (ex : Dynamic) { 17 | exception = Std.string(ex); 18 | true; 19 | } 20 | 21 | return if(!status) { 22 | var message = 'Expected expression "${e.toString()}" to fail compilation.'; 23 | var pos = toPosInfos(e.pos); 24 | 25 | macro { buddy.SuitesRunner.currentTest($v{status}, $v{message}, buddy.SuitesRunner.posInfosToStack($v{pos})); ""; }; 26 | } else { 27 | macro ($v{exception} : String); 28 | } 29 | } 30 | 31 | #if macro 32 | // Thanks to nadako: https://gist.github.com/nadako/6411325#file-testutils-hx-L33 33 | static function toPosInfos(p:haxe.macro.Expr.Position):haxe.PosInfos { 34 | var pi = haxe.macro.Context.getPosInfos(p); 35 | var line = sys.io.File.getContent(pi.file).substr(0, pi.min).split("\n").length; 36 | return { 37 | lineNumber: line, 38 | fileName: pi.file, 39 | className: haxe.macro.Context.getLocalClass().get().name, 40 | methodName: haxe.macro.Context.getLocalMethod() 41 | }; 42 | } 43 | #end 44 | } -------------------------------------------------------------------------------- /buddy.python.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /buddy.cpp.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /buddy.js.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /buddy.php.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /buddy.cs.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /buddy.java.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /buddy.nodejs.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /buddy.neko.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # The first thing is to tell which VM environment we want the test to be run on. 2 | # It dosen't quite matter for Haxe, just choose one of the targets our project support (e.g. PHP), 3 | # and than apt-get install the others (e.g. Neko, C++). 4 | # For more info, see http://about.travis-ci.org/docs/user/ci-environment/ 5 | language: node_js 6 | 7 | env: 8 | matrix: 9 | - TARGET=cpp 10 | - TARGET=cs 11 | - TARGET=java 12 | - TARGET=js 13 | - TARGET=neko 14 | - TARGET=nodejs 15 | - TARGET=php 16 | - TARGET=flash 17 | - TARGET=python 18 | 19 | before_install: 20 | # Install Haxe 21 | - wget http://ciscoheat.github.io/cdn/haxe/haxe-3.2.1-linux-installer.sh 22 | - sudo sh haxe-3.2.1-linux-installer.sh -y 23 | 24 | # haxelib dependencies for all targets 25 | - haxelib install promhx 26 | - haxelib install utest 27 | - haxelib install slambda 28 | - haxelib install asynctools 29 | - haxelib install tink_await 30 | 31 | # apt packages for each target 32 | - sudo apt-get update 33 | - sh -c "if [ '$TARGET' = 'cpp' ]; then sudo apt-get install gcc-multilib g++-multilib -y; fi" 34 | - sh -c "if [ '$TARGET' = 'cs' ]; then sudo apt-get install mono-devel -y; fi" 35 | - sh -c "if [ '$TARGET' = 'js' ]; then sudo apt-get install phantomjs -y; fi" 36 | - sh -c "if [ '$TARGET' = 'php' ]; then sudo apt-get install php5-cli -y; fi" 37 | - sh -c "if [ '$TARGET' = 'flash' ]; then chmod +x ./flash-travis-setup.sh && ./flash-travis-setup.sh; fi" 38 | - sh -c "if [ '$TARGET' = 'python' ]; then sudo apt-get install python3 -y; fi" 39 | 40 | # haxelib dependencies for each target 41 | - sh -c "if [ '$TARGET' = 'cpp' ]; then haxelib install hxcpp; fi" 42 | - sh -c "if [ '$TARGET' = 'java' ]; then haxelib install hxjava; fi" 43 | - sh -c "if [ '$TARGET' = 'cs' ]; then haxelib install hxcs; fi" 44 | 45 | # Run the test! 46 | script: 47 | - haxe buddy.${TARGET}.hxml && haxe -dce full buddy.${TARGET}.hxml 48 | 49 | branches: 50 | only: 51 | - master -------------------------------------------------------------------------------- /buddy.flash.hxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/buddy/reporting/ConsoleReporter.hx: -------------------------------------------------------------------------------- 1 | package buddy.reporting; 2 | 3 | import buddy.BuddySuite; 4 | import buddy.reporting.Reporter; 5 | import haxe.CallStack; 6 | import buddy.reporting.TraceReporter.Color; 7 | using Lambda; 8 | using StringTools; 9 | 10 | #if nodejs 11 | import buddy.internal.sys.NodeJs; 12 | private typedef Sys = NodeJs; 13 | #elseif js 14 | import js.html.PreElement; 15 | import js.Browser; 16 | import buddy.internal.sys.Js; 17 | private typedef Sys = Js; 18 | #elseif flash 19 | import buddy.internal.sys.Flash; 20 | private typedef Sys = Flash; 21 | #end 22 | 23 | class ConsoleReporter extends TraceReporter 24 | { 25 | var progressString = ""; 26 | 27 | public function new(colors = false) { 28 | super(colors); 29 | } 30 | 31 | override public function start() 32 | { 33 | // A small convenience for PHP, to avoid creating a new reporter. 34 | #if php 35 | if (untyped __call__("php_sapi_name") != "cli") println("
");
36 | 		#end
37 | 
38 | 		return resolveImmediately(true);
39 | 	}
40 | 
41 | 	override public function progress(spec : Spec)
42 | 	{
43 | 		var status = switch(spec.status) {
44 | 			case Failed: strCol(Red) + "X";
45 | 			case Passed: strCol(Green) + ".";
46 | 			case Pending: strCol(Yellow) + "P";
47 | 			case Unknown: strCol(Yellow) + "?";
48 | 		}
49 | 		
50 | 		progressString += status;
51 | 		print(status + strCol(Default));
52 | 
53 | 		return resolveImmediately(spec);
54 | 	}
55 | 
56 | 	override public function done(suites : Iterable, status : Bool)
57 | 	{
58 | 		#if (js && !nodejs && !travix)
59 | 		Sys.println(progressString);
60 | 		#end
61 | 		
62 | 		var output = super.done(suites, status);
63 | 
64 | 		#if php
65 | 		if(untyped __call__("php_sapi_name") != "cli") println("
"); 66 | #end 67 | 68 | return output; 69 | } 70 | 71 | override private function print(s : String) 72 | { 73 | Sys.print(s); 74 | #if php 75 | untyped __call__("flush"); 76 | #end 77 | } 78 | 79 | override private function println(s : String) 80 | { 81 | Sys.println(s); 82 | #if php 83 | untyped __call__("flush"); 84 | #end 85 | } 86 | } -------------------------------------------------------------------------------- /src/buddy/tools/AsyncTools.hx: -------------------------------------------------------------------------------- 1 | package buddy.tools ; 2 | import promhx.Promise; 3 | import promhx.Deferred; 4 | 5 | #if neko 6 | import neko.vm.Thread; 7 | #elseif cs 8 | import cs.system.timers.ElapsedEventHandler; 9 | import cs.system.timers.ElapsedEventArgs; 10 | import cs.system.timers.Timer; 11 | #elseif java 12 | import java.util.concurrent.FutureTask; 13 | import java.util.concurrent.Callable; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | #elseif cpp 17 | import cpp.vm.Thread; 18 | #elseif python 19 | @:pythonImport("threading", "Timer") 20 | extern class Timer { 21 | public function new(delayS : Float, callback : Void -> Void); 22 | public function start() : Void; 23 | public function cancel() : Void; 24 | } 25 | #else 26 | import haxe.Timer; 27 | #end 28 | 29 | class AsyncTools 30 | { 31 | public static function iterateAsyncBool(it : Iterable, action : T -> Promise) : Promise 32 | { 33 | return iterateAsync(it, action, true); 34 | } 35 | 36 | public static function iterateAsync(it : Iterable, action : T -> Promise, resolveWith : T2) : Promise 37 | { 38 | var finished = new Deferred(); 39 | var pr = finished.promise(); 40 | next(it.iterator(), action, finished, resolveWith); 41 | return pr; 42 | } 43 | 44 | public static function wait(ms : Int) : Promise 45 | { 46 | var def = new Deferred(); 47 | var pr = def.promise(); 48 | var done = function() { if (!pr.isFulfilled()) def.resolve(true); }; 49 | 50 | #if (neko && !macro) 51 | Thread.create(function() { 52 | Sys.sleep(ms / 1000); 53 | done(); 54 | }); 55 | #elseif python 56 | new Timer(ms / 1000, done).start(); 57 | #elseif (js || flash) 58 | Timer.delay(function() done(), ms); 59 | #elseif cs 60 | var t = new Timer(ms); 61 | t.add_Elapsed(new ElapsedEventHandler(function(sender : Dynamic, e : ElapsedEventArgs) { 62 | t.Stop(); t = null; 63 | done(); 64 | })); 65 | t.Start(); 66 | #elseif java 67 | var executor = Executors.newFixedThreadPool(1); 68 | var call = new AsyncCallable(function() { 69 | executor.shutdown(); executor = null; 70 | done(); 71 | }, ms); 72 | executor.execute(new FutureTask(call)); 73 | #elseif cpp 74 | Thread.create(function() { 75 | Sys.sleep(ms / 1000); 76 | done(); 77 | }); 78 | #elseif php 79 | throw "AsyncTools.wait not supported for PHP."; 80 | #else 81 | throw "AsyncTools.wait not supported for current target."; 82 | #end 83 | 84 | return pr; 85 | } 86 | 87 | private static function next(it : Iterator, action : T -> Promise, def : Deferred, resolveWith : T2) 88 | { 89 | if (!it.hasNext()) { 90 | def.resolve(resolveWith); 91 | } 92 | else { 93 | var n = it.next(); 94 | action(n).then(function(_) { next(it, action, def, resolveWith); } ); 95 | } 96 | } 97 | } 98 | 99 | #if java 100 | private class AsyncCallable implements Callable 101 | { 102 | private var done : Void -> Void; 103 | private var waitMs : Int; 104 | 105 | public function new(done : Void -> Void, waitMs : Int) 106 | { 107 | this.done = done; 108 | this.waitMs = waitMs; 109 | } 110 | 111 | public function call() : String 112 | { 113 | Sys.sleep(waitMs / 1000); 114 | done(); 115 | return ""; 116 | } 117 | } 118 | #end -------------------------------------------------------------------------------- /src/buddy/reporting/TraceReporter.hx: -------------------------------------------------------------------------------- 1 | package buddy.reporting; 2 | 3 | import haxe.CallStack.StackItem; 4 | import promhx.Deferred; 5 | import promhx.Promise; 6 | 7 | import buddy.BuddySuite.Spec; 8 | import buddy.BuddySuite.Suite; 9 | import buddy.BuddySuite.SpecStatus; 10 | 11 | using Lambda; 12 | using StringTools; 13 | 14 | @:enum abstract Color(Int) { 15 | var Default = 39; 16 | var Red = 31; 17 | var Yellow = 33; 18 | var Green = 32; 19 | var White = 37; 20 | 21 | @:to public function ansiCode() : String return String.fromCharCode(27) + '[${this}m'; 22 | } 23 | 24 | class TraceReporter implements Reporter 25 | { 26 | var colors : Bool; 27 | 28 | public function new(colors = false) { 29 | this.colors = colors; 30 | } 31 | 32 | public function start() { 33 | return resolveImmediately(true); 34 | } 35 | 36 | public function progress(spec:Spec) { 37 | // No progress is shown, it would generate too much noise. 38 | return resolveImmediately(spec); 39 | } 40 | 41 | public function done(suites : Iterable, status : Bool) { 42 | #if (js && !nodejs && !travix) 43 | // Skip newline, already printed in console.log() 44 | #else 45 | println(""); 46 | #end 47 | 48 | var total = 0; 49 | var failures = 0; 50 | var pending = 0; 51 | 52 | var countTests : Suite -> Void = null; 53 | var printTests : Suite -> Int -> Void = null; 54 | 55 | countTests = function(s : Suite) { 56 | if (s.error != null) failures++; // Count a crashed BuddySuite as a failure? 57 | 58 | for (sp in s.steps) switch sp { 59 | case TSpec(sp): 60 | total++; 61 | if (sp.status == Failed) failures++; 62 | else if (sp.status == Pending) pending++; 63 | case TSuite(s): 64 | countTests(s); 65 | } 66 | }; 67 | 68 | suites.iter(countTests); 69 | 70 | printTests = function(s : Suite, indentLevel : Int) { 71 | function print(str : String, color : Color = Default) { 72 | var start = strCol(color), end = strCol(Default); 73 | println(start + str.lpad(" ", str.length + Std.int(Math.max(0, indentLevel * 2))) + end); 74 | } 75 | 76 | function printStack(indent : String, stack : Array) { 77 | if (stack == null || stack.length == 0) return; 78 | for (s in stack) switch s { 79 | case FilePos(_, file, line) if (line > 0 && file.indexOf("buddy/internal/") != 0 && file.indexOf("buddy.SuitesRunner") != 0): 80 | print(indent + '@ $file:$line', Yellow); 81 | case _: 82 | } 83 | } 84 | 85 | function printTraces(spec : Spec) { 86 | for (t in spec.traces) print(" " + t, Yellow); 87 | } 88 | 89 | if (s.description.length > 0) print(s.description); 90 | 91 | if (s.error != null) { 92 | // The whole suite crashed. 93 | print("ERROR: " + s.error, Red); 94 | printStack(' ', s.stack); 95 | return; 96 | } 97 | 98 | for (step in s.steps) switch step { 99 | case TSpec(sp): 100 | if (sp.status == Failed) { 101 | print(" " + sp.description + " (FAILED)", Red); 102 | 103 | for(failure in sp.failures) { 104 | print(" " + failure.error, Yellow); 105 | printStack(' ', failure.stack); 106 | } 107 | } 108 | else { 109 | print(" " + sp.description + " (" + sp.status + ")", sp.status == Passed ? Green : Yellow); 110 | } 111 | printTraces(sp); 112 | case TSuite(s): 113 | printTests(s, indentLevel + 1); 114 | } 115 | }; 116 | 117 | suites.iter(printTests.bind(_, -1)); 118 | 119 | var totalColor = if (failures > 0) Red else Green; 120 | var pendingColor = if (pending > 0) Yellow else totalColor; 121 | 122 | println( 123 | strCol(totalColor) + '$total specs, $failures failures, ' + 124 | strCol(pendingColor) + '$pending pending' + 125 | strCol(Default) 126 | ); 127 | 128 | return resolveImmediately(suites); 129 | } 130 | 131 | private function print(s : String) { 132 | // Override when needed. 133 | } 134 | 135 | private function println(s : String) { 136 | // Override when needed. 137 | #if flash 138 | flash.Lib.trace(s); 139 | #elseif (js && !nodejs) 140 | js.Browser.document.writeln(s); 141 | #else 142 | trace(s); 143 | #end 144 | } 145 | 146 | private function strCol(color : Color) return this.colors ? color.ansiCode() : ""; 147 | 148 | /** 149 | * Convenience method. 150 | */ 151 | private function resolveImmediately(o : T) : Promise { 152 | var def = new Deferred(); 153 | var pr = def.promise(); 154 | def.resolve(o); 155 | return pr; 156 | } 157 | } -------------------------------------------------------------------------------- /src/buddy/internal/GenerateMain.hx: -------------------------------------------------------------------------------- 1 | package buddy.internal ; 2 | 3 | #if macro 4 | import buddy.reporting.ConsoleReporter; 5 | import buddy.SuitesRunner; 6 | import haxe.macro.Compiler; 7 | import haxe.macro.Expr; 8 | import haxe.macro.ExprTools; 9 | import haxe.macro.Type; 10 | import haxe.macro.Context; 11 | import haxe.rtti.Meta; 12 | import Type in HaxeType; 13 | 14 | using haxe.macro.ExprTools; 15 | using Lambda; 16 | #end 17 | 18 | class GenerateMain 19 | { 20 | // Will be set in SuitesRunner 21 | public static var testsRunning : Bool; 22 | 23 | #if macro 24 | macro public static function withSuites(?buddySuites : Expr) : Array 25 | { 26 | var cls = Context.getLocalClass().get(); 27 | var fields = Context.getBuildFields(); 28 | 29 | function error() Context.error("Buddy must use an array of type paths as parameter.", cls.pos); 30 | 31 | if (buddySuites == null || buddySuites.expr.equals(EConst(CIdent("null")))) { 32 | var buddyInterface = cls.interfaces.find(function(f) return f.t.get().name == 'Buddy'); 33 | if (buddyInterface == null) { 34 | if (cls.superClass.t != null && cls.superClass.t.get().name == "SingleSuite") 35 | buddySuites = macro [$p{cls.pack.concat([cls.name])}]; 36 | else 37 | error(); 38 | } else { 39 | switch buddyInterface.params[0] { 40 | case TInst(t, _): switch t.get().kind { 41 | case KExpr(e): buddySuites = e; 42 | case _: error(); 43 | } 44 | case _: error(); 45 | } 46 | } 47 | } 48 | 49 | var addedSuites = []; 50 | 51 | function addSuite(type) switch type { 52 | case TInst(t, params): 53 | var type = t.get(); 54 | if (type.meta.has('exclude')) return; 55 | 56 | addedSuites.push({expr: ENew({ 57 | name: type.name, 58 | pack: type.pack, 59 | params: [] 60 | }, []), pos: cls.pos}); 61 | case _: error(); 62 | }; 63 | 64 | switch buddySuites.expr { 65 | case EArrayDecl(values): for (c in values) switch c.expr { 66 | case EField(e, field): 67 | addSuite(Context.getType(e.toString() + '.' + field)); 68 | case EConst(c): switch c { 69 | case CString(s), CIdent(s): 70 | addSuite(Context.getType(s)); 71 | case _: error(); 72 | } 73 | case ENew(t, params): 74 | addedSuites.push(c); 75 | case _: error(); 76 | } 77 | case _: error(); 78 | } 79 | 80 | buildMain(currentMain(fields), cls, reporter(), { expr: EArrayDecl(addedSuites), pos: cls.pos }); 81 | 82 | return fields; 83 | } 84 | 85 | private static function currentMain(fields : Array) : Array 86 | { 87 | for (f in fields) 88 | { 89 | if (f.name == "main" && f.access.exists(function(a) { return a == Access.AStatic; } )) 90 | { 91 | switch(f.kind) 92 | { 93 | case FFun(f2): 94 | switch(f2.expr.expr) 95 | { 96 | case EBlock(exprs): return exprs; 97 | case _: 98 | } 99 | case _: 100 | } 101 | } 102 | } 103 | 104 | var exprs = new Array(); 105 | 106 | var func = { 107 | ret: null, 108 | params: [], 109 | expr: {pos: Context.currentPos(), expr: EBlock(exprs)}, 110 | args: [] 111 | }; 112 | 113 | var main = { 114 | pos: Context.currentPos(), 115 | name: "main", 116 | meta: [], 117 | kind: FFun(func), 118 | doc: null, 119 | access: [Access.AStatic, Access.APublic] 120 | }; 121 | 122 | fields.push(main); 123 | return exprs; 124 | } 125 | 126 | private static function reporter() : String 127 | { 128 | var cls = Context.getLocalClass().get(); 129 | 130 | var colors = ["buddy-colors", "buddy_colors"].exists(Context.defined) || 131 | ["buddy-colors", "buddy_colors", "colors", "color", "colorize"].exists(cls.meta.has); 132 | 133 | if (["buddy-no-colors", "buddy_no_colors"].exists(Context.defined)) 134 | colors = false; 135 | 136 | // Set default reporter 137 | var reporter = if (flashCI()) { 138 | "buddy.reporting.TraceReporter"; 139 | } else { 140 | colors ? "buddy.reporting.ConsoleColorReporter" : "buddy.reporting.ConsoleReporter"; 141 | } 142 | 143 | if (Context.defined("reporter")) 144 | { 145 | reporter = Context.definedValue("reporter"); 146 | } 147 | else if (cls.meta.has("reporter")) 148 | { 149 | reporter = cls.meta.get().find(function(m) return m.name == "reporter").params[0].getValue(); 150 | } 151 | 152 | return reporter; 153 | } 154 | 155 | private static function typeIsSuite(classes : Array) : Array 156 | { 157 | var output = new Array(); 158 | var include = new Array(); 159 | 160 | for (c in classes) { 161 | if (c.meta.has("exclude")) continue; 162 | if (c.superClass != null && c.superClass.t.get().name == "BuddySuite") 163 | { 164 | if (c.meta.has("include")) include.push(c); 165 | else output.push(c); 166 | } 167 | } 168 | 169 | return include.length > 0 ? include : output; 170 | } 171 | 172 | private static function flashCI() 173 | return Context.defined("fdb-ci") || Context.defined("exit-flash") || Context.defined("flash-exit"); 174 | 175 | private static function buildMain(exprs : Array, cls : ClassType, reporter : String, ?allSuites : ExprOf>) { 176 | if (allSuites == null) Context.error("No BuddySuites found.", cls.pos); 177 | 178 | function toTypeStringExpr(type : ClassType) : Expr { 179 | return {expr: EConst(CString(type.pack.concat([type.name]).join("."))), pos: Context.currentPos()}; 180 | } 181 | 182 | var e = toTypeStringExpr(cls); 183 | var body : Expr; 184 | 185 | var pack = reporter.split("."); 186 | var type = pack.pop(); 187 | 188 | var rep = { 189 | sub: null, 190 | params: null, 191 | pack: pack, 192 | name: type 193 | } 194 | 195 | var header = macro { 196 | var testsDone : Bool = false; // For platforms without event loop 197 | var runner : buddy.SuitesRunner = null; 198 | var oldTrace = haxe.Log.trace; 199 | 200 | function outputError() { 201 | haxe.Log.trace = oldTrace; // Restore original trace 202 | trace(runner.unrecoverableError); 203 | 204 | var stack = runner.unrecoverableErrorStack; 205 | if (stack == null || stack.length == 0) return; 206 | 207 | for (s in stack) switch s { 208 | case FilePos(_, file, line) if(line > 0): trace(file+":"+line); 209 | case _: 210 | } 211 | } 212 | 213 | function startRun(done : Void -> Void) : Void { 214 | runner = new buddy.SuitesRunner($allSuites, new $rep()); 215 | runner.run().then(function(_) { 216 | if (runner.unrecoverableError != null) outputError(); 217 | done(); 218 | }); 219 | } 220 | }; 221 | 222 | // Targets that requires a waiting loop 223 | if (Context.defined("neko") || Context.defined("cpp") || Context.defined("python")) 224 | { 225 | body = macro { 226 | startRun(function() testsDone = true); 227 | while (!testsDone) Sys.sleep(0.1); 228 | Sys.exit(runner.statusCode()); 229 | }; 230 | } 231 | else if(Context.defined("cs")) 232 | { 233 | body = macro { 234 | startRun(function() testsDone = true); 235 | while (!testsDone) cs.system.threading.Thread.Sleep(10); 236 | cs.system.Environment.Exit(runner.statusCode()); 237 | }; 238 | } 239 | else if(Context.defined("nodejs")) 240 | { 241 | body = macro { 242 | untyped __js__("process.on('uncaughtException', function(err) {"); 243 | runner.haveUnrecoverableError(untyped err); 244 | untyped __js__("})"); 245 | startRun(function() untyped __js__("process.exit(runner.statusCode())")); 246 | }; 247 | } 248 | else if(Context.defined("sys")) 249 | { 250 | body = macro { 251 | startRun(function() Sys.exit(runner.statusCode())); 252 | }; 253 | } 254 | else if (flashCI()) 255 | { 256 | body = macro { 257 | startRun(function() flash.system.System.exit(runner.statusCode())); 258 | } 259 | } 260 | else if(Context.defined('js') && Context.defined('travix') && !Context.defined('nodejs')) 261 | { 262 | body = macro { 263 | startRun(function() travix.Logger.exit(runner.statusCode())); 264 | } 265 | } 266 | else 267 | { 268 | body = macro { 269 | startRun(function() {}); 270 | }; 271 | } 272 | 273 | // Merge the blocks 274 | for(block in [header, body]) switch block.expr { 275 | case EBlock(exprs2): 276 | for (e in exprs2) exprs.push(e); 277 | case _: 278 | throw "header or body isn't a block expression."; 279 | } 280 | } 281 | #end 282 | } 283 | -------------------------------------------------------------------------------- /src/buddy/BuddySuite.hx: -------------------------------------------------------------------------------- 1 | package buddy ; 2 | import buddy.reporting.Reporter; 3 | import haxe.CallStack; 4 | import haxe.macro.Expr; 5 | import haxe.PosInfos; 6 | import haxe.ds.GenericStack; 7 | import haxe.rtti.Meta; 8 | import promhx.Deferred; 9 | import promhx.Promise; 10 | import buddy.Should; 11 | 12 | #if (cpp && hxcpp) 13 | import hxcpp.StaticStd; 14 | import hxcpp.StaticRegexp; 15 | #end 16 | 17 | using buddy.tools.AsyncTools; 18 | using Lambda; 19 | 20 | // Final status of a Spec 21 | enum SpecStatus { 22 | Unknown; 23 | Passed; 24 | Pending; 25 | Failed; 26 | } 27 | 28 | // An alias for backwards compatibility 29 | @:deprecated("TestStatus is deprecated, please rename to SpecStatus") 30 | typedef TestStatus = SpecStatus; 31 | 32 | // A completed test step inside a Describe (Either a Suite or a Spec) 33 | enum Step { 34 | TSuite(s : Suite); 35 | TSpec(s : Spec); 36 | } 37 | 38 | // A completed test suite ("Describe") 39 | class Suite 40 | { 41 | public var description(default, null) : String; 42 | 43 | @:allow(buddy.SuitesRunner) public var steps(default, null) = new Array(); 44 | @:allow(buddy.SuitesRunner) public var error(default, null) : Dynamic; 45 | @:allow(buddy.SuitesRunner) public var stack(default, null) = new Array(); 46 | 47 | public var specs(get, never) : Array; 48 | private function get_specs() { 49 | var output = []; 50 | for(step in steps) switch step { 51 | case TSpec(s): output.push(s); 52 | case _: 53 | } 54 | return output; 55 | } 56 | 57 | public var suites(get, never) : Array; 58 | private function get_suites() { 59 | var output = []; 60 | for(step in steps) switch step { 61 | case TSuite(s): output.push(s); 62 | case _: 63 | } 64 | return output; 65 | } 66 | 67 | /** 68 | * Returns true if this suite and all below it passed. 69 | */ 70 | public function passed() : Bool { 71 | if (specs.exists(function(spec) return spec.status == Failed)) return false; 72 | return !suites.exists(function(suite) return !suite.passed()); 73 | } 74 | 75 | public function new(description : String) { 76 | if (description == null) throw "Suite requires a description."; 77 | this.description = description; 78 | } 79 | } 80 | 81 | // A completed spec ("It") 82 | class Spec 83 | { 84 | public var description(default, null) : String; 85 | @:allow(buddy.SuitesRunner) public var status(default, null) : SpecStatus = Unknown; 86 | @:allow(buddy.SuitesRunner) public var failures(default, null) = new Array(); 87 | @:allow(buddy.SuitesRunner) public var traces(default, null) = new Array(); 88 | @:allow(buddy.SuitesRunner) public var fileName(default, null) : String = ""; 89 | 90 | public function new(description : String, fileName : String) { 91 | if(description == null) throw "Spec must have a description."; 92 | this.description = description; 93 | this.fileName = fileName; 94 | } 95 | } 96 | 97 | // A failed should 98 | class Failure 99 | { 100 | public var error(default, null) : Dynamic; 101 | public var stack(default, null) : Array; 102 | 103 | public function new(error : Dynamic, stack : Array) { 104 | if (error == null) throw "Failure must have an error."; 105 | this.error = error; 106 | this.stack = stack == null ? [] : stack; 107 | } 108 | } 109 | 110 | ///// Classes and enums starting with "Test" represents the system before testing is completed. 111 | ///// While testing, they are transformed into Spec and Suite. 112 | 113 | enum TestFunc { 114 | Async(f : (Void -> Void) -> Void); 115 | Sync(f : Void -> Void); 116 | } 117 | 118 | enum TestSpec { 119 | Describe(suite : TestSuite, included : Bool); 120 | It(description : String, test : Null, included : Bool, pos : PosInfos); 121 | } 122 | 123 | class TestSuite 124 | { 125 | public var description(default, null) : String; 126 | 127 | public var beforeAll = new List(); 128 | public var beforeEach = new List(); 129 | 130 | public var specs = new List(); 131 | 132 | public var afterEach = new List(); 133 | public var afterAll = new List(); 134 | 135 | public function new(description : String) { 136 | if(description == null) throw "TestSuite must have a description. Can be empty."; 137 | this.description = description; 138 | } 139 | } 140 | 141 | @:autoBuild(buddy.internal.SuiteBuilder.build()) 142 | class BuddySuite 143 | { 144 | /** 145 | * If true, the default Log.trace will be used. Used for debugging. 146 | */ 147 | public static var useDefaultTrace : Bool = false; 148 | 149 | ///// Internal vars ///// 150 | 151 | /** 152 | * Top-level test suite, used in reporting. 153 | */ 154 | @:allow(buddy.SuitesRunner) public var suite(default, null) : TestSuite; 155 | 156 | // For building the test suite structure. 157 | @:noCompletion @:allow(buddy.SuitesRunner) var currentSuite(default, default) : TestSuite; 158 | // Note: Cannot be List, problem with PHP. 159 | @:noCompletion @:allow(buddy.SuitesRunner) var describeQueue(default, null) : Array<{ suite: TestSuite, spec: TestFunc }>; 160 | 161 | ///// Buddy API ///// 162 | 163 | public function new() { 164 | suite = currentSuite = new TestSuite(""); 165 | describeQueue = new Array<{suite: TestSuite, spec: TestFunc}>(); 166 | } 167 | 168 | /** 169 | * Milliseconds before a spec timeout with an error. Default is 5000 (5 sec). 170 | */ 171 | public var timeoutMs(default, null) = 5000; 172 | 173 | /** 174 | * Defines a test Suite, containing Specs and other Suites. 175 | * @param description Name that will be reported 176 | * @param spec A block or function of additional defines 177 | * @param hasInclude Only used internally 178 | */ 179 | private function describe(description : String, spec : TestFunc, _hasInclude = false) : Void { 180 | var suite = new TestSuite(description); 181 | 182 | currentSuite.specs.add(TestSpec.Describe(suite, _hasInclude)); 183 | // Will be looped through in SuitesRunner: 184 | describeQueue.push( { suite: suite, spec: spec } ); 185 | } 186 | 187 | /** 188 | * Defines a test Suite, but will not include it in any test execution. 189 | * @param description Name that will be reported 190 | * @param spec A block or function of additional defines 191 | * @param hasInclude Only used internally 192 | */ 193 | private function xdescribe(description : String, spec : TestFunc, _hasInclude = false) : Void { 194 | // Do nothing, suite is excluded. 195 | } 196 | 197 | /** 198 | * Deprecated, use beforeEach instead. 199 | */ 200 | @:deprecated("Use beforeEach instead.") 201 | private function before(init : TestFunc) : Void beforeEach(init); 202 | 203 | /** 204 | * Deprecated, use afterEach instead. 205 | */ 206 | @:deprecated("Use afterEach instead.") 207 | private function after(init : TestFunc) : Void afterEach(init); 208 | 209 | /** 210 | * Defines a function that will be run before each underlying Spec. 211 | */ 212 | private function beforeEach(init : TestFunc) : Void currentSuite.beforeEach.add(init); 213 | 214 | /** 215 | * Defines a function that will be run once at the beginning of the current Suite. 216 | */ 217 | private function beforeAll(init : TestFunc) : Void currentSuite.beforeAll.add(init); 218 | 219 | /** 220 | * Defines a function that will be run after each underlying Spec. 221 | */ 222 | private function afterEach(init : TestFunc) : Void currentSuite.afterEach.add(init); 223 | 224 | /** 225 | * Defines a function that will be run once at the end of the current Suite. 226 | */ 227 | private function afterAll(init : TestFunc) : Void currentSuite.afterAll.add(init); 228 | 229 | /** 230 | * Defines a Spec, a test of conditions. Should is used for verifying the test itself. 231 | * @param desc Test description 232 | * @param spec A block or function of tests, or leave out for pending 233 | * @param hasInclude Used internally only 234 | */ 235 | private function it(desc : String, ?spec : TestFunc, _hasInclude = false, ?pos:PosInfos) : Void { 236 | if (currentSuite == suite) throw "Cannot use 'it' outside of a describe block."; 237 | currentSuite.specs.add(TestSpec.It(desc, spec, _hasInclude, pos)); 238 | } 239 | 240 | /** 241 | * Defines a pending Spec. 242 | * @param desc Test description 243 | * @param spec A block or function of tests, or leave out 244 | * @param hasInclude Used internally only 245 | */ 246 | private function xit(desc : String, ?spec : TestFunc, _hasInclude = false, ?pos:PosInfos) : Void { 247 | if (currentSuite == suite) throw "Cannot use 'it' outside of a describe block."; 248 | currentSuite.specs.add(TestSpec.It(desc, null, _hasInclude, pos)); 249 | } 250 | 251 | /** 252 | * Fails the current Spec, with an optional error message. 253 | */ 254 | @:allow(buddy.SuitesRunner) private var fail : ?Dynamic -> ?PosInfos -> Void; 255 | 256 | /** 257 | * Makes the current Spec pending, with an optional message (currently does nothing). 258 | */ 259 | @:allow(buddy.SuitesRunner) private var pending : ?String -> ?PosInfos -> Void; 260 | } 261 | -------------------------------------------------------------------------------- /src/buddy/internal/SuiteBuilder.hx: -------------------------------------------------------------------------------- 1 | package buddy.internal; 2 | #if macro 3 | import haxe.macro.Compiler; 4 | import haxe.macro.Expr; 5 | import haxe.macro.ExprTools; 6 | import haxe.macro.Type; 7 | import haxe.macro.Context; 8 | 9 | using haxe.macro.ExprTools; 10 | 11 | class SuiteBuilder 12 | { 13 | private static var buddySuiteClass : ClassType; 14 | 15 | private static function setIncludeMode() { 16 | if (!buddySuiteClass.meta.has("includeMode")) 17 | buddySuiteClass.meta.add("includeMode", [], Context.currentPos()); 18 | } 19 | 20 | private static var sync = macro buddy.BuddySuite.TestFunc.Sync; 21 | private static var async = macro buddy.BuddySuite.TestFunc.Async; 22 | 23 | private static function injectAsync(e : Expr) 24 | { 25 | switch(e.expr) { 26 | // Fix autocomplete for should without parenthesis 27 | case EDisplay(e2, isCall): switch e2 { 28 | case macro $a.should: 29 | var change = macro $a.should(); 30 | e2.expr = change.expr; 31 | 32 | case _: 33 | } 34 | 35 | // Fix fail calls, so PosInfos works. 36 | // Skip calls to fail, they will work 37 | case ECall({expr: EConst(CIdent("fail")), pos: _}, params): 38 | return; 39 | 40 | // Callbacks however, have to be wrapped. 41 | case EConst(CIdent("fail")): 42 | var change = macro function(?err : Dynamic) fail(err); 43 | e.expr = change.expr; 44 | return; 45 | 46 | case _: 47 | } 48 | 49 | switch(e) { 50 | 51 | ///// Should 52 | 53 | case macro $a.should().$b, macro $a.should.$b: 54 | var change = macro $a.should().$b; 55 | e.expr = change.expr; 56 | 57 | ///// Include 58 | 59 | case macro @include describe($s, $f): 60 | setIncludeMode(); 61 | var change = macro describe($s, $f, true); 62 | e.expr = change.expr; 63 | injectAsync(e); 64 | 65 | case macro @include it($s): 66 | setIncludeMode(); 67 | var change = macro it($s, null, true); 68 | e.expr = change.expr; 69 | injectAsync(e); 70 | 71 | case macro @include it($s, $f): 72 | setIncludeMode(); 73 | var change = macro it($s, $f, true); 74 | e.expr = change.expr; 75 | injectAsync(e); 76 | 77 | ///// Exclude 78 | 79 | case macro @exclude describe($s, $f): 80 | var change = macro xdescribe($s, $f); 81 | e.expr = change.expr; 82 | injectAsync(e); 83 | 84 | case macro @exclude it($s): 85 | var change = macro xit($s); 86 | e.expr = change.expr; 87 | injectAsync(e); 88 | 89 | case macro @exclude it($s, $f): 90 | var change = macro xit($s, $f); 91 | e.expr = change.expr; 92 | injectAsync(e); 93 | 94 | ///// Describe 95 | 96 | case macro describe($s, $f): 97 | switch getFunction(f) { 98 | case null: 99 | var change = macro describe($s, function() $f); 100 | e.expr = change.expr; 101 | injectAsync(e); 102 | case fun if(fun.args.length == 0): 103 | var change = macro describe($s, $sync($f)); 104 | e.expr = change.expr; 105 | f.iter(injectAsync); 106 | default: 107 | var change = macro describe($s, $async($f)); 108 | e.expr = change.expr; 109 | f.iter(injectAsync); 110 | } 111 | 112 | case macro describe($s, function() $f, $i): 113 | var change = macro describe($s, $sync(function() $f), $i); 114 | e.expr = change.expr; 115 | f.iter(injectAsync); 116 | 117 | case macro describe($s, function($n) $f, $i): 118 | var change = macro describe($s, $async(function($n) $f), $i); 119 | e.expr = change.expr; 120 | f.iter(injectAsync); 121 | 122 | case macro describe($s, $f, $i): 123 | var change = macro describe($s, function() $f, $i); 124 | e.expr = change.expr; 125 | injectAsync(e); 126 | 127 | ///// Describe 128 | 129 | case macro xdescribe($s, function() $f): 130 | var change = macro xdescribe($s, $sync(function() $f)); 131 | e.expr = change.expr; 132 | f.iter(injectAsync); 133 | 134 | case macro xdescribe($s, function($n) $f): 135 | var change = macro xdescribe($s, $async(function($n) $f)); 136 | e.expr = change.expr; 137 | f.iter(injectAsync); 138 | 139 | case macro xdescribe($s, function() $f, $i): 140 | var change = macro xdescribe($s, $sync(function() $f), $i); 141 | e.expr = change.expr; 142 | f.iter(injectAsync); 143 | 144 | case macro xdescribe($s, function($n) $f, $i): 145 | var change = macro xdescribe($s, $async(function($n) $f), $i); 146 | e.expr = change.expr; 147 | f.iter(injectAsync); 148 | 149 | case macro xdescribe($s, $f): 150 | var change = macro xdescribe($s, function() $f); 151 | e.expr = change.expr; 152 | injectAsync(e); 153 | 154 | case macro xdescribe($s, $f, $i): 155 | var change = macro xdescribe($s, function() $f, $i); 156 | e.expr = change.expr; 157 | injectAsync(e); 158 | 159 | ///// BeforeEach/AfterEach 160 | 161 | case macro before(function($n) $f): 162 | var change = macro @:pos(e.pos) before($async(function($n) $f)); 163 | e.expr = change.expr; 164 | f.iter(injectAsync); 165 | 166 | case macro before(function() $f), macro before($f): 167 | var change = macro @:pos(e.pos) before($sync(function() $f)); 168 | e.expr = change.expr; 169 | f.iter(injectAsync); 170 | 171 | case macro beforeEach($f): 172 | var change = switch getFunction(f) { 173 | case null: 174 | macro beforeEach($sync(function() $f)); 175 | case fun if(fun.args.length == 0): 176 | macro beforeEach($sync($f)); 177 | default: 178 | macro beforeEach($async($f)); 179 | } 180 | e.expr = change.expr; 181 | f.iter(injectAsync); 182 | 183 | case macro after(function($n) $f): 184 | var change = macro @:pos(e.pos) after($async(function($n) $f)); 185 | e.expr = change.expr; 186 | f.iter(injectAsync); 187 | 188 | case macro afterEach(function($n) $f): 189 | var change = macro afterEach($async(function($n) $f)); 190 | e.expr = change.expr; 191 | f.iter(injectAsync); 192 | 193 | case macro after(function() $f), macro after($f): 194 | var change = macro @:pos(e.pos) after($sync(function() $f)); 195 | e.expr = change.expr; 196 | f.iter(injectAsync); 197 | 198 | case macro afterEach(function() $f), macro afterEach($f): 199 | var change = macro afterEach($sync(function() $f)); 200 | e.expr = change.expr; 201 | f.iter(injectAsync); 202 | 203 | ///// BeforeAll/AfterAll 204 | 205 | case macro beforeAll(function($n) $f): 206 | var change = macro beforeAll($async(function($n) $f)); 207 | e.expr = change.expr; 208 | f.iter(injectAsync); 209 | 210 | case macro beforeAll(function() $f), macro beforeAll($f): 211 | var change = macro beforeAll($sync(function() $f)); 212 | e.expr = change.expr; 213 | f.iter(injectAsync); 214 | 215 | case macro afterAll(function($n) $f): 216 | var change = macro afterAll($async(function($n) $f)); 217 | e.expr = change.expr; 218 | f.iter(injectAsync); 219 | 220 | case macro afterAll(function() $f), macro afterAll($f): 221 | var change = macro afterAll($sync(function() $f)); 222 | e.expr = change.expr; 223 | f.iter(injectAsync); 224 | 225 | ///// It 226 | 227 | case macro it($s): 228 | var change = macro xit($s, null); 229 | e.expr = change.expr; 230 | 231 | case macro it($s, $f): 232 | if(isEmptyBlock(f)) { 233 | var change = macro xit($s, null); 234 | e.expr = change.expr; 235 | } else { 236 | switch getFunction(f) { 237 | case null: 238 | var change = macro it($s, function() $f); 239 | e.expr = change.expr; 240 | injectAsync(e); 241 | case fun if(fun.args.length == 0): 242 | var change = macro it($s, $sync($f)); 243 | e.expr = change.expr; 244 | f.iter(injectAsync); 245 | default: 246 | var change = macro it($s, $async($f)); 247 | e.expr = change.expr; 248 | f.iter(injectAsync); 249 | } 250 | } 251 | 252 | case macro it($s, $f, $i): 253 | switch getFunction(f) { 254 | case null: 255 | var change = macro it($s, function() $f, $i); 256 | e.expr = change.expr; 257 | injectAsync(e); 258 | case fun if(fun.args.length == 0): 259 | var change = macro it($s, $sync($f), $i); 260 | e.expr = change.expr; 261 | f.iter(injectAsync); 262 | default: 263 | var change = macro it($s, $async($f), $i); 264 | e.expr = change.expr; 265 | f.iter(injectAsync); 266 | 267 | } 268 | 269 | ///// Xit 270 | 271 | case macro xit($s), macro xit($s, {}), macro xit($s, function() {}): 272 | var change = macro xit($s, null); 273 | e.expr = change.expr; 274 | 275 | case macro xit($s, function($n) $f): 276 | var change = macro xit($s, $async(function($n) $f)); 277 | e.expr = change.expr; 278 | f.iter(injectAsync); 279 | 280 | case macro xit($s, function() $f): 281 | var change = macro xit($s, $sync(function() $f)); 282 | e.expr = change.expr; 283 | f.iter(injectAsync); 284 | 285 | case macro xit($s, function($n) $f, $i): 286 | var change = macro xit($s, $async(function($n) $f), $i); 287 | e.expr = change.expr; 288 | f.iter(injectAsync); 289 | 290 | case macro xit($s, $f): 291 | var change = macro xit($s, function() $f); 292 | e.expr = change.expr; 293 | injectAsync(e); 294 | 295 | case macro xit($s, $f, $i): 296 | var change = macro xit($s, function() $f, $i); 297 | e.expr = change.expr; 298 | injectAsync(e); 299 | 300 | ///// 301 | 302 | case _: e.iter(injectAsync); 303 | } 304 | } 305 | 306 | static function getFunction(e:Expr) 307 | return switch e.expr { 308 | case EFunction(_, f): f; 309 | case EMeta(_, e): getFunction(e); 310 | default: null; 311 | } 312 | 313 | static function isEmptyBlock(e:Expr) 314 | return switch e.expr { 315 | case EBlock(a) if(a.length == 0): true; 316 | case EMeta(_, e): isEmptyBlock(e); 317 | default: false; 318 | } 319 | 320 | macro public static function build() : Array 321 | { 322 | var exists = false; 323 | var cls = Context.getLocalClass(); 324 | if (cls == null || cls.get().superClass == null) return null; 325 | 326 | buddySuiteClass = cls.get().superClass.t.get(); 327 | if (cls.get().meta.has("include")) setIncludeMode(); 328 | 329 | var fields = Context.getBuildFields(); 330 | for (f in fields) if(f.name == "new") { 331 | switch f.kind { 332 | case FFun(f): 333 | switch(f.expr.expr) { 334 | case EBlock(exprs): 335 | for (e in exprs) switch e { 336 | case macro super(): 337 | exists = true; 338 | break; 339 | case _: 340 | } 341 | 342 | if(!exists) exprs.unshift(macro super()); 343 | 344 | case _: 345 | } 346 | f.expr.iter(injectAsync); 347 | 348 | case _: 349 | } 350 | } 351 | 352 | return fields; 353 | } 354 | } 355 | #end 356 | -------------------------------------------------------------------------------- /src/buddy/SuitesRunner.hx: -------------------------------------------------------------------------------- 1 | package buddy; 2 | import buddy.internal.GenerateMain; 3 | import buddy.reporting.Reporter; 4 | import haxe.CallStack; 5 | import haxe.CallStack.StackItem; 6 | import haxe.Constraints.Function; 7 | import haxe.Log; 8 | import haxe.PosInfos; 9 | import haxe.rtti.Meta; 10 | import promhx.Deferred; 11 | import promhx.Promise; 12 | import buddy.BuddySuite; 13 | import buddy.tools.AsyncTools in BuddyAsync; 14 | 15 | #if utest 16 | import utest.Assert; 17 | import utest.Assertation; 18 | #end 19 | 20 | using Lambda; 21 | using AsyncTools; 22 | 23 | #if python 24 | @:pythonImport("sys") 25 | extern class PythonSys { 26 | public static function setrecursionlimit(i : Int) : Void; 27 | } 28 | #end 29 | 30 | private typedef Tests = { 31 | buddySuite: BuddySuite, 32 | testSuite: TestSuite, 33 | run: T 34 | } 35 | 36 | private typedef SyncTestResult = { 37 | error : Dynamic, 38 | step : Step 39 | } 40 | 41 | private typedef SyncSuiteResult = { 42 | error : Dynamic, 43 | suite : Suite 44 | } 45 | 46 | @:keep // Prevent dead code elimination, since SuitesRunner is created dynamically 47 | class SuitesRunner 48 | { 49 | // Used in Should 50 | public static var currentTest : Should.SpecAssertion; 51 | 52 | public var unrecoverableError : Dynamic = null; 53 | public var unrecoverableErrorStack : Array = null; 54 | 55 | private var allTestsPassed : Bool = false; 56 | private var buddySuites : Iterable; 57 | private var reporter : Reporter; 58 | private var runCompleted : Deferred; 59 | private var includeMode : Bool; 60 | 61 | private var oldLog : Dynamic -> ?PosInfos -> Void; 62 | 63 | /////////////////////////////////////////////////////////////////////// 64 | 65 | public static function posInfosToStack(p : Null) : Array { 66 | return p == null 67 | ? [StackItem.FilePos(null, "", 0)] 68 | : [StackItem.FilePos(null, p.fileName, p.lineNumber)]; 69 | } 70 | 71 | public function new(buddySuites : Iterable, ?reporter : Reporter) { 72 | this.buddySuites = buddySuites; 73 | this.reporter = reporter == null ? new buddy.reporting.ConsoleReporter() : reporter; 74 | this.oldLog = Log.trace; 75 | this.includeMode = Reflect.hasField(Meta.getType(BuddySuite), "includeMode"); 76 | } 77 | 78 | public function run() : Promise { 79 | #if python 80 | PythonSys.setrecursionlimit(100000); 81 | #end 82 | 83 | runCompleted = new Deferred(); 84 | var runCompletedPromise = runCompleted.promise(); 85 | 86 | runDescribes(function(err) { 87 | if (err != null) { 88 | haveUnrecoverableError(err); 89 | return; 90 | } 91 | 92 | if (includeMode) startIncludeMode(); 93 | startRun(); 94 | }); 95 | 96 | return runCompletedPromise; 97 | } 98 | 99 | private function runDescribes(cb : Dynamic -> Void) : Void { 100 | var asyncQueue = new Array Void) -> Void>>(); 101 | var syncQueue = new Array Void>>(); 102 | 103 | function processSuiteDescribes(suite : BuddySuite) { 104 | while (!suite.describeQueue.empty()) { 105 | var current = suite.describeQueue.pop(); 106 | 107 | switch current.spec { 108 | case Async(f): asyncQueue.push({ 109 | buddySuite: suite, 110 | testSuite: current.suite, 111 | run: f 112 | }); 113 | 114 | case Sync(f): syncQueue.push({ 115 | buddySuite: suite, 116 | testSuite: current.suite, 117 | run: f 118 | }); 119 | } 120 | } 121 | } 122 | 123 | function processBuddySuites() : Void { 124 | // Process the queue of describe calls 125 | for (buddySuite in buddySuites) processSuiteDescribes(buddySuite); 126 | 127 | if(syncQueue.length > 0) { 128 | try for (test in syncQueue) { 129 | test.buddySuite.currentSuite = test.testSuite; 130 | test.run(); 131 | } catch (err : Dynamic) { 132 | return cb(err); 133 | } 134 | 135 | syncQueue = []; 136 | processBuddySuites(); 137 | } else if(asyncQueue.length > 0) { 138 | AsyncTools.aEachSeries(asyncQueue, function(test : Tests<(Void -> Void) -> Void>, cb : Dynamic -> Void) { 139 | test.buddySuite.currentSuite = test.testSuite; 140 | test.run(function() cb(null)); 141 | }, function(err) { 142 | if (err != null) return cb(err); 143 | asyncQueue = []; 144 | processBuddySuites(); 145 | }); 146 | } else 147 | cb(null); 148 | } 149 | 150 | processBuddySuites(); 151 | } 152 | 153 | public function failed() return !allTestsPassed; 154 | public function statusCode() return failed() ? 1 : 0; 155 | 156 | ///////////////////////////////////////////////////////////////////////////// 157 | 158 | private function startRun() : Void { 159 | reporter.start().then(function(go) { 160 | if (!go) { 161 | reporter.done([], false).then(function(_) runCompleted.resolve(this)); 162 | return; 163 | } 164 | 165 | var beforeEachStack = [[]]; 166 | var afterEachStack = [[]]; 167 | 168 | AsyncTools.aMapSeries(buddySuites, function(buddySuite, done) { 169 | function suiteDone(err : Dynamic, suite : Suite) { 170 | if (err == null && suite == null) return; 171 | // Errors outside it() 172 | if (err != null) { 173 | suite.error = err; 174 | suite.stack = CallStack.exceptionStack(); 175 | } 176 | done(err, suite); 177 | } 178 | 179 | var syncSuite = mapTestSuite( 180 | buddySuite, 181 | buddySuite.suite, 182 | beforeEachStack, 183 | afterEachStack, 184 | suiteDone 185 | ); 186 | if (syncSuite != null) { 187 | suiteDone(syncSuite.error, syncSuite.suite); 188 | } 189 | 190 | }, function(err, suites) { 191 | if (err != null) haveUnrecoverableError(err); 192 | else { 193 | allTestsPassed = !suites.exists(function(suite) return !suite.passed()); 194 | reporter.done(suites, allTestsPassed).then(function(_) runCompleted.resolve(this)); 195 | } 196 | }); 197 | }); 198 | } 199 | 200 | private function startIncludeMode() { 201 | // Filter out all tests not marked with @include 202 | function traverse(suite : TestSuite) : Bool { 203 | suite.specs = suite.specs.filter(function(spec) { 204 | switch spec { 205 | case Describe(suite, included): 206 | if (included) return true; 207 | else return traverse(suite); 208 | case It(desc, _, included, _): 209 | return included; 210 | } 211 | }); 212 | return suite.specs.length > 0; 213 | } 214 | 215 | buddySuites = buddySuites.filter(function(buddySuite) { 216 | var suiteMeta = Meta.getType(Type.getClass(buddySuite)); 217 | if (Reflect.hasField(suiteMeta, "include")) return true; 218 | 219 | return traverse(buddySuite.suite); 220 | }); 221 | } 222 | 223 | private function mapTestSuite( 224 | buddySuite : BuddySuite, 225 | testSuite : TestSuite, 226 | beforeEachStack : Array>, 227 | afterEachStack : Array>, 228 | done : Dynamic -> Suite -> Void 229 | ) : Null { 230 | var currentSuite = buddy.tests.SelfTest.lastSuite = new Suite(testSuite.description); 231 | 232 | beforeEachStack.push(testSuite.beforeEach.array()); 233 | afterEachStack.unshift(testSuite.afterEach.array()); 234 | 235 | var allSync = isSync(testSuite.beforeAll) && isSync(testSuite.afterAll); 236 | var result : SyncSuiteResult = null; 237 | var syncResultCount = 0; 238 | 239 | // === Run beforeAll 240 | runTestFuncs(testSuite.beforeAll, function(err) { 241 | if (err != null) { 242 | if (isSync(testSuite.beforeAll)) result = { error: err, suite: currentSuite }; 243 | else done(err, currentSuite); 244 | return; 245 | } 246 | 247 | // === Map TestSpec -> Step 248 | AsyncTools.aMapSeries(testSuite.specs, function(testSpec : TestSpec, cb : Dynamic -> Step -> Void) { 249 | var result2 = mapTestSpec(buddySuite, testSuite, beforeEachStack, afterEachStack, testSpec, cb); 250 | if (result2 != null) { 251 | syncResultCount++; 252 | cb(result2.error, result2.step); 253 | } 254 | }, function(err : Dynamic, testSteps : Array) { 255 | // It's important to return currentSuite as well in this function, not null. 256 | 257 | allSync = allSync && testSteps.length == syncResultCount; 258 | 259 | if (err != null) { 260 | if (allSync) result = { error: err, suite: currentSuite }; 261 | else done(err, currentSuite); 262 | return; 263 | } 264 | 265 | // === Run afterAll 266 | runTestFuncs(testSuite.afterAll, function(err) { 267 | if (err != null) { 268 | if (allSync) result = { error: err, suite: currentSuite }; 269 | else done(err, currentSuite); 270 | return; 271 | } 272 | 273 | currentSuite.steps = testSteps; 274 | beforeEachStack.pop(); 275 | afterEachStack.shift(); 276 | 277 | if (allSync) result = { error: null, suite: currentSuite }; 278 | else done(null, currentSuite); 279 | }); 280 | }); 281 | }); 282 | 283 | if (result != null) done(null, null); 284 | return result; 285 | } 286 | 287 | private function runTestFuncs(funcs : Iterable, done : Dynamic -> Void) { 288 | var syncQ = []; 289 | var asyncQ = []; 290 | 291 | for(func in funcs) switch func { 292 | case Async(f): asyncQ.push(f); 293 | case Sync(f): syncQ.push(f); 294 | } 295 | 296 | try for (f in syncQ) f() 297 | catch (err : Dynamic) return done(err); 298 | 299 | AsyncTools.aEachSeries(asyncQ, function(f, done) { 300 | f(function() done()); 301 | }, done); 302 | } 303 | 304 | private function flatten(arr : Array>) : Array { 305 | return [for(a in arr) for(b in a) b]; 306 | } 307 | 308 | private function isSync(funcs : Iterable) : Bool { 309 | for (f in funcs) switch f { 310 | case Async(_): return false; 311 | default: 312 | } 313 | return true; 314 | } 315 | 316 | private function mapTestSpec( 317 | buddySuite : BuddySuite, 318 | testSuite : TestSuite, 319 | beforeEachStack : Array>, 320 | afterEachStack : Array>, 321 | testSpec : TestSpec, 322 | done : Dynamic -> Step -> Void 323 | ) 324 | : Null 325 | { 326 | var hasCompleted = false; 327 | var oldFail : ?Dynamic -> ?PosInfos -> Void = null; 328 | 329 | oldFail = buddySuite.fail = function(err : Dynamic = "Exception", ?p : PosInfos) { 330 | // Test if it still references the same suite. 331 | if (!hasCompleted && oldFail == buddySuite.fail) { 332 | done(err, null); 333 | } 334 | } 335 | var oldPending = buddySuite.pending = function(?message : String, ?p : PosInfos) { 336 | done("Cannot call pending here.", null); 337 | } 338 | 339 | switch testSpec { 340 | case Describe(testSuite, _): 341 | // === Map TestSuite -> Suite 342 | var result = mapTestSuite(buddySuite, testSuite, beforeEachStack, afterEachStack, function(err : Dynamic, newSuite : Suite) { 343 | if (err == null && newSuite == null) return; 344 | if (err != null) done(err, null); 345 | else done(null, TSuite(newSuite)); 346 | }); 347 | if (result != null) return { error: result.error, step: TSuite(result.suite) }; 348 | else return null; 349 | 350 | case It(desc, test, _, pos): 351 | // Assign top-level spec var here, so it can be used in reporting. 352 | //trace("Starting it: " + desc); 353 | var spec = buddy.tests.SelfTest.lastSpec = new Spec(desc, pos.fileName); 354 | 355 | var beforeEach = flatten(beforeEachStack); 356 | var afterEach = flatten(afterEachStack); 357 | 358 | var eachIsSync = isSync(beforeEach) && isSync(afterEach); 359 | 360 | var returnSync = if(test == null) eachIsSync else switch test { 361 | case Sync(_): eachIsSync; 362 | case Async(_): false; 363 | } 364 | 365 | // Log traces for each Spec, so they can be outputted in the reporter 366 | if(!BuddySuite.useDefaultTrace) Log.trace = function(v, ?pos : PosInfos) { 367 | if(pos == null) spec.traces.push(Std.string(v)); 368 | else spec.traces.push(pos.fileName + ":" + pos.lineNumber + ": " + v); 369 | }; 370 | 371 | function reportFailure(error : Dynamic, stack : Array) : Void { 372 | if (hasCompleted) return; 373 | spec.status = Failed; 374 | spec.failures.push(new Failure(error, stack)); 375 | } 376 | 377 | function specCompleted(status : SpecStatus) : Null { 378 | if (hasCompleted) return null; 379 | hasCompleted = true; 380 | 381 | if(spec.status == Unknown) spec.status = status; 382 | 383 | // Restore Log and set Suites fail function to null 384 | if(!BuddySuite.useDefaultTrace) Log.trace = oldLog; 385 | buddySuite.fail = oldFail; 386 | buddySuite.pending = oldPending; 387 | 388 | var syncResult = null; 389 | 390 | // === Run afterEach 391 | runTestFuncs(afterEach, function(err : Dynamic) { 392 | if (returnSync) { 393 | syncResult = {error: err, step: err == null ? TSpec(spec) : null}; 394 | reporter.progress(spec); 395 | } else { 396 | if (err != null) done(err, null); 397 | else reporter.progress(spec).then(function(_) { 398 | done(null, TSpec(spec)); 399 | }); 400 | } 401 | }); 402 | 403 | return syncResult; 404 | } 405 | 406 | // Test if spec is Pending (has only description) 407 | if (test == null) { 408 | return specCompleted(Pending); 409 | } 410 | 411 | // Create a test function that will be used in Should 412 | // note that multiple successfull tests doesn't mean the Spec is completed. 413 | SuitesRunner.currentTest = function(testStatus : Bool, error : Dynamic, stack : Array) { 414 | if (testStatus != true) reportFailure(error, stack); 415 | } 416 | 417 | // Set up utest if available 418 | #if utest 419 | Assert.results = new List(); 420 | 421 | function checkUtestResults() { 422 | for (a in Assert.results) switch a { 423 | case Success(_): 424 | case Warning(msg): 425 | spec.traces.push(msg); 426 | case Failure(e, pos): 427 | reportFailure(e, posInfosToStack(pos)); 428 | case Error(e, stack), SetupError(e, stack), TeardownError(e, stack), AsyncError(e, stack): 429 | reportFailure(e, stack); 430 | case TimeoutError(e, stack): 431 | reportFailure(e, stack); 432 | } 433 | } 434 | #end 435 | 436 | #if (!php && !macro) 437 | // Set up timeout for the current spec 438 | if(!returnSync && buddySuite.timeoutMs > 0) { 439 | BuddyAsync.wait(buddySuite.timeoutMs) 440 | .catchError(function(e : Dynamic) { 441 | reportFailure(e, CallStack.exceptionStack()); 442 | specCompleted(Failed); 443 | }) 444 | .then(function(_) { 445 | reportFailure('Timeout after ${buddySuite.timeoutMs} ms', []); 446 | specCompleted(Failed); 447 | }); 448 | } 449 | #end 450 | 451 | //////////////////////////////////////////////////////////////////////////// 452 | 453 | var _syncResult : SyncTestResult = null; 454 | 455 | function setSyncResult(status) { 456 | if (!returnSync || _syncResult != null) return; 457 | _syncResult = status; 458 | } 459 | 460 | // Set up fail and pending function 461 | buddySuite.fail = function(err : Dynamic = "Manually", ?p : PosInfos) { 462 | reportFailure(err, posInfosToStack(p)); 463 | setSyncResult(specCompleted(Failed)); 464 | } 465 | 466 | buddySuite.pending = function(?message : String, ?p : PosInfos) { 467 | var msg = p.fileName + ":" + p.lineNumber + (message != null ? ': $message' : ''); 468 | spec.traces.push(msg); 469 | setSyncResult(specCompleted(Pending)); 470 | } 471 | 472 | // === Run beforeEach 473 | runTestFuncs(beforeEach, function(err) { 474 | if (err != null) { 475 | if(returnSync) setSyncResult({ error: err, step: null }); 476 | else done(err, null); 477 | return; 478 | } 479 | 480 | function runTestFunc(func : TestFunc, done : Dynamic -> Void) { 481 | try switch func { 482 | case Async(f): f(function() done(null)); 483 | case Sync(f): f(); done(null); 484 | } catch (e : Dynamic) { 485 | done(e); 486 | } 487 | } 488 | 489 | runTestFunc(test, function(err) { 490 | #if utest 491 | checkUtestResults(); 492 | #end 493 | if (err != null) { 494 | reportFailure(err, CallStack.exceptionStack()); 495 | setSyncResult(specCompleted(Failed)); 496 | } 497 | else 498 | setSyncResult(specCompleted(Passed)); 499 | }); 500 | }); 501 | 502 | //trace(_syncResult); 503 | return _syncResult; 504 | } 505 | } 506 | 507 | public function haveUnrecoverableError(err) { 508 | unrecoverableError = err; 509 | unrecoverableErrorStack = CallStack.exceptionStack(); 510 | runCompleted.resolve(this); 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /src/buddy/Should.hx: -------------------------------------------------------------------------------- 1 | package buddy; 2 | import buddy.Should.ShouldIterable; 3 | import haxe.PosInfos; 4 | import haxe.CallStack; 5 | import haxe.Int64; 6 | #if python 7 | import python.internal.UBuiltins; 8 | import python.lib.Builtins; 9 | #end 10 | 11 | using Lambda; 12 | using StringTools; 13 | 14 | /** 15 | * A function that specifies the status for a spec with an optional error message and stack. 16 | */ 17 | typedef SpecAssertion = Bool -> String -> Array -> Void; 18 | 19 | /** 20 | * This must be the first class in this package, since it overrides all other assertions otherwise. 21 | */ 22 | class ShouldDynamic extends Should 23 | { 24 | static public function should(d : Dynamic) 25 | { 26 | return new ShouldDynamic(d); 27 | } 28 | 29 | public var not(get, never) : ShouldDynamic; 30 | private function get_not() { return new ShouldDynamic(value, !inverse); } 31 | } 32 | 33 | class ShouldEnum extends Should 34 | { 35 | static public function should(e : EnumValue) 36 | { 37 | return new ShouldEnum(e); 38 | } 39 | 40 | public function new(value : EnumValue, inverse = false) 41 | { 42 | super(value, inverse); 43 | } 44 | 45 | public var not(get, never) : ShouldEnum; 46 | private function get_not() { return new ShouldEnum(value, !inverse); } 47 | 48 | ////////// 49 | 50 | @:deprecated("Use should.equal instead for enum comparisons") 51 | override public function be(expected : EnumValue, ?p : PosInfos) : Void { 52 | equal(expected, p); 53 | } 54 | 55 | public function equal(expected : EnumValue, ?p : PosInfos) 56 | { 57 | test(Type.enumEq(value, expected), p, 58 | 'Expected ${quote(expected)}, was ${quote(value)}', 59 | 'Didn\'t expect ${quote(value)} but was equal to that' 60 | ); 61 | } 62 | } 63 | 64 | class ShouldInt extends Should 65 | { 66 | static public function should(i : Int) 67 | { 68 | return new ShouldInt(i); 69 | } 70 | 71 | public function new(value : Int, inverse = false) 72 | { 73 | super(value, inverse); 74 | } 75 | 76 | public var not(get, never) : ShouldInt; 77 | private function get_not() { return new ShouldInt(value, !inverse); } 78 | 79 | ////////// 80 | 81 | public function beLessThan(expected : Int, ?p : PosInfos) 82 | { 83 | test(value < expected, p, 84 | 'Expected less than ${quote(expected)}, was ${quote(value)}', 85 | 'Expected not less than ${quote(expected)}, was ${quote(value)}' 86 | ); 87 | } 88 | 89 | public function beGreaterThan(expected : Int, ?p : PosInfos) 90 | { 91 | test(value > expected, p, 92 | 'Expected greater than ${quote(expected)}, was ${quote(value)}', 93 | 'Expected not greater than ${quote(expected)}, was ${quote(value)}' 94 | ); 95 | } 96 | } 97 | 98 | class ShouldInt64 extends Should 99 | { 100 | static public function should(i : Int64) 101 | { 102 | return new ShouldInt64(i); 103 | } 104 | 105 | public function new(value : Int64, inverse = false) 106 | { 107 | super(value, inverse); 108 | } 109 | 110 | public var not(get, never) : ShouldInt64; 111 | private function get_not() { return new ShouldInt64(value, !inverse); } 112 | 113 | ////////// 114 | 115 | public override function be(expected : Int64, ?p : PosInfos) : Void 116 | { 117 | var result = Int64.compare(expected, value) == 0; 118 | test(result, p, 119 | 'Expected ${quote(expected)}, was ${quote(value)}', 120 | 'Didn\'t expect ${quote(expected)} but was equal to that' 121 | ); 122 | } 123 | 124 | public function beLessThan(expected : Int64, ?p : PosInfos) 125 | { 126 | test(value < expected, p, 127 | 'Expected less than ${quote(expected)}, was ${quote(value)}', 128 | 'Expected not less than ${quote(expected)}, was ${quote(value)}' 129 | ); 130 | } 131 | 132 | public function beGreaterThan(expected : Int64, ?p : PosInfos) 133 | { 134 | test(value > expected, p, 135 | 'Expected greater than ${quote(expected)}, was ${quote(value)}', 136 | 'Expected not greater than ${quote(expected)}, was ${quote(value)}' 137 | ); 138 | } 139 | } 140 | 141 | class ShouldFloat extends Should 142 | { 143 | static public function should(i : Float) 144 | { 145 | return new ShouldFloat(i); 146 | } 147 | 148 | public function new(value : Float, inverse = false) 149 | { 150 | super(value, inverse); 151 | } 152 | 153 | public var not(get, never) : ShouldFloat; 154 | private function get_not() { return new ShouldFloat(value, !inverse); } 155 | 156 | ////////// 157 | 158 | public function beLessThan(expected : Float, ?p : PosInfos) 159 | { 160 | test(value < expected, p, 161 | 'Expected less than ${quote(expected)}, was ${quote(value)}', 162 | 'Expected not less than ${quote(expected)}, was ${quote(value)}' 163 | ); 164 | } 165 | 166 | public function beGreaterThan(expected : Float, ?p : PosInfos) 167 | { 168 | test(value > expected, p, 169 | 'Expected greater than ${quote(expected)}, was ${quote(value)}', 170 | 'Expected not greater than ${quote(expected)}, was ${quote(value)}' 171 | ); 172 | } 173 | 174 | public function beCloseTo(expected : Float, precision : Null = 2, ?p : PosInfos) 175 | { 176 | // For some reason, precision must be of a Nullable type in flash or it will be 0 sometimes?! 177 | var expr = Math.abs(expected - value) < (Math.pow(10, -precision) / 2); 178 | 179 | test(expr, p, 180 | 'Expected close to ${quote(expected)}, was ${quote(value)}', 181 | 'Expected ${quote(value)} not to be close to ${quote(expected)}' 182 | ); 183 | } 184 | } 185 | 186 | class ShouldDate extends Should 187 | { 188 | static public function should(i : Date) 189 | { 190 | return new ShouldDate(i); 191 | } 192 | 193 | public function new(value : Date, inverse = false) 194 | { 195 | super(value, inverse); 196 | } 197 | 198 | public var not(get, never) : ShouldDate; 199 | private function get_not() { return new ShouldDate(value, !inverse); } 200 | 201 | ////////// 202 | 203 | public function beOn(expected : Date, ?p : PosInfos) 204 | { 205 | test(value.getTime() == expected.getTime(), p, 206 | 'Expected date equal to ${quote(expected)}, was ${quote(value)}', 207 | 'Expected date not equal to ${quote(expected)}' 208 | ); 209 | } 210 | 211 | public function beBefore(expected : Date, ?p : PosInfos) 212 | { 213 | test(value.getTime() < expected.getTime(), p, 214 | 'Expected date before ${quote(expected)}, was ${quote(value)}', 215 | 'Expected date not before ${quote(expected)}, was ${quote(value)}' 216 | ); 217 | } 218 | 219 | public function beAfter(expected : Date, ?p : PosInfos) 220 | { 221 | test(value.getTime() > expected.getTime(), p, 222 | 'Expected date after ${quote(expected)}, was ${quote(value)}', 223 | 'Expected date not after ${quote(expected)}, was ${quote(value)}' 224 | ); 225 | } 226 | 227 | public function beOnStr(expected : String, ?p : PosInfos) 228 | return beOn(Date.fromString(expected), p); 229 | 230 | public function beBeforeStr(expected : String, ?p : PosInfos) 231 | return beBefore(Date.fromString(expected), p); 232 | 233 | public function beAfterStr(expected : String, ?p : PosInfos) 234 | return beAfter(Date.fromString(expected), p); 235 | } 236 | 237 | class ShouldString extends Should 238 | { 239 | static public function should(str : String) 240 | { 241 | return new ShouldString(str); 242 | } 243 | 244 | public function new(value : String, inverse = false) 245 | { 246 | super(value, inverse); 247 | } 248 | 249 | public var not(get, never) : ShouldString; 250 | private function get_not() { return new ShouldString(value, !inverse); } 251 | 252 | ////////// 253 | 254 | public function contain(substring : String, ?p : PosInfos) 255 | { 256 | if (value == null) return fail( 257 | 'Expected string to contain ${quote(substring)} but string was null', 258 | 'Expected string not to contain ${quote(substring)} but string was null', 259 | p); 260 | 261 | test(value.indexOf(substring) >= 0, p, 262 | 'Expected ${quote(value)} to contain ${quote(substring)}', 263 | 'Expected ${quote(value)} not to contain ${quote(substring)}' 264 | ); 265 | } 266 | 267 | public function startWith(substring : String, ?p : PosInfos) 268 | { 269 | if (value == null) return fail( 270 | 'Expected string to start with ${quote(substring)} but string was null', 271 | 'Expected string not to start with ${quote(substring)} but string was null', 272 | p); 273 | 274 | test(value.startsWith(substring), p, 275 | 'Expected ${quote(value)} to start with ${quote(substring)}', 276 | 'Expected ${quote(value)} not to start with ${quote(substring)}' 277 | ); 278 | } 279 | 280 | public function endWith(substring : String, ?p : PosInfos) 281 | { 282 | if (value == null) return fail( 283 | 'Expected string to end with ${quote(substring)} but string was null', 284 | 'Expected string not to end with ${quote(substring)} but string was null', 285 | p); 286 | 287 | test(value.endsWith(substring), p, 288 | 'Expected ${quote(value)} to end with ${quote(substring)}', 289 | 'Expected ${quote(value)} not to end with ${quote(substring)}' 290 | ); 291 | } 292 | 293 | public function match(regexp : EReg, ?p : PosInfos) 294 | { 295 | if (value == null) return fail( 296 | 'Expected string to match regular expression but string was null', 297 | 'Expected string not to match regular expression but string was null', 298 | p); 299 | 300 | test(regexp.match(value), p, 301 | 'Expected ${quote(value)} to match regular expression', 302 | 'Expected ${quote(value)} not to match regular expression' 303 | ); 304 | } 305 | } 306 | 307 | class ShouldIterable extends Should> 308 | { 309 | static public function should(value : Iterable) 310 | { 311 | return new ShouldIterable(value); 312 | } 313 | 314 | public function new(value : Iterable, inverse = false) 315 | { 316 | super(value, inverse); 317 | } 318 | 319 | public var not(get, never) : ShouldIterable; 320 | private function get_not() { return new ShouldIterable(value, !inverse); } 321 | 322 | ////////// 323 | 324 | public function contain(o : T, ?p : PosInfos) 325 | { 326 | test(Lambda.exists(value, function(el) return el == o), p, 327 | 'Expected ${quote(value)} to contain ${quote(o)}', 328 | 'Expected ${quote(value)} not to contain ${quote(o)}' 329 | ); 330 | } 331 | 332 | /** 333 | * Test if iterable contains all of the following values. 334 | */ 335 | public function containAll(values : Iterable, ?p : PosInfos) 336 | { 337 | var expr = true; 338 | 339 | // Having problem with java compilation for Lambda, using a simpler version: 340 | for (a in values) 341 | { 342 | if (!value.exists(function(v) { return v == a; } )) 343 | { 344 | expr = false; 345 | break; 346 | } 347 | } 348 | 349 | test(expr, p, 350 | 'Expected ${quote(value)} to contain all of ${quote(values)}', 351 | 'Expected ${quote(value)} not to contain all of ${quote(values)}' 352 | ); 353 | } 354 | 355 | /** 356 | * Test if iterable contains exactly the following values and in the same iteration order. 357 | */ 358 | public function containExactly(values : Iterable, ?p : PosInfos) 359 | { 360 | var a = value.iterator(); 361 | var b = values.iterator(); 362 | var expr = true; 363 | 364 | while (a.hasNext() || b.hasNext()) 365 | { 366 | if (a.next() != b.next()) 367 | { 368 | expr = false; 369 | break; 370 | } 371 | } 372 | 373 | test(expr, p, 374 | 'Expected ${quote(value)} to contain exactly ${quote(values)}', 375 | 'Expected ${quote(value)} not to contain exactly ${quote(values)}' 376 | ); 377 | } 378 | } 379 | 380 | // Some problem with C++ forces this class not to be derived from Should 381 | class ShouldFunctions 382 | { 383 | var value : Void -> Void; 384 | var inverse : Bool; 385 | 386 | public function new(value : Void -> Void, inverse = false) 387 | { 388 | this.value = value; 389 | this.inverse = inverse; 390 | } 391 | 392 | public var not(get, never) : ShouldFunctions; 393 | private function get_not() { return new ShouldFunctions(value, !inverse); } 394 | 395 | static public function should(value : Void -> Void) 396 | { 397 | return new ShouldFunctions(value); 398 | } 399 | 400 | /** 401 | * Will call the specified method and test if it throws anything. 402 | */ 403 | public function throwAnything(?p : PosInfos) : Null 404 | { 405 | var caught = false; 406 | var exception : Dynamic = null; 407 | 408 | try { value(); } 409 | catch (e : Dynamic) { exception = e; caught = true; }; 410 | 411 | test(caught, p, 412 | 'Expected ${quote(value)} to throw anything, nothing was thrown', 413 | 'Expected ${quote(value)} not to throw anything, ${quote(exception)} was thrown' 414 | ); 415 | 416 | return exception; 417 | } 418 | 419 | /** 420 | * Will call the specified method and test if it throws a specific value. 421 | */ 422 | public function throwValue(v : T, ?p : PosInfos) : Null 423 | { 424 | var caught = false; 425 | var exception : T = null; 426 | 427 | try { value(); } 428 | catch (e : Dynamic) 429 | { 430 | exception = e; 431 | caught = e == v; 432 | } 433 | 434 | test(caught, p, 435 | 'Expected ${quote(value)} to throw ${quote(v)}', 436 | 'Expected ${quote(value)} not to throw ${quote(v)}' 437 | ); 438 | 439 | return exception; 440 | } 441 | 442 | /** 443 | * Will call the specified method and test if it throws a specific type. 444 | */ 445 | public function throwType(type : Class, ?p : PosInfos) : Null 446 | { 447 | var caught = false; 448 | var name : String = Type.getClassName(type); 449 | var exceptionName : String = null; 450 | var exception : T = null; 451 | 452 | try { value(); } 453 | catch (e : Dynamic) 454 | { 455 | exception = e; 456 | exceptionName = Type.getClassName(Type.getClass(e)); 457 | caught = Std.is(e, type); 458 | } 459 | 460 | if (exceptionName == null) exceptionName = "no exception"; 461 | 462 | test(caught, p, 463 | 'Expected ${quote(value)} to throw type $name, $exceptionName was thrown instead', 464 | 'Expected ${quote(value)} not to throw type $name' 465 | ); 466 | 467 | return exception; 468 | } 469 | 470 | /** 471 | * Test for equality between two value types (bool, int, float), or identity for reference types 472 | */ 473 | public function be(expected : Void -> Void, ?p : PosInfos) : Void 474 | { 475 | test(value == expected, p, 476 | 'Expected ${quote(expected)}, was ${quote(value)}', 477 | 'Didn\'t expect ${quote(expected)} but was equal to that' 478 | ); 479 | } 480 | 481 | private function quote(v : Dynamic) 482 | { 483 | if (Std.is(v, String)) return '"$v"'; 484 | if (Std.is(v, List)) return Std.string(Lambda.array(v)); 485 | return Std.string(v); 486 | } 487 | 488 | private function test(expr : Bool, p : PosInfos, error : String, errorInverted : String) 489 | { 490 | if (SuitesRunner.currentTest == null) throw "SuitesRunner.currentTest was null"; 491 | 492 | if(!inverse) 493 | SuitesRunner.currentTest(expr, error, SuitesRunner.posInfosToStack(p)); 494 | else 495 | SuitesRunner.currentTest(!expr, errorInverted, SuitesRunner.posInfosToStack(p)); 496 | } 497 | } 498 | 499 | ////////// 500 | 501 | class Should 502 | { 503 | var value : T; 504 | var inverse : Bool; 505 | 506 | public function new(value : T, inverse = false) 507 | { 508 | this.value = value; 509 | this.inverse = inverse; 510 | } 511 | 512 | /** 513 | * Test for equality between "expected" value types (bool, int, int64, float, string), identity for other (reference) types 514 | */ 515 | public function be(expected : T, ?p : PosInfos) : Void 516 | { 517 | #if python 518 | // Python arrays compare arrays (list) by item-by-item equality as default. 519 | var result = UBuiltins.isinstance(value, UBuiltins.list) && UBuiltins.isinstance(expected, UBuiltins.list) 520 | ? Builtins.id(cast value) == Builtins.id(cast expected) 521 | : value == expected; 522 | #else 523 | var result = value == expected; 524 | #end 525 | test(result, p, 526 | 'Expected ${quote(expected)}, was ${quote(value)}', 527 | 'Didn\'t expect ${quote(expected)} but was equal to that' 528 | ); 529 | } 530 | 531 | public function beType(type : Dynamic, ?p : PosInfos) 532 | { 533 | test(Std.is(value, type), p, 534 | 'Expected ${quote(value)} to be type ${quote(type)}', 535 | 'Expected ${quote(value)} not to be type ${quote(type)}' 536 | ); 537 | } 538 | 539 | private function quote(v : Dynamic) 540 | { 541 | if (Std.is(v, String)) return '"$v"'; 542 | if (Std.is(v, List)) return Std.string(Lambda.array(v)); 543 | return Std.string(v); 544 | } 545 | 546 | private function fail(error : String, errorInverted : String, p : PosInfos) 547 | { 548 | SuitesRunner.currentTest(false, inverse ? errorInverted : error, SuitesRunner.posInfosToStack(p)); 549 | } 550 | 551 | private function test(expr : Bool, p : PosInfos, error : String, errorInverted : String) 552 | { 553 | if (SuitesRunner.currentTest == null) throw "SuitesRunner.currentTest was null"; 554 | 555 | if(!inverse) 556 | SuitesRunner.currentTest(expr, error, SuitesRunner.posInfosToStack(p)); 557 | else 558 | SuitesRunner.currentTest(!expr, errorInverted, SuitesRunner.posInfosToStack(p)); 559 | } 560 | } 561 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Buddy 2 | 3 | Your friendly BDD testing library for Haxe! 4 | 5 | ## Quickstart 6 | 7 | 1) Install the lib: 8 | 9 | `haxelib install buddy` 10 | 11 | 2) Create a test file called **Main.hx**: 12 | 13 | ```haxe 14 | using buddy.Should; 15 | 16 | class Main extends buddy.SingleSuite { 17 | public function new() { 18 | // A test suite: 19 | describe("Using Buddy", { 20 | var experience = "?"; 21 | var mood = "?"; 22 | 23 | beforeEach({ 24 | experience = "great"; 25 | }); 26 | 27 | it("should be a great testing experience", { 28 | experience.should.be("great"); 29 | }); 30 | 31 | it("should make the tester really happy", { 32 | mood.should.be("happy"); 33 | }); 34 | 35 | afterEach({ 36 | mood = "happy"; 37 | }); 38 | }); 39 | } 40 | } 41 | ``` 42 | 43 | 3) Make a quick test: 44 | 45 | `haxe -x Main -lib buddy` 46 | 47 | ``` 48 | .. 49 | Using Buddy 50 | should be a great testing experience (Passed) 51 | should make the tester really happy (Passed) 52 | 2 specs, 0 failures, 0 pending 53 | ``` 54 | 55 | But please don't stop there. Try using it on other targets than Neko, Buddy supports them all on both Windows and Linux! The only thing you need to remember is to add `-D nodejs` to your hxml, if you're targeting Node.js. 56 | 57 | ## Asynchronous support 58 | 59 | Buddy was built from the ground up to have great support for async testing, so it's fully compatible with Node.js and handles ajax requests with ease. To use it, just create the specification with a function that takes one argument (targeting javascript now): 60 | 61 | ```haxe 62 | using buddy.Should; 63 | 64 | class Main extends buddy.SingleSuite { 65 | public function new() { 66 | describe("Using Buddy asynchronously", { 67 | var mood = "?"; 68 | 69 | // Add function(done) here to enable async testing: 70 | beforeAll(function(done) { 71 | haxe.Timer.delay(function() { 72 | mood = "thrilled"; 73 | done(); // Call the done() function when the async operation is complete. 74 | }, 100); 75 | }); 76 | 77 | // Can be added to "it" and "after" as well if needed. 78 | it("can be described in a certain word", { 79 | mood.should.be("thrilled"); 80 | }); 81 | }); 82 | } 83 | } 84 | ``` 85 | 86 | The default timeout is 5000 ms, after which the spec will automatically fail if `done()` hasn't been called. If you want to change the timeout, set the property `timeoutMs` in the `BuddySuite` **before** the actual `it()` specification, or in the before/after block. Here's an example: 87 | 88 | ```haxe 89 | using buddy.Should; 90 | 91 | class Main extends buddy.SingleSuite { 92 | public function new() { 93 | describe("Using Buddy asynchronously", { 94 | timeoutMs = 100; 95 | it("should fail specs after a timeout set before it()", function(done) { 96 | // This test will fail after 100 ms. 97 | haxe.Timer.delay(done, 200); 98 | }); 99 | }); 100 | } 101 | } 102 | ``` 103 | 104 | You can set `timeoutMs` to 0 to disable the timeout check. **Note:** When using `function(done)`, on some targets the timeout check will run in a separate thread. Also, timeouts and asynchronous behavior aren't supported when targeting PHP. 105 | 106 | ## Before/After 107 | 108 | To setup tests, you can use `beforeAll`, `beforeEach`, `afterEach` and `afterAll`: 109 | 110 | ```haxe 111 | using buddy.Should; 112 | 113 | class BeforeAfterTest extends buddy.SingleSuite { 114 | public function new() { 115 | describe("Using before/after", { 116 | var test = 0; 117 | 118 | // Will run once as the first thing in the current describe block 119 | beforeAll({ 120 | test++; 121 | }); 122 | 123 | // Will run before each "it" in the current and before each "it" in any nested describes. 124 | beforeEach({ 125 | test++; 126 | }); 127 | 128 | it("should be a convenient way to set up tests", { 129 | test.should.be(2); 130 | }); 131 | 132 | describe("When nesting describes", { 133 | beforeEach({ 134 | test++; 135 | }); 136 | 137 | it("should run all before/afterEach defined here or above", { 138 | test.should.be(3); 139 | }); 140 | 141 | afterEach({ 142 | test--; 143 | }); 144 | }); 145 | 146 | it("should run in correct order too", { 147 | test.should.be(2); 148 | }); 149 | 150 | // Will run after each "it" in the current and before each "it" in any nested describes. 151 | afterEach({ 152 | test--; 153 | }); 154 | 155 | // Will run once as the last thing in the current describe block 156 | afterAll({ 157 | test--; 158 | }); 159 | }); 160 | } 161 | } 162 | ``` 163 | 164 | ## "Should" assertions 165 | 166 | As you've seen in the examples, testing if specifications are correct is as simple as adding `using Buddy.should` to the package and then use the `should` extension for the identifier you want to test. The following assertions are supported: 167 | 168 | ### All types 169 | 170 | `a.should.be(b)` - Tests equality for value types (`Bool`, `Float`, `Int`, `Int64`, and the immutable `String`) and identity for the other (reference) types. 171 | 172 | `a.should.beType(b)` - Tests if `a` is of type `b`. Basically a wrapper around `Std.is`. 173 | 174 | ### Int / Int64 175 | 176 | `a.should.beLessThan(b)` 177 | 178 | `a.should.beGreaterThan(b)` 179 | 180 | ### Float 181 | 182 | Same as Int plus 183 | 184 | `a.should.beCloseTo(b, p = 2)` - `a` should be close to `b` with `p` decimals precision, so you can easily compare floats without worrying about precision issues. 185 | 186 | ### String 187 | 188 | `a.should.contain(substr)` - Test if `a` contains a given substring. 189 | 190 | `a.should.match(regexp)` - Test if `a` matches a regular expression (`EReg`). 191 | 192 | `a.should.startWith(substr)` - Test if `a` starts with a given substring. 193 | 194 | `a.should.endWith(substr)` - Test if `a` ends with a given substring. 195 | 196 | ### Date 197 | 198 | `a.should.beOn(date)` - Test if `a` is on a given date 199 | 200 | `a.should.beOnStr(string)` - Test if `a` is on a date specified by a string in the [Date.fromString](http://api.haxe.org/Date.html#fromString) accepted formats. 201 | 202 | `a.should.beBefore(date)` - Test if `a` is before a given date. 203 | 204 | `a.should.beBeforeStr(string)` - Same as above, but specified by a string. 205 | 206 | `a.should.beAfter(date)` - Test if `a` is after a given date. 207 | 208 | `a.should.beAfterStr(string)` - Same as above, but specified by a string. 209 | 210 | ### Iterable 211 | 212 | `a.should.contain(b)` - Test if an Iterable contains `b`. 213 | 214 | `a.should.containAll(b)` - Test if an Iterable contains all objects in Iterable `b`. 215 | 216 | `a.should.containExactly(b)` - Test if an Iterable contains exactly the same objects as in Iterable `b` and in the same order. 217 | 218 | ### Enum 219 | 220 | `a.should.equal(b)` - Makes a deep equality check, using `Type.enumEq`. A warning will be given when enums are compared by `should.be`, since the result of that comparison is undefined. 221 | 222 | ### Exceptions 223 | 224 | Testing if a function throws an exception is made easy using the special `bind` field which exists for every function. 225 | 226 | If the function signature is `String -> Void` then apply the string argument like this: 227 | 228 | `a.bind("test").should.throwValue("error")` 229 | 230 | `a.bind("test").should.throwType(String)` 231 | 232 | `a.bind("test").should.throwAnything()` 233 | 234 | You can also test an anonymous function directly: 235 | 236 | `(function() { throw "error"; }).should.throwType(String)` 237 | 238 | The throw methods will return the exception object, so it can be tested further. This works synchonously only. 239 | 240 | ### Inverting assertions 241 | 242 | Every assertion can be negated using `not` which is present on all `should` fields: 243 | 244 | `a.should.not.contain("test")` 245 | 246 | ## Failing tests 247 | 248 | A test can be failed using the `fail(o : Dynamic) : Void` method available in a `BuddySuite`. The test will fail with the string value of `o` as a message. If you're testing asynchronously you can pass the `fail` method to the error handler. Here are some examples: 249 | 250 | ```haxe 251 | it("should fail manually when using fail()", { 252 | fail("Totally on purpose."); 253 | }); 254 | 255 | it("should fail if a promise fails", function(done) { 256 | request.getJson("/some/url") 257 | .then(function(r) { 258 | r.statusCode.should.be(200); 259 | done(); 260 | }) 261 | .catchError(fail); 262 | }); 263 | 264 | it("should also fail when throwing an exception", { 265 | throw "But only synchronously!"; 266 | }); 267 | ``` 268 | 269 | ## Testing compilation failures 270 | 271 | If you want to test if some part of your code fails to compile, guess what, there is a macro for that: 272 | 273 | ```haxe 274 | import buddy.CompilationShould; 275 | 276 | class Main extends buddy.SingleSuite 277 | { 278 | public function new() { 279 | describe("Using CompilationShould", { 280 | it("should pass if an expression won't compile", { 281 | CompilationShould.failFor(this.will.not.compile); 282 | }); 283 | }); 284 | } 285 | } 286 | ``` 287 | 288 | The method will return a string representation of the compilation failure, or an empty string if compilation succeded, in case you want to test it further. 289 | 290 | ## General error handling 291 | 292 | Exceptions in `it` will be handled as above, but if something goes wrong in a `before/after` section, Buddy will stop executing the whole suite. It will also count as a failure. 293 | 294 | If you're getting an early runtime error, you might want to disable the trace capture that buddy uses. You can do that globally by putting `BuddySuite.useDefaultTrace = true` in the beginning of a test class. Then you'll see the traces immediately instead of in the reporter. 295 | 296 | Please note that putting code that should be tested outside a `describe`, `it` or any `before/after` block can result in undefined behavior. 297 | 298 | ## Pending tests 299 | 300 | Since BDD is also made for non-programmers to use, a common development style is to write empty, or *pending* tests, and let a programmer implement them later. To do this, just write a string in the `it` methods, nothing else. Our previous test class would then look like this: 301 | 302 | **Main.hx** 303 | 304 | ```haxe 305 | using buddy.Should; 306 | 307 | class Main extends buddy.SingleSuite 308 | { 309 | public function new() { 310 | describe("Using Buddy", { 311 | it("should be a great testing experience"); 312 | it("should really make the tester happy"); 313 | }); 314 | } 315 | } 316 | ``` 317 | 318 | And the output would be: 319 | 320 | ``` 321 | PP 322 | Using Buddy 323 | should be a great testing experience (Pending) 324 | should really make the tester happy (Pending) 325 | 2 specs, 0 failures, 2 pending 326 | ``` 327 | 328 | There is also a `pending(reason : String)` method available to make a spec pending, similar to `fail`. 329 | 330 | ## Including and excluding tests 331 | 332 | Classes, suites and specs can all be marked with `@include` and `@exclude` metadata. 333 | 334 | * `@include` will only run the tests that are marked, removing everything else. 335 | * `@exclude` does the opposite, it removes the marked ones. 336 | 337 | If you have a huge test suite, it can be convenient to mark the suite you're currently working on with `@include`. 338 | 339 | ## Multiple test suites 340 | 341 | Extending `buddy.SingleSuite` is nice and simple, but you can have multiple test classes, and separate them from the main class if you like. Here's how to do it: 342 | 343 | **Main.hx** 344 | 345 | ```haxe 346 | import buddy.*; 347 | using buddy.Should; 348 | 349 | // Implement "Buddy" and define an array of classes within the brackets: 350 | class Main implements Buddy<[ 351 | Tests, 352 | path.to.YourBuddySuite, 353 | AnotherTestSuite, 354 | new SpecialSuite("Constant value", 123) 355 | ]> {} 356 | 357 | // All test classes should now extend BuddySuite (not SingleSuite) 358 | class Tests extends BuddySuite 359 | { 360 | public function new() { 361 | describe("Using Buddy", { 362 | it("should be a great testing experience"); 363 | it("should really make the tester happy"); 364 | }); 365 | } 366 | } 367 | ``` 368 | 369 | ## Customizing output and reporting 370 | 371 | ### Adding colors 372 | 373 | Enable ANSI color output is easy: 374 | 375 | ```haxe 376 | @colorize 377 | class Main extends buddy.SingleSuite { 378 | // ... 379 | } 380 | ``` 381 | 382 | Or you can do it when compiling with `-D buddy-colors`, or disallow it with `-D buddy-no-colors`. 383 | 384 | The compilation flag will override the metadata, if both are set. 385 | 386 | ### Creating a custom reporter 387 | 388 | You can make your own reporter by implementing the [buddy.reporting.Reporter](https://github.com/ciscoheat/buddy/blob/master/src/buddy/reporting/Reporter.hx) interface. Then there are two ways to use it: 389 | 390 | ```haxe 391 | @reporter("path.to.your.Reporter") 392 | class Main extends buddy.SingleSuite { 393 | // ... 394 | } 395 | ``` 396 | 397 | Or do it when compiling with `-D reporter=path.to.your.Reporter`. 398 | 399 | The compilation flag will override the metadata, if both are set. 400 | 401 | ### List of built-in Reporters 402 | 403 | `buddy.reporting.ConsoleReporter` is the default reporter. 404 | 405 | `buddy.reporting.ConsoleColorReporter` is identical to the default reporter but with ANSI color output. This reporter will be used if colors are enabled with metadata or compilation flags, as specified above. 406 | 407 | `buddy.reporting.TraceReporter` outputs to `trace()`, and is especially useful for CI with flash. If you define `-D flash-exit`, the default reporter will be the TraceReporter, and flash will exit if the correct permissions are set. This is tricky to get right, so the easiest way is to use [travix](https://github.com/back2dos/travix/). 408 | 409 | ## FAQ 410 | 411 | ### Where's main()? 412 | 413 | Ok, you noticed that it was missing! Using some macro magic, you only need to extend `buddy.SingleSuite`, or alternatively implement `buddy.Buddy` on your Main class. Then platform-specific code will be generated for waiting until the tests are finished. On all server platforms, exit code 0 will be returned for "all tests passed" and 1 if not, so you can use Buddy in CI tools. 414 | 415 | ### Autocompletion sometimes doesn't work for "x.should." or numbers. 416 | 417 | The compiler seems to be a bit too good at optimizing sometimes, especially at the beginning of functions, though this seems to have improved greatly in 3.2. If you have this problem, add a parenthesis after "should", and wrap numbers in parenthesis too. 418 | 419 | `x.should.be("ok")` -> `x.should().be("ok")` 420 | 421 | `123.should.beGreaterThan(100)` -> `(123).should.beGreaterThan(100)` 422 | 423 | ### Can I use other assertion libraries than the built-in 'should'? 424 | 425 | Yes, there is special support for [utest](http://code.google.com/p/utest/) and general support for all libraries that throws an exception on failure (like [mockatoo](https://github.com/misprintt/mockatoo) ). To use utest, just call any `utest.Assert` method inside a spec, no need to set up anything else. 426 | 427 | ### There's an exception thrown in an asynchronous method, but Buddy won't catch it and fail the test? 428 | 429 | It's not possible to do that, since the program has already passed the exception handling code when the exception is thrown. You need to handle asynchronous exceptions yourself and test if something went wrong before calling `done` in the spec, or use the `fail` method as described in the section "Failing tests". 430 | 431 | ### I'm having problem compiling with C++ 432 | 433 | This usually happens if you're not linking in the correct `.ndll` files. An easy fix is to add `-lib hxcpp` to your hxml. Another problem could be fixed by adding `-D HXCPP_M64` if you're targeting C++ on a 64bit platform (seems to vary between Linux and Win). 434 | 435 | ### Can I run the tests manually, without generating main? 436 | 437 | Yes, but make sure you know what you're doing, some targets requires a wait loop if you have asynchronous tests, for example... Here's a minimal setup for synchronous execution: 438 | 439 | ```haxe 440 | import buddy.reporting.ConsoleColorReporter; 441 | 442 | class Main { 443 | public static function main() { 444 | var reporter = new ConsoleColorReporter(); 445 | 446 | var runner = new buddy.SuitesRunner([ 447 | new FirstTestSuite(), 448 | new AnotherTestSuite() 449 | ], reporter); 450 | 451 | runner.run(); 452 | 453 | #if sys 454 | Sys.exit(runner.statusCode()); 455 | #end 456 | } 457 | } 458 | ``` 459 | 460 | Please make sure that the auto-generated version doesn't work in your case before doing this. 461 | 462 | ## The story behind Buddy 463 | 464 | After my [speech about HaxeContracts](http://www.silexlabs.org/wwx2014-speech-andreas-soderlund-dci-how-to-get-ahead-in-system-architecture/) at WWX2014, I concluded that one does not simply bash unit testing without providing a nice alternative! Having used [Jasmine](http://jasmine.github.io/2.0/introduction.html) before, I borrowed some if its nice features, and found the perfect aid to implementing async support with the [promhx](https://github.com/jdonaldson/promhx) library. 465 | 466 | The [HaxeContracts](https://github.com/ciscoheat/HaxeContracts) library is a nice complement to BDD, so check it out for more information about why most unit testing is waste, and why BDD is a better alternative...! 467 | 468 | ## Upcoming features 469 | 470 | - [ ] Nicer browser reporter 471 | - [ ] Your choice! Send me a gmail (ciscoheat) or create an issue here. 472 | 473 | Have a good time and much productivity with your new Buddy! :) 474 | 475 | [![Build Status](https://travis-ci.org/ciscoheat/buddy.svg?branch=master)](https://travis-ci.org/ciscoheat/buddy) 476 | -------------------------------------------------------------------------------- /src/buddy/tests/AllTests.hx: -------------------------------------------------------------------------------- 1 | package buddy.tests ; 2 | 3 | import buddy.BuddySuite; 4 | import buddy.tests.AllTests.ColorTree; 5 | import buddy.tools.AsyncTools; 6 | import buddy.CompilationShould; 7 | import tink.core.Future; 8 | 9 | import haxe.CallStack; 10 | import haxe.Int64; 11 | import promhx.Deferred; 12 | import promhx.Promise; 13 | import Slambda.fn; 14 | import utest.Assert; 15 | 16 | using buddy.Should; 17 | using Slambda; 18 | using StringTools; 19 | 20 | #if !flash 21 | @colorize 22 | #end 23 | class AllTests implements Buddy<[ 24 | TestBasicFeatures, 25 | TestExclude, 26 | FailTest, 27 | #if !php 28 | TestAsync, 29 | FailTestAsync, 30 | TinkAwaitTest, 31 | #end 32 | UtestUsage, 33 | TestExceptionHandling, 34 | BeforeAfterDescribe, 35 | BeforeAfterDescribe2, 36 | BeforeAfterDescribe3, 37 | NestedBeforeAfter, 38 | SimpleNestedBeforeAfter, 39 | CallDoneTest, 40 | CompilationFailTest, 41 | HugeTest 42 | ]> {} 43 | 44 | class EmptyTestClass { public function new() {} } 45 | 46 | enum Color { Red; Green; Blue; } 47 | 48 | enum ColorTree { Leaf(c : Color); Node(l : ColorTree, r : ColorTree); } 49 | 50 | class TestBasicFeatures extends BuddySuite 51 | { 52 | private var testAfter : String; 53 | 54 | public function new() 55 | { 56 | var top = 0; 57 | 58 | beforeAll({ 59 | top++; 60 | }); 61 | 62 | describe("Testing before", { 63 | var a = 0; 64 | 65 | beforeEach({ 66 | a = 1; 67 | }); 68 | 69 | it("should set the variable a to 1 in the before function", { 70 | a.should.be(1); 71 | a = 2; 72 | }); 73 | 74 | it("'before' should be run before every 'it' specification", { 75 | a.should.be(1); 76 | }); 77 | }); 78 | 79 | describe("Testing top-level beforeAll", { 80 | it("should be possible to set beforeAll before a top-level describe", { 81 | top.should.be(1); 82 | }); 83 | }); 84 | 85 | describe("Testing after", { 86 | it("should not set the property testAfter in this first spec", { 87 | testAfter.should.be(null); 88 | }); 89 | 90 | afterEach({ 91 | testAfter = "after executed"; 92 | }); 93 | }); 94 | 95 | describe("Testing dynamics", function() { 96 | var obj1 = { id: 1 }; 97 | var obj2 = { id: 2 }; 98 | var color1 = Red; 99 | var color2 = Green; 100 | 101 | it("should compare objects with be()", { 102 | obj1.should.be(obj1); 103 | obj1.should.not.be(obj2); 104 | Red.should.equal( Red ); 105 | Red.should.not.equal( Green ); 106 | }); 107 | 108 | it("should compare types with beType()", { 109 | "str".should.beType(String); 110 | new EmptyTestClass().should.beType(EmptyTestClass); 111 | color1.should.beType(Color); 112 | color2.should.not.beType(Int); 113 | 114 | Std.is([1, 2, 3], Array).should.be(true); 115 | // Problem on C#: 116 | //[1, 2, 3].should.beType(Array); 117 | }); 118 | 119 | it("should compare objects correctly when cast to Dynamic", { 120 | var arr : Dynamic = new Array(); 121 | var fn = function() return arr; 122 | 123 | arr.should.be(fn()); 124 | // fn().should.be(arr); // Will fail because it's Unknown<0>, cast to fix. 125 | }); 126 | }); 127 | 128 | #if !php 129 | describe("Testing async describe definitions", function(done) { 130 | var a = 0; 131 | 132 | it("should run specs after done has been called.", { 133 | a.should().be(1); 134 | }); 135 | 136 | AsyncTools.wait(10).then(function(_) { 137 | a = 1; 138 | done(); 139 | }); 140 | }); 141 | #end 142 | 143 | describe("Testing strings", { 144 | var str = "abc"; 145 | 146 | it("should compare same string value with be()", { 147 | str.should().be("abc"); 148 | str.should().not.be("cde"); 149 | }); 150 | 151 | it("should have a contain() method for matching substrings", { 152 | str.should().contain("a"); 153 | str.should().contain("abc"); 154 | str.should().not.contain("abcd"); 155 | }); 156 | 157 | it("should have a match() method for matching regexps", { 158 | str.should().match(~/a/); 159 | str.should().match(~/a\w+/); 160 | str.should().not.match(~/\d+/); 161 | }); 162 | 163 | it("should have a startWith() method", { 164 | str.should().startWith("a"); 165 | str.should().startWith("abc"); 166 | str.should().not.startWith("b"); 167 | }); 168 | 169 | it("should have an endWith() method", { 170 | str.should().endWith("c"); 171 | str.should().endWith("abc"); 172 | str.should().not.endWith("b"); 173 | }); 174 | }); 175 | 176 | describe("Testing Enums", { 177 | it("should make deep Enum comparisons", { 178 | var tree = Node(Node(Leaf(Red), Leaf(Green)), Leaf(Blue)); 179 | tree.should.equal(Node(Node(Leaf(Red), Leaf(Green)), Leaf(Blue))); 180 | 181 | switch tree { 182 | case Node(l, r): 183 | l.should.equal(Node(Leaf(Red), Leaf(Green))); 184 | r.should.equal(Leaf(Blue)); 185 | r.should.not.equal(Leaf(Green)); 186 | case _: 187 | fail("Incorrect tree structure for the test."); 188 | } 189 | }); 190 | }); 191 | 192 | describe("Testing ints", { 193 | var int = 3; 194 | 195 | it("should have a beLessThan() method", { 196 | int.should.beLessThan(4); 197 | }); 198 | 199 | it("beLessThan should compare against float", { 200 | int.should.beLessThan(3.1); 201 | }); 202 | 203 | it("should have a beMoreThan() method", { 204 | int.should.beGreaterThan(2); 205 | }); 206 | 207 | it("beMoreThan should compare against float", { 208 | int.should.beGreaterThan(2.9); 209 | }); 210 | }); 211 | 212 | describe("Testing Int64", { 213 | var int64 = Int64.make(1, 1); 214 | 215 | it("should have a beLessThan() method", { 216 | int64.should.beLessThan(Int64.make(2, 0)); 217 | }); 218 | 219 | it("should have a beMoreThan() method", { 220 | int64.should.beGreaterThan(2147483647); 221 | }); 222 | 223 | it("should test 'be' with equality, not identity", { 224 | var one : Int64 = 1000341504; 225 | var two : Int64 = 1000341504; 226 | 227 | one.should.be(two); 228 | }); 229 | }); 230 | 231 | describe("Testing floats", { 232 | var number = 3.14; 233 | var lostSignificance = 3.140000001; 234 | 235 | it("should have a beLessThan() method", { 236 | number.should.beLessThan(4.23); 237 | }); 238 | 239 | it("beLessThan should compare against int", { 240 | number.should.beLessThan(cast(4, Int)); 241 | }); 242 | 243 | it("should have a beMoreThan() method", { 244 | number.should.beGreaterThan(2.9); 245 | }); 246 | 247 | it("beMoreThan should compare against int", { 248 | number.should.beGreaterThan(cast(2, Int)); 249 | }); 250 | 251 | it("should have a beCloseTo() method", { 252 | number.should().beCloseTo(3.14); 253 | number.should().beCloseTo(3.1, 1); 254 | number.should().beCloseTo(3.141); 255 | 256 | number.should().beCloseTo(lostSignificance); 257 | lostSignificance.should().beCloseTo(number); 258 | 259 | number.should().not.beCloseTo(3.1); 260 | number.should().not.beCloseTo(3.13); 261 | number.should().not.beCloseTo(3.15); 262 | }); 263 | }); 264 | 265 | describe("Testing dates", { 266 | var date : Date; 267 | 268 | beforeEach({ 269 | date = Date.fromString("2015-01-02 12:13:14"); 270 | }); 271 | 272 | it("should have a beOn() method", { 273 | date.should.beOn(Date.fromString("2015-01-02 12:13:14")); 274 | }); 275 | 276 | it("should have a beOnStr() method", { 277 | date.should.beOnStr("2015-01-02 12:13:14"); 278 | }); 279 | 280 | it("should have a beAfter() method", { 281 | date.should.beAfter(Date.fromString("2015-01-01 12:13:14")); 282 | }); 283 | 284 | it("should have a beAfterStr() method", { 285 | date.should.beAfterStr("2015-01-02 12:13:13"); 286 | }); 287 | 288 | it("should have a beBefore() method", { 289 | date.should.beBefore(Date.fromString("2016-01-01 12:13:14")); 290 | }); 291 | 292 | it("should have a beBeforeStr() method", { 293 | date.should.beBeforeStr("2015-01-02 12:13:15"); 294 | }); 295 | }); 296 | 297 | describe("Testing Iterable", { 298 | var a = [1, 2, 3]; 299 | var b = [1, 2, 3]; 300 | 301 | var c = new List(); 302 | c.add(1); 303 | c.add(2); 304 | c.add(3); 305 | 306 | it("should compare by identity", { 307 | a.should().be(a); 308 | a.should().not.be(b); 309 | }); 310 | 311 | it("should have a contain() method", { 312 | a.should().contain(1); 313 | a.should().not.contain(4); 314 | }); 315 | 316 | it("should have a containExactly() method", { 317 | a.should().containExactly([1, 2, 3]); 318 | a.should().containExactly(b); 319 | a.should().containExactly(c); // Different types 320 | 321 | a.should().not.containExactly([3, 2, 1]); 322 | a.should().not.containExactly([1, 2]); 323 | a.should().not.containExactly([1, 2, 3, 4]); 324 | 325 | [].should.containExactly([]); 326 | }); 327 | 328 | it("should have a containAll() method", { 329 | a.should().containAll([1, 2, 3]); 330 | a.should().containAll(b); 331 | a.should().containAll([1]); 332 | 333 | a.should().not.containAll([3, 4]); 334 | }); 335 | }); 336 | 337 | describe("Testing functions", { 338 | var f = function() { return throw "a"; }; 339 | var g = function() { return throw new EmptyTestClass(); }; 340 | 341 | var h = function(a : String) { return throw a; }; 342 | var i = function(a : String) { return throw a.toUpperCase(); }; 343 | 344 | var j = function(a : String) : String { return throw a; }; 345 | var k = function(a : String) : String { return throw a.toUpperCase(); }; 346 | 347 | it("should have a be method", { 348 | f.should().be(f); 349 | j.should().be(j); 350 | 351 | f.should().not.be(function() { throw "a"; }); 352 | j.should().not.be(k); 353 | }); 354 | 355 | it("should have a throwValue() method", { 356 | f.should().throwValue("a"); 357 | var value = f.should().not.throwValue("b"); 358 | 359 | value.length.should.be(1); 360 | value.charCodeAt(0).should.be(97); 361 | }); 362 | 363 | it("should have a throwType() method", { 364 | var obj = g.should().throwType(EmptyTestClass); 365 | 366 | g.should().not.throwType(String); 367 | obj.should.beType(EmptyTestClass); 368 | }); 369 | 370 | it("should have a throwType() method that can be used with bind", { 371 | h.bind("a").should().throwValue("a"); 372 | i.bind("a").should().not.throwValue("a"); 373 | 374 | j.bind("a").should().throwValue("a"); 375 | k.bind("a").should().not.throwValue("a"); 376 | }); 377 | 378 | it("should work with Slambda", { 379 | [1, 2, 3].filter.fn(_ > 2).should.containExactly([3]); 380 | [1, 2, 3].filter.fn(x => x > 1).should.containExactly([2, 3]); 381 | [1, 1, 1].mapi.fn([i, a] => i + a).should.containExactly([1, 2, 3]); 382 | [1, 2, 3].filter(fn(_ > 2)).should.containExactly([3]); 383 | 384 | ["1", "1", "1"].fold.fn(_2 + Std.parseInt(_1), 10).should.be(13); 385 | 386 | fn('$$_1')().should.be("$_1"); 387 | 388 | var attr = function(name : String, cb : String -> Int -> Dynamic) { 389 | name.should.be("test"); 390 | cb("a", 1).should.be("a-1"); 391 | } 392 | 393 | attr.fn("test", _1 + "-" + _2); 394 | attr.fn("test", [a,b] => a + "-" + b); 395 | }); 396 | }); 397 | 398 | describe("Testing should.not", { 399 | it("should invert the test condition", { 400 | "a".should.not.be("b"); 401 | "a".should.not.not.be("a"); 402 | (123).should.not.beLessThan(100); 403 | }); 404 | }); 405 | 406 | describe("Testing null", { 407 | it("should pass even if the var is null", { 408 | var s : EmptyTestClass = null; 409 | var d : Dynamic = null; 410 | s.should.be(null); 411 | d.should.be(null); 412 | 413 | #if (js || neko || php || python) 414 | var i : Int = null; 415 | i.should.be(null); 416 | #end 417 | 418 | #if js 419 | var undef : Dynamic = untyped __js__("undefined"); 420 | undef.should.be(null); 421 | #end 422 | }); 423 | }); 424 | 425 | describe("Excluding specs with @exclude and xit()", { 426 | @exclude it("should mark this spec as pending.", { 427 | true.should.be(false); // Make it fail if it runs 428 | }); 429 | 430 | xit("should mark this as pending too.", { 431 | true.should.be(false); // Make it fail if it runs 432 | }); 433 | 434 | it("should mark a spec with no body as pending too."); 435 | it("should mark a spec with an empty block as pending too.", {}); 436 | it("should mark a spec with an empty function as pending too.", function() {}); 437 | }); 438 | 439 | xdescribe("Excluding suites with xdescribe()", { 440 | it("should not display this suite or its specs at all.", { 441 | true.should.be(false); 442 | }); 443 | }); 444 | 445 | @exclude describe("Excluding suites with @exclude", { 446 | it("should not display this suite or its specs at all.", { 447 | true.should.be(false); 448 | }); 449 | }); 450 | 451 | describe("Making specs pending manually", { 452 | it("can be done with the 'pending' method.", { 453 | pending("Manually"); 454 | }); 455 | 456 | afterEach({ 457 | var test = SelfTest.lastSpec; 458 | SelfTest.passLastSpecIf(test.status == Pending && 459 | test.traces.length == 1 && 460 | test.traces[0].endsWith("Manually"), "Test wasn't pending with a message"); 461 | }); 462 | }); 463 | 464 | describe("Using trace() calls", { 465 | it("should reroute the trace output to the reporter", { 466 | trace("Test trace"); 467 | trace("Test trace 2"); 468 | }); 469 | 470 | afterEach({ 471 | var test = SelfTest.lastSpec; 472 | if (test.traces[0].startsWith("AllTests.hx") 473 | && test.traces.length == 2 474 | && test.traces[0].endsWith("Test trace") 475 | && test.traces[1].endsWith("Test trace 2")) 476 | { 477 | SelfTest.setLastSpec(Passed); 478 | } else { 479 | SelfTest.setLastSpec(Failed); 480 | } 481 | }); 482 | }); 483 | 484 | describe("Multiple 'should' failures in the same spec", { 485 | it("should report every failure in the spec", { 486 | (10).should.be(1); 487 | (11).should.be(1); 488 | }); 489 | 490 | afterEach({ 491 | var test = SelfTest.lastSpec; 492 | if (test.failures.length == 2 && 493 | test.failures[0].error == "Expected 1, was 10" && 494 | test.failures[1].error == "Expected 1, was 11") 495 | { 496 | SelfTest.setLastSpec(Passed); 497 | } else 498 | SelfTest.setLastSpec(Failed); 499 | }); 500 | }); 501 | } 502 | } 503 | 504 | @exclude 505 | class TestExclude extends BuddySuite 506 | { 507 | public function new() 508 | { 509 | describe("Excluding a whole BuddySuite", { 510 | it("should not not display any suites inside a class marked with @exclude.", { 511 | true.should.be(false); 512 | }); 513 | }); 514 | } 515 | } 516 | 517 | #if !php 518 | class TestAsync extends BuddySuite 519 | { 520 | public function new() 521 | { 522 | describe("Testing async", { 523 | var a; 524 | var timeoutErrorTest : buddy.BuddySuite.Spec; 525 | var timeoutErrorTestDone : ?Bool -> Void = null; 526 | 527 | beforeEach(function(done) { 528 | a = 0; 529 | timeoutMs = 10; 530 | AsyncTools.wait(1).then(function(_) { a = 1; done(); } ); 531 | }); 532 | 533 | it("should set the variable a to 1 in before, even though it's an async operation", { 534 | a.should.be(1); 535 | }); 536 | 537 | var timeoutTestDescription = "should timeout with an error after an amount of time specified outside it()"; 538 | it(timeoutTestDescription, function(done) { 539 | // Wait long enough for all targets to fail properly. (Had problems on flash when wait = 20) 540 | AsyncTools.wait(100).then(function(_) { 541 | true.should.be(true); 542 | done(); 543 | }); 544 | }); 545 | 546 | afterEach({ 547 | var test = SelfTest.lastSpec; 548 | if(test.description == timeoutTestDescription) 549 | SelfTest.passLastSpecIf(test.status == Failed, "Didn't timeout"); 550 | }); 551 | }); 552 | 553 | describe("An async failing assertion", { 554 | it("should not throw an AlreadyResolved exception when done is called", function(done) { 555 | // This test won't assert that the test passed, but it will throw an exception if it fails. 556 | (1).should.be(2); 557 | done(); 558 | }); 559 | 560 | afterEach({ 561 | var test = SelfTest.lastSpec; 562 | SelfTest.passLastSpecIf(test.status == Failed && 563 | test.failures.length == 1 && 564 | test.failures[0].error == "Expected 2, was 1", "Threw an exception when done called after fail"); 565 | }); 566 | }); 567 | } 568 | } 569 | #end 570 | 571 | class TestExceptionHandling extends BuddySuite 572 | { 573 | public function new() 574 | { 575 | describe("An exception thrown during testing", { 576 | this.timeoutMs = 50; 577 | var throwError = function() throw "Test error!"; 578 | 579 | it("should be caught and the current spec will fail.", { 580 | throwError(); 581 | }); 582 | 583 | afterEach({ 584 | var test = SelfTest.lastSpec; 585 | SelfTest.passLastSpecIf(test.status == Failed && 586 | test.failures.length == 1 && 587 | test.failures[0].error == "Test error!", "Exception wasn't caught"); 588 | }); 589 | }); 590 | } 591 | } 592 | 593 | #if utest 594 | class UtestUsage extends BuddySuite 595 | { 596 | public function new() 597 | { 598 | describe("Using utest for assertions", { 599 | it("should pass a test when using the Assert class.", { 600 | var a = { test: {a: 123}, cls: new EmptyTestClass() }; 601 | var b = { test: { a: 123 }, cls: new EmptyTestClass() }; 602 | 603 | Assert.isTrue(true); 604 | Assert.same(a, b); 605 | }); 606 | 607 | var failTestDesc = "should fail a test when using the Assert class."; 608 | it(failTestDesc, { 609 | Assert.isTrue(false); 610 | }); 611 | 612 | #if !php 613 | it("should pass on asynchronous tests.", function(done) { 614 | AsyncTools.wait(5).then(function(_) { 615 | Assert.match(~/\d{3}/, "abc123"); 616 | done(); 617 | }); 618 | }); 619 | #end 620 | 621 | afterEach({ 622 | var test = SelfTest.lastSpec; 623 | if(test.description == failTestDesc) { 624 | SelfTest.passLastSpecIf(test.status == Failed && 625 | test.failures.length == 1 && 626 | test.failures[0].error == "expected true", "Didn't fail using utest.Assert" 627 | ); 628 | } 629 | }); 630 | }); 631 | 632 | describe("Using utest for maps", { 633 | it ("should compare maps correctly", { 634 | Assert.same([1 => 2], [1 => 2]); 635 | Assert.same(["a" => 1], ["a" => 1]); 636 | }); 637 | }); 638 | } 639 | } 640 | #end 641 | 642 | class BeforeAfterDescribe extends BuddySuite 643 | { 644 | public function new() 645 | { 646 | var a = 0; 647 | 648 | beforeEach({ 649 | a = 1; 650 | }); 651 | 652 | describe("Using a 'before' and 'after' block outside describe", { 653 | it("should run the blocks before each describe in the class.", { 654 | a.should.be(1); 655 | }); 656 | }); 657 | 658 | describe("Using a 'before' and 'after' block outside another describe", { 659 | it("should run the blocks before and after that describe too.", { 660 | a.should.be(1); 661 | }); 662 | }); 663 | 664 | afterEach({ 665 | a = 0; 666 | }); 667 | } 668 | } 669 | 670 | class BeforeAfterDescribe2 extends BuddySuite 671 | { 672 | public function new() 673 | { 674 | var a = 0; 675 | 676 | afterEach(function(done) { 677 | a = 1; 678 | done(); 679 | }); 680 | 681 | describe("Using an 'after' block outside describe", { 682 | it("should not run the after block before the first describe.", { 683 | a.should.be(0); 684 | }); 685 | }); 686 | 687 | describe("Using an 'after' block outside another describe in the same class", { 688 | it("should run the after blocks before and after the second describe.", { 689 | a.should.be(1); 690 | }); 691 | }); 692 | } 693 | } 694 | 695 | class BeforeAfterDescribe3 extends BuddySuite 696 | { 697 | public function new() 698 | { 699 | describe('Using nested describes', function () { 700 | var a = 0; 701 | var b = 0; 702 | 703 | beforeEach({ 704 | a = 1; 705 | }); 706 | 707 | it('should not run the specs described after an "it"', function() { 708 | a = 0; 709 | b.should.be(0); 710 | }); 711 | 712 | describe('When nesting describes', function () { 713 | it('should run the inner "before" function before the spec', function() { 714 | b = 1; 715 | a.should.be(1); 716 | }); 717 | }); 718 | 719 | it('should have run the specs described before an "it"', function() { 720 | b.should.be(1); 721 | }); 722 | }); 723 | } 724 | } 725 | 726 | class NestedBeforeAfter extends BuddySuite 727 | { 728 | public function new() 729 | { 730 | var a = -1000; 731 | var order = []; 732 | 733 | beforeAll({ 734 | a = 0; // Level 0 735 | order.push("BA0"); 736 | }); 737 | 738 | beforeEach({ 739 | order.push("BE0"); 740 | }); 741 | 742 | describe('Using nested describes with multiple befores', function () { 743 | beforeAll({ 744 | order.push("BA1"); 745 | a++; 746 | }); 747 | 748 | beforeEach({ 749 | order.push("BE1A"); 750 | }); 751 | 752 | beforeEach({ 753 | order.push("BE1B"); 754 | }); 755 | 756 | it('should run befores outwards and in, and after inwards and out', function() { 757 | order.push("IT1"); 758 | true.should.be(true); // Could change in after() 759 | }); 760 | 761 | it('should run the befores defined up to this nested level', function() { 762 | order.push("IT2"); 763 | a.should.be(1); 764 | }); 765 | 766 | describe('When nesting on another level', function () { 767 | beforeAll( { 768 | order.push("BA2"); 769 | a++; 770 | }); 771 | 772 | beforeEach({ 773 | order.push("BE2"); 774 | }); 775 | 776 | it('should run the before defined up to this level', function () { 777 | order.push("IT3"); 778 | a.should.be(2); 779 | 780 | //trace(CallStack.callStack().map(function(s) return s + "\n")); 781 | }); 782 | 783 | afterEach({ 784 | order.push("AE2"); 785 | }); 786 | 787 | afterAll( { 788 | order.push("AA2"); 789 | a--; 790 | }); 791 | }); 792 | 793 | it('should run in correct order when mixing its and describes', { 794 | order.push("IT4"); 795 | a.should.be(1); 796 | }); 797 | 798 | afterEach({ 799 | order.push("AE1"); 800 | }); 801 | 802 | afterAll({ 803 | a--; 804 | order.push("AA1"); 805 | }); 806 | }); 807 | 808 | afterEach({ 809 | order.push("AE0"); 810 | }); 811 | 812 | afterAll({ 813 | order.push("AA0"); 814 | SelfTest.passLastSpecIf(a == 0 && order.join(",") == 815 | "BA0,BA1,BE0,BE1A,BE1B,IT1,AE1,AE0,BE0,BE1A,BE1B,IT2,AE1,AE0," + 816 | "BA2,BE0,BE1A,BE1B,BE2,IT3,AE2,AE1,AE0,AA2,BE0,BE1A,BE1B,IT4,AE1,AE0,AA1,AA0", 817 | ("Incorrect nested order: " + order) 818 | ); 819 | }); 820 | } 821 | } 822 | 823 | class SimpleNestedBeforeAfter extends BuddySuite 824 | { 825 | public function new() { 826 | describe("Using before/after", { 827 | var test = 0; 828 | 829 | // Will run once in the current describe block 830 | beforeAll({ 831 | test++; 832 | }); 833 | 834 | // Will run before every "it" in this *and* in any nested describes. 835 | beforeEach({ 836 | test++; 837 | }); 838 | 839 | it("should be a convenient way to set up tests", { 840 | test.should.be(2); 841 | }); 842 | 843 | describe("When nesting describes", { 844 | beforeEach({ 845 | test++; 846 | }); 847 | 848 | it("should run all before/afterEach defined here or above", { 849 | test.should.be(3); 850 | }); 851 | 852 | afterEach({ 853 | test--; 854 | }); 855 | }); 856 | 857 | it("should run in correct order too", { 858 | test.should.be(2); 859 | }); 860 | 861 | // Will run after every "it" in this *and* in any nested describes. 862 | afterEach({ 863 | test--; 864 | }); 865 | 866 | // Will run once as the last thing in the current describe block 867 | afterAll({ 868 | test--; 869 | }); 870 | }); 871 | } 872 | } 873 | 874 | class FailTest extends BuddySuite 875 | { 876 | public function new() 877 | { 878 | describe("Failing a test manually", { 879 | it('can be done by throwing an exception', { 880 | throw "Exceptionally"; 881 | }); 882 | 883 | afterEach({ 884 | var test = SelfTest.lastSpec; 885 | SelfTest.passLastSpecIf(test.failures.length == 1 && 886 | test.failures[0].error == "Exceptionally", "Didn't fail when exception was thrown"); 887 | }); 888 | }); 889 | 890 | describe("Failing a test manually", { 891 | it('can be done with the fail() method', { 892 | fail("fail()"); 893 | }); 894 | 895 | afterEach({ 896 | var test = SelfTest.lastSpec; 897 | SelfTest.passLastSpecIf(test.failures.length == 1 && 898 | test.failures[0].error == "fail()", "Didn't fail when fail() was called"); 899 | }); 900 | }); 901 | } 902 | } 903 | 904 | #if !php 905 | class FailTestAsync extends BuddySuite 906 | { 907 | public function new() 908 | { 909 | describe("Failing an async test manually", { 910 | it('can be done by passing the fail method as a callback', function(done) { 911 | var def = new Deferred(); 912 | var pr = def.promise(); 913 | 914 | pr.catchError(fail); 915 | pr.reject("Rejected"); 916 | }); 917 | 918 | afterEach({ 919 | var test = SelfTest.lastSpec; 920 | SelfTest.passLastSpecIf(test.failures.length == 1 && 921 | test.failures[0].error == "Rejected", "Didn't fail when using fail as a callback"); 922 | }); 923 | }); 924 | } 925 | } 926 | 927 | @await 928 | class TinkAwaitTest extends BuddySuite 929 | { 930 | @:await public function new() { 931 | describe("Using tink_await with @await and @async on a BuddySuite", @await function() { 932 | it("should work properly", @await function(done) { 933 | var test = new UsingTinkAwait(); 934 | var status : Bool = @:await test.waitForIt(); 935 | status.should.be(true); 936 | done(); 937 | }); 938 | }); 939 | } 940 | } 941 | 942 | class UsingTinkAwait 943 | { 944 | public function new() {} 945 | 946 | public function waitForIt() { 947 | return Future.async(function(cb) AsyncTools.wait(1).then(cb)); 948 | } 949 | } 950 | #end 951 | 952 | class CallDoneTest extends BuddySuite 953 | { 954 | public function new() 955 | { 956 | describe("A test with no assertions and a call to done()", { 957 | it('should pass, not be marked as pending', function(done) { 958 | done(); 959 | }); 960 | }); 961 | } 962 | } 963 | 964 | class CompilationFailTest extends BuddySuite 965 | { 966 | public function new() 967 | { 968 | describe("A test calling CompilationShould.failFor with an non-compiling expression", { 969 | it('should pass the test', { 970 | CompilationShould.failFor(this.is.not.compiling); 971 | }); 972 | }); 973 | 974 | describe("A test calling CompilationShould.failFor with an compiling expression", { 975 | it('should fail the test', { 976 | CompilationShould.failFor(BuddySuite.useDefaultTrace); 977 | }); 978 | 979 | afterEach({ 980 | var test = SelfTest.lastSpec; 981 | SelfTest.passLastSpecIf(test.failures.length == 1 && test.status == Failed, 982 | "Failed when calling 'CompilationShould.failFor' with a valid expression." 983 | ); 984 | }); 985 | }); 986 | } 987 | } 988 | 989 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 990 | ///////////////////////////////////////////////////////////////////////////////////////////////////////// 991 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 992 | 993 | class HugeTest extends BuddySuite 994 | { 995 | public function new() 996 | { 997 | describe("Testing many and deep tests synchronously", { 998 | it('should not cause a stack overflow', { (true).should.be(true); }); 999 | describe("Testing many and deep tests synchronously", { 1000 | it('should not cause a stack overflow', { (true).should.be(true); }); 1001 | }); 1002 | 1003 | describe("Testing many and deep tests synchronously", { 1004 | it('should not cause a stack overflow', { (true).should.be(true); }); 1005 | describe("Testing many and deep tests synchronously", { 1006 | it('should not cause a stack overflow', { (true).should.be(true); }); 1007 | }); 1008 | }); 1009 | describe("Testing many and deep tests synchronously", { 1010 | it('should not cause a stack overflow', { (true).should.be(true); }); 1011 | describe("Testing many and deep tests synchronously", { 1012 | it('should not cause a stack overflow', { (true).should.be(true); }); 1013 | }); 1014 | describe("Testing many and deep tests synchronously", { 1015 | it('should not cause a stack overflow', { (true).should.be(true); }); 1016 | describe("Testing many and deep tests synchronously", { 1017 | it('should not cause a stack overflow', { (true).should.be(true); }); 1018 | }); 1019 | describe("Testing many and deep tests synchronously", { 1020 | it('should not cause a stack overflow', { (true).should.be(true); }); 1021 | }); 1022 | describe("Testing many and deep tests synchronously", { 1023 | it('should not cause a stack overflow', { (true).should.be(true); }); 1024 | }); 1025 | describe("Testing many and deep tests synchronously", { 1026 | it('should not cause a stack overflow', { (true).should.be(true); }); 1027 | }); 1028 | describe("Testing many and deep tests synchronously", { 1029 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1030 | describe("Testing many and deep tests synchronously", { 1031 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1032 | describe("Testing many and deep tests synchronously", { 1033 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1034 | }); 1035 | describe("Testing many and deep tests synchronously", { 1036 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1037 | describe("Testing many and deep tests synchronously", { 1038 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1039 | }); 1040 | describe("Testing many and deep tests synchronously", { 1041 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1042 | }); 1043 | describe("Testing many and deep tests synchronously", { 1044 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1045 | }); 1046 | describe("Testing many and deep tests synchronously", { 1047 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1048 | describe("Testing many and deep tests synchronously", { 1049 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1050 | describe("Testing many and deep tests synchronously", { 1051 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1052 | }); 1053 | describe("Testing many and deep tests synchronously", { 1054 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1055 | }); 1056 | describe("Testing many and deep tests synchronously", { 1057 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1058 | }); 1059 | describe("Testing many and deep tests synchronously", { 1060 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1061 | describe("Testing many and deep tests synchronously", { 1062 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1063 | describe("Testing many and deep tests synchronously", { 1064 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1065 | }); 1066 | describe("Testing many and deep tests synchronously", { 1067 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1068 | }); 1069 | describe("Testing many and deep tests synchronously", { 1070 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1071 | }); 1072 | describe("Testing many and deep tests synchronously", { 1073 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1074 | }); 1075 | describe("Testing many and deep tests synchronously", { 1076 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1077 | 1078 | describe("Testing many and deep tests synchronously", { 1079 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1080 | describe("Testing many and deep tests synchronously", { 1081 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1082 | }); 1083 | describe("Testing many and deep tests synchronously", { 1084 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1085 | }); 1086 | describe("Testing many and deep tests synchronously", { 1087 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1088 | }); 1089 | describe("Testing many and deep tests synchronously", { 1090 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1091 | }); 1092 | describe("Testing many and deep tests synchronously", { 1093 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1094 | }); 1095 | describe("Testing many and deep tests synchronously", { 1096 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1097 | }); 1098 | describe("Testing many and deep tests synchronously", { 1099 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1100 | }); 1101 | describe("Testing many and deep tests synchronously", { 1102 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1103 | }); 1104 | describe("Testing many and deep tests synchronously", { 1105 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1106 | }); 1107 | }); 1108 | describe("Testing many and deep tests synchronously", { 1109 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1110 | describe("Testing many and deep tests synchronously", { 1111 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1112 | }); 1113 | describe("Testing many and deep tests synchronously", { 1114 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1115 | }); 1116 | describe("Testing many and deep tests synchronously", { 1117 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1118 | }); 1119 | describe("Testing many and deep tests synchronously", { 1120 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1121 | }); 1122 | describe("Testing many and deep tests synchronously", { 1123 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1124 | }); 1125 | describe("Testing many and deep tests synchronously", { 1126 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1127 | }); 1128 | describe("Testing many and deep tests synchronously", { 1129 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1130 | }); 1131 | describe("Testing many and deep tests synchronously", { 1132 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1133 | }); 1134 | describe("Testing many and deep tests synchronously", { 1135 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1136 | }); 1137 | }); 1138 | 1139 | }); 1140 | describe("Testing many and deep tests synchronously", { 1141 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1142 | }); 1143 | describe("Testing many and deep tests synchronously", { 1144 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1145 | }); 1146 | describe("Testing many and deep tests synchronously", { 1147 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1148 | }); 1149 | describe("Testing many and deep tests synchronously", { 1150 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1151 | }); 1152 | }); 1153 | describe("Testing many and deep tests synchronously", { 1154 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1155 | describe("Testing many and deep tests synchronously", { 1156 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1157 | }); 1158 | describe("Testing many and deep tests synchronously", { 1159 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1160 | }); 1161 | describe("Testing many and deep tests synchronously", { 1162 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1163 | }); 1164 | describe("Testing many and deep tests synchronously", { 1165 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1166 | }); 1167 | describe("Testing many and deep tests synchronously", { 1168 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1169 | }); 1170 | describe("Testing many and deep tests synchronously", { 1171 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1172 | }); 1173 | describe("Testing many and deep tests synchronously", { 1174 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1175 | }); 1176 | describe("Testing many and deep tests synchronously", { 1177 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1178 | }); 1179 | describe("Testing many and deep tests synchronously", { 1180 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1181 | }); 1182 | }); 1183 | 1184 | }); 1185 | describe("Testing many and deep tests synchronously", { 1186 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1187 | }); 1188 | describe("Testing many and deep tests synchronously", { 1189 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1190 | }); 1191 | describe("Testing many and deep tests synchronously", { 1192 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1193 | }); 1194 | describe("Testing many and deep tests synchronously", { 1195 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1196 | }); 1197 | describe("Testing many and deep tests synchronously", { 1198 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1199 | }); 1200 | }); 1201 | 1202 | }); 1203 | describe("Testing many and deep tests synchronously", { 1204 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1205 | }); 1206 | describe("Testing many and deep tests synchronously", { 1207 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1208 | }); 1209 | describe("Testing many and deep tests synchronously", { 1210 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1211 | }); 1212 | describe("Testing many and deep tests synchronously", { 1213 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1214 | }); 1215 | describe("Testing many and deep tests synchronously", { 1216 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1217 | }); 1218 | }); 1219 | describe("Testing many and deep tests synchronously", { 1220 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1221 | }); 1222 | describe("Testing many and deep tests synchronously", { 1223 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1224 | }); 1225 | describe("Testing many and deep tests synchronously", { 1226 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1227 | }); 1228 | describe("Testing many and deep tests synchronously", { 1229 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1230 | }); 1231 | describe("Testing many and deep tests synchronously", { 1232 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1233 | }); 1234 | describe("Testing many and deep tests synchronously", { 1235 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1236 | }); 1237 | describe("Testing many and deep tests synchronously", { 1238 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1239 | }); 1240 | describe("Testing many and deep tests synchronously", { 1241 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1242 | }); 1243 | }); 1244 | describe("Testing many and deep tests synchronously", { 1245 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1246 | }); 1247 | describe("Testing many and deep tests synchronously", { 1248 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1249 | }); 1250 | describe("Testing many and deep tests synchronously", { 1251 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1252 | }); 1253 | describe("Testing many and deep tests synchronously", { 1254 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1255 | }); 1256 | describe("Testing many and deep tests synchronously", { 1257 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1258 | }); 1259 | }); 1260 | }); 1261 | }); 1262 | }); 1263 | 1264 | 1265 | 1266 | 1267 | describe("Testing many and deep tests synchronously", { 1268 | it('should not cause a stack overflow', { (true).should.be(true); }); 1269 | describe("Testing many and deep tests synchronously", { 1270 | it('should not cause a stack overflow', { (true).should.be(true); }); 1271 | }); 1272 | 1273 | describe("Testing many and deep tests synchronously", { 1274 | it('should not cause a stack overflow', { (true).should.be(true); }); 1275 | describe("Testing many and deep tests synchronously", { 1276 | it('should not cause a stack overflow', { (true).should.be(true); }); 1277 | }); 1278 | }); 1279 | describe("Testing many and deep tests synchronously", { 1280 | it('should not cause a stack overflow', { (true).should.be(true); }); 1281 | describe("Testing many and deep tests synchronously", { 1282 | it('should not cause a stack overflow', { (true).should.be(true); }); 1283 | }); 1284 | describe("Testing many and deep tests synchronously", { 1285 | it('should not cause a stack overflow', { (true).should.be(true); }); 1286 | describe("Testing many and deep tests synchronously", { 1287 | it('should not cause a stack overflow', { (true).should.be(true); }); 1288 | }); 1289 | describe("Testing many and deep tests synchronously", { 1290 | it('should not cause a stack overflow', { (true).should.be(true); }); 1291 | }); 1292 | describe("Testing many and deep tests synchronously", { 1293 | it('should not cause a stack overflow', { (true).should.be(true); }); 1294 | }); 1295 | describe("Testing many and deep tests synchronously", { 1296 | it('should not cause a stack overflow', { (true).should.be(true); }); 1297 | }); 1298 | describe("Testing many and deep tests synchronously", { 1299 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1300 | describe("Testing many and deep tests synchronously", { 1301 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1302 | describe("Testing many and deep tests synchronously", { 1303 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1304 | }); 1305 | describe("Testing many and deep tests synchronously", { 1306 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1307 | describe("Testing many and deep tests synchronously", { 1308 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1309 | }); 1310 | describe("Testing many and deep tests synchronously", { 1311 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1312 | }); 1313 | describe("Testing many and deep tests synchronously", { 1314 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1315 | }); 1316 | describe("Testing many and deep tests synchronously", { 1317 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1318 | }); 1319 | describe("Testing many and deep tests synchronously", { 1320 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1321 | }); 1322 | describe("Testing many and deep tests synchronously", { 1323 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1324 | }); 1325 | describe("Testing many and deep tests synchronously", { 1326 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1327 | }); 1328 | describe("Testing many and deep tests synchronously", { 1329 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1330 | }); 1331 | describe("Testing many and deep tests synchronously", { 1332 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1333 | }); 1334 | }); 1335 | describe("Testing many and deep tests synchronously", { 1336 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1337 | }); 1338 | describe("Testing many and deep tests synchronously", { 1339 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1340 | }); 1341 | describe("Testing many and deep tests synchronously", { 1342 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1343 | }); 1344 | describe("Testing many and deep tests synchronously", { 1345 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1346 | }); 1347 | describe("Testing many and deep tests synchronously", { 1348 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1349 | }); 1350 | describe("Testing many and deep tests synchronously", { 1351 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1352 | }); 1353 | describe("Testing many and deep tests synchronously", { 1354 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1355 | }); 1356 | describe("Testing many and deep tests synchronously", { 1357 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1358 | }); 1359 | }); 1360 | describe("Testing many and deep tests synchronously", { 1361 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1362 | }); 1363 | describe("Testing many and deep tests synchronously", { 1364 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1365 | }); 1366 | describe("Testing many and deep tests synchronously", { 1367 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1368 | }); 1369 | describe("Testing many and deep tests synchronously", { 1370 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1371 | }); 1372 | describe("Testing many and deep tests synchronously", { 1373 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1374 | }); 1375 | }); 1376 | }); 1377 | }); 1378 | }); 1379 | describe("Testing many and deep tests synchronously", { 1380 | it('should not cause a stack overflow', { (true).should.be(true); }); 1381 | describe("Testing many and deep tests synchronously", { 1382 | it('should not cause a stack overflow', { (true).should.be(true); }); 1383 | }); 1384 | 1385 | describe("Testing many and deep tests synchronously", { 1386 | it('should not cause a stack overflow', { (true).should.be(true); }); 1387 | describe("Testing many and deep tests synchronously", { 1388 | it('should not cause a stack overflow', { (true).should.be(true); }); 1389 | }); 1390 | }); 1391 | describe("Testing many and deep tests synchronously", { 1392 | it('should not cause a stack overflow', { (true).should.be(true); }); 1393 | describe("Testing many and deep tests synchronously", { 1394 | it('should not cause a stack overflow', { (true).should.be(true); }); 1395 | }); 1396 | describe("Testing many and deep tests synchronously", { 1397 | it('should not cause a stack overflow', { (true).should.be(true); }); 1398 | describe("Testing many and deep tests synchronously", { 1399 | it('should not cause a stack overflow', { (true).should.be(true); }); 1400 | }); 1401 | describe("Testing many and deep tests synchronously", { 1402 | it('should not cause a stack overflow', { (true).should.be(true); }); 1403 | }); 1404 | describe("Testing many and deep tests synchronously", { 1405 | it('should not cause a stack overflow', { (true).should.be(true); }); 1406 | }); 1407 | describe("Testing many and deep tests synchronously", { 1408 | it('should not cause a stack overflow', { (true).should.be(true); }); 1409 | }); 1410 | describe("Testing many and deep tests synchronously", { 1411 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1412 | describe("Testing many and deep tests synchronously", { 1413 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1414 | describe("Testing many and deep tests synchronously", { 1415 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1416 | }); 1417 | describe("Testing many and deep tests synchronously", { 1418 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1419 | describe("Testing many and deep tests synchronously", { 1420 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1421 | }); 1422 | describe("Testing many and deep tests synchronously", { 1423 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1424 | }); 1425 | describe("Testing many and deep tests synchronously", { 1426 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1427 | }); 1428 | describe("Testing many and deep tests synchronously", { 1429 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1430 | }); 1431 | describe("Testing many and deep tests synchronously", { 1432 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1433 | }); 1434 | describe("Testing many and deep tests synchronously", { 1435 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1436 | }); 1437 | describe("Testing many and deep tests synchronously", { 1438 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1439 | }); 1440 | describe("Testing many and deep tests synchronously", { 1441 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1442 | }); 1443 | describe("Testing many and deep tests synchronously", { 1444 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1445 | }); 1446 | }); 1447 | describe("Testing many and deep tests synchronously", { 1448 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1449 | }); 1450 | describe("Testing many and deep tests synchronously", { 1451 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1452 | }); 1453 | describe("Testing many and deep tests synchronously", { 1454 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1455 | }); 1456 | describe("Testing many and deep tests synchronously", { 1457 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1458 | }); 1459 | describe("Testing many and deep tests synchronously", { 1460 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1461 | }); 1462 | describe("Testing many and deep tests synchronously", { 1463 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1464 | }); 1465 | describe("Testing many and deep tests synchronously", { 1466 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1467 | }); 1468 | describe("Testing many and deep tests synchronously", { 1469 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1470 | }); 1471 | }); 1472 | describe("Testing many and deep tests synchronously", { 1473 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1474 | }); 1475 | describe("Testing many and deep tests synchronously", { 1476 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1477 | }); 1478 | describe("Testing many and deep tests synchronously", { 1479 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1480 | }); 1481 | describe("Testing many and deep tests synchronously", { 1482 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1483 | }); 1484 | describe("Testing many and deep tests synchronously", { 1485 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1486 | }); 1487 | }); 1488 | }); 1489 | }); 1490 | }); 1491 | describe("Testing many and deep tests synchronously", { 1492 | it('should not cause a stack overflow', { (true).should.be(true); }); 1493 | describe("Testing many and deep tests synchronously", { 1494 | it('should not cause a stack overflow', { (true).should.be(true); }); 1495 | }); 1496 | 1497 | describe("Testing many and deep tests synchronously", { 1498 | it('should not cause a stack overflow', { (true).should.be(true); }); 1499 | describe("Testing many and deep tests synchronously", { 1500 | it('should not cause a stack overflow', { (true).should.be(true); }); 1501 | }); 1502 | }); 1503 | describe("Testing many and deep tests synchronously", { 1504 | it('should not cause a stack overflow', { (true).should.be(true); }); 1505 | describe("Testing many and deep tests synchronously", { 1506 | it('should not cause a stack overflow', { (true).should.be(true); }); 1507 | }); 1508 | describe("Testing many and deep tests synchronously", { 1509 | it('should not cause a stack overflow', { (true).should.be(true); }); 1510 | describe("Testing many and deep tests synchronously", { 1511 | it('should not cause a stack overflow', { (true).should.be(true); }); 1512 | }); 1513 | describe("Testing many and deep tests synchronously", { 1514 | it('should not cause a stack overflow', { (true).should.be(true); }); 1515 | }); 1516 | describe("Testing many and deep tests synchronously", { 1517 | it('should not cause a stack overflow', { (true).should.be(true); }); 1518 | }); 1519 | describe("Testing many and deep tests synchronously", { 1520 | it('should not cause a stack overflow', { (true).should.be(true); }); 1521 | }); 1522 | describe("Testing many and deep tests synchronously", { 1523 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1524 | describe("Testing many and deep tests synchronously", { 1525 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1526 | describe("Testing many and deep tests synchronously", { 1527 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1528 | }); 1529 | describe("Testing many and deep tests synchronously", { 1530 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1531 | describe("Testing many and deep tests synchronously", { 1532 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1533 | }); 1534 | describe("Testing many and deep tests synchronously", { 1535 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1536 | }); 1537 | describe("Testing many and deep tests synchronously", { 1538 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1539 | }); 1540 | describe("Testing many and deep tests synchronously", { 1541 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1542 | }); 1543 | describe("Testing many and deep tests synchronously", { 1544 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1545 | }); 1546 | describe("Testing many and deep tests synchronously", { 1547 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1548 | }); 1549 | describe("Testing many and deep tests synchronously", { 1550 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1551 | }); 1552 | describe("Testing many and deep tests synchronously", { 1553 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1554 | }); 1555 | describe("Testing many and deep tests synchronously", { 1556 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1557 | }); 1558 | }); 1559 | describe("Testing many and deep tests synchronously", { 1560 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1561 | }); 1562 | describe("Testing many and deep tests synchronously", { 1563 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1564 | }); 1565 | describe("Testing many and deep tests synchronously", { 1566 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1567 | }); 1568 | describe("Testing many and deep tests synchronously", { 1569 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1570 | }); 1571 | describe("Testing many and deep tests synchronously", { 1572 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1573 | }); 1574 | describe("Testing many and deep tests synchronously", { 1575 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1576 | }); 1577 | describe("Testing many and deep tests synchronously", { 1578 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1579 | }); 1580 | describe("Testing many and deep tests synchronously", { 1581 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1582 | }); 1583 | }); 1584 | describe("Testing many and deep tests synchronously", { 1585 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1586 | }); 1587 | describe("Testing many and deep tests synchronously", { 1588 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1589 | }); 1590 | describe("Testing many and deep tests synchronously", { 1591 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1592 | }); 1593 | describe("Testing many and deep tests synchronously", { 1594 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1595 | }); 1596 | describe("Testing many and deep tests synchronously", { 1597 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1598 | }); 1599 | }); 1600 | }); 1601 | }); 1602 | }); 1603 | describe("Testing many and deep tests synchronously", { 1604 | it('should not cause a stack overflow', { (true).should.be(true); }); 1605 | describe("Testing many and deep tests synchronously", { 1606 | it('should not cause a stack overflow', { (true).should.be(true); }); 1607 | }); 1608 | 1609 | describe("Testing many and deep tests synchronously", { 1610 | it('should not cause a stack overflow', { (true).should.be(true); }); 1611 | describe("Testing many and deep tests synchronously", { 1612 | it('should not cause a stack overflow', { (true).should.be(true); }); 1613 | }); 1614 | }); 1615 | describe("Testing many and deep tests synchronously", { 1616 | it('should not cause a stack overflow', { (true).should.be(true); }); 1617 | describe("Testing many and deep tests synchronously", { 1618 | it('should not cause a stack overflow', { (true).should.be(true); }); 1619 | }); 1620 | describe("Testing many and deep tests synchronously", { 1621 | it('should not cause a stack overflow', { (true).should.be(true); }); 1622 | describe("Testing many and deep tests synchronously", { 1623 | it('should not cause a stack overflow', { (true).should.be(true); }); 1624 | }); 1625 | describe("Testing many and deep tests synchronously", { 1626 | it('should not cause a stack overflow', { (true).should.be(true); }); 1627 | }); 1628 | describe("Testing many and deep tests synchronously", { 1629 | it('should not cause a stack overflow', { (true).should.be(true); }); 1630 | }); 1631 | describe("Testing many and deep tests synchronously", { 1632 | it('should not cause a stack overflow', { (true).should.be(true); }); 1633 | }); 1634 | describe("Testing many and deep tests synchronously", { 1635 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1636 | describe("Testing many and deep tests synchronously", { 1637 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1638 | describe("Testing many and deep tests synchronously", { 1639 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1640 | }); 1641 | describe("Testing many and deep tests synchronously", { 1642 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1643 | describe("Testing many and deep tests synchronously", { 1644 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1645 | }); 1646 | describe("Testing many and deep tests synchronously", { 1647 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1648 | }); 1649 | describe("Testing many and deep tests synchronously", { 1650 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1651 | }); 1652 | describe("Testing many and deep tests synchronously", { 1653 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1654 | }); 1655 | describe("Testing many and deep tests synchronously", { 1656 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1657 | }); 1658 | describe("Testing many and deep tests synchronously", { 1659 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1660 | }); 1661 | describe("Testing many and deep tests synchronously", { 1662 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1663 | }); 1664 | describe("Testing many and deep tests synchronously", { 1665 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1666 | }); 1667 | describe("Testing many and deep tests synchronously", { 1668 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1669 | }); 1670 | }); 1671 | describe("Testing many and deep tests synchronously", { 1672 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1673 | }); 1674 | describe("Testing many and deep tests synchronously", { 1675 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1676 | }); 1677 | describe("Testing many and deep tests synchronously", { 1678 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1679 | }); 1680 | describe("Testing many and deep tests synchronously", { 1681 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1682 | }); 1683 | describe("Testing many and deep tests synchronously", { 1684 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1685 | }); 1686 | describe("Testing many and deep tests synchronously", { 1687 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1688 | }); 1689 | describe("Testing many and deep tests synchronously", { 1690 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1691 | }); 1692 | describe("Testing many and deep tests synchronously", { 1693 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1694 | }); 1695 | }); 1696 | describe("Testing many and deep tests synchronously", { 1697 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1698 | }); 1699 | describe("Testing many and deep tests synchronously", { 1700 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1701 | }); 1702 | describe("Testing many and deep tests synchronously", { 1703 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1704 | }); 1705 | describe("Testing many and deep tests synchronously", { 1706 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1707 | }); 1708 | describe("Testing many and deep tests synchronously", { 1709 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1710 | }); 1711 | }); 1712 | }); 1713 | }); 1714 | }); 1715 | describe("Testing many and deep tests synchronously", { 1716 | it('should not cause a stack overflow', { (true).should.be(true); }); 1717 | describe("Testing many and deep tests synchronously", { 1718 | it('should not cause a stack overflow', { (true).should.be(true); }); 1719 | }); 1720 | 1721 | describe("Testing many and deep tests synchronously", { 1722 | it('should not cause a stack overflow', { (true).should.be(true); }); 1723 | describe("Testing many and deep tests synchronously", { 1724 | it('should not cause a stack overflow', { (true).should.be(true); }); 1725 | }); 1726 | }); 1727 | describe("Testing many and deep tests synchronously", { 1728 | it('should not cause a stack overflow', { (true).should.be(true); }); 1729 | describe("Testing many and deep tests synchronously", { 1730 | it('should not cause a stack overflow', { (true).should.be(true); }); 1731 | }); 1732 | describe("Testing many and deep tests synchronously", { 1733 | it('should not cause a stack overflow', { (true).should.be(true); }); 1734 | describe("Testing many and deep tests synchronously", { 1735 | it('should not cause a stack overflow', { (true).should.be(true); }); 1736 | }); 1737 | describe("Testing many and deep tests synchronously", { 1738 | it('should not cause a stack overflow', { (true).should.be(true); }); 1739 | }); 1740 | describe("Testing many and deep tests synchronously", { 1741 | it('should not cause a stack overflow', { (true).should.be(true); }); 1742 | }); 1743 | describe("Testing many and deep tests synchronously", { 1744 | it('should not cause a stack overflow', { (true).should.be(true); }); 1745 | }); 1746 | describe("Testing many and deep tests synchronously", { 1747 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1748 | describe("Testing many and deep tests synchronously", { 1749 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1750 | describe("Testing many and deep tests synchronously", { 1751 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1752 | }); 1753 | describe("Testing many and deep tests synchronously", { 1754 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1755 | describe("Testing many and deep tests synchronously", { 1756 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1757 | }); 1758 | describe("Testing many and deep tests synchronously", { 1759 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1760 | }); 1761 | describe("Testing many and deep tests synchronously", { 1762 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1763 | }); 1764 | describe("Testing many and deep tests synchronously", { 1765 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1766 | }); 1767 | describe("Testing many and deep tests synchronously", { 1768 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1769 | }); 1770 | describe("Testing many and deep tests synchronously", { 1771 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1772 | }); 1773 | describe("Testing many and deep tests synchronously", { 1774 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1775 | }); 1776 | describe("Testing many and deep tests synchronously", { 1777 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1778 | }); 1779 | describe("Testing many and deep tests synchronously", { 1780 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1781 | }); 1782 | }); 1783 | describe("Testing many and deep tests synchronously", { 1784 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1785 | }); 1786 | describe("Testing many and deep tests synchronously", { 1787 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1788 | }); 1789 | describe("Testing many and deep tests synchronously", { 1790 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1791 | }); 1792 | describe("Testing many and deep tests synchronously", { 1793 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1794 | }); 1795 | describe("Testing many and deep tests synchronously", { 1796 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1797 | }); 1798 | describe("Testing many and deep tests synchronously", { 1799 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1800 | }); 1801 | describe("Testing many and deep tests synchronously", { 1802 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1803 | }); 1804 | describe("Testing many and deep tests synchronously", { 1805 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1806 | }); 1807 | }); 1808 | describe("Testing many and deep tests synchronously", { 1809 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1810 | }); 1811 | describe("Testing many and deep tests synchronously", { 1812 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1813 | }); 1814 | describe("Testing many and deep tests synchronously", { 1815 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1816 | }); 1817 | describe("Testing many and deep tests synchronously", { 1818 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1819 | }); 1820 | describe("Testing many and deep tests synchronously", { 1821 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1822 | }); 1823 | }); 1824 | }); 1825 | }); 1826 | }); 1827 | describe("Testing many and deep tests synchronously", { 1828 | it('should not cause a stack overflow', { (true).should.be(true); }); 1829 | describe("Testing many and deep tests synchronously", { 1830 | it('should not cause a stack overflow', { (true).should.be(true); }); 1831 | }); 1832 | 1833 | describe("Testing many and deep tests synchronously", { 1834 | it('should not cause a stack overflow', { (true).should.be(true); }); 1835 | describe("Testing many and deep tests synchronously", { 1836 | it('should not cause a stack overflow', { (true).should.be(true); }); 1837 | }); 1838 | }); 1839 | describe("Testing many and deep tests synchronously", { 1840 | it('should not cause a stack overflow', { (true).should.be(true); }); 1841 | describe("Testing many and deep tests synchronously", { 1842 | it('should not cause a stack overflow', { (true).should.be(true); }); 1843 | }); 1844 | describe("Testing many and deep tests synchronously", { 1845 | it('should not cause a stack overflow', { (true).should.be(true); }); 1846 | describe("Testing many and deep tests synchronously", { 1847 | it('should not cause a stack overflow', { (true).should.be(true); }); 1848 | }); 1849 | describe("Testing many and deep tests synchronously", { 1850 | it('should not cause a stack overflow', { (true).should.be(true); }); 1851 | }); 1852 | describe("Testing many and deep tests synchronously", { 1853 | it('should not cause a stack overflow', { (true).should.be(true); }); 1854 | }); 1855 | describe("Testing many and deep tests synchronously", { 1856 | it('should not cause a stack overflow', { (true).should.be(true); }); 1857 | }); 1858 | describe("Testing many and deep tests synchronously", { 1859 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1860 | describe("Testing many and deep tests synchronously", { 1861 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1862 | describe("Testing many and deep tests synchronously", { 1863 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1864 | }); 1865 | describe("Testing many and deep tests synchronously", { 1866 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1867 | describe("Testing many and deep tests synchronously", { 1868 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1869 | }); 1870 | describe("Testing many and deep tests synchronously", { 1871 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1872 | }); 1873 | describe("Testing many and deep tests synchronously", { 1874 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1875 | }); 1876 | describe("Testing many and deep tests synchronously", { 1877 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1878 | }); 1879 | describe("Testing many and deep tests synchronously", { 1880 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1881 | }); 1882 | describe("Testing many and deep tests synchronously", { 1883 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1884 | }); 1885 | describe("Testing many and deep tests synchronously", { 1886 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1887 | }); 1888 | describe("Testing many and deep tests synchronously", { 1889 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1890 | }); 1891 | describe("Testing many and deep tests synchronously", { 1892 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1893 | }); 1894 | }); 1895 | describe("Testing many and deep tests synchronously", { 1896 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1897 | }); 1898 | describe("Testing many and deep tests synchronously", { 1899 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1900 | }); 1901 | describe("Testing many and deep tests synchronously", { 1902 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1903 | }); 1904 | describe("Testing many and deep tests synchronously", { 1905 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1906 | }); 1907 | describe("Testing many and deep tests synchronously", { 1908 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1909 | }); 1910 | describe("Testing many and deep tests synchronously", { 1911 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1912 | }); 1913 | describe("Testing many and deep tests synchronously", { 1914 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1915 | }); 1916 | describe("Testing many and deep tests synchronously", { 1917 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1918 | }); 1919 | }); 1920 | describe("Testing many and deep tests synchronously", { 1921 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1922 | }); 1923 | describe("Testing many and deep tests synchronously", { 1924 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1925 | }); 1926 | describe("Testing many and deep tests synchronously", { 1927 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1928 | }); 1929 | describe("Testing many and deep tests synchronously", { 1930 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1931 | }); 1932 | describe("Testing many and deep tests synchronously", { 1933 | it('should not cause a stack overflow', { (true).should.be(true); } ); 1934 | }); 1935 | }); 1936 | }); 1937 | }); 1938 | }); 1939 | 1940 | } 1941 | } 1942 | --------------------------------------------------------------------------------