From: Robert Gerigk <Robert-Gerigk@online.de> Temporarily expose a second child on the test bus, enumerate both present children and verify DEVPKEY_Device_Siblings on each: - the property is a DEVPROP_TYPE_STRING_LIST, - it contains the instance ID of every other child, - it does not contain the device's own instance ID. The child is removed and the arrival/removal counters restored so the surrounding test flow (which asserts exactly one child arrival and one child removal) is not affected. Signed-off-by: Jan Robert Gerigk <Robert-Gerigk@online.de> --- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 82 ++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 9d9b8b37c62..867816bda33 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -2341,6 +2341,88 @@ static void test_pnp_devices(void) ok(GetLastError() == ERROR_IO_PENDING, "got error %lu\n", GetLastError()); ok(size == 0, "got size %lu\n", size); + /* DEVPKEY_Device_Siblings — add a transient second child so each + * PDO has a non-empty siblings list, then verify the property + * contents. Counters AND the DevQuery arrival/removal semaphores + * are saved / drained so the surrounding test flow (which asserts + * exactly one child arrival and one removal for child 1) is not + * perturbed by the transient child 2 events. */ + { + unsigned int saved_child_arrival = got_child_arrival; + unsigned int saved_child_removal = got_child_removal; + ULONG saved_dev_added = devquery_data.child_dev_added; + ULONG saved_dev_removed = devquery_data.child_dev_removed; + SP_DEVINFO_DATA child_dev = { sizeof(child_dev) }; + HDEVINFO sibset; + int extra_id = 2; + int scan_i; + WCHAR *entry; + + ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_ADD_CHILD, &extra_id, sizeof(extra_id), + NULL, 0, &size, NULL); + ok(ret, "failed to add sibling child, error %lu\n", GetLastError()); + pump_messages(); + + sibset = SetupDiGetClassDevsA(&child_class, NULL, NULL, + DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + ok(sibset != INVALID_HANDLE_VALUE, "got %#lx\n", GetLastError()); + + /* For each enumerated child, DEVPKEY_Device_Siblings must list + * every *other* child and must not list the child itself. */ + for (scan_i = 0; SetupDiEnumDeviceInfo(sibset, scan_i, &child_dev); scan_i++) + { + WCHAR self_id[MAX_PATH]; + BOOL found_other, found_self; + + ret = SetupDiGetDeviceInstanceIdW(sibset, &child_dev, self_id, + ARRAY_SIZE(self_id), NULL); + ok(ret, "failed to get instance ID, error %#lx\n", GetLastError()); + + prop_type = DEVPROP_TYPE_EMPTY; + size = 0; + memset(buffer_w, 0, sizeof(buffer_w)); + ret = SetupDiGetDevicePropertyW(sibset, &child_dev, &DEVPKEY_Device_Siblings, + &prop_type, (BYTE *)buffer_w, sizeof(buffer_w), + &size, 0); + ok(ret, "DEVPKEY_Device_Siblings missing for %s, error %#lx\n", + debugstr_w(self_id), GetLastError()); + ok(prop_type == DEVPROP_TYPE_STRING_LIST, "got type %#lx\n", prop_type); + + found_other = FALSE; + found_self = FALSE; + for (entry = buffer_w; *entry; entry += wcslen(entry) + 1) + { + if (!wcscmp(entry, self_id)) + found_self = TRUE; + else + found_other = TRUE; + } + ok(!found_self, "%s appeared in its own sibling list\n", debugstr_w(self_id)); + ok(found_other, "no sibling entry for %s\n", debugstr_w(self_id)); + } + SetupDiDestroyDeviceInfoList(sibset); + + /* Tear down the transient child. */ + ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_REMOVE_CHILD, &extra_id, sizeof(extra_id), + NULL, 0, &size, NULL); + ok(ret, "failed to remove sibling child, error %lu\n", GetLastError()); + pump_messages(); + + /* Restore counters and drain the DevQuery semaphores that child 2 + * arrival / removal signalled — they are binary (max count 1) and + * stay signalled until consumed, which would break the later + * WAIT_TIMEOUT assertions on the same semaphores. */ + got_child_arrival = saved_child_arrival; + got_child_removal = saved_child_removal; + devquery_data.child_dev_added = saved_dev_added; + devquery_data.child_dev_removed = saved_dev_removed; + if (have_devquery) + { + WaitForSingleObject(devquery_data.device_added_sem, 0); + WaitForSingleObject(devquery_data.device_removed_sem, 0); + } + } + id = 1; ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_REMOVE_CHILD, &id, sizeof(id), NULL, 0, &size, NULL); ok(ret, "got error %lu\n", GetLastError()); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10604