Snaipe / Criterion

A cross-platform C and C++ unit testing framework for the 21st century

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

tests that need to fork

bbp-brieuc opened this issue · comments

Hello,

I'm testing code that will be used in a multi-process context, so I want to exercise the behavior when different processes access it concurrently.

But I'm running into several issues when I fork from a test:

  1. my test never ends
  2. criterion seems upset that it "receives messages" from pids it doesn't know about
  3. the test executable hangs until I ^C it and even after that, the pid it complained about is still running, I have to kill -KILL it

Is this supposed to work? Or do I need to create my child processes in a particular way for it to work?

Here's some detailed info below about the environment and symptoms I observe.

I'm using the default libcriterion that comes with my distro, which seems to be criterion version 3.2.0:

$ find /usr -name 'libcriterion.so.*.*.*'
/usr/lib/aarch64-linux-gnu/libcriterion.so.3.2.0

I'm running tests on an arm64 Ubuntu:

$ uname -a
Linux arm 5.15.0-1049-oracle #55-Ubuntu SMP Mon Nov 20 19:53:49 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux

Minimalistic issue reproduction::

$ cat forking_test.c 
#include <sys/wait.h>
#include <unistd.h>

#include <criterion/criterion.h>

Test(foo, bar) {
  if (fork()) {
    int status;
    wait(&status);
  }
}
$ gcc -Wall -O2 -o forking_test forking_test.c -lcriterion
$ ./forking_test
[ERR ] Received message identified by a PID '4230' that is not a child process.
^C
$ ps -C forking_test
    PID TTY          TIME CMD
   4230 ?        00:00:00 forking_test
$ kill -KILL 4230

Calling fork() means you take on the responsibility for your child processes, which means waiting for them and making sure the child processes call _exit. You're successfully waiting for your child but then you're allowing the fork to escape back out of the test function, which is not allowed. In general, forks are not allowed to communicate with Criterion, because the runner doesn't know what they are, and what test they originate from. This also means that calling cr_assert and friends from the fork is not allowed; only the main test process can do so.

Thank you for your answer. Then if I can't assert from the child, it will be a bit painful to use it, so I think I'll use another test framework for this, rather than engaging in complex inter process comms.

Do note that the list of functions that are fork-safe is very, very small; essentially just the syscall wrappers and no stdlib function. Finding obscure and hard-to-debug bugs around fork-safety is precisely why we have these rules in Criterion, and why the framework moved from a fork model to a fork-exec one.

My 2 cts on the matter is that unit tests are not system tests; instead of testing a function that forks, test smaller units of work, like for instance the logic that the child is supposed to be doing. Most of the difficulty around testing is having the right model, regardless of what method you use for testing.