Skip to content

Commit 5f38f94

Browse files
tests: mock sd_event timers with trio MockClock
In tests, replace all sd_event timer usages with trio.testing.MockClock backed I/O sources. Because we still have real timeouts (D-Bus call to mctpd subprocess, mctpd SO_RCVTIMEO wait, ...), autojump_threshold (how much to wait before skipping Trio timeouts) is set to a reasonable value to take that into account. Signed-off-by: Khang D Nguyen <khangng@os.amperecomputing.com>
1 parent 4109ced commit 5f38f94

File tree

9 files changed

+205
-13
lines changed

9 files changed

+205
-13
lines changed

meson.build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,13 @@ toml_dep = declare_dependency(
6060
executable('mctp',
6161
sources: ['src/mctp.c'] + netlink_sources + util_sources + ops_sources,
6262
install: true,
63+
c_args: ['-DHAVE_LIBSYSTEMD=0'],
6364
)
6465

6566
mctp_test = executable('test-mctp',
6667
sources: ['src/mctp.c'] + netlink_sources + util_sources + test_ops_sources,
6768
include_directories: include_directories('src'),
69+
c_args: ['-DHAVE_LIBSYSTEMD=0'],
6870
)
6971

7072
executable('mctp-req',
@@ -92,6 +94,7 @@ if libsystemd.found()
9294
dependencies: [libsystemd, toml_dep],
9395
install: true,
9496
install_dir: get_option('sbindir'),
97+
c_args: ['-DHAVE_LIBSYSTEMD=1'],
9598
)
9699

97100
mctpd_test = executable('test-mctpd',
@@ -100,6 +103,7 @@ if libsystemd.found()
100103
] + test_ops_sources + netlink_sources + util_sources,
101104
include_directories: include_directories('src'),
102105
dependencies: [libsystemd, toml_dep],
106+
c_args: ['-DHAVE_LIBSYSTEMD=1'],
103107
)
104108
endif
105109

src/mctp-ops.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#include <unistd.h>
1111
#include <linux/netlink.h>
12+
#if HAVE_LIBSYSTEMD
13+
#include <systemd/sd-event.h>
14+
#endif
1215
#include <err.h>
1316

1417
#include "mctp.h"
@@ -74,6 +77,12 @@ const struct mctp_ops mctp_ops = {
7477
.recvfrom = mctp_op_recvfrom,
7578
.close = mctp_op_close,
7679
},
80+
#if HAVE_LIBSYSTEMD
81+
.sd_event = {
82+
.add_time_relative = sd_event_add_time_relative,
83+
.source_set_time_relative = sd_event_source_set_time_relative,
84+
},
85+
#endif
7786
.bug_warn = mctp_bug_warn,
7887
};
7988

src/mctp-ops.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#include <sys/socket.h>
1111
#include <stdarg.h>
12+
#if HAVE_LIBSYSTEMD
13+
#include <systemd/sd-event.h>
14+
#endif
1215

1316
#define _GNU_SOURCE
1417

@@ -24,9 +27,19 @@ struct socket_ops {
2427
int (*close)(int sd);
2528
};
2629

30+
#if HAVE_LIBSYSTEMD
31+
struct sd_event_ops {
32+
typeof(sd_event_add_time_relative) *add_time_relative;
33+
typeof(sd_event_source_set_time_relative) *source_set_time_relative;
34+
};
35+
#endif
36+
2737
struct mctp_ops {
2838
struct socket_ops mctp;
2939
struct socket_ops nl;
40+
#if HAVE_LIBSYSTEMD
41+
struct sd_event_ops sd_event;
42+
#endif
3043
void (*bug_warn)(const char *fmt, va_list args);
3144
};
3245

src/mctpd.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,9 @@ static int wait_fd_timeout(int fd, short events, uint64_t timeout_usec)
497497
if (rc < 0)
498498
goto out;
499499

500-
rc = sd_event_add_time_relative(ev, NULL, CLOCK_MONOTONIC, timeout_usec,
501-
0, cb_exit_loop_timeout, NULL);
500+
rc = mctp_ops.sd_event.add_time_relative(ev, NULL, CLOCK_MONOTONIC,
501+
timeout_usec, 0,
502+
cb_exit_loop_timeout, NULL);
502503
if (rc < 0)
503504
goto out;
504505

@@ -3239,8 +3240,8 @@ static int peer_endpoint_recover(sd_event_source *s, uint64_t usec,
32393240

32403241
reschedule:
32413242
if (peer->recovery.npolls > 0) {
3242-
rc = sd_event_source_set_time_relative(peer->recovery.source,
3243-
peer->recovery.delay);
3243+
rc = mctp_ops.sd_event.source_set_time_relative(
3244+
peer->recovery.source, peer->recovery.delay);
32443245
if (rc >= 0) {
32453246
rc = sd_event_source_set_enabled(peer->recovery.source,
32463247
SD_EVENT_ONESHOT);
@@ -3275,7 +3276,7 @@ static int method_endpoint_recover(sd_bus_message *call, void *data,
32753276
peer->recovery.npolls = MCTP_I2C_TSYM_MN1_MIN + 1;
32763277
peer->recovery.delay =
32773278
(MCTP_I2C_TSYM_TRECLAIM_MIN_US / 2) - ctx->mctp_timeout;
3278-
rc = sd_event_add_time_relative(
3279+
rc = mctp_ops.sd_event.add_time_relative(
32793280
ctx->event, &peer->recovery.source, CLOCK_MONOTONIC, 0,
32803281
ctx->mctp_timeout, peer_endpoint_recover, peer);
32813282
if (rc < 0) {

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import pytest
55
import asyncdbus
6+
import trio.testing
67

78
import mctpenv
89

@@ -35,3 +36,10 @@ async def mctpd(nursery, dbus, sysnet, config):
3536
@pytest.fixture
3637
async def mctp(nursery, sysnet):
3738
return mctpenv.MctpWrapper(nursery, sysnet)
39+
40+
@pytest.fixture
41+
def autojump_clock():
42+
"""
43+
Custom autojump clock with a reasonable threshold for non-time I/O waits
44+
"""
45+
return trio.testing.MockClock(autojump_threshold=0.01)

tests/mctp-ops-test.c

Lines changed: 125 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#define _GNU_SOURCE
99

10+
#include <assert.h>
1011
#include <err.h>
1112
#include <errno.h>
1213
#include <stdlib.h>
@@ -18,6 +19,9 @@
1819
#include <sys/socket.h>
1920
#include <sys/un.h>
2021

22+
#if HAVE_LIBSYSTEMD
23+
#include <systemd/sd-event.h>
24+
#endif
2125
#include <linux/netlink.h>
2226

2327
#include "mctp-ops.h"
@@ -38,10 +42,12 @@ static int mctp_op_socket(int type)
3842
struct iovec iov;
3943
int rc, var, sd;
4044

41-
if (type == AF_MCTP)
45+
if (type == CONTROL_OP_SOCKET_MCTP)
4246
req.type = CONTROL_OP_SOCKET_MCTP;
43-
else if (type == AF_NETLINK)
47+
else if (type == CONTROL_OP_SOCKET_NL)
4448
req.type = CONTROL_OP_SOCKET_NL;
49+
else if (type == CONTROL_OP_TIMER)
50+
req.type = CONTROL_OP_TIMER;
4551
else
4652
errx(EXIT_FAILURE, "invalid socket type?");
4753

@@ -72,12 +78,12 @@ static int mctp_op_socket(int type)
7278

7379
static int mctp_op_mctp_socket(void)
7480
{
75-
return mctp_op_socket(AF_MCTP);
81+
return mctp_op_socket(CONTROL_OP_SOCKET_MCTP);
7682
}
7783

7884
static int mctp_op_netlink_socket(void)
7985
{
80-
return mctp_op_socket(AF_NETLINK);
86+
return mctp_op_socket(CONTROL_OP_SOCKET_NL);
8187
}
8288

8389
static int mctp_op_bind(int sd, struct sockaddr *addr, socklen_t addrlen)
@@ -221,6 +227,115 @@ static void mctp_bug_warn(const char *fmt, va_list args)
221227
abort();
222228
}
223229

230+
#if HAVE_LIBSYSTEMD
231+
struct wrapped_time_userdata {
232+
sd_event_time_handler_t callback;
233+
void *userdata;
234+
};
235+
236+
int wrapped_time_callback(sd_event_source *source, int fd, uint revents,
237+
void *userdata)
238+
{
239+
struct wrapped_time_userdata *wrapud = userdata;
240+
uint64_t usec;
241+
ssize_t rc;
242+
243+
rc = read(fd, &usec, sizeof(usec));
244+
if (rc != 8)
245+
errx(EXIT_FAILURE, "ops protocol error");
246+
247+
rc = wrapud->callback(source, usec, wrapud->userdata);
248+
warnx("%ld", rc);
249+
250+
return 0;
251+
}
252+
253+
void wrapped_time_destroy(void *wrapud)
254+
{
255+
free(wrapud);
256+
}
257+
258+
static int mctp_op_sd_event_add_time_relative(
259+
sd_event *e, sd_event_source **ret, clockid_t clock, uint64_t usec,
260+
uint64_t accuracy, sd_event_time_handler_t callback, void *userdata)
261+
{
262+
struct wrapped_time_userdata *wrapud = NULL;
263+
sd_event_source *source = NULL;
264+
int sd = -1;
265+
int rc = 0;
266+
267+
sd = mctp_op_socket(CONTROL_OP_TIMER);
268+
if (sd < 0)
269+
return -errno;
270+
271+
rc = write(sd, &usec, sizeof(usec));
272+
if (rc != 8)
273+
errx(EXIT_FAILURE, "ops protocol error");
274+
275+
wrapud = malloc(sizeof(*wrapud));
276+
if (!wrapud) {
277+
rc = -ENOMEM;
278+
goto fail;
279+
}
280+
281+
wrapud->callback = callback;
282+
wrapud->userdata = userdata;
283+
284+
rc = sd_event_add_io(e, &source, sd, EPOLLIN, wrapped_time_callback,
285+
wrapud);
286+
if (rc < 0)
287+
goto fail;
288+
289+
rc = sd_event_source_set_destroy_callback(source, wrapped_time_destroy);
290+
if (rc < 0)
291+
goto fail;
292+
293+
wrapud = NULL;
294+
295+
rc = sd_event_source_set_io_fd_own(source, 1);
296+
if (rc < 0)
297+
goto fail;
298+
299+
sd = -1;
300+
301+
rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
302+
if (rc < 0)
303+
goto fail;
304+
305+
if (!ret) {
306+
rc = sd_event_source_set_floating(source, 1);
307+
if (rc < 0)
308+
goto fail;
309+
310+
sd_event_source_unref(source);
311+
} else {
312+
*ret = source;
313+
}
314+
315+
return 0;
316+
317+
fail:
318+
if (sd > 0)
319+
close(sd);
320+
free(wrapud);
321+
sd_event_source_disable_unref(*ret);
322+
return rc;
323+
}
324+
325+
static int mctp_op_sd_event_source_set_time_relative(sd_event_source *s,
326+
uint64_t usec)
327+
{
328+
int sd = sd_event_source_get_io_fd(s);
329+
ssize_t rc;
330+
331+
rc = write(sd, &usec, sizeof(usec));
332+
if (rc != 8)
333+
errx(EXIT_FAILURE, "ops protocol error");
334+
335+
return 0;
336+
}
337+
#endif
338+
224339
const struct mctp_ops mctp_ops = {
225340
.mctp = {
226341
.socket = mctp_op_mctp_socket,
@@ -238,6 +353,12 @@ const struct mctp_ops mctp_ops = {
238353
.recvfrom = mctp_op_recvfrom,
239354
.close = mctp_op_close,
240355
},
356+
#if HAVE_LIBSYSTEMD
357+
.sd_event = {
358+
.add_time_relative = mctp_op_sd_event_add_time_relative,
359+
.source_set_time_relative = mctp_op_sd_event_source_set_time_relative,
360+
},
361+
#endif
241362
.bug_warn = mctp_bug_warn,
242363
};
243364

tests/mctpenv/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import array
33
import enum
44
import errno
5+
import math
56
import os
67
import signal
78
import socket
@@ -1113,6 +1114,31 @@ async def notify_delroute(self, route):
11131114
await self._notify_route(route, rtnl.RTM_DELROUTE);
11141115

11151116

1117+
class TimerSocket(BaseSocket):
1118+
def __init__(self, sock):
1119+
super().__init__(sock)
1120+
self.delay = sys.maxsize
1121+
1122+
async def run(self):
1123+
while True:
1124+
try:
1125+
with trio.move_on_after(self.delay / 1000000) as scope:
1126+
# mctpd requests a new uint64_t delay
1127+
data = await self.sock.recv(8)
1128+
if len(data) == 0:
1129+
break
1130+
1131+
(next_delay,) = struct.unpack('@Q', data)
1132+
self.delay = next_delay
1133+
1134+
# timed out
1135+
if scope.cancelled_caught:
1136+
await self.sock.send(struct.pack('@Q', math.floor(trio.current_time() * 1000000)))
1137+
self.delay = sys.maxsize
1138+
except (ConnectionResetError, BrokenPipeError) as ex:
1139+
break
1140+
1141+
11161142
async def send_fd(sock, fd):
11171143
fdarray = array.array("i", [fd])
11181144
await sock.sendmsg([b'x'], [
@@ -1158,6 +1184,14 @@ async def handle_control(self, nursery):
11581184
remote.close()
11591185
nursery.start_soon(nl.run)
11601186

1187+
elif op == 0x03:
1188+
# Timer socket
1189+
(local, remote) = self.socketpair()
1190+
sd = TimerSocket(local)
1191+
await send_fd(self.sock_local, remote.fileno())
1192+
remote.close()
1193+
nursery.start_soon(sd.run)
1194+
11611195
else:
11621196
print(f"unknown op {op}")
11631197

tests/test-proto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ enum {
99
CONTROL_OP_INIT,
1010
CONTROL_OP_SOCKET_MCTP,
1111
CONTROL_OP_SOCKET_NL,
12+
CONTROL_OP_TIMER,
1213
};
1314

1415
struct control_msg_req {

0 commit comments

Comments
 (0)