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