<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Mar 30, 2015 at 8:22 AM, Pekka Paalanen <span dir="ltr"><<a href="mailto:ppaalanen@gmail.com" target="_blank">ppaalanen@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Mon, 30 Mar 2015 06:37:59 -0400<br>
Marek Chalupa <<a href="mailto:mchqwerty@gmail.com">mchqwerty@gmail.com</a>> wrote:<br>
<br>
> Test misc races when adding/releasing devices<br>
><br>
> v2.: use one roundtrip after releasing devices<br>
>      add touch support<br>
><br>
> Signed-off-by: Marek Chalupa <<a href="mailto:mchqwerty@gmail.com">mchqwerty@gmail.com</a>><br>
> ---<br>
>  Makefile.am          |   7 +-<br>
>  tests/devices-test.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++<br>
>  2 files changed, 305 insertions(+), 1 deletion(-)<br>
>  create mode 100644 tests/devices-test.c<br>
<br>
</span><span class="">> diff --git a/tests/devices-test.c b/tests/devices-test.c<br>
> new file mode 100644<br>
> index 0000000..cf41471<br>
> --- /dev/null<br>
> +++ b/tests/devices-test.c<br>
<br>
</span><div><div class="h5">> +TEST(device_release_before_destroy)<br>
> +{<br>
> +     struct client *cl = client_create(100, 100, 100, 100);<br>
> +<br>
> +     /* we can release pointer when we won't be using it anymore.<br>
> +      * Do it and see what happens if the device is destroyed<br>
> +      * right after that */<br>
> +     wl_pointer_release(cl->input->pointer->wl_pointer);<br>
> +     /* we must free and set to NULL the structures, otherwise<br>
> +      * seat capabilities will double-free them */<br>
> +     free(cl->input->pointer);<br>
> +     cl->input->pointer = NULL;<br>
> +<br>
> +     wl_keyboard_release(cl->input->keyboard->wl_keyboard);<br>
> +     free(cl->input->keyboard);<br>
> +     cl->input->keyboard = NULL;<br>
> +<br>
> +     wl_touch_release(cl->input->touch->wl_touch);<br>
> +     free(cl->input->touch);<br>
> +     cl->input->touch = NULL;<br>
> +<br>
> +     weston_test_device_release(cl->test->weston_test, "pointer");<br>
> +     weston_test_device_release(cl->test->weston_test, "keyboard");<br>
> +     weston_test_device_release(cl->test->weston_test, "touch");<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     assert(cl->input->caps == 0);<br>
> +     assert(wl_display_get_error(cl->wl_display) == 0);<br>
<br>
</div></div>Is this display error check needed? Doesn't client_roundtrip()<br>
guarantee success?<br>
<span class=""></span> </blockquote><div>Nope, it is not needed, really.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> +<br>
> +     /* restore previous state */<br>
> +     weston_test_device_add(cl->test->weston_test, "pointer");<br>
> +     weston_test_device_add(cl->test->weston_test, "keyboard");<br>
> +     weston_test_device_add(cl->test->weston_test, "touch");<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);<br>
> +}<br>
> +<br>
> +TEST(device_release_before_destroy_multiple)<br>
> +{<br>
> +     int i;<br>
> +<br>
> +     /* if weston crashed during this test, then there is<br>
> +      * some inconsistency */<br>
> +     for (i = 0; i < 100; ++i) {<br>
> +             device_release_before_destroy();<br>
<br>
</span>Is it intentional to create a hundred clients without destroying the<br>
old ones? Or is that exactly the point?<br>
<br></blockquote><div><br></div><div>It is intentional to run it so many times, but it is not intentional to run a hundred clients at a time.<br></div><div>The problem is that currently we have no destroy function for client. However, the clients do not<br></div><div>run simultaneously but serially, so the effect should be the same as if we'd destroy them<br></div><div>(after the client finishes its body, it just 'is' and does nothing until the process exits)<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Maybe it's a good test just like this but needs a comment, otherwise<br>
someone might "fix" it.<br>
<div><div class="h5"><br>
> +     }<br>
> +}<br>
> +<br>
> +/* normal work-flow test */<br>
> +TEST(device_release_after_destroy)<br>
> +{<br>
> +     struct client *cl = client_create(100, 100, 100, 100);<br>
> +<br>
> +     weston_test_device_release(cl->test->weston_test, "pointer");<br>
> +     wl_pointer_release(cl->input->pointer->wl_pointer);<br>
> +     /* we must free the memory manually, otherwise seat.capabilities<br>
> +      * will try to free it and will use invalid proxy */<br>
> +     free(cl->input->pointer);<br>
> +     cl->input->pointer = NULL;<br>
> +<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     weston_test_device_release(cl->test->weston_test, "keyboard");<br>
> +     wl_keyboard_release(cl->input->keyboard->wl_keyboard);<br>
> +     free(cl->input->keyboard);<br>
> +     cl->input->keyboard = NULL;<br>
> +<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     weston_test_device_release(cl->test->weston_test, "touch");<br>
> +     wl_touch_release(cl->input->touch->wl_touch);<br>
> +     free(cl->input->touch);<br>
> +     cl->input->touch = NULL;<br>
> +<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     assert(cl->input->caps == 0);<br>
> +     assert(wl_display_get_error(cl->wl_display) == 0);<br>
<br>
</div></div>Is the display error check needed?<br>
<span class=""><br>
> +<br>
> +     /* restore previous state */<br>
> +     weston_test_device_add(cl->test->weston_test, "pointer");<br>
> +     weston_test_device_add(cl->test->weston_test, "keyboard");<br>
> +     weston_test_device_add(cl->test->weston_test, "touch");<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);<br>
> +}<br>
> +<br>
> +TEST(device_release_after_destroy_multiple)<br>
> +{<br>
> +     int i;<br>
> +<br>
> +     /* if weston crashed during this test, then there is<br>
> +      * some inconsistency */<br>
> +     for (i = 0; i < 100; ++i) {<br>
> +             device_release_after_destroy();<br>
<br>
</span>Again hundred simultaneous clients?<br>
<div><div class="h5"><br>
> +     }<br>
> +}<br>
> +<br>
> +/* see <a href="https://bugzilla.gnome.org/show_bug.cgi?id=745008" target="_blank">https://bugzilla.gnome.org/show_bug.cgi?id=745008</a><br>
> + * It is a mutter bug, but highly relevant. Weston does not<br>
> + * suffer from this bug atm, but it is worth of testing. */<br>
> +TEST(get_device_after_destroy)<br>
> +{<br>
> +     struct client *cl = client_create(100, 100, 100, 100);<br>
> +     struct wl_pointer *wl_pointer;<br>
> +     struct wl_keyboard *wl_keyboard;<br>
> +     struct wl_touch *wl_touch;<br>
> +<br>
> +     /* There's a race:<br>
> +      *  1) compositor destroyes device<br>
> +      *  2) client asks for the device, because it has not got<br>
> +      *     new capabilities yet<br>
> +      *  3) compositor gets request with new_id for destroyed device<br>
> +      *  4) client uses the new_id<br>
> +      *  5) client gets new capabilities, destroying the objects<br>
> +      *<br>
> +      * If compositor just bail out in step 3) and does not create<br>
> +      * resource, then client gets error in step 4) - even though<br>
> +      * it followed the protocol (it just didn't know about new<br>
> +      * capabilities).<br>
> +      *<br>
> +      * This test simulates this situation<br>
> +      */<br>
> +<br>
> +     /* connection is buffered, so after calling client_roundtrip(),<br>
> +      * this whole batch will be delivered to compositor and will<br>
> +      * exactly simulate our situation */<br>
> +     weston_test_device_release(cl->test->weston_test, "pointer");<br>
> +     wl_pointer = wl_seat_get_pointer(cl->input->wl_seat);<br>
> +     assert(wl_pointer);<br>
> +<br>
> +     /* this should be ignored */<br>
> +     wl_pointer_set_cursor(wl_pointer, 0, NULL, 0, 0);<br>
> +<br>
> +     /* this should not be ignored */<br>
> +     wl_pointer_release(wl_pointer);<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     weston_test_device_release(cl->test->weston_test, "keyboard");<br>
> +     wl_keyboard = wl_seat_get_keyboard(cl->input->wl_seat);<br>
> +     assert(wl_keyboard);<br>
> +     wl_keyboard_release(wl_keyboard);<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     weston_test_device_release(cl->test->weston_test, "touch");<br>
> +     wl_touch = wl_seat_get_touch(cl->input->wl_seat);<br>
> +     assert(wl_touch);<br>
> +     wl_touch_release(wl_touch);<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     assert(wl_display_get_error(cl->wl_display) == 0);<br>
<br>
</div></div>And this check.<br>
<span class=""><br>
> +<br>
> +     /* get weston to the previous state, so that other tests<br>
> +      * have the same environment */<br>
<br>
</span>This is such an important note, that it could stand on its own. Like in<br>
the beginning of this whole file as a major doc block.<br>
<span class=""><br>
> +     weston_test_device_add(cl->test->weston_test, "pointer");<br>
> +     weston_test_device_add(cl->test->weston_test, "keyboard");<br>
> +     weston_test_device_add(cl->test->weston_test, "touch");<br>
> +     client_roundtrip(cl);<br>
> +<br>
> +     assert(cl->input->caps == WL_SEAT_CAPABILITY_ALL);<br>
> +}<br>
> +<br>
> +TEST(get_device_afer_destroy_multiple)<br>
> +{<br>
> +     int i;<br>
> +<br>
> +     /* if weston crashed during this test, then there is<br>
> +      * some inconsistency */<br>
> +     for (i = 0; i < 100; ++i) {<br>
> +             get_device_after_destroy();<br>
<br>
</span>Again a hundred clients.<br>
<br>
> +     }<br>
> +}<br>
<br>
Patches 1, 2 and 4 are R-b me. Patch 3 I commented on IRC, I didn't<br>
like the hardcoded call to seat_handle_capabilities().<br>
<br>
<br>
Thanks,<br>
pq<br>
</blockquote></div><br></div><div class="gmail_extra">Fixed the code according the notes, thanks for the review!<br><br></div><div class="gmail_extra">Marek<br></div></div>