1 /** 2 Integration tests. 3 */ 4 module it; 5 6 public import unit_threaded; 7 import unit_threaded.integration; 8 9 10 version(dpp2) 11 alias WIP2 = ShouldFail; 12 else 13 enum WIP2; 14 15 /// C code 16 struct C { 17 string code; 18 } 19 20 /// C++ code 21 struct Cpp { 22 string code; 23 } 24 25 /// D code 26 struct D { 27 string code; 28 } 29 30 struct RuntimeArgs { 31 string[] args; 32 } 33 34 struct IncludeSandbox { 35 36 alias sandbox this; 37 38 Sandbox sandbox; 39 40 static auto opCall() @safe { 41 IncludeSandbox ret; 42 ret.sandbox = Sandbox(); 43 return ret; 44 } 45 46 void run(string[] args...) @safe const { 47 import dpp.runtime.options: Options; 48 import dpp.runtime.app: dppRun = run; 49 import std.algorithm: map; 50 import std.array: array; 51 52 const baseLineArgs = [ 53 "d++", 54 "--include-path", 55 sandboxPath 56 ]; 57 auto options = Options(baseLineArgs ~ args); 58 options.dppFileNames[] = options.dppFileNames.map!(a => sandbox.inSandboxPath(a)).array; 59 60 dppRun(options); 61 } 62 63 void runPreprocessOnly(string[] args...) @safe const { 64 run(["--preprocess-only", "--keep-pre-cpp-files"] ~ args); 65 } 66 67 void shouldCompile(string file = __FILE__, size_t line = __LINE__) 68 (in string[] srcFiles...) 69 @safe const 70 { 71 try 72 sandbox.shouldSucceed!(file, line)([dCompiler, "-o-", "-c"] ~ srcFiles); 73 catch(Exception e) 74 adjustMessage(e, srcFiles); 75 } 76 77 void shouldNotCompile(string file = __FILE__, size_t line = __LINE__) 78 (in string[] srcFiles...) 79 @safe const 80 { 81 try 82 sandbox.shouldFail!(file, line)([dCompiler, "-o-", "-c"] ~ srcFiles); 83 catch(Exception e) 84 adjustMessage(e, srcFiles); 85 } 86 87 void shouldCompileButNotLink(string file = __FILE__, size_t line = __LINE__) 88 (in string[] srcFiles...) 89 @safe const 90 { 91 try 92 sandbox.shouldSucceed!(file, line)([dCompiler, "-c", "-ofblob.o"] ~ srcFiles); 93 catch(Exception e) 94 adjustMessage(e, srcFiles); 95 96 shouldFail(dCompiler, "-ofblob", "blob.o"); 97 } 98 99 private void adjustMessage(Exception e, in string[] srcFiles) @safe const { 100 import dpp.runtime.app: preamble; 101 import std.algorithm: map; 102 import std.array: join; 103 import std.file: readText; 104 import std..string: splitLines; 105 import std.range: enumerate, drop; 106 import std.format: format; 107 108 throw new UnitTestException( 109 "\n\n" ~ e.msg ~ "\n\n" ~ srcFiles 110 .map!(a => a ~ ":\n----------\n" ~ readText(sandbox.inSandboxPath(a)) 111 .splitLines 112 .enumerate(1) 113 .map!(b => format!"%5d: %s"(b[0], b[1])) 114 .drop(preamble.splitLines.length + 1) 115 .join("\n") 116 ) 117 .join("\n\n"), e.file, e.line); 118 } 119 120 private string includeLine(in string headerFileName) @safe pure nothrow const { 121 return `#include "` ~ inSandboxPath(headerFileName) ~ `"`; 122 } 123 124 void writeHeaderAndApp(in string headerFileName, in string headerText, in D app) @safe const { 125 writeFile(headerFileName, headerText); 126 // take care of including the header and putting the D 127 // code in a function 128 const dCode = 129 includeLine(headerFileName) ~ "\n" ~ 130 `void main() {` ~ "\n" ~ app.code ~ "\n}\n"; 131 writeFile("app.dpp", dCode); 132 } 133 } 134 135 /** 136 Convenience function in the typical case that a test has a C 137 header and a D main file. 138 */ 139 void shouldCompile(string file = __FILE__, size_t line = __LINE__) 140 (in C header, in D app) 141 { 142 shouldCompile!(file, line)("hdr.h", header.code, app); 143 } 144 145 /** 146 Convenience function in the typical case that a test has a C 147 header and a D main file. 148 */ 149 void shouldCompile(string file = __FILE__, size_t line = __LINE__) 150 (in Cpp header, in D app) 151 { 152 shouldCompile!(file, line)("hdr.hpp", header.code, app); 153 } 154 155 156 private void shouldCompile(string file = __FILE__, size_t line = __LINE__) 157 (in string headerFileName, in string headerText, in D app) 158 { 159 with(const IncludeSandbox()) { 160 writeHeaderAndApp(headerFileName, headerText, app); 161 runPreprocessOnly("app.dpp"); 162 shouldCompile!(file, line)("app.d"); 163 } 164 } 165 166 void shouldNotCompile(string file = __FILE__, size_t line = __LINE__) 167 (in C header, in D app) 168 { 169 with(const IncludeSandbox()) { 170 writeHeaderAndApp("hdr.h", header.code, app); 171 runPreprocessOnly("app.dpp"); 172 shouldNotCompile!(file, line)("app.d"); 173 } 174 } 175 176 177 alias shouldRun = shouldCompileAndRun; 178 179 /** 180 Convenience function in the typical case that a test has a C 181 header and a D main file. 182 */ 183 void shouldCompileAndRun(string file = __FILE__, size_t line = __LINE__) 184 (in C header, in C cSource, in D app, in RuntimeArgs args = RuntimeArgs()) 185 { 186 shouldCompileAndRun!(file, line)("hdr.h", header.code, "c.c", cSource.code, app, args); 187 } 188 189 /** 190 Convenience function in the typical case that a test has a C 191 header and a D main file. 192 */ 193 void shouldCompileAndRun(string file = __FILE__, size_t line = __LINE__) 194 (in Cpp header, in Cpp cppSource, in D app, in RuntimeArgs args = RuntimeArgs()) 195 { 196 shouldCompileAndRun!(file, line)("hdr.hpp", header.code, "cpp.cpp", cppSource.code, app, args); 197 } 198 199 200 private void shouldCompileAndRun 201 (string file = __FILE__, size_t line = __LINE__) 202 ( 203 in string headerFileName, 204 in string headerText, 205 in string cSourceFileName, 206 in string cText, in D app, 207 in RuntimeArgs args = RuntimeArgs(), 208 ) 209 { 210 import std.process: environment; 211 212 with(const IncludeSandbox()) { 213 writeHeaderAndApp(headerFileName, headerText, app); 214 writeFile(cSourceFileName, includeLine(headerFileName) ~ cText); 215 216 const isCpp = headerFileName == "hdr.hpp"; 217 const compilerName = isCpp ? "g++" : "gcc"; 218 const compilerVersion = environment.get("TRAVIS", "") == "" ? "" : "-7"; 219 const compiler = compilerName ~ compilerVersion; 220 const languageStandard = isCpp ? "-std=c++17" : "-std=c11"; 221 222 shouldSucceed(compiler, "-o", "c.o", "-g", languageStandard, "-c", cSourceFileName); 223 224 runPreprocessOnly("app.dpp"); 225 226 const linkStdLib = isCpp ? ["-L-lstdc++"] : []; 227 228 try 229 shouldSucceed!(file, line)([dCompiler, "-g", "app.d", "c.o"] ~ linkStdLib); 230 catch(Exception e) 231 adjustMessage(e, ["app.d"]); 232 233 try 234 shouldSucceed!(file, line)(["./app"] ~ args.args); 235 catch(Exception e) 236 adjustMessage(e, ["app.d"]); 237 } 238 } 239 240 241 private string dCompiler() @safe { 242 import std.process: environment; 243 return environment.get("DC", "dmd"); 244 }