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