diff --git a/posix.mak b/posix.mak index d4d6051a85..c27c2926d4 100644 --- a/posix.mak +++ b/posix.mak @@ -107,8 +107,10 @@ $(ROOT)/tests_extractor: tests_extractor.d ################################################################################ test_tests_extractor: $(ROOT)/tests_extractor - $< -i ./test/tests_extractor/ascii.d | diff - ./test/tests_extractor/ascii.d.ext - $< -i ./test/tests_extractor/iteration.d | diff - ./test/tests_extractor/iteration.d.ext + for file in ascii iteration ; do \ + $< -i "./test/tests_extractor/$${file}.d" | diff -p - "./test/tests_extractor/$${file}.d.ext"; \ + done + $< -a betterc -i "./test/tests_extractor/attributes.d" | diff -p - "./test/tests_extractor/attributes.d.ext"; RDMD_TEST_COMPILERS = $(DMD) RDMD_TEST_EXECUTABLE = $(ROOT)/rdmd diff --git a/test/tests_extractor/attributes.d b/test/tests_extractor/attributes.d new file mode 100644 index 0000000000..1d7416a650 --- /dev/null +++ b/test/tests_extractor/attributes.d @@ -0,0 +1,44 @@ +module attributes; + +enum betterc; + +@betterc @safe @("foo") unittest +{ + assert(1 == 1); +} + +@safe @("foo") unittest +{ + assert(2 == 2); +} + +/// +@("foo") unittest +{ + assert(3 == 3); +} + +@("foo") @betterc unittest +{ + assert(4 == 4); +} + +@("betterc") @([1, 2, 3]) unittest +{ + assert(5 == 5); +} + +@nogc @("foo", "betterc", "bar") @safe unittest +{ + assert(6 == 6); +} + +@nogc @("foo", "better", "bar") @safe unittest +{ + assert(7 == 7); +} + +@("betterd") unittest +{ + assert(8 == 8); +} diff --git a/test/tests_extractor/attributes.d.ext b/test/tests_extractor/attributes.d.ext new file mode 100644 index 0000000000..cf8af609f7 --- /dev/null +++ b/test/tests_extractor/attributes.d.ext @@ -0,0 +1,32 @@ +# line 3 +unittest +{ + import attributes; + + assert(1 == 1); +} + +# line 19 +unittest +{ + import attributes; + + assert(4 == 4); +} + +# line 24 +unittest +{ + import attributes; + + assert(5 == 5); +} + +# line 29 +unittest +{ + import attributes; + + assert(6 == 6); +} + diff --git a/tests_extractor.d b/tests_extractor.d index f7099d21d6..de9c1387c9 100755 --- a/tests_extractor.d +++ b/tests_extractor.d @@ -7,7 +7,7 @@ dependency "libdparse" version="~>0.8.0" * Parses all public unittests that are visible on dlang.org * (= annotated with three slashes) * - * Copyright (C) 2017 by D Language Foundation + * Copyright (C) 2018 by D Language Foundation * * Author: Sebastian Wilzbach * @@ -33,11 +33,13 @@ class TestVisitor : ASTVisitor File outFile; ubyte[] sourceCode; string moduleName; + string[] attributes; - this(File outFile, ubyte[] sourceCode) + this(File outFile, ubyte[] sourceCode, string[] attributes) { this.outFile = outFile; this.sourceCode = sourceCode; + this.attributes = attributes; } alias visit = ASTVisitor.visit; @@ -58,13 +60,51 @@ class TestVisitor : ASTVisitor override void visit(const Declaration decl) { - if (decl.unittest_ !is null && hasDdocHeader(sourceCode, decl)) + if (decl.unittest_ !is null && shouldIncludeUnittest(decl)) print(decl.unittest_); decl.accept(this); } private: + + bool shouldIncludeUnittest(const Declaration decl) + { + if (!attributes.empty) + return filterForUDAs(decl); + else + return hasDdocHeader(sourceCode, decl); + } + + bool filterForUDAs(const Declaration decl) + { + foreach (attr; decl.attributes) + { + // check for @myArg + if (attributes.canFind(attr.atAttribute.identifier.text)) + return true; + + // support @("myArg") too + if (auto argList = attr.atAttribute.argumentList) + { + foreach (arg; argList.items) + { + if (auto unaryExp = cast(UnaryExpression) arg) + if (auto primaryExp = unaryExp.primaryExpression) + { + auto attribute = primaryExp.primary.text; + if (attribute.length >= 2) + { + attribute = attribute[1 .. $ - 1]; + if (attributes.canFind(attribute)) + return true; + } + } + } + } + } + return false; + } void print(const Unittest u) { /* @@ -93,7 +133,7 @@ private: } } -void parseFile(File inFile, File outFile) +void parseFile(File inFile, File outFile, string[] attributes) { import dparse.lexer; import dparse.parser : parseModule; @@ -111,11 +151,11 @@ void parseFile(File inFile, File outFile) RollbackAllocator rba; auto m = parseModule(tokens.array, inFile.name, &rba); - auto visitor = new TestVisitor(outFile, sourceCode); + auto visitor = new TestVisitor(outFile, sourceCode, attributes); visitor.visit(m); } -void parseFileDir(string inputDir, string fileName, string outputDir) +void parseFileDir(string inputDir, string fileName, string outputDir, string[] attributes) { import std.path : buildPath, dirSeparator, buildNormalizedPath; @@ -132,7 +172,7 @@ void parseFileDir(string inputDir, string fileName, string outputDir) // convert the file path to a nice output file, e.g. std/uni.d -> std_uni.d string outName = fileNameNormalized.replace(dirSeparator, "_"); - parseFile(File(fileName), File(buildPath(outputDir, outName), "w")); + parseFile(File(fileName), File(buildPath(outputDir, outName), "w"), attributes); } void main(string[] args) @@ -143,12 +183,15 @@ void main(string[] args) string inputDir; string outputDir = "./out"; string ignoredFilesStr; - string modulePrefix = ""; + string modulePrefix; + string attributesStr; auto helpInfo = getopt(args, config.required, "inputdir|i", "Folder to start the recursive search for unittest blocks (can be a single file)", &inputDir, "outputdir|o", "Folder to which the extracted test files should be saved (stdout for a single file)", &outputDir, - "ignore", "Comma-separated list of files to exclude (partial matching is supported)", &ignoredFilesStr); + "ignore", "Comma-separated list of files to exclude (partial matching is supported)", &ignoredFilesStr, + "attributes|a", "Comma-separated list of UDAs that the unittest should have", &attributesStr, + ); if (helpInfo.helpWanted) { @@ -162,6 +205,7 @@ to in the output directory. inputDir = inputDir.asNormalizedPath.array; Algebraic!(string, File) outputLocation = cast(string) outputDir.asNormalizedPath.array; + auto attributes = attributesStr.split(","); if (!exists(outputDir)) mkdir(outputDir); @@ -196,8 +240,8 @@ to in the output directory. { stderr.writeln("parsing ", file); outputLocation.visit!( - (string outputFolder) => parseFileDir(inputDir, file, outputFolder), - (File outputFile) => parseFile(File(file.name, "r"), outputFile), + (string outputFolder) => parseFileDir(inputDir, file, outputFolder, attributes), + (File outputFile) => parseFile(File(file.name, "r"), outputFile, attributes), ); } else