@@ -33,13 +33,14 @@ class TestVisitor : ASTVisitor
3333 File outFile;
3434 ubyte [] sourceCode;
3535 string moduleName;
36- string [] attributes;
36+ VisitorConfig config;
37+ int unittestCounter; // in betterC output mode, `void unittest_%d() {` are emitted instead of `unittest {
3738
38- this (File outFile, ubyte [] sourceCode, string [] attributes )
39+ this (File outFile, ubyte [] sourceCode, VisitorConfig config )
3940 {
4041 this .outFile = outFile;
4142 this .sourceCode = sourceCode;
42- this .attributes = attributes ;
43+ this .config = config ;
4344 }
4445
4546 alias visit = ASTVisitor.visit;
@@ -56,6 +57,14 @@ class TestVisitor : ASTVisitor
5657 moduleName = outFile.name.replace(" .d" , " " ).replace(dirSeparator, " ." ).replace(" .package" , " " );
5758 }
5859 m.accept(this );
60+ // -betterC doesn't support running unittests yet
61+ if (config.betterCOutput)
62+ {
63+ outFile.writeln(" extern(C) void main()\n {" );
64+ scope (exit) outFile.writeln(" }" );
65+ foreach (i; 0 .. unittestCounter)
66+ outFile.writefln(" unittest_%d();" , i);
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 }
@@ -116,7 +128,10 @@ private:
116128 outFile.writefln(" # line %d" , u.line > 2 ? u.line - 2 : 0 );
117129
118130 // write the unittest block
119- outFile.write(" unittest\n {\n " );
131+ if (config.betterCOutput)
132+ outFile.writef(" void unittest_%d()\n {\n " , unittestCounter++ );
133+ else
134+ outFile.write(" unittest\n {\n " );
120135 scope (exit) outFile.writeln(" }\n " );
121136
122137 // add an import to the current module
@@ -133,7 +148,7 @@ private:
133148 }
134149}
135150
136- void parseFile (File inFile, File outFile, string [] attributes )
151+ void parseFile (File inFile, File outFile, VisitorConfig visitorConfig )
137152{
138153 import dparse.lexer;
139154 import dparse.parser : parseModule;
@@ -151,11 +166,11 @@ void parseFile(File inFile, File outFile, string[] attributes)
151166
152167 RollbackAllocator rba;
153168 auto m = parseModule(tokens.array, inFile.name, &rba);
154- auto visitor = new TestVisitor(outFile, sourceCode, attributes );
169+ auto visitor = new TestVisitor(outFile, sourceCode, visitorConfig );
155170 visitor.visit(m);
156171}
157172
158- void parseFileDir (string inputDir, string fileName, string outputDir, string [] attributes )
173+ void parseFileDir (string inputDir, string fileName, string outputDir, VisitorConfig visitorConfig )
159174{
160175 import std.path : buildPath, dirSeparator, buildNormalizedPath;
161176
@@ -172,7 +187,13 @@ void parseFileDir(string inputDir, string fileName, string outputDir, string[] a
172187 // convert the file path to a nice output file, e.g. std/uni.d -> std_uni.d
173188 string outName = fileNameNormalized.replace(dirSeparator, " _" );
174189
175- parseFile(File (fileName), File (buildPath(outputDir, outName), " w" ), attributes);
190+ parseFile(File (fileName), File (buildPath(outputDir, outName), " w" ), visitorConfig);
191+ }
192+
193+ struct VisitorConfig
194+ {
195+ string [] attributes; // / List of attributes to extract;
196+ bool betterCOutput; // / Emit unittest functions and a custom main method compatible with -betterC"
176197}
177198
178199void main (string [] args)
@@ -185,12 +206,14 @@ void main(string[] args)
185206 string ignoredFilesStr;
186207 string modulePrefix;
187208 string attributesStr;
209+ VisitorConfig visitorConfig;
188210
189211 auto helpInfo = getopt(args, config.required,
190212 " inputdir|i" , " Folder to start the recursive search for unittest blocks (can be a single file)" , &inputDir,
191213 " outputdir|o" , " Folder to which the extracted test files should be saved (stdout for a single file)" , &outputDir,
192214 " ignore" , " Comma-separated list of files to exclude (partial matching is supported)" , &ignoredFilesStr,
193215 " attributes|a" , " Comma-separated list of UDAs that the unittest should have" , &attributesStr,
216+ " betterC" , " Emit unittest functions and a custom main method compatible with -betterC" , &visitorConfig.betterCOutput,
194217 );
195218
196219 if (helpInfo.helpWanted)
@@ -205,7 +228,7 @@ to in the output directory.
205228
206229 inputDir = inputDir.asNormalizedPath.array;
207230 Algebraic! (string , File ) outputLocation = cast (string ) outputDir.asNormalizedPath.array;
208- auto attributes = attributesStr.split(" ," );
231+ visitorConfig. attributes = attributesStr.split(" ," );
209232
210233 if (! exists(outputDir))
211234 mkdir(outputDir);
@@ -240,8 +263,8 @@ to in the output directory.
240263 {
241264 stderr.writeln(" parsing " , file);
242265 outputLocation.visit! (
243- (string outputFolder) => parseFileDir(inputDir, file, outputFolder, attributes ),
244- (File outputFile) => parseFile(File (file.name, " r" ), outputFile, attributes ),
266+ (string outputFolder) => parseFileDir(inputDir, file, outputFolder, visitorConfig ),
267+ (File outputFile) => parseFile(File (file.name, " r" ), outputFile, visitorConfig ),
245268 );
246269 }
247270 else
0 commit comments