1020304050607080901001101201301401501601701801902002102202302416325026027028029163301633103216333034163350361633716338039163400410420430440451574615747792480491575015751052053054055056057058659060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092161293161294095145209609732289832289901003228101010201030104010501060107806108806109806110011180611201138061140115806116806117806118011980612080612101220123806124012580612601270128012901300131013201330134013501360137013801390140014101420143014401450146014701480149015001510152151531515401550156015701580159016001610162016311640165016601670168016901700171017201730174017501760177017801790180018101820183018401850186018701880189019001910192019301940195019601970198019902000201020202030204020502060207020802090210021102120213021402150216021702180219022002210222022302240225022602270228022902300231023202330234023502360237023802390240024102420243024402450246024702480249025002510252025302540255025602570258025902600261026202630264026502660267026802690270027102720273027402750 module trial.discovery.spec; import std.algorithm; import std.stdio; import std.array; import std.traits; import std.string; import trial.interfaces; import trial.discovery.code; alias SetupFunction = void delegate() @system; private string[] suitePath; private ulong[string] testsPerSuite; private TestCase[] testCases; private SetupFunction[] beforeList; private SetupFunction[] afterList; void describe(T)(string name, T description) { if (suitePath.length == 0) { suitePath = [moduleName!description]; } auto beforeListIndex = beforeList.length; auto afterListIndex = afterList.length; suitePath ~= name; description(); beforeList = beforeList[0 .. beforeListIndex]; afterList = afterList[0 .. afterListIndex]; suitePath = suitePath[0 .. $ - 1]; } void before(T)(T setup) { bool wasRun; beforeList ~= { if (!wasRun) { setup(); wasRun = true; } }; } void beforeEach(T)(T setup) { beforeList ~= { setup(); }; } void afterEach(T)(T setup) { afterList ~= { setup(); }; } void after(T)(T setup) { string suiteName = suitePath.join("."); long executedTests; bool wasRun; afterList ~= { if (wasRun) { return; } executedTests++; if (testsPerSuite[suiteName] < executedTests) { setup(); wasRun = true; } }; } private void updateTestCounter(string[] path, long value) { string tmp; string glue; foreach (key; path) { tmp ~= glue ~ key; glue = "."; testsPerSuite[tmp] += value; } } void it(T)(string name, T test, string file = __FILE__, size_t line = __LINE__) { auto before = beforeList.dup; auto after = afterList.dup; auto path = suitePath.dup; reverse(after); updateTestCounter(path, 1); auto testCase = TestCase(suitePath.join("."), name, ({ before.each!"a()"; test(); updateTestCounter(path, -1); after.each!"a()"; })); testCase.location = SourceLocation(file, line); testCases ~= testCase; } void it(string name, string file = __FILE__, size_t line = __LINE__) { auto before = beforeList.dup; auto after = afterList.dup; auto path = suitePath.dup; reverse(after); updateTestCounter(path, 1); auto testCase = TestCase(suitePath.join("."), name, ({ throw new PendingTestException(); })); testCase.location = SourceLocation(file, line); testCases ~= testCase; } template Spec(alias definition) { shared static this() { suitePath = [moduleName!definition]; definition(); } } class SpecTestDiscovery : ITestDiscovery { TestCase[] getTestCases() { return testCases; } void addModule(string file, string moduleName)() { } private void noTest() { assert(false, "you can not run this test"); } version(Have_libdparse) { private TestCase[] getTestCasesFromSpec(string file, string suite, const(Token)[] tokens) { TestCase[] testCases; auto iterator = TokenIterator(tokens); foreach(token; iterator) { if(token.text == "describe") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "(") { iterator.skipUntilType("stringLiteral"); string suiteName = iterator.currentToken.text.parseString.strip; auto block = iterator.readNextBlock; testCases ~= getTestCasesFromSpec(file, suite ~ "." ~ suiteName, block); } } if(token.text == "it") { iterator.skipOne.skipWsAndComments; auto location = SourceLocation(file, iterator.currentToken.line); if(str(iterator.currentToken.type) == "(") { iterator.skipUntilType("stringLiteral"); string testName = iterator.currentToken.text.parseString; testCases ~= TestCase(suite, testName, &this.noTest, [], location); } } } return testCases; } } TestCase[] discoverTestCases(string file) { TestCase[] testCases = []; version(Have_fluent_asserts) version(Have_libdparse) { import fluentasserts.core.results; auto tokens = fileToDTokens(file); auto iterator = TokenIterator(tokens); auto moduleName = iterator.skipUntilType("module").skipOne.readUntilType(";").strip; string lastName; DLangAttribute[] attributes; foreach (token; iterator) { auto type = str(token.type); if(token.text == "Spec") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "!") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "(") { auto block = iterator.readNextBlock; testCases ~= getTestCasesFromSpec(file, moduleName, block); } } } } } return testCases; } } string parseString(string someString) { if(someString == ""){ return ""; } if(someString[0] == '"') { return someString[1..$-1].replace(`\"`, `"`); } return someString[1..$-1]; }
module trial.discovery.spec; import std.algorithm; import std.stdio; import std.array; import std.traits; import std.string; import trial.interfaces; import trial.discovery.code; alias SetupFunction = void delegate() @system; private string[] suitePath; private ulong[string] testsPerSuite; private TestCase[] testCases; private SetupFunction[] beforeList; private SetupFunction[] afterList; void describe(T)(string name, T description) { if (suitePath.length == 0) { suitePath = [moduleName!description]; } auto beforeListIndex = beforeList.length; auto afterListIndex = afterList.length; suitePath ~= name; description(); beforeList = beforeList[0 .. beforeListIndex]; afterList = afterList[0 .. afterListIndex]; suitePath = suitePath[0 .. $ - 1]; } void before(T)(T setup) { bool wasRun; beforeList ~= { if (!wasRun) { setup(); wasRun = true; } }; } void beforeEach(T)(T setup) { beforeList ~= { setup(); }; } void afterEach(T)(T setup) { afterList ~= { setup(); }; } void after(T)(T setup) { string suiteName = suitePath.join("."); long executedTests; bool wasRun; afterList ~= { if (wasRun) { return; } executedTests++; if (testsPerSuite[suiteName] < executedTests) { setup(); wasRun = true; } }; } private void updateTestCounter(string[] path, long value) { string tmp; string glue; foreach (key; path) { tmp ~= glue ~ key; glue = "."; testsPerSuite[tmp] += value; } } void it(T)(string name, T test, string file = __FILE__, size_t line = __LINE__) { auto before = beforeList.dup; auto after = afterList.dup; auto path = suitePath.dup; reverse(after); updateTestCounter(path, 1); auto testCase = TestCase(suitePath.join("."), name, ({ before.each!"a()"; test(); updateTestCounter(path, -1); after.each!"a()"; })); testCase.location = SourceLocation(file, line); testCases ~= testCase; } void it(string name, string file = __FILE__, size_t line = __LINE__) { auto before = beforeList.dup; auto after = afterList.dup; auto path = suitePath.dup; reverse(after); updateTestCounter(path, 1); auto testCase = TestCase(suitePath.join("."), name, ({ throw new PendingTestException(); })); testCase.location = SourceLocation(file, line); testCases ~= testCase; } template Spec(alias definition) { shared static this() { suitePath = [moduleName!definition]; definition(); } } class SpecTestDiscovery : ITestDiscovery { TestCase[] getTestCases() { return testCases; } void addModule(string file, string moduleName)() { } private void noTest() { assert(false, "you can not run this test"); } version(Have_libdparse) { private TestCase[] getTestCasesFromSpec(string file, string suite, const(Token)[] tokens) { TestCase[] testCases; auto iterator = TokenIterator(tokens); foreach(token; iterator) { if(token.text == "describe") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "(") { iterator.skipUntilType("stringLiteral"); string suiteName = iterator.currentToken.text.parseString.strip; auto block = iterator.readNextBlock; testCases ~= getTestCasesFromSpec(file, suite ~ "." ~ suiteName, block); } } if(token.text == "it") { iterator.skipOne.skipWsAndComments; auto location = SourceLocation(file, iterator.currentToken.line); if(str(iterator.currentToken.type) == "(") { iterator.skipUntilType("stringLiteral"); string testName = iterator.currentToken.text.parseString; testCases ~= TestCase(suite, testName, &this.noTest, [], location); } } } return testCases; } } TestCase[] discoverTestCases(string file) { TestCase[] testCases = []; version(Have_fluent_asserts) version(Have_libdparse) { import fluentasserts.core.results; auto tokens = fileToDTokens(file); auto iterator = TokenIterator(tokens); auto moduleName = iterator.skipUntilType("module").skipOne.readUntilType(";").strip; string lastName; DLangAttribute[] attributes; foreach (token; iterator) { auto type = str(token.type); if(token.text == "Spec") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "!") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "(") { auto block = iterator.readNextBlock; testCases ~= getTestCasesFromSpec(file, moduleName, block); } } } } } return testCases; } } string parseString(string someString) { if(someString == ""){ return ""; } if(someString[0] == '"') { return someString[1..$-1].replace(`\"`, `"`); } return someString[1..$-1]; }