// Copyright (c) 2019 Klemens D. Morgenstern // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #define BOOST_TEST_MAIN #define BOOST_TEST_IGNORE_SIGCHLD #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(BOOST_WINDOWS_API) #include #include #endif namespace fs = boost::filesystem; namespace bp = boost::process; namespace bt = boost::this_process; BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5)) { using boost::unit_test::framework::master_test_suite; #if defined(BOOST_WINDOWS_API) const auto get_handle = [](FILE * f) {return reinterpret_cast(_get_osfhandle(_fileno(f)));}; const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);}; #else const auto get_handle = [](FILE * f) {return fileno(f);}; const auto socket_to_handle = [](int i){ return i;}; #endif std::error_code ec; auto fd_list = bt::get_handles(ec); BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdin)), 1); BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdout)), 1); BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stderr)), 1); BOOST_CHECK(bt::is_stream_handle(get_handle(stdin), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(get_handle(stdout), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(get_handle(stderr), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK_GE(fd_list.size(), 3); BOOST_CHECK_GE(bt::get_handles(ec).size(), fd_list.size()); bp::pipe p; { auto fd_list_new = bt::get_handles(ec); BOOST_CHECK_MESSAGE(!ec, ec); BOOST_CHECK_LE(fd_list.size() + 2, fd_list_new.size()); fd_list = std::move(fd_list_new); } BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1); BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1); BOOST_CHECK(bt::is_stream_handle(p.native_source(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(p.native_sink(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); p.close(); fd_list = bt::get_handles(ec); BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0); BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0); #if defined( BOOST_WINDOWS_API ) std::thread thr([]{}); BOOST_CHECK(!bt::is_stream_handle(thr.native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); thr.join(); #else # if defined(TFD_CLOEXEC) //check timer int timer_fd = ::timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); BOOST_CHECK(!bt::is_stream_handle(timer_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); #endif # if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) int event_fd =::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); BOOST_CHECK(!bt::is_stream_handle(event_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); #endif int dir_fd = ::dirfd(::opendir(".")); BOOST_CHECK(!bt::is_stream_handle(dir_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); #endif boost::asio::io_context ioc; boost::asio::ip::tcp::socket tcp_socket(ioc); boost::asio::ip::udp::socket udp_socket(ioc); bp::async_pipe ap(ioc); tcp_socket.open(boost::asio::ip::tcp::v4()); udp_socket.open(boost::asio::ip::udp::v4()); BOOST_CHECK(bt::is_stream_handle(socket_to_handle(tcp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(socket_to_handle(udp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(std::move(ap).sink(). native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(std::move(ap).source().native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); } struct on_setup_t { std::vector &res; on_setup_t(std::vector & res) : res(res) {} template void operator()(Executor & e) { bp::extend::foreach_used_handle(e, [this](bt::native_handle_type handle) { res.push_back(handle); std::cout << "Pushing " << handle << std::endl; }); } }; BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5)) { using boost::unit_test::framework::master_test_suite; std::vector res; bp::pipe p_in; bp::pipe p_out; auto source = p_in.native_source(); auto sink = p_out.native_sink(); std::error_code ec; BOOST_WARN_NE(source, sink); //Sanity check const auto ret = bp::system(master_test_suite().argv[1], "--exit-code" , "42", bp::std_in < p_out, bp::std_out > p_in, bp::extend::on_setup(on_setup_t(res)), ec); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK_EQUAL(ret, 42); BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0); BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0); } BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) { #if defined(BOOST_WINDOWS_API) const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));}; #else const auto get_handle = [](FILE * f){return std::to_string(fileno(f));}; #endif using boost::unit_test::framework::master_test_suite; BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr), EXIT_SUCCESS); BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE); BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS); }