1 // Integration tests for the binary backend
2 module tests.it.backend.binary;
3 
4 import reggae;
5 import unit_threaded;
6 import std.file;
7 import std.string;
8 
9 
10 enum origFileName = "original.txt";
11 enum copyFileName = "copy.txt";
12 
13 
14 private Build binaryBuild() {
15     mixin build!(Target(copyFileName, `cp $in $out`, Target(origFileName)),
16                  optional(Target.phony(`opt`, `echo Optional!`)));
17     return buildFunc();
18 }
19 
20 private void writeOrigFile() {
21     import std.stdio: File;
22     auto file = File(origFileName, "w");
23     file.writeln("See the little goblin");
24 }
25 
26 private struct FakeFile {
27     string[] lines;
28     void writeln(T...)(T args) {
29         import std.conv;
30         lines ~= text(args);
31     }
32 }
33 
34 
35 @("Do nothing after build") unittest {
36     scope(exit) {
37         remove(copyFileName);
38         remove(origFileName);
39     }
40 
41     writeOrigFile;
42 
43     auto file = FakeFile();
44     auto binary = Binary(binaryBuild, getOptions(["./reggae", "-b", "binary"]), file);
45     auto args = ["./build", "--norerun"];
46     binary.run(args);
47 
48     copyFileName.exists.shouldBeTrue;
49 
50     file.lines = [];
51     binary.run(args);
52     file.lines.shouldEqual(["[build] Nothing to do"]);
53 }
54 
55 
56 @("Targets should only be built once") unittest {
57     import std.process;
58     import std.stdio: File;
59     import std.range;
60     import std.algorithm: map;
61     import std.conv: to;
62 
63     enum fooSrcName = "foo.txt";
64     enum barSrcName = "bar.txt";
65 
66     scope(exit) {
67         foreach(name; [fooSrcName, barSrcName, "foo", "bar"])
68             remove(name);
69         executeShell("rm -rf objs");
70     }
71 
72     {
73         // create the src files so the rule fires
74         auto fooSrc = File(fooSrcName, "w");
75         auto barSrc = File(barSrcName, "w");
76     }
77 
78     auto foo = Target("$project/foo", "echo foo >> $out", [], [Target(fooSrcName)]);
79     auto bar = Target("$project/bar", "echo bar >> $out", [], [Target(barSrcName)]);
80     auto mids = iota(10).map!(a => Target.phony("$project/" ~a.to!string, "echo " ~ a.to!string, [foo, bar])).array;
81     auto top = Target.phony("top", "echo top", mids);
82 
83     auto binary = Binary(Build(top), getOptions(["reggae", "--export"]));
84     binary.run(["./build"]);
85 
86     // only one line -> rule only called once
87     readText("foo").chomp.split("\n").shouldEqual(["foo"]);
88     readText("bar").chomp.split("\n").shouldEqual(["bar"]);
89 }
90 
91 
92 @("List of targets") unittest {
93     auto file = FakeFile();
94     auto binary = Binary(binaryBuild, getOptions(["reggae", "-b", "binary"]), file);
95     binary.run(["./build", "-l"]);
96     file.lines.shouldEqual(
97         ["List of available top-level targets:",
98          "- copy.txt",
99          "- opt (optional)"]);
100 }
101 
102 @("List of targets with $project in the name") unittest {
103     import std.path;
104 
105     auto build = Build(optional(Target("$project/../druntime/" ~ copyFileName, `cp $in $out`, Target(origFileName))),
106                        Target.phony(`opt`, `echo Optional!`));
107     auto file = FakeFile();
108     auto binary = Binary(build, getOptions(["reggae", "-b", "binary"]), file);
109     binary.run(["./build", "-l"]);
110     file.lines.shouldEqual(
111         [
112             "List of available top-level targets:",
113             "- opt",
114             "- " ~ buildPath(getcwd(), "..", "druntime", "copy.txt") ~ " (optional)",
115         ]
116     );
117 }