root/trunk/test/d_do_test.d

Revision 891, 11.2 kB (checked in by braddr, 1 year ago)

fix#2 for the phobos replace changes

  • Property svn:executable set to *
Line 
1 module d_do_test;
2
3 import std.algorithm;
4 import std.array;
5 import std.conv;
6 import std.exception;
7 import std.file;
8 import std.format;
9 import std.process;
10 import std.random;
11 import std.stdio;
12 import std.string;
13 import core.sys.posix.sys.wait;
14
15 void usage()
16 {
17     write("d_do_test <input_dir> <test_name> <test_extension>\n"
18           "\n"
19           "   input_dir: one of: compilable, fail_compilation, runnable\n"
20           "   test_name: basename of test case to run\n"
21           "   test_extension: one of: d, html, or sh\n"
22           "\n"
23           "   example: d_do_test runnable pi d\n"
24           "\n"
25           "   relevant environment variables:\n"
26           "      ARGS:        set to execute all combinations of\n"
27           "      DMD:         compiler to use, ex: ../src/dmd\n"
28           "      OS:          win32, linux, freebsd, osx\n"
29           "      RESULTS_DIR: base directory for test results\n"
30           "   windows vs non-windows portability env vars:\n"
31           "      DSEP:        \\\\ or /\n"
32           "      SEP:         \\ or /\n"
33           "      OBJ:        .obj or .o\n"
34           "      EXE:        .exe or <null>\n");
35 }
36
37 enum TestMode
38 {
39     COMPILE,
40     FAIL_COMPILE,
41     RUN
42 }
43
44 struct TestArgs
45 {
46     TestMode mode;
47
48     bool     compileSeparately;
49     string   executeArgs;
50     string[] sources;
51     string   permuteArgs;
52     string   postScript;
53     string   requiredArgs;
54 }
55
56 struct EnvData
57 {
58     string all_args;
59     string dmd;
60     string results_dir;
61     string sep;
62     string dsep;
63     string obj;
64     string exe;
65     string os;
66     string model;
67 }
68
69 bool findTestParameter(string file, string token, ref string result)
70 {
71     auto tokenStart = std.string.indexOf(file, token);
72     if (tokenStart == -1) return false;
73
74     auto lineEndR = std.string.indexOf(file[tokenStart .. $], "\r");
75     auto lineEndN = std.string.indexOf(file[tokenStart .. $], "\n");
76     auto lineEnd  = lineEndR == -1 ?
77         (lineEndN == -1 ? file.length : lineEndN) :
78         (lineEndN == -1 ? lineEndR    : min(lineEndR, lineEndN));
79
80     //writeln("found ", token, " in line: ", file.length, ", ", tokenStart, ", ", tokenStart+lineEnd);
81     //writeln("found ", token, " in line: '", file[tokenStart .. tokenStart+lineEnd], "'");
82
83     result = strip(file[tokenStart+token.length .. tokenStart+lineEnd]);
84     // skips the :, if present
85     if (result.length > 0 && result[0] == ':')
86         result = strip(result[1 .. $]);
87
88     //writeln("arg: '", result, "'");
89
90     return true;
91 }
92
93 void gatherTestParameters(ref TestArgs testArgs, string input_dir, string input_file, const ref EnvData envData)
94 {
95     string file = cast(string)std.file.read(input_file);
96
97     if (findTestParameter(file, "REQUIRED_ARGS", testArgs.requiredArgs) &&
98         testArgs.requiredArgs.length > 0)
99     {
100         testArgs.requiredArgs ~= " ";
101     }
102
103     // TODO: should the win32 anti -fPIC code be moved out and made unilateral, would eliminate
104     // different ARGS settings in the Makefile
105     if (findTestParameter(file, "PERMUTE_ARGS", testArgs.permuteArgs))
106     {
107         if (envData.os == "win32")
108         {
109             auto index = std.string.indexOf(testArgs.permuteArgs, "-fPIC");
110             if (index != -1)
111                 testArgs.permuteArgs = testArgs.permuteArgs[0 .. index] ~ testArgs.permuteArgs[index+5 .. $];
112         }
113     }
114     else
115     {
116         if (testArgs.mode != TestMode.FAIL_COMPILE)
117             testArgs.permuteArgs = envData.all_args;
118     }
119
120     findTestParameter(file, "EXECUTE_ARGS", testArgs.executeArgs);
121
122     string extraSourcesStr;
123     findTestParameter(file, "EXTRA_SOURCES", extraSourcesStr);
124     testArgs.sources = [input_file];
125     // prepend input_dir to each extra source file
126     foreach(s; split(extraSourcesStr, " "))
127         testArgs.sources ~= input_dir ~ "/" ~ s;
128
129     // swap / with $SEP
130     if (envData.sep && envData.sep != "/")
131         foreach (ref s; testArgs.sources)
132             s = replace(s, "/", to!string(envData.sep));
133     //writeln ("sources: ", testArgs.sources);
134
135     string compileSeparatelyStr;
136     testArgs.compileSeparately = findTestParameter(file, "COMPILE_SEPARATELY", compileSeparatelyStr);
137
138     if (findTestParameter(file, "POST_SCRIPT", testArgs.postScript))
139         testArgs.postScript = replace(testArgs.postScript, "/", to!string(envData.sep));
140 }
141
142 string[] combinations(string argstr)
143 {
144     string[] results;
145     string[] args = split(argstr, " ");
146     long combinations = 1 << args.length;
147     for (size_t i = 0; i < combinations; i++)
148     {
149         string r;
150         bool printed = false;
151
152         for (size_t j = 0; j < args.length; j++)
153         {
154             if (i & 1 << j)
155             {
156                 if (printed)
157                     r ~= " ";
158                 r ~= args[j];
159                 printed = true;
160             }
161         }
162
163         results ~= r;
164     }
165
166     return results;
167 }
168
169 string genTempFilename()
170 {
171     auto a = appender!string();
172     foreach (ref e; 0 .. 8)
173     { 
174         formattedWrite(a, "%x", rndGen.front);
175         rndGen.popFront;
176     }
177
178     return a.data;
179 }
180
181 int system(string command)
182 {
183     if (!command) return std.c.process.system(null);
184     const commandz = toStringz(command);
185     auto status = std.c.process.system(commandz);
186     if (status == -1) return status;
187     version (Windows) status <<= 8;
188     return status;
189 }
190
191 version(Windows)
192 {
193     extern (D) bool WIFEXITED( int status )    { return ( status & 0x7F ) == 0; }
194     extern (D) int  WEXITSTATUS( int status )  { return ( status & 0xFF00 ) >> 8; }
195     extern (D) int  WTERMSIG( int status )     { return status & 0x7F; }
196     extern (D) bool WIFSIGNALED( int status )
197     {
198         return ( cast(byte) ( ( status & 0x7F ) + 1 ) >> 1 ) > 0;
199     }
200 }
201
202 void execute(ref File f, string command, bool expectpass)
203 {
204     auto filename = genTempFilename();
205     scope(exit) if (std.file.exists(filename)) std.file.remove(filename);
206
207     f.writeln(command);
208     auto rc = system(command ~ " > " ~ filename ~ " 2>&1");
209
210     f.write(readText(filename));
211
212     if (WIFSIGNALED(rc))
213     {
214         auto value = WTERMSIG(rc);
215         enforce(0 == value, "caught signal: " ~ to!string(value));
216     }
217     else if (WIFEXITED(rc))
218     {
219         auto value = WEXITSTATUS(rc);
220         if (expectpass)
221             enforce(0 == value, "expected rc == 0, exited with rc=" ~ to!string(value));
222         else
223             enforce(1 == value, "expected rc == 1, but exited with rc=" ~ to!string(value));
224     }
225 }
226
227 int main(string[] args)
228 {
229     if (args.length != 4)
230     {
231         usage();
232         return 1;
233     }
234
235     string input_dir      = args[1];
236     string test_name      = args[2];
237     string test_extension = args[3];
238
239     EnvData envData;
240     envData.all_args      = getenv("ARGS");
241     envData.results_dir   = getenv("RESULTS_DIR");
242     envData.sep           = getenv("SEP");
243     envData.dsep          = getenv("DSEP");
244     envData.obj           = getenv("OBJ");
245     envData.exe           = getenv("EXE");
246     envData.os            = getenv("OS");
247     envData.dmd           = replace(getenv("DMD"), "/", envData.sep);
248     envData.model         = getenv("MODEL");
249
250     string input_file     = input_dir ~ envData.sep ~ test_name ~ "." ~ test_extension;
251     string output_dir     = envData.results_dir ~ envData.sep ~ input_dir;
252     string output_file    = envData.results_dir ~ envData.sep ~ input_dir ~ envData.sep ~ test_name ~ "." ~ test_extension ~ ".out";
253     string test_app_dmd_base = output_dir ~ envData.sep ~ test_name ~ "_";
254
255     TestArgs testArgs;
256
257     switch (input_dir)
258     {
259         case "compilable":       testArgs.mode = TestMode.COMPILE;      break;
260         case "fail_compilation": testArgs.mode = TestMode.FAIL_COMPILE; break;
261         case "runnable":         testArgs.mode = TestMode.RUN;          break;
262     }
263
264     gatherTestParameters(testArgs, input_dir, input_file, envData);
265
266     writefln(" ... %-30s %s%s(%s)",
267             input_file,
268             testArgs.requiredArgs,
269             (testArgs.requiredArgs ? " " : ""),
270             testArgs.permuteArgs);
271
272     if (std.file.exists(output_file))
273         std.file.remove(output_file);
274
275     auto f = File(output_file, "a");
276
277     foreach(i, c; combinations(testArgs.permuteArgs))
278     {
279         string[] toCleanup;
280
281         string test_app_dmd = test_app_dmd_base ~ to!string(i) ~ envData.exe;
282
283         try
284         {
285             if (!testArgs.compileSeparately)
286             {
287                 string objfile = output_dir ~ envData.sep ~ test_name ~ "_" ~ to!string(i) ~ envData.obj;
288                 toCleanup ~= objfile;
289
290                 if (testArgs.mode == TestMode.RUN)
291                     toCleanup ~= test_app_dmd;
292
293                 string command = format("%s -m%s -I%s %s %s -od%s -of%s %s%s", envData.dmd, envData.model, input_dir,
294                         testArgs.requiredArgs, c, output_dir,
295                         (testArgs.mode == TestMode.RUN ? test_app_dmd : objfile),
296                         (testArgs.mode == TestMode.RUN ? "" : "-c "),
297                         join(testArgs.sources, " "));
298                 version(Windows) command ~= " -map nul.map";
299                 execute(f, command, testArgs.mode != TestMode.FAIL_COMPILE);
300             }
301             else
302             {
303                 foreach (filename; testArgs.sources)
304                 {
305                     string newo= envData.results_dir ~ envData.sep ~
306                         replace(replace(filename, ".d", envData.obj), envData.sep~"imports"~envData.sep, envData.sep);
307                     toCleanup ~= newo;
308
309                     string command = format("%s -m%s -I%s %s %s -od%s -c %s", envData.dmd, envData.model, input_dir,
310                         testArgs.requiredArgs, c, output_dir, filename);
311                     execute(f, command, testArgs.mode != TestMode.FAIL_COMPILE);
312                 }
313
314                 if (testArgs.mode == TestMode.RUN)
315                 {
316                     // link .o's into an executable
317                     string command = format("%s -m%s -od%s -of%s %s", envData.dmd, envData.model, output_dir, test_app_dmd, join(toCleanup, " "));
318                     version(Windows) command ~= " -map nul.map";
319
320                     // add after building the command so that before now, it's purely the .o's involved
321                     toCleanup ~= test_app_dmd;
322
323                     execute(f, command, true);
324                 }
325             }
326
327             if (testArgs.mode == TestMode.RUN)
328             {
329                 string command = test_app_dmd;
330                 if (testArgs.executeArgs) command ~= " " ~ testArgs.executeArgs;
331
332                 execute(f, command, true);
333             }
334
335             if (testArgs.postScript)
336             {
337                 f.write("Executing post-test script: ");
338                 version (Windows) testArgs.postScript = "bash " ~ testArgs.postScript;
339                 execute(f, testArgs.postScript, true);
340             }
341
342             // cleanup
343             foreach (file; toCleanup)
344                 collectException(std.file.remove(file));
345
346             f.writeln();
347         }
348         catch(Exception e)
349         {
350             f.writeln("Caught exception, bailing: ", e.toString());
351             f.close();
352
353             writeln("Test failed.  The logged output:");
354             if (std.file.exists(output_file))
355             {
356                 writeln(std.file.read(output_file));
357                 std.file.remove(output_file);
358             }
359             return 1;
360         }
361     }
362
363     return 0;
364 }
Note: See TracBrowser for help on using the browser.