[PATCH] dinput/tests: Make overlapped format tests more robust.

Rémi Bernon rbernon at codeweavers.com
Wed Aug 4 16:44:14 CDT 2021


On 8/4/21 11:00 PM, Arkadiusz Hiler wrote:
> On Wed, Aug 04, 2021 at 02:25:49PM -0500, Zebediah Figura (she/her) wrote:
>> On 8/4/21 2:20 PM, Rémi Bernon wrote:
> <SNIP>
>>>> +BOOL wait_for_device_data_and_discard_(int line,
>>>> IDirectInputDeviceA *device)
>>>> +{
>>>> +    DWORD cnt;
>>>> +    HRESULT hr;
>>>> +    DWORD start_time = GetTickCount();
>>>> +
>>>> +    do
>>>> +    {
>>>> +        cnt = 10;
>>>> +        hr = IDirectInputDevice_GetDeviceData(device,
>>>> sizeof(DIDEVICEOBJECTDATA_DX3), NULL, &cnt, 0);
>>>> +        ok_(__FILE__, line)(SUCCEEDED(hr),
>>>> "IDirectInputDevice_GetDeviceData() failed: %08x\n", hr);
>>>> +        ok_(__FILE__, line)(cnt == 0 || cnt == 1, "Unexpected
>>>> number of events: %d\n", cnt);
>>>> +    } while (cnt != 1 && (GetTickCount() - start_time < 500));
>>>> +
>>>> +    return cnt == 1;
>>>> +}
>>>> +
>>>> +#define wait_for_device_data_and_discard(device)
>>>> wait_for_device_data_and_discard_(__LINE__, device)
>>>> +
>>>
>>> NP: These macros are usually declared right before the function itself.
>>
>> I don't think it's that consistent; I've seen both organizations.
> 
> Indeed. I've checked 3 instances that I've found by looking for `ok_`.
> All three were declared this way, so I went with that.
> 

It's not completely consistent yes, but I think it quite stands out that 
most of the time it's the case.

$ egrep 'define.*\W[a-z]\w+_\(' dlls/*/tests/* -A1

>>>>    void overlapped_format_tests(IDirectInputA *pDI, HWND hwnd)
>>>>    {
>>>>        HRESULT hr;
>>>>        struct overlapped_state state;
>>>>        IDirectInputDeviceA *keyboard = NULL;
>>>> +    DIPROPDWORD dp;
>>>> +    DWORD tries;
>>>>        hr = IDirectInput_CreateDevice(pDI, &GUID_SysKeyboard,
>>>> &keyboard, NULL);
>>>>        ok(SUCCEEDED(hr), "IDirectInput_CreateDevice() failed:
>>>> %08x\n", hr);
>>>> @@ -355,15 +376,28 @@ void overlapped_format_tests(IDirectInputA
>>>> *pDI, HWND hwnd)
>>>>        /* test overlapped slider - default value 0 */
>>>>        hr = IDirectInputDevice_SetDataFormat(keyboard,
>>>> &overlapped_slider_format);
>>>>        ok(SUCCEEDED(hr), "IDirectInputDevice_SetDataFormat() failed:
>>>> %08x\n", hr);
>>>> +
>>>> +    dp.diph.dwSize = sizeof(DIPROPDWORD);
>>>> +    dp.diph.dwHeaderSize = sizeof(DIPROPHEADER);
>>>> +    dp.diph.dwHow = DIPH_DEVICE;
>>>> +    dp.diph.dwObj = 0;
>>>> +    dp.dwData = 10;
>>>> +    hr = IDirectInputDevice_SetProperty(keyboard,
>>>> DIPROP_BUFFERSIZE, &dp.diph);
>>>> +    ok(SUCCEEDED(hr), "IDirectInputDevice_SetProperty() failed:
>>>> %08x\n", hr);
>>>> +
>>>>        hr = IDirectInputDevice_Acquire(keyboard);
>>>>        ok(SUCCEEDED(hr), "IDirectInputDevice_Acquire() failed:
>>>> %08x\n", hr);
>>>>        SetFocus(hwnd);
>>>>        pump_messages();
>>>> -    /* press D */
>>>> -    keybd_event(0, DIK_D, KEYEVENTF_SCANCODE, 0);
>>>> -    pump_messages();
>>>> +    tries = 2;
>>>> +    do
>>>> +    {
>>>> +        /* press D */
>>>> +        keybd_event(0, DIK_D, KEYEVENTF_SCANCODE, 0);
>>>> +        pump_messages();
>>>> +    } while (!wait_for_device_data_and_discard(keyboard) && tries--);
>>>>        memset(&state, 0xFF, sizeof(state));
>>>>        hr = IDirectInputDevice_GetDeviceState(keyboard,
>>>> sizeof(state), &state);
>>>
>>> I think it could be worth having a dedicated helper to acquire and wait,
>>> instead of inlining these loops. The same helper could then be (copied)
>>> and used in the other tests.
>>>
>>> The tries counter and its hardcoded value is a bit confusing at first,
>>> and instead of keeping the key pressed the helper could also make sure
>>> it's released on function exit (maybe use a dedicated unusual key for
>>> that?).
> 
> "Unusual key" may be hard to pick as AFAIU it has to be present in the
> data format.
> 
> How about something like:
> 
> static void flush_acquire_keyboard_(int line, IDirectInputDeviceA *device, DWORD valid_dik)
> {
>      int tries = 2;
>      do
>      {
>          keybd_event(0, valid_dik, KEYEVENTF_SCANCODE, 0);
>      } while (!wait_for_device_data_and_discard(device) && tries--);
> 
>      keybd_event(0, valid_dik, KEYEVENTF_SCANCODE|KEYEVENTF_KEYUP, 0);
>      ok_(__FILE__, line)(wait_for_device_data_and_discard(device),
>              "Timed out while waiting for injected events to be picked up by DirectInput.\n");
> }
> 
> #define flush_acquire_keyboard(device, valid_dik) flush_acquire_keyboard_(__LINE__, device, valid_dik)
> 
> And then we can:
> 
> hr = IDirectInputDevice_Acquire(keyboard);
> ok(SUCCEEDED(hr), "IDirectInputDevice_Acquire() failed: %08x\n", hr);
> flush_acquire_keyboard(keyboard, DIK_F);
> 

Why not even call Acquire inside the helper (and name it 
acquire_and_wait or something like that)?

> We would also need a mouse variant.
> 

You could inject mouse events too, but if the helper is copied in other 
test sources then it's probably better to use a slightly different 
version with mouse events instead.

> I wonder if instead of copying it verbatim all over the place it would
> be okay to create dlls/dinput/tests/event_helpers.h and include it from
> the dinput8 tests.
> 

I'm not sure it's worth a header to avoid coping it to a few files 
(especially considering we may rather have two helpers for mouse / 
keyboard). And I don't think it's a good idea to make dinput8 tests 
include files from another test folder so at least it would be 
duplicated (also because the device interface type is different).

>>>> @@ -378,6 +412,8 @@ void overlapped_format_tests(IDirectInputA *pDI,
>>>> HWND hwnd)
>>>>        /* release D */
>>>>        keybd_event(0, DIK_D, KEYEVENTF_SCANCODE|KEYEVENTF_KEYUP, 0);
>>>>        pump_messages();
>>>> +    ok(wait_for_device_data_and_discard(keyboard),
>>>> +            "Timed out while waiting for injected events to be
>>>> picked up by DirectInput.\n");
>>>>        hr = IDirectInputDevice_Unacquire(keyboard);
>>>>        ok(SUCCEEDED(hr), "IDirectInputDevice_Unacquire() failed:
>>>> %08x\n", hr);
>>>
>>> NP: Yes, dinput code is heavily inconsistent, but I don't think it uses
>>> the two level indent on line continuation style anywhere yet. Personally
>>> I don't find it very readable and I would rather align on the paren.
>>
>> The problem with aligning to the parenthesis (or other semantic unit) is
>> that it makes things really ugly when the parenthesis falls late in the
>> line. For whatever my opinion is worth, I'm distinctly in favor of a
>> constant eight-space indent, including spreading that to more code.
> 
> I share the same preference as zf. I think I've even set my editor to do
> that after seeing this convention used a lot in msvcrt and a few other
> places.
> 

As much as I don't like arguing about style variations, this one is 
actually bothering me. Alignment (on the paren, on operators, etc) is a 
pretty common style, and I think it actually makes things more readable.

I feel like the fixed two level indentation only helps when writing 
code, by making the decision simpler, or possibly diffs by saving 
re-alignments, but it doesn't help reading the code at all, especially 
in cases with nested parentheses.

On function declaration, and because we often have a lot of declspec 
(and possibly long ones like STDMETHODCALLTYPE) it may be relevant but 
otherwise in the code I don't feel like it's an improvement.


Then again, although dinput style is inconsistent it's not using that 
style yet anywhere (except for function declarations and even only in a 
few locations), so unless we intend to rewrite it all I don't see reason 
to add another style variation.

In my recent contributions to dinput and as some of the existing code 
was already like this, I chose a style similar to the user32 / ntdll 
style, with space in parentheses (see ansi.c). And unless it really 
bothers someone I intended to use it for my HID joystick too.

Cheers,
-- 
Rémi Bernon <rbernon at codeweavers.com>



More information about the wine-devel mailing list