Skip to content

Commit ca6afa2

Browse files
committed
test: fix CI failures from parallel ctest and LeakSanitizer
The per-backend suite registrations run as concurrent ctest processes, and unseeded std::rand() produces the same sequence in each one, so the rand-named temp files in stream_file, random_access_file, and tls_context collided across processes. Name them with a random_device seed plus an atomic counter instead. Tests that destroy the io_context with ops still parked abandon the suspended coroutine frames: op destroy() must not resume or destroy them, so the frames are unreachable at exit and LeakSanitizer reports them. Gate those teardown tests behind COROSIO_TEST_HAS_ASAN.
1 parent 45b0bd5 commit ca6afa2

7 files changed

Lines changed: 67 additions & 3 deletions

File tree

test/unit/context.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,20 @@
9393
COROSIO_TEST_KQUEUE_(impl, name) \
9494
COROSIO_TEST_SELECT_(impl, name)
9595

96+
// Tests that destroy the io_context with ops still parked abandon the
97+
// suspended coroutine frames: op destroy() must not resume or destroy
98+
// them (resuming runs user code during teardown; destroying recurses
99+
// through the promise destructor). LeakSanitizer rightly reports those
100+
// frames, so such tests are skipped under ASAN.
101+
#if defined(__SANITIZE_ADDRESS__)
102+
#define COROSIO_TEST_HAS_ASAN 1
103+
#elif defined(__has_feature)
104+
#if __has_feature(address_sanitizer)
105+
#define COROSIO_TEST_HAS_ASAN 1
106+
#endif
107+
#endif
108+
#ifndef COROSIO_TEST_HAS_ASAN
109+
#define COROSIO_TEST_HAS_ASAN 0
110+
#endif
111+
96112
#endif

test/unit/local_stream_socket.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,10 @@ struct local_stream_socket_test
916916
testStopTokenAccept();
917917
testAcceptPendingConnection();
918918
testAcceptWithoutListen();
919+
#if !COROSIO_TEST_HAS_ASAN
920+
// Abandons a parked coroutine frame by design; see context.hpp.
919921
testDestroyWithParkedAccept();
922+
#endif
920923
#endif
921924
testAcceptorOnClosedNoOp();
922925
testAcceptorBindClosedThrows();

test/unit/random_access_file.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include <cstring>
3030
#include <filesystem>
3131
#include <fstream>
32+
#include <random>
33+
#include <atomic>
3234
#include <limits>
3335
#include <stop_token>
3436
#include <string>
@@ -47,14 +49,25 @@ namespace boost::corosio {
4749

4850
namespace {
4951

52+
// Unique across concurrently running per-backend test processes;
53+
// unseeded std::rand() yields the same sequence in every process.
54+
inline std::string
55+
unique_path_suffix()
56+
{
57+
static unsigned const seed = std::random_device{}();
58+
static std::atomic<unsigned> counter{0};
59+
return std::to_string(seed) + "_"
60+
+ std::to_string(counter.fetch_add(1, std::memory_order_relaxed));
61+
}
62+
5063
struct temp_file
5164
{
5265
std::filesystem::path path;
5366

5467
temp_file(std::string_view prefix = "corosio_raf_test_")
5568
{
5669
path = std::filesystem::temp_directory_path()
57-
/ (std::string(prefix) + std::to_string(std::rand()));
70+
/ (std::string(prefix) + unique_path_suffix());
5871
}
5972

6073
temp_file(std::string_view prefix, std::string_view contents)

test/unit/reactor_paths.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,7 +1666,10 @@ struct reactor_paths_test
16661666
testLocalDgramShutdownReceive();
16671667
testStopTokenLocalStreamRead();
16681668
testLocalStreamWriteEAGAIN();
1669+
#if !COROSIO_TEST_HAS_ASAN
1670+
// Abandons parked coroutine frames by design; see context.hpp.
16691671
testShutdownWithParkedOps();
1672+
#endif
16701673
#endif
16711674
}
16721675
};

test/unit/stream_file.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include <cstring>
3030
#include <filesystem>
3131
#include <fstream>
32+
#include <random>
33+
#include <atomic>
3234
#include <limits>
3335
#include <stop_token>
3436
#include <string>
@@ -48,6 +50,17 @@ namespace boost::corosio {
4850

4951
namespace {
5052

53+
// Unique across concurrently running per-backend test processes;
54+
// unseeded std::rand() yields the same sequence in every process.
55+
inline std::string
56+
unique_path_suffix()
57+
{
58+
static unsigned const seed = std::random_device{}();
59+
static std::atomic<unsigned> counter{0};
60+
return std::to_string(seed) + "_"
61+
+ std::to_string(counter.fetch_add(1, std::memory_order_relaxed));
62+
}
63+
5164
// RAII helper that creates a temp file and removes it on destruction.
5265
struct temp_file
5366
{
@@ -56,7 +69,7 @@ struct temp_file
5669
temp_file(std::string_view prefix = "corosio_test_")
5770
{
5871
path = std::filesystem::temp_directory_path()
59-
/ (std::string(prefix) + std::to_string(std::rand()));
72+
/ (std::string(prefix) + unique_path_suffix());
6073
}
6174

6275
// Create with initial contents

test/unit/tcp_acceptor.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,10 @@ struct tcp_acceptor_test
907907
#ifndef _WIN32
908908
testAcceptPendingConnection();
909909
testAcceptWithoutListen();
910+
#if !COROSIO_TEST_HAS_ASAN
911+
// Abandons a parked coroutine frame by design; see context.hpp.
910912
testDestroyWithParkedAccept();
913+
#endif
911914
#endif
912915
}
913916
};

test/unit/tls_context.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,26 @@
2020
#include <cstdlib>
2121
#include <filesystem>
2222
#include <fstream>
23+
#include <random>
24+
#include <atomic>
2325
#include <string>
2426
#include <system_error>
2527

2628
namespace boost::corosio {
2729

2830
namespace {
2931

32+
// Unique across concurrently running per-backend test processes;
33+
// unseeded std::rand() yields the same sequence in every process.
34+
inline std::string
35+
unique_path_suffix()
36+
{
37+
static unsigned const seed = std::random_device{}();
38+
static std::atomic<unsigned> counter{0};
39+
return std::to_string(seed) + "_"
40+
+ std::to_string(counter.fetch_add(1, std::memory_order_relaxed));
41+
}
42+
3043
// RAII helper that creates a temp file with given contents and removes it.
3144
struct temp_file
3245
{
@@ -35,7 +48,7 @@ struct temp_file
3548
temp_file(std::string_view prefix, std::string_view contents)
3649
{
3750
path = std::filesystem::temp_directory_path()
38-
/ (std::string(prefix) + std::to_string(std::rand()));
51+
/ (std::string(prefix) + unique_path_suffix());
3952
std::ofstream ofs(path, std::ios::binary);
4053
ofs.write(contents.data(), static_cast<std::streamsize>(contents.size()));
4154
}

0 commit comments

Comments
 (0)