1 /**
2  * This module implements functionality helpful for writing integration tests
3  * as opposed to the unit variety where unit-tests are defined as not
4  * having global side-effects. In constrast, this module implements
5  * assertions that check for global side-effects such as writing to the
6  * file system.
7  */
8 
9 module unit_threaded.integration;
10 
11 //version(Windows) {
12 //    extern(C) int mkdir(char*);
13 //    extern(C) char* mktemp(char* template_);
14 //
15 //    char* mkdtemp(char* t) {
16 //        version(unitUnthreaded)
17 //            return mkdtempImpl(t);
18 //        else {
19 //            synchronized {
20 //                return mkdtempImpl(t);
21 //            }
22 //        }
23 //    }
24 //
25 //    char* mkdtempImpl(char* t) {
26 //        char* result = mktemp(t);
27 //
28 //        if(result is null) return null;
29 //        if (mkdir(result)) return null;
30 //
31 //        return result;
32 //    }
33 //
34 //} else {
35 //    extern(C) char* mkdtemp(char* template_);
36 //}
37 //
38 //
39 //shared static this() {
40 //    import std.file: exists, rmdirRecurse;
41 //
42 //    if(Sandbox.sandboxesPath.exists)
43 //        rmdirRecurse(Sandbox.sandboxesPath);
44 //}
45 //
46 //
47 //@safe:
48 //
49 ///**
50 // Responsible for creating a temporary directory to serve as a sandbox where
51 // files can be created, written to or deleted.
52 // */
53 //struct Sandbox {
54 //    import std.path;
55 //
56 //    enum defaultSandboxesPath = buildPath("tmp", "unit-threaded");
57 //    static string sandboxesPath = defaultSandboxesPath;
58 //    string testPath;
59 //
60 //    /// Instantiate a Sandbox object
61 //    static Sandbox opCall() {
62 //        Sandbox ret;
63 //        ret.testPath = newTestDir;
64 //        return ret;
65 //    }
66 //
67 //
68 //    static void setPath(string path) {
69 //        import std.file: exists, mkdirRecurse;
70 //        sandboxesPath = path;
71 //        if(!sandboxesPath.exists) () @trusted { mkdirRecurse(sandboxesPath); }();
72 //    }
73 //
74 //
75 //    static void resetPath() {
76 //        sandboxesPath = defaultSandboxesPath;
77 //    }
78 //
79 //    /// Write a file to the sandbox
80 //    void writeFile(in string fileName, in string output = "") const {
81 //        import std.stdio: File;
82 //        import std.path: buildPath, dirName;
83 //        import std.file: mkdirRecurse;
84 //
85 //        () @trusted { mkdirRecurse(buildPath(testPath, fileName.dirName)); }();
86 //        File(buildPath(testPath, fileName), "w").writeln(output);
87 //    }
88 //
89 //    /// Write a file to the sanbox
90 //    void writeFile(in string fileName, in string[] lines) const {
91 //        import std.array;
92 //        writeFile(fileName, lines.join("\n"));
93 //    }
94 //
95 //
96 //    /// Assert that a file exists in the sandbox
97 //    void shouldExist(string fileName, in string file = __FILE__, in size_t line = __LINE__) const {
98 //        import std.file: exists;
99 //        import std.path: buildPath;
100 //        import unit_threaded.exception: fail;
101 //
102 //        fileName = buildPath(testPath, fileName);
103 //        if(!fileName.exists)
104 //            fail("Expected " ~ fileName ~ " to exist but it didn't", file, line);
105 //    }
106 //
107 //    /// Assert that a file does not exist in the sandbox
108 //    void shouldNotExist(string fileName, in string file = __FILE__, in size_t line = __LINE__) const {
109 //        import std.file: exists;
110 //        import std.path: buildPath;
111 //        import unit_threaded.exception: fail;
112 //
113 //        fileName = buildPath(testPath, fileName);
114 //        if(fileName.exists)
115 //            fail("Expected " ~ fileName ~ " to not exist but it did", file, line);
116 //    }
117 //
118 //    /// read a file in the test sandbox and verify its contents
119 //    void shouldEqualContent(in string fileName, in string content,
120 //                            in string file = __FILE__, in size_t line = __LINE__)
121 //        const
122 //    {
123 //        import std.file: readText;
124 //        import std.string: chomp, splitLines;
125 //        import unit_threaded.assertions: shouldEqual;
126 //
127 //        readText(buildPath(testPath, fileName)).shouldEqual(content, file, line);
128 //    }
129 //
130 //    /// read a file in the test sandbox and verify its contents
131 //    void shouldEqualLines(in string fileName, in string[] lines,
132 //                          string file = __FILE__, size_t line = __LINE__)
133 //        const
134 //    {
135 //        import std.file: readText;
136 //        import std.string: chomp, splitLines;
137 //        import unit_threaded.assertions: shouldEqual;
138 //
139 //        readText(buildPath(testPath, fileName)).chomp.splitLines
140 //            .shouldEqual(lines, file, line);
141 //    }
142 //
143 //    // `fileName` should contain `needle`
144 //    void fileShouldContain(in string fileName,
145 //                           in string needle,
146 //                           in string file = __FILE__,
147 //                           in size_t line = __LINE__)
148 //    {
149 //        import std.file: readText;
150 //        import unit_threaded.assertions: shouldBeIn;
151 //        needle.shouldBeIn(readText(inSandboxPath(fileName)), file, line);
152 //    }
153 //
154 //    string sandboxPath() @safe @nogc pure nothrow const {
155 //        return testPath;
156 //    }
157 //
158 //    string inSandboxPath(in string fileName) @safe pure nothrow const {
159 //        import std.path: buildPath;
160 //        return buildPath(sandboxPath, fileName);
161 //    }
162 //
163 //    /**
164 //       Executing `args` should succeed
165 //     */
166 //    void shouldSucceed(string file = __FILE__, size_t line = __LINE__)
167 //                      (in string[] args...)
168 //        @safe const
169 //    {
170 //        import unit_threaded.exception: UnitTestException;
171 //        import std.conv: text;
172 //        import std.array: join;
173 //
174 //        const res = executeInSandbox(args);
175 //        if(res.status != 0)
176 //           throw new UnitTestException(text("Could not execute `", args.join(" "), "`:\n", res.output),
177 //                                       file, line);
178 //    }
179 //
180 //    alias shouldExecuteOk = shouldSucceed;
181 //
182 //    /**
183 //       Executing `args` should fail
184 //     */
185 //    void shouldFail(string file = __FILE__, size_t line = __LINE__)
186 //                   (in string[] args...)
187 //        @safe const
188 //    {
189 //        import unit_threaded.exception: UnitTestException;
190 //        import std.conv: text;
191 //        import std.array: join;
192 //
193 //        const res = executeInSandbox(args);
194 //        if(res.status == 0)
195 //            throw new UnitTestException(
196 //                text("`", args.join(" "), "` should have failed but didn't:\n", res.output),
197 //                file,
198 //                line);
199 //    }
200 //
201 //
202 //private:
203 //
204 //    auto executeInSandbox(in string[] args) @safe const {
205 //        import std.process: execute, Config;
206 //        import std.algorithm: startsWith;
207 //        import std.array: replace;
208 //
209 //        const string[string] env = null;
210 //        const config = Config.none;
211 //        const maxOutput = size_t.max;
212 //        const workDir = testPath;
213 //
214 //        const executable = args[0].startsWith("./")
215 //            ? inSandboxPath(args[0].replace("./", ""))
216 //            : args[0];
217 //
218 //        return execute(executable ~ args[1..$], env, config, maxOutput, workDir);
219 //    }
220 //
221 //    static string newTestDir() {
222 //        import std.file: exists, mkdirRecurse;
223 //
224 //        if(!sandboxesPath.exists) {
225 //            () @trusted { mkdirRecurse(sandboxesPath); }();
226 //        }
227 //
228 //        return makeTempDir();
229 //    }
230 //
231 //    static string makeTempDir() {
232 //        import std.algorithm: copy;
233 //        import std.exception: enforce;
234 //        import std.conv: to;
235 //        import std.string: fromStringz;
236 //        import core.stdc.string: strerror;
237 //        import core.stdc.errno: errno;
238 //
239 //        char[2048] template_;
240 //        copy(buildPath(sandboxesPath, "XXXXXX") ~ '\0', template_[]);
241 //
242 //        auto path = () @trusted { return mkdtemp(&template_[0]).to!string; }();
243 //
244 //        enforce(path != "",
245 //                "\n" ~
246 //                "Failed to create temporary directory name using template '" ~
247 //                () @trusted { return fromStringz(&template_[0]); }() ~ "': " ~
248 //                () @trusted { return strerror(errno).to!string; }());
249 //
250 //        return path.absolutePath;
251 //    }
252 //}