Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generating input events #247

Closed
mkemlogic opened this issue May 28, 2024 · 11 comments · Fixed by #259
Closed

generating input events #247

mkemlogic opened this issue May 28, 2024 · 11 comments · Fixed by #259

Comments

@mkemlogic
Copy link

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().

@mkemlogic
Copy link
Author

I've made this PR mkemlogic#1 as a workaround. This allows me to generate one event at a time, or multiple if needed.

@mkemlogic
Copy link
Author

@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.

@martinpitt
Copy link
Owner

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!

@mkemlogic
Copy link
Author

Thank you for the quick reply!
My PR mkemlogic#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.

@martinpitt
Copy link
Owner

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?

@mkemlogic
Copy link
Author

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.

@martinpitt
Copy link
Owner

martinpitt commented Dec 19, 2024

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.

@mkemlogic
Copy link
Author

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.

@martinpitt
Copy link
Owner

@mkemlogic OK! You picked a good time, I'll have some time for hobby project hacking on this on the train ride tomorrow 😉

@mkemlogic
Copy link
Author

:-)
Thank you for picking this up. umockdev is a great tool and saves a lot of time while improving test quality. Well done!

martinpitt added a commit that referenced this issue Dec 21, 2024
Script replay happens in the background in a thread. This API waits for
a script (or evemu events, which are translated to scripts) to finish,
so that tests can synchronize them with other system events.

Fixes #247
@martinpitt
Copy link
Owner

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.

martinpitt added a commit that referenced this issue Dec 22, 2024
Script replay happens in the background in a thread. This API waits for
a script (or evemu events, which are translated to scripts) to finish,
so that tests can synchronize them with other system events.

Fixes #247
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants