Skip to content

Commit b2cb7b8

Browse files
committed
Add -betterC unittest output
1 parent ca5384d commit b2cb7b8

4 files changed

Lines changed: 74 additions & 13 deletions

File tree

posix.mak

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ test_tests_extractor: $(ROOT)/tests_extractor
111111
$< -i "./test/tests_extractor/$${file}.d" | diff -p - "./test/tests_extractor/$${file}.d.ext"; \
112112
done
113113
$< -a betterc -i "./test/tests_extractor/attributes.d" | diff -p - "./test/tests_extractor/attributes.d.ext";
114+
$< --betterC -i "./test/tests_extractor/betterc.d" | diff -p - "./test/tests_extractor/betterc.d.ext";
114115

115116
RDMD_TEST_COMPILERS = $(DMD)
116117
RDMD_TEST_EXECUTABLE = $(ROOT)/rdmd

test/tests_extractor/betterc.d

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module betterc;
2+
3+
///
4+
unittest
5+
{
6+
int a = 1;
7+
assert(a == 2);
8+
}
9+
10+
///
11+
unittest
12+
{
13+
int b = 2;
14+
assert(b == 2);
15+
assert(b == 3);
16+
}

test/tests_extractor/betterc.d.ext

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# line 2
2+
unittest
3+
{
4+
import betterc;
5+
6+
int a = 1;
7+
assert(a == 2);
8+
}
9+
10+
# line 9
11+
unittest
12+
{
13+
import betterc;
14+
15+
int b = 2;
16+
assert(b == 2);
17+
assert(b == 3);
18+
}
19+
20+
extern(C) void main()
21+
{
22+
static foreach(u; __traits(getUnitTests, __traits(parent, main)))
23+
u();
24+
}

tests_extractor.d

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ class TestVisitor : ASTVisitor
3333
File outFile;
3434
ubyte[] sourceCode;
3535
string moduleName;
36-
string[] attributes;
36+
VisitorConfig config;
3737

38-
this(File outFile, ubyte[] sourceCode, string[] attributes)
38+
this(File outFile, ubyte[] sourceCode, VisitorConfig config)
3939
{
4040
this.outFile = outFile;
4141
this.sourceCode = sourceCode;
42-
this.attributes = attributes;
42+
this.config = config;
4343
}
4444

4545
alias visit = ASTVisitor.visit;
@@ -56,6 +56,15 @@ class TestVisitor : ASTVisitor
5656
moduleName = outFile.name.replace(".d", "").replace(dirSeparator, ".").replace(".package", "");
5757
}
5858
m.accept(this);
59+
// -betterC doesn't run unittests out of the box
60+
if (config.betterCOutput)
61+
{
62+
outFile.writeln(q{extern(C) void main()
63+
{
64+
static foreach(u; __traits(getUnitTests, __traits(parent, main)))
65+
u();
66+
}});
67+
}
5968
}
6069

6170
override void visit(const Declaration decl)
@@ -70,7 +79,7 @@ private:
7079

7180
bool shouldIncludeUnittest(const Declaration decl)
7281
{
73-
if (!attributes.empty)
82+
if (!config.attributes.empty)
7483
return filterForUDAs(decl);
7584
else
7685
return hasDdocHeader(sourceCode, decl);
@@ -80,8 +89,11 @@ private:
8089
{
8190
foreach (attr; decl.attributes)
8291
{
92+
if (attr.atAttribute is null)
93+
continue;
94+
8395
// check for @myArg
84-
if (attributes.canFind(attr.atAttribute.identifier.text))
96+
if (config.attributes.canFind(attr.atAttribute.identifier.text))
8597
return true;
8698

8799
// support @("myArg") too
@@ -96,7 +108,7 @@ private:
96108
if (attribute.length >= 2)
97109
{
98110
attribute = attribute[1 .. $ - 1];
99-
if (attributes.canFind(attribute))
111+
if (config.attributes.canFind(attribute))
100112
return true;
101113
}
102114
}
@@ -133,7 +145,7 @@ private:
133145
}
134146
}
135147

136-
void parseFile(File inFile, File outFile, string[] attributes)
148+
void parseFile(File inFile, File outFile, VisitorConfig visitorConfig)
137149
{
138150
import dparse.lexer;
139151
import dparse.parser : parseModule;
@@ -151,11 +163,11 @@ void parseFile(File inFile, File outFile, string[] attributes)
151163

152164
RollbackAllocator rba;
153165
auto m = parseModule(tokens.array, inFile.name, &rba);
154-
auto visitor = new TestVisitor(outFile, sourceCode, attributes);
166+
auto visitor = new TestVisitor(outFile, sourceCode, visitorConfig);
155167
visitor.visit(m);
156168
}
157169

158-
void parseFileDir(string inputDir, string fileName, string outputDir, string[] attributes)
170+
void parseFileDir(string inputDir, string fileName, string outputDir, VisitorConfig visitorConfig)
159171
{
160172
import std.path : buildPath, dirSeparator, buildNormalizedPath;
161173

@@ -172,7 +184,13 @@ void parseFileDir(string inputDir, string fileName, string outputDir, string[] a
172184
// convert the file path to a nice output file, e.g. std/uni.d -> std_uni.d
173185
string outName = fileNameNormalized.replace(dirSeparator, "_");
174186

175-
parseFile(File(fileName), File(buildPath(outputDir, outName), "w"), attributes);
187+
parseFile(File(fileName), File(buildPath(outputDir, outName), "w"), visitorConfig);
188+
}
189+
190+
struct VisitorConfig
191+
{
192+
string[] attributes; /// List of attributes to extract;
193+
bool betterCOutput; /// Add custom extern(C) main method for running D's unittests
176194
}
177195

178196
void main(string[] args)
@@ -185,12 +203,14 @@ void main(string[] args)
185203
string ignoredFilesStr;
186204
string modulePrefix;
187205
string attributesStr;
206+
VisitorConfig visitorConfig;
188207

189208
auto helpInfo = getopt(args, config.required,
190209
"inputdir|i", "Folder to start the recursive search for unittest blocks (can be a single file)", &inputDir,
191210
"outputdir|o", "Folder to which the extracted test files should be saved (stdout for a single file)", &outputDir,
192211
"ignore", "Comma-separated list of files to exclude (partial matching is supported)", &ignoredFilesStr,
193212
"attributes|a", "Comma-separated list of UDAs that the unittest should have", &attributesStr,
213+
"betterC", "Add custom extern(C) main method for running D's unittests", &visitorConfig.betterCOutput,
194214
);
195215

196216
if (helpInfo.helpWanted)
@@ -205,7 +225,7 @@ to in the output directory.
205225

206226
inputDir = inputDir.asNormalizedPath.array;
207227
Algebraic!(string, File) outputLocation = cast(string) outputDir.asNormalizedPath.array;
208-
auto attributes = attributesStr.split(",");
228+
visitorConfig.attributes = attributesStr.split(",");
209229

210230
if (!exists(outputDir))
211231
mkdir(outputDir);
@@ -240,8 +260,8 @@ to in the output directory.
240260
{
241261
stderr.writeln("parsing ", file);
242262
outputLocation.visit!(
243-
(string outputFolder) => parseFileDir(inputDir, file, outputFolder, attributes),
244-
(File outputFile) => parseFile(File(file.name, "r"), outputFile, attributes),
263+
(string outputFolder) => parseFileDir(inputDir, file, outputFolder, visitorConfig),
264+
(File outputFile) => parseFile(File(file.name, "r"), outputFile, visitorConfig),
245265
);
246266
}
247267
else

0 commit comments

Comments
 (0)