mergerfs/tests/TEST_io_passthrough_open_race
Antonio SJ Musumeci 943f91ff98 Strengthen mergerfs integration tests
- Make the test runner fail on failed tests and report explicit skips
- Strengthen assertions for O_DIRECT, LUP policy, EXDEV fallback, and xattrs
- Fix cleanup and file descriptor handling across test scripts
2026-04-29 20:25:20 -04:00

132 lines
No EOL
3.4 KiB
Python
Executable file

#!/usr/bin/env python3
import os
import sys
import tempfile
import threading
NUM_THREADS = 50
TEST_DATA = b"race condition test data\n"
def open_and_operate(filepath, barrier, results, index):
fd = None
try:
barrier.wait()
fd = os.open(filepath, os.O_RDWR)
offset = index * len(TEST_DATA)
os.lseek(fd, offset, os.SEEK_SET)
bytes_written = os.write(fd, TEST_DATA)
if bytes_written != len(TEST_DATA):
results[index] = ("write_error", "expected {} bytes, wrote {}".format(len(TEST_DATA), bytes_written))
os.close(fd)
fd = None
return
os.lseek(fd, offset, os.SEEK_SET)
read_data = os.read(fd, len(TEST_DATA))
if read_data != TEST_DATA:
results[index] = ("read_error", "data mismatch at offset {}".format(offset))
os.close(fd)
fd = None
return
results[index] = ("success", fd)
fd = None
except Exception as e:
if fd is not None:
try:
os.close(fd)
except OSError:
pass
results[index] = ("exception", str(e))
def main():
if len(sys.argv) != 2:
print("usage: TEST_io_passthrough_open_race <mountpoint>", file=sys.stderr)
return 1
(fd, filepath) = tempfile.mkstemp(dir=sys.argv[1])
total_size = NUM_THREADS * len(TEST_DATA)
os.ftruncate(fd, total_size)
os.close(fd)
barrier = threading.Barrier(NUM_THREADS)
results = [None] * NUM_THREADS
threads = []
for i in range(NUM_THREADS):
t = threading.Thread(target=open_and_operate, args=(filepath, barrier, results, i))
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join()
failed = False
fds_to_close = []
for i, result in enumerate(results):
if result is None:
print("thread {} returned no result".format(i))
failed = True
elif result[0] == "success":
fds_to_close.append(result[1])
else:
print("thread {} failed: {} - {}".format(i, result[0], result[1]))
failed = True
if failed:
for fd in fds_to_close:
os.close(fd)
os.unlink(filepath)
return 1
if fds_to_close:
verify_fd = fds_to_close[0]
os.lseek(verify_fd, 0, os.SEEK_SET)
all_data = os.read(verify_fd, total_size)
expected_data = TEST_DATA * NUM_THREADS
if all_data != expected_data:
print("final verification failed: data mismatch")
print("expected {} bytes, got {} bytes".format(len(expected_data), len(all_data)))
for fd in fds_to_close:
os.close(fd)
os.unlink(filepath)
return 1
if len(fds_to_close) >= 2:
fd_a = fds_to_close[0]
fd_b = fds_to_close[1]
new_data = b"cross-fd visibility test\n"
os.lseek(fd_a, 0, os.SEEK_SET)
os.write(fd_a, new_data)
os.lseek(fd_b, 0, os.SEEK_SET)
read_back = os.read(fd_b, len(new_data))
if read_back != new_data:
print("cross-fd visibility failed: write from fd_a not visible on fd_b")
for fd in fds_to_close:
os.close(fd)
os.unlink(filepath)
return 1
for fd in fds_to_close:
os.close(fd)
os.unlink(filepath)
return 0
if __name__ == "__main__":
raise SystemExit(main())