umockdev
umockdev copied to clipboard
generating input events
Currently it is possible to play back recorded events with umockdev_testbed_load_evemu_events(). It would be very useful to be able to generate input events directly from the code, similar to umockdev_testbed_uevent(). Maybe there is a way I'm not aware of?
I have tried:
cmd = g_strdup_printf("evemu-event --sync /dev/input/event1 --type EV_SW --code 15 --value 1");
g_assert(g_spawn_command_line_async(cmd, NULL));
but the events are nowhere to be found. evtest does not pick them up when called like that:
cmd = g_strdup_printf("evtest /dev/input/event1");
g_assert(g_spawn_command_line_async(cmd, NULL));
Note that evtest run this way can see events submitted by umockdev_testbed_load_evemu_events().
I've made this PR https://github.com/mkemlogic/umockdev/pull/1 as a workaround. This allows me to generate one event at a time, or multiple if needed.
@martinpitt is this something you would consider to include? lack of this is a show stopper for one of my major clients (~500 employees). They have to maintain their own fork, which they would rather not.
Hello @mkemlogic! Sorry, I didn't see that PR, as it is against your fork. Please feel free to do one against this repo.
I tried to understand your originally described issue. When I run
evemu-event --sync /dev/input/event1 --type EV_KEY --code 30 --value 1; evemu-event --sync /dev/input/event1 --type EV_KEY --code 30 --value 0 --sync;
then in evtest I do see it:
Event: time 1734595744.414407, type 1 (EV_KEY), code 30 (KEY_A), value 1
Event: time 1734595744.414407, -------------- SYN_REPORT ------------
Event: time 1734595744.418573, type 1 (EV_KEY), code 30 (KEY_A), value 0
Event: time 1734595744.418573, -------------- SYN_REPORT ------------
Event: time 1734595754.478819, type 1 (EV_KEY), code 30 (KEY_A), value 1
Event: time 1734595754.478819, -------------- SYN_REPORT ------------
Event: time 1734595754.482789, type 1 (EV_KEY), code 30 (KEY_A), value 0
Event: time 1734595754.482789, -------------- SYN_REPORT ------------
It is true that I don't see the actual A key press anywhere, but I figure that's because I'm running this in QEMU. (I don't have evtest/evemu installed on my host, read-only image blabla).
So the essence of your PR is that you load the script runner not into a thread, but execute it in umockdev's main thread blocking. I.e. during that time it cannot do any of its other functionality such as picking up uevents or servicing other loaded scripts. Also, it seems you need to write things into pipe buffers and crossing your fingers that you don't overrun them, as at that point the write would block and everything is deadlocked.
So I'm not a fan of this. We could probably find a secret/undocumented/unsupported way of doing, but I'd like to understand what the actual problem is first? You need more control over the playback, i.e. get a callback or signal or flag file or anything else when the script replay (the ScriptRunner thread) is done? We could build something around eventfd or SIGALRM or similar, with a convenient API, but let's discuss the problem first.
Thanks!
Thank you for the quick reply! My PR https://github.com/mkemlogic/umockdev/pull/1 is a dirty hack so please look at it as a demonstration of the functionality I'm requesting. Although it runs stable in a bunch of tests. So what I need is a way to generate single (maybe also multiple) input events from code, and wait until they are fully submitted.
Right now I've created files with a single input event in each file. Then I use this function to trigger one event at a time.
umockdev_testbed_load_evemu_events_blocking(testbed->umockdev_testbed, device, event_file, &error);
You need more control over the playback, i.e. get a callback or signal or flag file or anything else when the script replay (the ScriptRunner thread) is done?
Pretty much yes. Or just wait_for_playback_to_finish()
So if the solution is very different from my PR then it is maybe better that a new fresh PR is created.
I too ran everything in QEMU. Did you run evemu-event and evtest in the umockdev context or directly from the console? The latter works fine. It's running in umockdev that does not work for me.
Did you run evemu-event and evtest in the umockdev context or directly from the console?
Directly from the console. Are you tying to do something like umockdev-record -e /dev/input/event1=/tmp/keys -- evemu-event? That doesn't work:
libumockdev-preload: evemu format only supports reads from the device
I.e. you can type, but not record the writes into the device. (I suppose that could be implemented, but it isn't).
wait_for_playback_to_finish()
So, something like
assert (umockdev_testbed_load_script(tb, "/dev/input/event1", script_path, NULL));
umockdev_testbed_wait_script(tb, "/dev/input/event1");
would be fairly easy to implement. Possibly even with a timeout and corresponding success return code, although that shouldn't be necessary for unit tests. Would that work for you as an API?
I get that to work from the console too. But it did not work in umockdev-wrapper context. I was actually trying this in my code
cmd = g_strdup_printf("evemu-event --sync /dev/input/event1 --type EV_SW --code 15 --value 1");
g_assert(g_spawn_command_line_async(cmd, NULL));
and then
cmd = g_strdup_printf("evtest /dev/input/event1");
g_assert(g_spawn_command_line_async(cmd, NULL));
The evtest part works if events are loaded with umockdev_testbed_load_script().
assert (umockdev_testbed_load_script(tb, "/dev/input/event1", script_path, NULL));
umockdev_testbed_wait_script(tb, "/dev/input/event1");
That would work, but it would be even better if one could generate events without even loading them from files.
Also, we don't use umockdev for unit tests, but for system test. We use umockdev-wrapper to start a program that starts a server in background. The program then listens to dbus messages from the server triggered by different input events, uevents and sysfs attributes.
it would be even better if one could generate events without even loading them from files.
Err, of course I meant umockdev_testbed_load_evemu_events(), not umockdev_testbed_load_script() -- as far as I understood you, you want to work with evemu format, not the umockdev script format, right?
But either way, this then?
// umockdev script format
umockdev_testbed_set_script("/dev/input/event1", "r 200 blabla", &error);
// evemu format
umockdev_testbed_set_evemu_events("/dev/input/event1", "E: 1234.500000...", &error);
bool success = umockdev_testbed_wait_script(tb, "/dev/input/event1", 1000, &error);
The latter could fail with a timeout, or possibly some other fringe cases.
Yes, exactly what I need :-)
umockdev_testbed_wait_script could probably be named something without script, but this looks very good.
I think this will be a great improvement because one can now generate a bunch of events controlled solely from code and not form loaded files.
@mkemlogic OK! You picked a good time, I'll have some time for hobby project hacking on this on the train ride tomorrow :wink:
:-) Thank you for picking this up. umockdev is a great tool and saves a lot of time while improving test quality. Well done!
Ah bummer, Thread.join() doesn't have timeout functionality. So if you need that, your test needs to build it by itself with e.g. alarm().
@mkemlogic want to have a look at #259 ? It's not quite "done" yet, but functional.