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 30extern "C" { 31#include <sys/stat.h> 32#include <sys/wait.h> 33 34#include <fcntl.h> 35#include <unistd.h> 36} 37 38#include <cerrno> 39#include <cstddef> 40#include <cstdlib> 41#include <cstring> 42#include <fstream> 43#include <iostream> 44#include <istream> 45#include <ostream> 46 47#include "../atf-c++/detail/sanity.hpp" 48#include "../atf-c++/macros.hpp" 49 50#include "io.hpp" 51#include "signals.hpp" 52 53// ------------------------------------------------------------------------ 54// Auxiliary functions. 55// ------------------------------------------------------------------------ 56 57static 58void 59systembuf_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 74static 75void 76systembuf_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 89static 90void 91systembuf_test_read(std::size_t length, std::size_t bufsize) 92{ 93 using atf::atf_run::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 108static 109void 110systembuf_test_write(std::size_t length, std::size_t bufsize) 111{ 112 using atf::atf_run::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 132ATF_TEST_CASE(file_handle_ctor); 133ATF_TEST_CASE_HEAD(file_handle_ctor) 134{ 135 set_md_var("descr", "Tests file_handle's constructors"); 136} 137ATF_TEST_CASE_BODY(file_handle_ctor) 138{ 139 using atf::atf_run::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 149ATF_TEST_CASE(file_handle_copy); 150ATF_TEST_CASE_HEAD(file_handle_copy) 151{ 152 set_md_var("descr", "Tests file_handle's copy constructor"); 153} 154ATF_TEST_CASE_BODY(file_handle_copy) 155{ 156 using atf::atf_run::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 172ATF_TEST_CASE(file_handle_get); 173ATF_TEST_CASE_HEAD(file_handle_get) 174{ 175 set_md_var("descr", "Tests the file_handle::get method"); 176} 177ATF_TEST_CASE_BODY(file_handle_get) 178{ 179 using atf::atf_run::file_handle; 180 181 file_handle fh1(STDOUT_FILENO); 182 ATF_REQUIRE_EQ(fh1.get(), STDOUT_FILENO); 183} 184 185ATF_TEST_CASE(file_handle_posix_remap); 186ATF_TEST_CASE_HEAD(file_handle_posix_remap) 187{ 188 set_md_var("descr", "Tests the file_handle::posix_remap method"); 189} 190ATF_TEST_CASE_BODY(file_handle_posix_remap) 191{ 192 using atf::atf_run::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 230ATF_TEST_CASE(systembuf_short_read); 231ATF_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} 236ATF_TEST_CASE_BODY(systembuf_short_read) 237{ 238 systembuf_test_read(64, 1024); 239} 240 241ATF_TEST_CASE(systembuf_long_read); 242ATF_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} 247ATF_TEST_CASE_BODY(systembuf_long_read) 248{ 249 systembuf_test_read(64 * 1024, 1024); 250} 251 252ATF_TEST_CASE(systembuf_short_write); 253ATF_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} 258ATF_TEST_CASE_BODY(systembuf_short_write) 259{ 260 systembuf_test_write(64, 1024); 261} 262 263ATF_TEST_CASE(systembuf_long_write); 264ATF_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} 269ATF_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 278ATF_TEST_CASE(pistream); 279ATF_TEST_CASE_HEAD(pistream) 280{ 281 set_md_var("descr", "Tests the pistream class"); 282} 283ATF_TEST_CASE_BODY(pistream) 284{ 285 using atf::atf_run::file_handle; 286 using atf::atf_run::pistream; 287 using atf::atf_run::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 312namespace { 313 314static void 315check_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 324class mock_muxer : public atf::atf_run::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 341public: 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 349static bool child_finished = false; 350static void sigchld_handler(int signo) 351{ 352 INV(signo == SIGCHLD); 353 child_finished = true; 354} 355 356static void 357child_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 377static void 378muxer_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 atf::atf_run::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 UNREACHABLE; 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 435ATF_TEST_CASE_WITHOUT_HEAD(muxer_small_buffer); 436ATF_TEST_CASE_BODY(muxer_small_buffer) 437{ 438 muxer_test(4, 20000); 439} 440 441ATF_TEST_CASE_WITHOUT_HEAD(muxer_large_buffer); 442ATF_TEST_CASE_BODY(muxer_large_buffer) 443{ 444 muxer_test(1024, 50000); 445} 446 447// ------------------------------------------------------------------------ 448// Main. 449// ------------------------------------------------------------------------ 450 451ATF_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