Zig test step hangs when called with `--listen=-`

hi! i’m running into an odd issue with testing my project. when running zig build test from the command line, the eventual call to the test binary will include a --listen=- argument. by comparing against what happens when i run the cached test binary directly from the command line without the argument, i’ve discovered that its presence causes the process to hang.

that is to say, using the default 0.13 test runner, my unit tests complete without errors when run with mainTerminal but not with mainServer.

if i attach a debugger to the running test process when called with zig build test, it appears that the test runner is blocked in server.receiveMessage (and so ultimately in a call to the system read function).

in case it’s useful, i’m on 0.13 on a mac running on Apple Silicon. the relevant part of my build.zig looks like

const tests = b.addTest(.{
    .target = target,
    .optimize = optimize,
    .root_source_file = b.path("src/main.zig"),
});
addImports(b, &tests.root_module, target, optimize);
const tests_run = b.addRunArtifact(tests);
const tests_step = b.step("test", "run the zig tests");
tests_step.dependOn(&tests_run.step);

(the addImports function sets up my project’s dependencies on ziglua and libxev.)

--listen=- will make the compiler server listen on standard input, since you don’t need the compiler server, you can remove it.

sorry, this isn’t super helpful: zig build test always appends this argument; i cannot remove it.

So --listen=- causes the process to hang while running inside zig test, not just when running it yourself?

i don’t think i understand, which indicates i maybe was not clear. i would like to run my unit tests with the zig build system, which always appends the --listen=- argument when it calls out to the zig compiler. unfortunately running my tests this way causes the test runner to hang.

i’m beginning to suspect that part of the problem may be my use of a thread pool in libxev. my shutdown code includes the following lines.

// self.pool is an instance of xev.ThreadPool
self.pool.shutdown();
self.pool.deinit();

this sequence of calls appears to complete fine when i test the execution of my program myself, but for some reason appears to not work as expected inside my tests…

Using libxev’s thread pool shouldn’t affect stdin. To clarify: --listen=- will cause any test to hang if passed by the user, because the user isn’t doing anything with the process’ stdin, but the build system does, so it shouldn’t hang inside zig build test. When you remove --listen=- and run the zig test yourself, does the test complete successfully?

bizarrely, not when it includes the calls to shutdown and deinit

1 Like

So is --listen=- responsible for the failure?

i suppose not, although i remain confused. it’s the same calls to shutdown and deinit that work under normal execution (i.e. zig build run) but not from a test

You’re right, it’s weird. deinit says in its documentation:

/// Wait for a thread to call shutdown() on the thread pool and kill the worker threads.

So, that means this code should work, but doesn’t. I’ll try to reproduce it.

welp, doesn’t look like that’s happening anytime soon, I can’t seem to build libxev with my zig version.

What happens when you run zig build test -j1 ?

the result is the same; the test hangs

ahhh, okay poking around a little more, i think the problem is that the call to read never returns—even though i’m cancelling it through libxev, there’s still a thread in the thread pool doing something…