1 // Refer to https://github.com/philsquared/Catch/blob/master/docs/own-main.md
2 // for providing our own main function to Catch
3 #define CATCH_CONFIG_RUNNER
4 #include <catch.hpp>
5 
6 #include <leatherman/execution/execution.hpp>
7 
8 #include <boost/filesystem.hpp>
9 #include <boost/algorithm/string/replace.hpp>
10 #include <boost/algorithm/string/trim.hpp>
11 #include <vector>
12 #include <fstream>
13 
14 using namespace std;
15 namespace fs = boost::filesystem;
16 namespace lth_exec = leatherman::execution;
17 namespace lth_util = leatherman::util;
18 
19 // Creates a unique temporary directory.
20 struct temp_directory {
temp_directorytemp_directory21     temp_directory() : dir{fs::absolute(fs::unique_path("execution_fixture_%%%%-%%%%-%%%%-%%%%"))} {
22         fs::create_directory(dir);
23     }
24 
~temp_directorytemp_directory25     ~temp_directory() {
26         fs::remove_all(dir);
27     }
28 
nametemp_directory29     string name() const {
30         auto s = dir.string();
31         boost::replace_all(s, "\\", "/");
32         return s;
33     }
34 
35 private:
36     fs::path dir;
37 };
38 
read(string filename)39 static string read(string filename)
40 {
41     ifstream file(filename);
42     return string(istreambuf_iterator<char>(file), istreambuf_iterator<char>());
43 }
44 
45 static fs::path exec_prefix;
46 
execute(string const & input)47 static lth_exec::result execute(string const& input)
48 {
49     return lth_exec::execute(
50 #ifdef _WIN32
51         "cmd.exe",
52         { "/c", (exec_prefix/"execution_wrapper").string() },
53 #else
54         (exec_prefix/"execution_wrapper").string(),
55         {},  // args
56 #endif
57         input,
58         0,   // timeout
59         lth_util::option_set<lth_exec::execution_options> {
60             lth_exec::execution_options::thread_safe,
61             lth_exec::execution_options::merge_environment,
62             lth_exec::execution_options::inherit_locale });
63 }
64 
65 TEST_CASE("runs an executable") {
66     temp_directory tmpdir;
67     auto dir = tmpdir.name();
68     auto executable = dir+"/init.bat";
69     auto input = "{\"executable\": \""+executable+"\", \"arguments\": [], "
70         "\"input\": \"{\\\"a\\\": 1, \\\"b\\\": [2, 3], \\\"c\\\": {\\\"hello\\\": \\\"goodbye foo\\\"}}\", "
71         "\"stdout\": \""+dir+"/out\", \"stderr\": \""+dir+"/err\", \"exitcode\": \""+dir+"/exit\"}";
72 
73     SECTION("passes input") {
74         ofstream foo(executable);
75 #ifdef _WIN32
76         foo << "@echo off" << endl;
77         foo << "more" << endl;
78 #else
79         foo << "#!/bin/sh" << endl;
80         foo << "cat -" << endl;
81 #endif
82         foo.close();
83 
84 #ifndef _WIN32
85         fs::permissions(executable, fs::owner_read|fs::owner_write|fs::owner_exe);
86 #endif
87 
88         auto exec = execute(input);
89         REQUIRE(exec.output == "");
90         REQUIRE(exec.error == "");
91         REQUIRE(exec.exit_code == 0);
92 
93         auto output = read(dir+"/out");
94         boost::trim(output);
95         REQUIRE(output == "{\"a\": 1, \"b\": [2, 3], \"c\": {\"hello\": \"goodbye foo\"}}");
96 
97         REQUIRE(read(dir+"/err") == "");
98         REQUIRE(read(dir+"/exit") == "0");
99     }
100 
101     SECTION("errors if executable not found") {
102         auto exec = execute(input);
103         REQUIRE(exec.output == "");
104         REQUIRE(exec.error == "");
105         REQUIRE(exec.exit_code == 127);
106 
107         REQUIRE(read(dir+"/out") == "");
108         REQUIRE(read(dir+"/err") == "");
109         REQUIRE(read(dir+"/exit") == "127");
110     }
111 
112     SECTION("executable is not executable") {
113         auto executable = dir+"/init";
114         auto input = "{\"executable\": \""+executable+"\", \"arguments\": [], \"input\": \"\", "
115             "\"stdout\": \""+dir+"/out\", \"stderr\": \""+dir+"/err\", \"exitcode\": \""+dir+"/exit\"}";
116         ofstream foo(executable);
117         foo << "";
118         foo.close();
119 
120         auto exec = execute(input);
121         REQUIRE(exec.output == "");
122         REQUIRE(exec.error == "");
123         REQUIRE(exec.exit_code == 127);
124 
125         REQUIRE(read(dir+"/out") == "");
126         REQUIRE(read(dir+"/err") == "");
127         REQUIRE(read(dir+"/exit") == "127");
128     }
129 }
130 
main(int argc,char ** argv)131 int main(int argc, char** argv) {
132     exec_prefix = fs::absolute(fs::path(argv[0]).parent_path());
133 
134     // Create the Catch session, pass CL args, and start it
135     Catch::Session test_session {};
136     return test_session.run(argc, argv);
137 }
138 
139