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