96.30% Lines (26/27)
100.00% Functions (4/4)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2026 Michael Vandeberg | 2 | // Copyright (c) 2026 Michael Vandeberg | |||||
| 3 | // | 3 | // | |||||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |||||
| 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 6 | // | 6 | // | |||||
| 7 | // Official repository: https://github.com/cppalliance/corosio | 7 | // Official repository: https://github.com/cppalliance/corosio | |||||
| 8 | // | 8 | // | |||||
| 9 | 9 | |||||||
| 10 | #ifndef BOOST_COROSIO_TEST_TEMP_PATH_HPP | 10 | #ifndef BOOST_COROSIO_TEST_TEMP_PATH_HPP | |||||
| 11 | #define BOOST_COROSIO_TEST_TEMP_PATH_HPP | 11 | #define BOOST_COROSIO_TEST_TEMP_PATH_HPP | |||||
| 12 | 12 | |||||||
| 13 | #include <atomic> | 13 | #include <atomic> | |||||
| 14 | #include <cstdint> | 14 | #include <cstdint> | |||||
| 15 | #include <cstdio> | 15 | #include <cstdio> | |||||
| 16 | #include <filesystem> | 16 | #include <filesystem> | |||||
| 17 | #include <random> | 17 | #include <random> | |||||
| 18 | #include <stdexcept> | 18 | #include <stdexcept> | |||||
| 19 | #include <string> | 19 | #include <string> | |||||
| 20 | #include <system_error> | 20 | #include <system_error> | |||||
| 21 | 21 | |||||||
| 22 | namespace boost::corosio::test { | 22 | namespace boost::corosio::test { | |||||
| 23 | 23 | |||||||
| 24 | /** RAII temp directory holding a path for a Unix-domain socket. | 24 | /** RAII temp directory holding a path for a Unix-domain socket. | |||||
| 25 | 25 | |||||||
| 26 | Creates a unique empty directory under | 26 | Creates a unique empty directory under | |||||
| 27 | `std::filesystem::temp_directory_path()` and exposes a path under | 27 | `std::filesystem::temp_directory_path()` and exposes a path under | |||||
| 28 | it suitable for binding `local_stream_socket` / | 28 | it suitable for binding `local_stream_socket` / | |||||
| 29 | `local_datagram_socket`. The destructor removes the directory | 29 | `local_datagram_socket`. The destructor removes the directory | |||||
| 30 | (and the bound socket file inside it) recursively, so tests that | 30 | (and the bound socket file inside it) recursively, so tests that | |||||
| 31 | throw mid-execution still clean up. | 31 | throw mid-execution still clean up. | |||||
| 32 | 32 | |||||||
| 33 | Naming entropy comes from a process-wide atomic counter mixed with | 33 | Naming entropy comes from a process-wide atomic counter mixed with | |||||
| 34 | a one-time `random_device` seed; that's enough to avoid collisions | 34 | a one-time `random_device` seed; that's enough to avoid collisions | |||||
| 35 | between parallel test runs without requiring cryptographic | 35 | between parallel test runs without requiring cryptographic | |||||
| 36 | randomness. The constructor retries on collision and throws if it | 36 | randomness. The constructor retries on collision and throws if it | |||||
| 37 | cannot create a directory in a reasonable number of attempts. | 37 | cannot create a directory in a reasonable number of attempts. | |||||
| 38 | 38 | |||||||
| 39 | Platform note: the helper exists so tests don't need to call | 39 | Platform note: the helper exists so tests don't need to call | |||||
| 40 | `mkdtemp` / `unlink` / `rmdir` directly. On Windows, the path is | 40 | `mkdtemp` / `unlink` / `rmdir` directly. On Windows, the path is | |||||
| 41 | a filesystem AF_UNIX path (Windows 10 1803+). | 41 | a filesystem AF_UNIX path (Windows 10 1803+). | |||||
| 42 | */ | 42 | */ | |||||
| 43 | class temp_socket_dir | 43 | class temp_socket_dir | |||||
| 44 | { | 44 | { | |||||
| 45 | public: | 45 | public: | |||||
| HITCBC | 46 | 60 | temp_socket_dir() | 46 | 60 | temp_socket_dir() | ||
| HITCBC | 47 | 60 | { | 47 | 60 | { | ||
| 48 | namespace fs = std::filesystem; | 48 | namespace fs = std::filesystem; | |||||
| HITCBC | 49 | 60 | auto const base = fs::temp_directory_path(); | 49 | 60 | auto const base = fs::temp_directory_path(); | ||
| 50 | 50 | |||||||
| 51 | // 64 bits of mixed entropy: a random seed established once | 51 | // 64 bits of mixed entropy: a random seed established once | |||||
| 52 | // at static init, XORed with a monotonic counter. | 52 | // at static init, XORed with a monotonic counter. | |||||
| HITCBC | 53 | 5 | static std::uint64_t const seed = [] { | 53 | 5 | static std::uint64_t const seed = [] { | ||
| HITCBC | 54 | 5 | std::random_device rd; | 54 | 5 | std::random_device rd; | ||
| HITCBC | 55 | 5 | return (static_cast<std::uint64_t>(rd()) << 32) | | 55 | 5 | return (static_cast<std::uint64_t>(rd()) << 32) | | ||
| HITCBC | 56 | 10 | static_cast<std::uint64_t>(rd()); | 56 | 10 | static_cast<std::uint64_t>(rd()); | ||
| HITCBC | 57 | 65 | }(); | 57 | 65 | }(); | ||
| 58 | static std::atomic<std::uint64_t> counter{0}; | 58 | static std::atomic<std::uint64_t> counter{0}; | |||||
| 59 | 59 | |||||||
| HITCBC | 60 | 60 | std::error_code ec; | 60 | 60 | std::error_code ec; | ||
| HITCBC | 61 | 60 | for (int tries = 0; tries < 32; ++tries) | 61 | 60 | for (int tries = 0; tries < 32; ++tries) | ||
| 62 | { | 62 | { | |||||
| HITCBC | 63 | 60 | auto const n = counter.fetch_add(1, std::memory_order_relaxed); | 63 | 60 | auto const n = counter.fetch_add(1, std::memory_order_relaxed); | ||
| HITCBC | 64 | 60 | auto const tag = seed ^ n; | 64 | 60 | auto const tag = seed ^ n; | ||
| 65 | 65 | |||||||
| 66 | char buf[32]; | 66 | char buf[32]; | |||||
| HITCBC | 67 | 60 | std::snprintf( | 67 | 60 | std::snprintf( | ||
| 68 | buf, sizeof(buf), "corosio_test_%016llx", | 68 | buf, sizeof(buf), "corosio_test_%016llx", | |||||
| 69 | static_cast<unsigned long long>(tag)); | 69 | static_cast<unsigned long long>(tag)); | |||||
| 70 | 70 | |||||||
| HITCBC | 71 | 60 | auto candidate = base / buf; | 71 | 60 | auto candidate = base / buf; | ||
| HITCBC | 72 | 60 | if (fs::create_directory(candidate, ec)) | 72 | 60 | if (fs::create_directory(candidate, ec)) | ||
| 73 | { | 73 | { | |||||
| HITCBC | 74 | 60 | dir_ = std::move(candidate); | 74 | 60 | dir_ = std::move(candidate); | ||
| HITCBC | 75 | 120 | return; | 75 | 120 | return; | ||
| 76 | } | 76 | } | |||||
| HITCBC | 77 | 60 | } | 77 | 60 | } | ||
| 78 | throw std::runtime_error( | 78 | throw std::runtime_error( | |||||
| MISUBC | 79 | ✗ | "temp_socket_dir: could not create temp directory"); | 79 | ✗ | "temp_socket_dir: could not create temp directory"); | ||
| HITCBC | 80 | 60 | } | 80 | 60 | } | ||
| 81 | 81 | |||||||
| HITCBC | 82 | 60 | ~temp_socket_dir() noexcept | 82 | 60 | ~temp_socket_dir() noexcept | ||
| 83 | { | 83 | { | |||||
| HITCBC | 84 | 60 | if (!dir_.empty()) | 84 | 60 | if (!dir_.empty()) | ||
| 85 | { | 85 | { | |||||
| HITCBC | 86 | 60 | std::error_code ec; | 86 | 60 | std::error_code ec; | ||
| HITCBC | 87 | 60 | std::filesystem::remove_all(dir_, ec); | 87 | 60 | std::filesystem::remove_all(dir_, ec); | ||
| 88 | } | 88 | } | |||||
| HITCBC | 89 | 60 | } | 89 | 60 | } | ||
| 90 | 90 | |||||||
| 91 | temp_socket_dir(temp_socket_dir const&) = delete; | 91 | temp_socket_dir(temp_socket_dir const&) = delete; | |||||
| 92 | temp_socket_dir& operator=(temp_socket_dir const&) = delete; | 92 | temp_socket_dir& operator=(temp_socket_dir const&) = delete; | |||||
| 93 | 93 | |||||||
| 94 | /// Path suitable for binding a local socket. | 94 | /// Path suitable for binding a local socket. | |||||
| HITCBC | 95 | 60 | std::string path() const | 95 | 60 | std::string path() const | ||
| 96 | { | 96 | { | |||||
| HITCBC | 97 | 60 | return (dir_ / "sock").string(); | 97 | 60 | return (dir_ / "sock").string(); | ||
| 98 | } | 98 | } | |||||
| 99 | 99 | |||||||
| 100 | private: | 100 | private: | |||||
| 101 | std::filesystem::path dir_; | 101 | std::filesystem::path dir_; | |||||
| 102 | }; | 102 | }; | |||||
| 103 | 103 | |||||||
| 104 | } // namespace boost::corosio::test | 104 | } // namespace boost::corosio::test | |||||
| 105 | 105 | |||||||
| 106 | #endif // BOOST_COROSIO_TEST_TEMP_PATH_HPP | 106 | #endif // BOOST_COROSIO_TEST_TEMP_PATH_HPP | |||||