1 module test;
2 
3 import std.array;
4 import std.process;
5 import std.stdio;
6 import std.file;
7 import std.path;
8 import std.algorithm;
9 import std..string;
10 import std.traits : ReturnType;
11 
12 void main ()
13 {
14     TestRunner().run();
15 }
16 
17 private:
18 
19 /**
20  * The available test groups.
21  *
22  * The tests will be run in the order specified here.
23  */
24 enum TestGroup
25 {
26     unit = "unit",
27     library = "library",
28     functional = "functional"
29 }
30 
31 struct TestRunner
32 {
33     void run ()
34     {
35         import std.traits : EnumMembers;
36 
37         foreach (test ; EnumMembers!TestGroup)
38             runTest!(test);
39 
40         stdout.flush();
41     }
42 
43     /**
44      * Run a single group of tests, i.e. functional, library or unit test.
45      *
46      * Params:
47      *  testGroup = the test group to run
48      *
49      * Returns: the exist code of the test run
50      */
51     void runTest (TestGroup testGroup)()
52     {
53         import std..string : capitalize;
54 
55         enum beforeFunction = "before" ~ testGroup.capitalize;
56 
57         static if (is(typeof(mixin(beforeFunction))))
58             mixin(beforeFunction ~ "();");
59 
60         writef("Running %s tests ", testGroup);
61         stdout.flush();
62         const command = dubShellCommand("--config=test-" ~ testGroup);
63         executeCommand(command);
64     }
65 
66     void beforeFunctional() @safe
67     {
68         if (dstepBuilt)
69             return;
70 
71         writeln("Building DStep");
72         const command = dubShellCommand("build", "--build=debug");
73         executeCommand(command);
74     }
75 }
76 
77 @safe:
78 
79 bool dstepBuilt()
80 {
81     import std.file : exists;
82 
83     version (Windows)
84         enum dstepPath = "bin/dstep.exe";
85     else version (Posix)
86         enum dstepPath = "bin/dstep";
87 
88     return exists(dstepPath);
89 }
90 
91 void executeCommand(const string[] args ...)
92 {
93     import std.process : spawnProcess, wait;
94     import std.array : join;
95 
96     if (spawnProcess(args).wait() != 0)
97         throw new Exception("Failed to execute command: " ~ args.join(' '));
98 }
99 
100 string[] dubShellCommand(string[] subCommands ...)
101 {
102     return (["dub", "--verror"] ~ subCommands ~ dubArch)
103         .filter!(e => e.length > 0)
104         .array;
105 }
106 
107 string defaultArchitecture()
108 {
109     version (X86_64)
110         return "x86_64";
111     else
112     {
113         version (DigitalMars)
114             return "x86_mscoff";
115         else
116             return "x86";
117     }
118 }
119 
120 string dubArch()
121 {
122     version (Windows)
123     {
124         import std.process : environment;
125         import std..string : split;
126 
127         const architecture = environment
128             .get("DUB_ARCH", defaultArchitecture)
129             .split(" ")[$ - 1];
130 
131         return "--arch=" ~ architecture;
132 
133     }
134     else
135         return "";
136 }