Mocking timeouts?

I’ve got a gen_statem that uses timeout, event_timeout, state_timeout. I’d like to test it without waiting around until the timers actually expire. But nor do I want to set the timeouts to ridiculously short values, because that would potentially introduce race conditions in my tests.

Does anyone have a good way to lie to gen_statem about the progress of time in unit tests? It uses erlang:start_timer, which looks to be impossible to hijack.

meck:new(erlang, [unstick, passthrough] hangs. To the point that it breaks Ctrl+G in the console, even.

I’ve used tracing in the past to see what timers exist, but I’ve never managed to subvert how they operate.

Ooh. I guess I could hot-patch gen_statem at runtime to replace calls to erlang:start_timer with calls to something else. (strokes chin)

Yeah, so that kinda works (not with gen_statem though … yet):

I’m not 100% sure if this will help you, but a while ago @paulo-f-oliveira and I created a mocking library for processes: GitHub - 2Latinos/nuntius: An Erlang/OTP library to mock registered processes

You may try running your code under Concuerror. It’s a model checker designed to find concurrency bugs in Erlang code by replacing the concept of real time with causal relationships between operations performed by processes. As far as I understand, it patches loaded beam code (including gen_statem, if you wish) and replaces all receive statement and time-related functions with its own instrumented implementations that are fed into the model checker. This is the best way to reliably test rare code paths. But keep in mind that Concuerror doesn’t support NIFs and code that interacts with the OS (like file), so things like that should be mocked.

1 Like

Concuerror looks interesting – I’d seen it before, but never got around to using it, but (unless I’m missing something from reading a couple of the tutorials), it’s not quite what I’m after. It’ll be useful for other scenarious though.

What I want is a way to test a gen_statem where I’ve got control over the passage of time. That is: An event occurs, which starts a timeout. Something interesting happens when that timeout elapses. I want to initially pause time so that I can inspect the state of the gen_statem, and then advance time, to trigger the timeout, and then assert something else.

For a specific example: kamock/src/kamock_coordinator.erl at main · happening-oss/kamock · GitHub is part of a mock Kafka broker that we use in testing our applications. I want to test the fake coordinator itself. Sending JoinGroup to the coordinator starts a timer, which elapses after (by default) 3 seconds. That’s far too long to wait around in a unit test, so I want to skip time ahead by (say) 3.5 seconds, and then assert something about the coordinator’s state.

So, because I’m using gen_statem, and the timers are started by gen_statem – I return a generic timeout, and gen_statem calls erlang:start_timer – I figured I’d try to subvert gen_statem’s use of timers.

This is probably inspired by looking at the Java test code for the actual implementation of Kafka, where it does interfere with the passage of time, if I’m reading it correctly.