Line data Source code
1 : // Copyright (c) 2012-2024 Made to Order Software Corp. All Rights Reserved 2 : // 3 : // https://snapwebsites.org/project/eventdispatcher 4 : // contact@m2osw.com 5 : // 6 : // This program is free software; you can redistribute it and/or modify 7 : // it under the terms of the GNU General Public License as published by 8 : // the Free Software Foundation; either version 2 of the License, or 9 : // (at your option) any later version. 10 : // 11 : // This program is distributed in the hope that it will be useful, 12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 : // GNU General Public License for more details. 15 : // 16 : // You should have received a copy of the GNU General Public License along 17 : // with this program; if not, write to the Free Software Foundation, Inc., 18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 : 20 : // self 21 : // 22 : #include "catch_main.h" 23 : 24 : 25 : // cppprocess 26 : // 27 : #include <cppprocess/io_capture_pipe.h> 28 : #include <cppprocess/io_data_pipe.h> 29 : #include <cppprocess/io_input_file.h> 30 : #include <cppprocess/io_output_file.h> 31 : #include <cppprocess/process.h> 32 : 33 : 34 : // snapdev 35 : // 36 : #include <snapdev/file_contents.h> 37 : 38 : 39 : // C++ 40 : // 41 : #include <fstream> 42 : 43 : 44 : // last include 45 : // 46 : #include <snapdev/poison.h> 47 : 48 : 49 : 50 6 : CATCH_TEST_CASE("Process", "[process]") 51 : { 52 6 : CATCH_START_SECTION("simple cat") 53 : { 54 3 : cppprocess::process p("simple-cat"); 55 : 56 1 : CATCH_REQUIRE(p.get_name() == "simple-cat"); 57 : 58 1 : CATCH_REQUIRE_FALSE(p.get_forced_environment()); 59 1 : p.set_forced_environment(true); 60 1 : CATCH_REQUIRE(p.get_forced_environment()); 61 1 : p.set_forced_environment(false); 62 1 : CATCH_REQUIRE_FALSE(p.get_forced_environment()); 63 : 64 1 : CATCH_REQUIRE(p.get_command() == std::string()); 65 1 : p.set_command("cat"); 66 1 : CATCH_REQUIRE(p.get_command() == "cat"); 67 : 68 1 : CATCH_REQUIRE(p.get_arguments().empty()); 69 1 : p.add_argument("/proc/self/comm"); 70 1 : CATCH_REQUIRE(p.get_arguments().size() == 1); 71 : 72 1 : CATCH_REQUIRE(p.get_environ().empty()); 73 : 74 1 : CATCH_REQUIRE(p.get_input_io() == nullptr); 75 1 : CATCH_REQUIRE(p.get_output_io() == nullptr); 76 1 : CATCH_REQUIRE(p.get_error_io() == nullptr); 77 : 78 1 : cppprocess::io_capture_pipe::pointer_t capture(std::make_shared<cppprocess::io_capture_pipe>()); 79 1 : p.set_output_io(capture); 80 1 : CATCH_REQUIRE(p.get_output_io() == capture); 81 : 82 1 : CATCH_REQUIRE(p.get_next_processes().empty()); 83 : 84 1 : CATCH_REQUIRE(p.start() == 0); 85 : 86 1 : int const code(p.wait()); 87 1 : CATCH_REQUIRE(code == 0); 88 : 89 1 : CATCH_REQUIRE(p.get_input_io() == nullptr); 90 1 : CATCH_REQUIRE(p.get_output_io() == capture); 91 1 : CATCH_REQUIRE(p.get_error_io() == nullptr); 92 : 93 1 : CATCH_REQUIRE(capture->get_output() == "cat\n"); 94 1 : CATCH_REQUIRE(capture->get_trimmed_output() == "cat"); 95 : 96 1 : cppprocess::buffer_t const output(capture->get_binary_output()); 97 1 : CATCH_REQUIRE(output.size() == 4); 98 1 : CATCH_REQUIRE(output[0] == 'c'); 99 1 : CATCH_REQUIRE(output[1] == 'a'); 100 1 : CATCH_REQUIRE(output[2] == 't'); 101 1 : CATCH_REQUIRE(output[3] == '\n'); 102 1 : } 103 6 : CATCH_END_SECTION() 104 : 105 6 : CATCH_START_SECTION("simple logger, we pipe the input as the message") 106 : { 107 3 : cppprocess::process p("in-logger"); 108 : 109 1 : CATCH_REQUIRE(p.get_name() == "in-logger"); 110 : 111 1 : CATCH_REQUIRE(p.get_command() == std::string()); 112 1 : p.set_command("logger"); 113 1 : CATCH_REQUIRE(p.get_command() == "logger"); 114 : 115 1 : CATCH_REQUIRE(p.get_arguments().empty()); 116 : 117 1 : CATCH_REQUIRE(p.get_environ().empty()); 118 : 119 1 : CATCH_REQUIRE(p.get_input_io() == nullptr); 120 1 : CATCH_REQUIRE(p.get_output_io() == nullptr); 121 1 : CATCH_REQUIRE(p.get_error_io() == nullptr); 122 : 123 1 : cppprocess::io_data_pipe::pointer_t input(std::make_shared<cppprocess::io_data_pipe>()); 124 1 : CATCH_REQUIRE_FALSE(input->is_writer()); 125 1 : input->add_input("Event Dispatcher Process Test\n"); 126 1 : CATCH_REQUIRE(input->is_writer()); 127 : 128 1 : CATCH_REQUIRE(input->get_input() == std::string("Event Dispatcher Process Test\n")); 129 1 : CATCH_REQUIRE(input->get_binary_input().size() == 30); 130 : 131 1 : p.set_input_io(input); 132 1 : CATCH_REQUIRE(p.get_input_io() == input); 133 : 134 1 : CATCH_REQUIRE(p.start() == 0); 135 : 136 1 : int const code(p.wait()); 137 1 : CATCH_REQUIRE(code == 0); 138 1 : } 139 6 : CATCH_END_SECTION() 140 : 141 6 : CATCH_START_SECTION("in | sed | out") 142 : { 143 3 : cppprocess::process p("in-sed-out"); 144 : 145 1 : CATCH_REQUIRE(p.get_name() == "in-sed-out"); 146 : 147 1 : CATCH_REQUIRE_FALSE(p.get_forced_environment()); 148 1 : p.set_forced_environment(true); 149 1 : CATCH_REQUIRE(p.get_forced_environment()); 150 1 : p.set_forced_environment(false); 151 1 : CATCH_REQUIRE_FALSE(p.get_forced_environment()); 152 : 153 1 : CATCH_REQUIRE(p.get_command() == std::string()); 154 1 : p.set_command("sed"); 155 1 : CATCH_REQUIRE(p.get_command() == "sed"); 156 : 157 1 : CATCH_REQUIRE(p.get_arguments().empty()); 158 1 : p.add_argument("-e"); 159 1 : p.add_argument("s/Hello/Hi/"); 160 1 : p.add_argument("-"); 161 1 : CATCH_REQUIRE(p.get_arguments().size() == 3); 162 : 163 1 : CATCH_REQUIRE(p.get_environ().empty()); 164 : 165 1 : CATCH_REQUIRE(p.get_input_io() == nullptr); 166 1 : CATCH_REQUIRE(p.get_output_io() == nullptr); 167 1 : CATCH_REQUIRE(p.get_error_io() == nullptr); 168 : 169 1 : cppprocess::io_data_pipe::pointer_t input(std::make_shared<cppprocess::io_data_pipe>()); 170 1 : CATCH_REQUIRE_FALSE(input->is_writer()); 171 1 : input->add_input("Hello World!\n"); 172 1 : CATCH_REQUIRE(input->is_writer()); 173 : 174 1 : CATCH_REQUIRE(input->get_input() == std::string("Hello World!\n")); 175 1 : CATCH_REQUIRE(input->get_binary_input().size() == 14); 176 : 177 1 : p.set_input_io(input); 178 1 : CATCH_REQUIRE(p.get_input_io() == input); 179 : 180 1 : cppprocess::io_capture_pipe::pointer_t capture(std::make_shared<cppprocess::io_capture_pipe>()); 181 1 : p.set_output_io(capture); 182 1 : CATCH_REQUIRE(p.get_output_io() == capture); 183 : 184 1 : CATCH_REQUIRE(capture->get_output().empty()); 185 1 : CATCH_REQUIRE(capture->get_trimmed_output().empty()); 186 1 : CATCH_REQUIRE(capture->get_binary_output().empty()); 187 1 : CATCH_REQUIRE(p.get_next_processes().empty()); 188 : 189 1 : CATCH_REQUIRE(p.start() == 0); 190 : 191 1 : int const code(p.wait()); 192 1 : CATCH_REQUIRE(code == 0); 193 : 194 1 : CATCH_REQUIRE(capture->get_output() == "Hi World!\n"); 195 1 : CATCH_REQUIRE(capture->get_trimmed_output(true) == "Hi World!"); 196 : 197 1 : cppprocess::buffer_t const output(capture->get_binary_output()); 198 1 : CATCH_REQUIRE(output.size() == 11); 199 1 : CATCH_REQUIRE(output[ 0] == 'H'); 200 1 : CATCH_REQUIRE(output[ 1] == 'i'); 201 1 : CATCH_REQUIRE(output[ 2] == ' '); 202 1 : CATCH_REQUIRE(output[ 3] == ' '); 203 1 : CATCH_REQUIRE(output[ 4] == 'W'); 204 1 : CATCH_REQUIRE(output[ 5] == 'o'); 205 1 : CATCH_REQUIRE(output[ 6] == 'r'); 206 1 : CATCH_REQUIRE(output[ 7] == 'l'); 207 1 : CATCH_REQUIRE(output[ 8] == 'd'); 208 1 : CATCH_REQUIRE(output[ 9] == '!'); 209 1 : CATCH_REQUIRE(output[10] == '\n'); 210 1 : } 211 6 : CATCH_END_SECTION() 212 : 213 6 : CATCH_START_SECTION("ls unknown-file, expect an error") 214 : { 215 3 : cppprocess::process p("ls-unknown-file"); 216 : 217 1 : CATCH_REQUIRE(p.get_name() == "ls-unknown-file"); 218 : 219 1 : p.set_command("ls"); 220 1 : CATCH_REQUIRE(p.get_command() == "ls"); 221 : 222 1 : p.add_argument("unknown-file"); 223 1 : CATCH_REQUIRE(p.get_arguments().size() == 1); 224 : 225 1 : CATCH_REQUIRE(p.get_environ().empty()); 226 : 227 1 : cppprocess::io_capture_pipe::pointer_t error(std::make_shared<cppprocess::io_capture_pipe>()); 228 1 : p.set_error_io(error); 229 1 : CATCH_REQUIRE(p.get_error_io() == error); 230 : 231 1 : CATCH_REQUIRE(error->get_output().empty()); 232 1 : CATCH_REQUIRE(error->get_trimmed_output().empty()); 233 1 : CATCH_REQUIRE(error->get_binary_output().empty()); 234 : 235 1 : CATCH_REQUIRE(p.start() == 0); 236 : 237 1 : int const code(p.wait()); 238 1 : CATCH_REQUIRE(code != 0); 239 : 240 1 : CATCH_REQUIRE(p.get_output_io() == nullptr); 241 1 : CATCH_REQUIRE(p.get_error_io() == error); 242 : 243 1 : CATCH_REQUIRE_FALSE(error->get_output().empty()); 244 : // the error message can change under our feet so at this time 245 : // do not compare to a specific message 246 1 : } 247 6 : CATCH_END_SECTION() 248 : 249 6 : CATCH_START_SECTION("cat | tr") 250 : { 251 1 : cppprocess::process::pointer_t tr(std::make_shared<cppprocess::process>("tr")); 252 1 : tr->set_command("tr"); 253 1 : tr->add_argument("TASP"); 254 1 : tr->add_argument("tasp"); 255 : 256 1 : cppprocess::io_data_pipe::pointer_t input(std::make_shared<cppprocess::io_data_pipe>()); 257 1 : CATCH_REQUIRE_FALSE(input->is_writer()); 258 1 : input->add_input("Test A Simple Pipeline\n"); 259 1 : CATCH_REQUIRE(input->is_writer()); 260 : 261 1 : cppprocess::io_capture_pipe::pointer_t capture(std::make_shared<cppprocess::io_capture_pipe>()); 262 1 : tr->set_output_io(capture); 263 1 : CATCH_REQUIRE(tr->get_output_io() == capture); 264 : 265 3 : cppprocess::process p("cat"); 266 1 : p.set_command("cat"); 267 1 : p.add_argument("-"); 268 1 : p.set_input_io(input); 269 1 : CATCH_REQUIRE(p.get_input_io() == input); 270 1 : p.add_next_process(tr); 271 : 272 1 : CATCH_REQUIRE(p.start() == 0); 273 : 274 1 : int const code(p.wait()); 275 1 : CATCH_REQUIRE(code == 0); 276 : 277 1 : CATCH_REQUIRE(capture->get_output() == "test a simple pipeline\n"); 278 1 : } 279 6 : CATCH_END_SECTION() 280 : 281 6 : CATCH_START_SECTION("file based: cat | tr") 282 : { 283 : // Equivalent to: 284 : // 285 : // cat - < input.data | tr TASP tasp > output.data 286 : // 287 1 : std::string & tmpdir(SNAP_CATCH2_NAMESPACE::g_tmp_dir()); 288 1 : std::string const input_filename(tmpdir + "/input.data"); 289 1 : std::string const output_filename(tmpdir + "/output.data"); 290 : { 291 1 : std::ofstream input_data(input_filename); 292 1 : input_data << "Test A Simple Pipeline\n"; 293 1 : } 294 : 295 1 : cppprocess::process::pointer_t tr(std::make_shared<cppprocess::process>("tr")); 296 1 : tr->set_command("tr"); 297 1 : tr->add_argument("TASP"); 298 1 : tr->add_argument("tasp"); 299 : 300 1 : cppprocess::io_output_file::pointer_t output(std::make_shared<cppprocess::io_output_file>(output_filename)); 301 1 : output->set_truncate(true); 302 1 : tr->set_output_io(output); 303 : 304 : // we could directly cat the file here, obviously but we want 305 : // to test the `< <filename>` functionality 306 : // 307 3 : cppprocess::process p("cat"); 308 1 : p.set_command("cat"); 309 1 : p.add_argument("-"); 310 : 311 1 : cppprocess::io_input_file::pointer_t input(std::make_shared<cppprocess::io_input_file>(input_filename)); 312 1 : p.set_input_io(input); 313 : 314 1 : p.add_next_process(tr); 315 : 316 1 : CATCH_REQUIRE(p.start() == 0); 317 : 318 1 : int const code(p.wait()); 319 1 : CATCH_REQUIRE(code == 0); 320 : 321 1 : snapdev::file_contents final_output(output_filename); 322 1 : CATCH_REQUIRE(final_output.read_all()); 323 1 : CATCH_REQUIRE(final_output.contents() == "test a simple pipeline\n"); 324 1 : } 325 6 : CATCH_END_SECTION() 326 6 : } 327 : 328 : 329 : // vim: ts=4 sw=4 et