1 // 2 // Automated Testing Framework (atf) 3 // 4 // Copyright (c) 2007 The NetBSD Foundation, Inc. 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions 9 // are met: 10 // 1. Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // 2. Redistributions in binary form must reproduce the above copyright 13 // notice, this list of conditions and the following disclaimer in the 14 // documentation and/or other materials provided with the distribution. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 // 29 30 extern "C" { 31 #include <sys/stat.h> 32 #include <sys/wait.h> 33 34 #include <fcntl.h> 35 #include <unistd.h> 36 } 37 38 #include <cassert> 39 #include <cerrno> 40 #include <cstddef> 41 #include <cstdlib> 42 #include <cstring> 43 #include <fstream> 44 #include <iostream> 45 #include <istream> 46 #include <ostream> 47 48 #include <atf-c++.hpp> 49 50 #include "io.hpp" 51 #include "signals.hpp" 52 53 // ------------------------------------------------------------------------ 54 // Auxiliary functions. 55 // ------------------------------------------------------------------------ 56 57 static 58 void 59 systembuf_check_data(std::istream& is, std::size_t length) 60 { 61 char ch = 'A', chr; 62 std::size_t cnt = 0; 63 while (is >> chr) { 64 ATF_REQUIRE_EQ(ch, chr); 65 if (ch == 'Z') 66 ch = 'A'; 67 else 68 ch++; 69 cnt++; 70 } 71 ATF_REQUIRE_EQ(cnt, length); 72 } 73 74 static 75 void 76 systembuf_write_data(std::ostream& os, std::size_t length) 77 { 78 char ch = 'A'; 79 for (std::size_t i = 0; i < length; i++) { 80 os << ch; 81 if (ch == 'Z') 82 ch = 'A'; 83 else 84 ch++; 85 } 86 os.flush(); 87 } 88 89 static 90 void 91 systembuf_test_read(std::size_t length, std::size_t bufsize) 92 { 93 using tools::io::systembuf; 94 95 std::ofstream f("test_read.txt"); 96 systembuf_write_data(f, length); 97 f.close(); 98 99 int fd = ::open("test_read.txt", O_RDONLY); 100 ATF_REQUIRE(fd != -1); 101 systembuf sb(fd, bufsize); 102 std::istream is(&sb); 103 systembuf_check_data(is, length); 104 ::close(fd); 105 ::unlink("test_read.txt"); 106 } 107 108 static 109 void 110 systembuf_test_write(std::size_t length, std::size_t bufsize) 111 { 112 using tools::io::systembuf; 113 114 int fd = ::open("test_write.txt", O_WRONLY | O_CREAT | O_TRUNC, 115 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 116 ATF_REQUIRE(fd != -1); 117 systembuf sb(fd, bufsize); 118 std::ostream os(&sb); 119 systembuf_write_data(os, length); 120 ::close(fd); 121 122 std::ifstream is("test_write.txt"); 123 systembuf_check_data(is, length); 124 is.close(); 125 ::unlink("test_write.txt"); 126 } 127 128 // ------------------------------------------------------------------------ 129 // Test cases for the "file_handle" class. 130 // ------------------------------------------------------------------------ 131 132 ATF_TEST_CASE(file_handle_ctor); 133 ATF_TEST_CASE_HEAD(file_handle_ctor) 134 { 135 set_md_var("descr", "Tests file_handle's constructors"); 136 } 137 ATF_TEST_CASE_BODY(file_handle_ctor) 138 { 139 using tools::io::file_handle; 140 141 file_handle fh1; 142 ATF_REQUIRE(!fh1.is_valid()); 143 144 file_handle fh2(STDOUT_FILENO); 145 ATF_REQUIRE(fh2.is_valid()); 146 fh2.disown(); 147 } 148 149 ATF_TEST_CASE(file_handle_copy); 150 ATF_TEST_CASE_HEAD(file_handle_copy) 151 { 152 set_md_var("descr", "Tests file_handle's copy constructor"); 153 } 154 ATF_TEST_CASE_BODY(file_handle_copy) 155 { 156 using tools::io::file_handle; 157 158 file_handle fh1; 159 file_handle fh2(STDOUT_FILENO); 160 161 file_handle fh3(fh2); 162 ATF_REQUIRE(!fh2.is_valid()); 163 ATF_REQUIRE(fh3.is_valid()); 164 165 fh1 = fh3; 166 ATF_REQUIRE(!fh3.is_valid()); 167 ATF_REQUIRE(fh1.is_valid()); 168 169 fh1.disown(); 170 } 171 172 ATF_TEST_CASE(file_handle_get); 173 ATF_TEST_CASE_HEAD(file_handle_get) 174 { 175 set_md_var("descr", "Tests the file_handle::get method"); 176 } 177 ATF_TEST_CASE_BODY(file_handle_get) 178 { 179 using tools::io::file_handle; 180 181 file_handle fh1(STDOUT_FILENO); 182 ATF_REQUIRE_EQ(fh1.get(), STDOUT_FILENO); 183 } 184 185 ATF_TEST_CASE(file_handle_posix_remap); 186 ATF_TEST_CASE_HEAD(file_handle_posix_remap) 187 { 188 set_md_var("descr", "Tests the file_handle::posix_remap method"); 189 } 190 ATF_TEST_CASE_BODY(file_handle_posix_remap) 191 { 192 using tools::io::file_handle; 193 194 int pfd[2]; 195 196 ATF_REQUIRE(::pipe(pfd) != -1); 197 file_handle rend(pfd[0]); 198 file_handle wend(pfd[1]); 199 200 ATF_REQUIRE(rend.get() != 10); 201 ATF_REQUIRE(wend.get() != 10); 202 wend.posix_remap(10); 203 ATF_REQUIRE_EQ(wend.get(), 10); 204 ATF_REQUIRE(::write(wend.get(), "test-posix-remap", 16) != -1); 205 { 206 char buf[17]; 207 ATF_REQUIRE_EQ(::read(rend.get(), buf, sizeof(buf)), 16); 208 buf[16] = '\0'; 209 ATF_REQUIRE(std::strcmp(buf, "test-posix-remap") == 0); 210 } 211 212 // Redo previous to ensure that remapping over the same descriptor 213 // has no side-effects. 214 ATF_REQUIRE_EQ(wend.get(), 10); 215 wend.posix_remap(10); 216 ATF_REQUIRE_EQ(wend.get(), 10); 217 ATF_REQUIRE(::write(wend.get(), "test-posix-remap", 16) != -1); 218 { 219 char buf[17]; 220 ATF_REQUIRE_EQ(::read(rend.get(), buf, sizeof(buf)), 16); 221 buf[16] = '\0'; 222 ATF_REQUIRE(std::strcmp(buf, "test-posix-remap") == 0); 223 } 224 } 225 226 // ------------------------------------------------------------------------ 227 // Test cases for the "systembuf" class. 228 // ------------------------------------------------------------------------ 229 230 ATF_TEST_CASE(systembuf_short_read); 231 ATF_TEST_CASE_HEAD(systembuf_short_read) 232 { 233 set_md_var("descr", "Tests that a short read (one that fits in the " 234 "internal buffer) works when using systembuf"); 235 } 236 ATF_TEST_CASE_BODY(systembuf_short_read) 237 { 238 systembuf_test_read(64, 1024); 239 } 240 241 ATF_TEST_CASE(systembuf_long_read); 242 ATF_TEST_CASE_HEAD(systembuf_long_read) 243 { 244 set_md_var("descr", "Tests that a long read (one that does not fit in " 245 "the internal buffer) works when using systembuf"); 246 } 247 ATF_TEST_CASE_BODY(systembuf_long_read) 248 { 249 systembuf_test_read(64 * 1024, 1024); 250 } 251 252 ATF_TEST_CASE(systembuf_short_write); 253 ATF_TEST_CASE_HEAD(systembuf_short_write) 254 { 255 set_md_var("descr", "Tests that a short write (one that fits in the " 256 "internal buffer) works when using systembuf"); 257 } 258 ATF_TEST_CASE_BODY(systembuf_short_write) 259 { 260 systembuf_test_write(64, 1024); 261 } 262 263 ATF_TEST_CASE(systembuf_long_write); 264 ATF_TEST_CASE_HEAD(systembuf_long_write) 265 { 266 set_md_var("descr", "Tests that a long write (one that does not fit " 267 "in the internal buffer) works when using systembuf"); 268 } 269 ATF_TEST_CASE_BODY(systembuf_long_write) 270 { 271 systembuf_test_write(64 * 1024, 1024); 272 } 273 274 // ------------------------------------------------------------------------ 275 // Test cases for the "pistream" class. 276 // ------------------------------------------------------------------------ 277 278 ATF_TEST_CASE(pistream); 279 ATF_TEST_CASE_HEAD(pistream) 280 { 281 set_md_var("descr", "Tests the pistream class"); 282 } 283 ATF_TEST_CASE_BODY(pistream) 284 { 285 using tools::io::file_handle; 286 using tools::io::pistream; 287 using tools::io::systembuf; 288 289 int fds[2]; 290 ATF_REQUIRE(::pipe(fds) != -1); 291 292 pistream rend(fds[0]); 293 294 systembuf wbuf(fds[1]); 295 std::ostream wend(&wbuf); 296 297 // XXX This assumes that the pipe's buffer is big enough to accept 298 // the data written without blocking! 299 wend << "1Test 1message\n"; 300 wend.flush(); 301 std::string tmp; 302 rend >> tmp; 303 ATF_REQUIRE_EQ(tmp, "1Test"); 304 rend >> tmp; 305 ATF_REQUIRE_EQ(tmp, "1message"); 306 } 307 308 // ------------------------------------------------------------------------ 309 // Tests for the "muxer" class. 310 // ------------------------------------------------------------------------ 311 312 namespace { 313 314 static void 315 check_stream(std::ostream& os) 316 { 317 // If we receive a signal while writing to the stream, the bad bit gets set. 318 // Things seem to behave fine afterwards if we clear such error condition. 319 // However, I'm not sure if it's safe to query errno at this point. 320 ATF_REQUIRE(os.good() || (os.bad() && errno == EINTR)); 321 os.clear(); 322 } 323 324 class mock_muxer : public tools::io::muxer { 325 void line_callback(const size_t index, const std::string& line) 326 { 327 // The following should be enabled but causes the output to be so big 328 // that it is annoying. Reenable at some point if we make atf store 329 // the output of the test cases in some other way (e.g. only if a test 330 // failes), because this message is the only help in seeing how the 331 // test fails. 332 //std::cout << "line_callback(" << index << ", " << line << ")\n"; 333 check_stream(std::cout); 334 switch (index) { 335 case 0: lines0.push_back(line); break; 336 case 1: lines1.push_back(line); break; 337 default: ATF_REQUIRE(false); 338 } 339 } 340 341 public: 342 mock_muxer(const int* fds, const size_t nfds, const size_t bufsize) : 343 muxer(fds, nfds, bufsize) {} 344 345 std::vector< std::string > lines0; 346 std::vector< std::string > lines1; 347 }; 348 349 static bool child_finished = false; 350 static void sigchld_handler(int signo) 351 { 352 assert(signo == SIGCHLD); 353 child_finished = true; 354 } 355 356 static void 357 child_printer(const int pipeout[2], const int pipeerr[2], 358 const size_t iterations) 359 { 360 ::close(pipeout[0]); 361 ::close(pipeerr[0]); 362 ATF_REQUIRE(::dup2(pipeout[1], STDOUT_FILENO) != -1); 363 ATF_REQUIRE(::dup2(pipeerr[1], STDERR_FILENO) != -1); 364 ::close(pipeout[1]); 365 ::close(pipeerr[1]); 366 367 for (size_t i = 0; i < iterations; i++) { 368 std::cout << "stdout " << i << "\n"; 369 std::cerr << "stderr " << i << "\n"; 370 } 371 372 std::cout << "stdout eof\n"; 373 std::cerr << "stderr eof\n"; 374 std::exit(EXIT_SUCCESS); 375 } 376 377 static void 378 muxer_test(const size_t bufsize, const size_t iterations) 379 { 380 int pipeout[2], pipeerr[2]; 381 ATF_REQUIRE(pipe(pipeout) != -1); 382 ATF_REQUIRE(pipe(pipeerr) != -1); 383 384 tools::signals::signal_programmer sigchld(SIGCHLD, sigchld_handler); 385 386 std::cout.flush(); 387 std::cerr.flush(); 388 389 pid_t pid = ::fork(); 390 ATF_REQUIRE(pid != -1); 391 if (pid == 0) { 392 sigchld.unprogram(); 393 child_printer(pipeout, pipeerr, iterations); 394 std::abort(); 395 } 396 ::close(pipeout[1]); 397 ::close(pipeerr[1]); 398 399 int fds[2] = {pipeout[0], pipeerr[0]}; 400 mock_muxer mux(fds, 2, bufsize); 401 402 mux.mux(child_finished); 403 check_stream(std::cout); 404 std::cout << "mux done\n"; 405 406 mux.flush(); 407 std::cout << "flush done\n"; 408 check_stream(std::cout); 409 410 sigchld.unprogram(); 411 int status; 412 ATF_REQUIRE(::waitpid(pid, &status, 0) != -1); 413 ATF_REQUIRE(WIFEXITED(status)); 414 ATF_REQUIRE(WEXITSTATUS(status) == EXIT_SUCCESS); 415 416 ATF_REQUIRE(std::cout.good()); 417 ATF_REQUIRE(std::cerr.good()); 418 for (size_t i = 0; i < iterations; i++) { 419 std::ostringstream exp0, exp1; 420 exp0 << "stdout " << i; 421 exp1 << "stderr " << i; 422 423 ATF_REQUIRE(mux.lines0.size() > i); 424 ATF_REQUIRE_EQ(exp0.str(), mux.lines0[i]); 425 ATF_REQUIRE(mux.lines1.size() > i); 426 ATF_REQUIRE_EQ(exp1.str(), mux.lines1[i]); 427 } 428 ATF_REQUIRE_EQ("stdout eof", mux.lines0[iterations]); 429 ATF_REQUIRE_EQ("stderr eof", mux.lines1[iterations]); 430 std::cout << "all done\n"; 431 } 432 433 } // anonymous namespace 434 435 ATF_TEST_CASE_WITHOUT_HEAD(muxer_small_buffer); 436 ATF_TEST_CASE_BODY(muxer_small_buffer) 437 { 438 muxer_test(4, 20000); 439 } 440 441 ATF_TEST_CASE_WITHOUT_HEAD(muxer_large_buffer); 442 ATF_TEST_CASE_BODY(muxer_large_buffer) 443 { 444 muxer_test(1024, 50000); 445 } 446 447 // ------------------------------------------------------------------------ 448 // Main. 449 // ------------------------------------------------------------------------ 450 451 ATF_INIT_TEST_CASES(tcs) 452 { 453 // Add the tests for the "file_handle" class. 454 ATF_ADD_TEST_CASE(tcs, file_handle_ctor); 455 ATF_ADD_TEST_CASE(tcs, file_handle_copy); 456 ATF_ADD_TEST_CASE(tcs, file_handle_get); 457 ATF_ADD_TEST_CASE(tcs, file_handle_posix_remap); 458 459 // Add the tests for the "systembuf" class. 460 ATF_ADD_TEST_CASE(tcs, systembuf_short_read); 461 ATF_ADD_TEST_CASE(tcs, systembuf_long_read); 462 ATF_ADD_TEST_CASE(tcs, systembuf_short_write); 463 ATF_ADD_TEST_CASE(tcs, systembuf_long_write); 464 465 // Add the tests for the "pistream" class. 466 ATF_ADD_TEST_CASE(tcs, pistream); 467 468 // Add the tests for the "muxer" class. 469 ATF_ADD_TEST_CASE(tcs, muxer_small_buffer); 470 ATF_ADD_TEST_CASE(tcs, muxer_large_buffer); 471 } 472