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