From 3d62ba2b42dd4d5a775b36b77a42672b20bfe677 Mon Sep 17 00:00:00 2001 From: FrogTheFrog Date: Sun, 19 May 2024 11:37:29 +0300 Subject: [PATCH 1/3] feat: port more win utils --- src/common/include/displaydevice/types.h | 19 ++ .../include/displaydevice/windows/types.h | 19 ++ .../displaydevice/windows/winapiutils.h | 81 ++++++- src/windows/winapiutils.cpp | 122 ++++++++++- src/windows/windisplaydevicetopology.cpp | 2 +- tests/unit/windows/test_winapiutils.cpp | 205 +++++++++++++++++- 6 files changed, 433 insertions(+), 15 deletions(-) create mode 100644 src/common/include/displaydevice/types.h diff --git a/src/common/include/displaydevice/types.h b/src/common/include/displaydevice/types.h new file mode 100644 index 0000000..00d9c0a --- /dev/null +++ b/src/common/include/displaydevice/types.h @@ -0,0 +1,19 @@ +#pragma once + +namespace display_device { + /** + * @brief Display's resolution. + */ + struct Resolution { + unsigned int m_width; + unsigned int m_height; + }; + + /** + * @brief Floating point is stored in a "numerator/denominator" form. + */ + struct Rational { + unsigned int m_numerator; + unsigned int m_denominator; + }; +} // namespace display_device diff --git a/src/windows/include/displaydevice/windows/types.h b/src/windows/include/displaydevice/windows/types.h index 369eacb..20215a9 100644 --- a/src/windows/include/displaydevice/windows/types.h +++ b/src/windows/include/displaydevice/windows/types.h @@ -9,6 +9,9 @@ #include #include +// local includes +#include "displaydevice/types.h" + namespace display_device { /** * @brief Type of query the OS should perform while searching for display devices. @@ -26,6 +29,14 @@ namespace display_device { std::vector m_modes; /**< Display modes for ACTIVE displays. */ }; + /** + * @brief Specifies additional constraints for the validated device. + */ + enum class ValidatedPathType { + Active, /**< The device path must be active. */ + Any /**< The device path can be active or inactive. */ + }; + /** * @brief Contains the device path and the id for a VALID device. * @see win_utils::getDeviceInfoForValidPath for what is considered a valid device. @@ -67,4 +78,12 @@ namespace display_device { */ using ActiveTopology = std::vector>; + /** + * @brief Display's mode (resolution + refresh rate). + */ + struct DisplayMode { + Resolution m_resolution; + Rational m_refresh_rate; + }; + } // namespace display_device diff --git a/src/windows/include/displaydevice/windows/winapiutils.h b/src/windows/include/displaydevice/windows/winapiutils.h index b0be2db..b07700a 100644 --- a/src/windows/include/displaydevice/windows/winapiutils.h +++ b/src/windows/include/displaydevice/windows/winapiutils.h @@ -1,5 +1,8 @@ #pragma once +// system includes +#include + // local includes #include "winapilayerinterface.h" @@ -187,7 +190,7 @@ namespace display_device::win_utils { * * @param w_api Reference to the Windows API layer. * @param path Path to validate and get info for. - * @param must_be_active Optionally request that the valid path must also be active. + * @param type Additional constraints for the path. * @returns Commonly used info for the path, or empty optional if the path is invalid. * @see WinApiLayerInterface::queryDisplayConfig on how to get paths and modes from the system. * @@ -195,11 +198,35 @@ namespace display_device::win_utils { * ```cpp * DISPLAYCONFIG_PATH_INFO path; * const WinApiLayerInterface* iface = getIface(...); - * const auto device_info = getDeviceInfoForValidPath(*iface, path, true); + * const auto device_info = getDeviceInfoForValidPath(*iface, path, ValidatedPathType::Active); * ``` */ [[nodiscard]] std::optional - getDeviceInfoForValidPath(const WinApiLayerInterface &w_api, const DISPLAYCONFIG_PATH_INFO &path, bool must_be_active); + getDeviceInfoForValidPath(const WinApiLayerInterface &w_api, const DISPLAYCONFIG_PATH_INFO &path, ValidatedPathType type); + + /** + * @brief Get the active path matching the device id. + * @param w_api Reference to the Windows API layer. + * @param device_id Id to search for in the the list. + * @param paths List to be searched. + * @returns A pointer to an active path matching our id, nullptr otherwise. + * @see WinApiLayerInterface::queryDisplayConfig on how to get paths and modes from the system. + * + * EXAMPLES: + * ```cpp + * const std::vector paths; + * const WinApiLayerInterface* iface = getIface(...); + * const DISPLAYCONFIG_PATH_INFO* active_path = get_active_path(*iface, "MY_DEVICE_ID", paths); + * ``` + */ + [[nodiscard]] const DISPLAYCONFIG_PATH_INFO * + getActivePath(const WinApiLayerInterface &w_api, const std::string &device_id, const std::vector &paths); + + /** + * @see getActivePath (const version) for the description. + */ + [[nodiscard]] DISPLAYCONFIG_PATH_INFO * + getActivePath(const WinApiLayerInterface &w_api, const std::string &device_id, std::vector &paths); /** * @brief Collect arbitrary source data from provided paths. @@ -242,4 +269,52 @@ namespace display_device::win_utils { */ [[nodiscard]] std::vector makePathsForNewTopology(const ActiveTopology &new_topology, const PathSourceIndexDataMap &path_source_data, const std::vector &paths); + + /** + * @brief Get all the missing duplicate device ids for the provided device ids. + * @param w_api Reference to the Windows API layer. + * @param device_ids Device ids to find the missing duplicate ids for. + * @returns A list of device ids containing the provided device ids and all unspecified ids + * for duplicated displays. + * + * EXAMPLES: + * ```cpp + * const WinApiLayerInterface* iface = getIface(...); + * const auto device_ids_with_duplicates = getAllDeviceIdsAndMatchingDuplicates(*iface, { "MY_ID1" }); + * ``` + */ + [[nodiscard]] std::set + getAllDeviceIdsAndMatchingDuplicates(const WinApiLayerInterface &w_api, const std::set &device_ids); + + /** + * @brief Check if the refresh rates are almost equal. + * @param lhs First refresh rate. + * @param rhs Second refresh rate. + * @return True if refresh rates are almost equal, false otherwise. + * + * EXAMPLES: + * ```cpp + * const bool almost_equal = fuzzyCompareRefreshRates(Rational { 60, 1 }, Rational { 5985, 100 }); + * const bool not_equal = fuzzyCompareRefreshRates(Rational { 60, 1 }, Rational { 5585, 100 }); + * ``` + */ + [[nodiscard]] bool + fuzzyCompareRefreshRates(const Rational &lhs, const Rational &rhs); + + /** + * @brief Check if the display modes are almost equal. + * @param lhs First mode. + * @param rhs Second mode. + * @return True if display modes are almost equal, false otherwise. + * + * EXAMPLES: + * ```cpp + * const bool almost_equal = fuzzyCompareModes(DisplayMode { { 1920, 1080 }, { 60, 1 } }, + * DisplayMode { { 1920, 1080 }, { 5985, 100 } }); + * const bool not_equal = fuzzyCompareModes(DisplayMode { { 1920, 1080 }, { 60, 1 } }, + * DisplayMode { { 1920, 1080 }, { 5585, 100 } }); + * ``` + */ + [[nodiscard]] bool + fuzzyCompareModes(const DisplayMode &lhs, const DisplayMode &rhs); } // namespace display_device::win_utils diff --git a/src/windows/winapiutils.cpp b/src/windows/winapiutils.cpp index 73bfc7b..08aa93a 100644 --- a/src/windows/winapiutils.cpp +++ b/src/windows/winapiutils.cpp @@ -38,6 +38,26 @@ namespace { toString(const LUID &id) { return std::to_string(id.HighPart) + std::to_string(id.LowPart); } + + /** + * @brief Check if the source modes are duplicated (cloned). + * @param lhs First mode to check. + * @param rhs Second mode to check. + * @returns True if both mode have the same origin point, false otherwise. + * @note Windows enforces the behaviour that only the duplicate devices can + * have the same origin point as otherwise the configuration is considered invalid by the OS. + * + * EXAMPLES: + * ```cpp + * DISPLAYCONFIG_SOURCE_MODE mode_a; + * DISPLAYCONFIG_SOURCE_MODE mode_b; + * const bool are_duplicated = are_modes_duplicated(mode_a, mode_b); + * ``` + */ + bool + are_modes_duplicated(const DISPLAYCONFIG_SOURCE_MODE &lhs, const DISPLAYCONFIG_SOURCE_MODE &rhs) { + return lhs.position.x == rhs.position.x && lhs.position.y == rhs.position.y; + } } // namespace namespace display_device::win_utils { @@ -169,13 +189,13 @@ namespace display_device::win_utils { } std::optional - getDeviceInfoForValidPath(const WinApiLayerInterface &w_api, const DISPLAYCONFIG_PATH_INFO &path, bool must_be_active) { + getDeviceInfoForValidPath(const WinApiLayerInterface &w_api, const DISPLAYCONFIG_PATH_INFO &path, const ValidatedPathType type) { if (!isAvailable(path)) { // Could be transient issue according to MSDOCS (no longer available, but still "active") return std::nullopt; } - if (must_be_active) { + if (type == ValidatedPathType::Active) { if (!isActive(path)) { return std::nullopt; } @@ -199,6 +219,27 @@ namespace display_device::win_utils { return ValidatedDeviceInfo { device_path, device_id }; } + const DISPLAYCONFIG_PATH_INFO * + getActivePath(const WinApiLayerInterface &w_api, const std::string &device_id, const std::vector &paths) { + for (const auto &path : paths) { + const auto device_info { getDeviceInfoForValidPath(w_api, path, ValidatedPathType::Active) }; + if (!device_info) { + continue; + } + + if (device_info->m_device_id == device_id) { + return &path; + } + } + + return nullptr; + } + + DISPLAYCONFIG_PATH_INFO * + getActivePath(const WinApiLayerInterface &w_api, const std::string &device_id, std::vector &paths) { + return const_cast(getActivePath(w_api, device_id, const_cast &>(paths))); + } + PathSourceIndexDataMap collectSourceDataForMatchingPaths(const WinApiLayerInterface &w_api, const std::vector &paths) { PathSourceIndexDataMap path_data; @@ -207,7 +248,7 @@ namespace display_device::win_utils { for (std::size_t index = 0; index < paths.size(); ++index) { const auto &path { paths[index] }; - const auto device_info { getDeviceInfoForValidPath(w_api, path, false) }; + const auto device_info { getDeviceInfoForValidPath(w_api, path, ValidatedPathType::Any) }; if (!device_info) { // Path is not valid continue; @@ -376,4 +417,79 @@ namespace display_device::win_utils { } return new_paths; } + + std::set + getAllDeviceIdsAndMatchingDuplicates(const WinApiLayerInterface &w_api, const std::set &device_ids) { + const auto display_data { w_api.queryDisplayConfig(QueryType::Active) }; + if (!display_data) { + // Error already logged + return {}; + } + + std::set all_device_ids; + for (const auto &device_id : device_ids) { + if (device_id.empty()) { + DD_LOG(error) << "Device it is empty!"; + return {}; + } + + const auto provided_path { getActivePath(w_api, device_id, display_data->m_paths) }; + if (!provided_path) { + DD_LOG(warning) << "Failed to find device for " << device_id << "!"; + return {}; + } + + const auto provided_path_source_mode { getSourceMode(getSourceIndex(*provided_path, display_data->m_modes), display_data->m_modes) }; + if (!provided_path_source_mode) { + DD_LOG(error) << "Active device does not have a source mode: " << device_id << "!"; + return {}; + } + + // We will now iterate over all the active paths (provided path included) and check if + // any of them are duplicated. + for (const auto &path : display_data->m_paths) { + const auto device_info { getDeviceInfoForValidPath(w_api, path, ValidatedPathType::Active) }; + if (!device_info) { + continue; + } + + if (all_device_ids.contains(device_info->m_device_id)) { + // Already checked + continue; + } + + const auto source_mode { getSourceMode(getSourceIndex(path, display_data->m_modes), display_data->m_modes) }; + if (!source_mode) { + DD_LOG(error) << "Active device does not have a source mode: " << device_info->m_device_id << "!"; + return {}; + } + + if (!are_modes_duplicated(*provided_path_source_mode, *source_mode)) { + continue; + } + + all_device_ids.insert(device_info->m_device_id); + } + } + + return all_device_ids; + } + + bool + fuzzyCompareRefreshRates(const Rational &lhs, const Rational &rhs) { + if (lhs.m_denominator > 0 && rhs.m_denominator > 0) { + const float lhs_f { static_cast(lhs.m_numerator) / static_cast(lhs.m_denominator) }; + const float rhs_f { static_cast(rhs.m_numerator) / static_cast(rhs.m_denominator) }; + return (std::abs(lhs_f - rhs_f) <= 0.9f); + } + + return false; + } + + bool + fuzzyCompareModes(const DisplayMode &lhs, const DisplayMode &rhs) { + return lhs.m_resolution.m_width == rhs.m_resolution.m_width && + lhs.m_resolution.m_height == rhs.m_resolution.m_height && + fuzzyCompareRefreshRates(lhs.m_refresh_rate, rhs.m_refresh_rate); + } } // namespace display_device::win_utils diff --git a/src/windows/windisplaydevicetopology.cpp b/src/windows/windisplaydevicetopology.cpp index 0475e46..c12c203 100644 --- a/src/windows/windisplaydevicetopology.cpp +++ b/src/windows/windisplaydevicetopology.cpp @@ -69,7 +69,7 @@ namespace display_device { std::unordered_map position_to_topology_index; ActiveTopology topology; for (const auto &path : display_data->m_paths) { - const auto device_info { win_utils::getDeviceInfoForValidPath(*m_w_api, path, true) }; + const auto device_info { win_utils::getDeviceInfoForValidPath(*m_w_api, path, display_device::ValidatedPathType::Active) }; if (!device_info) { continue; } diff --git a/tests/unit/windows/test_winapiutils.cpp b/tests/unit/windows/test_winapiutils.cpp index b9aaba8..83bc064 100644 --- a/tests/unit/windows/test_winapiutils.cpp +++ b/tests/unit/windows/test_winapiutils.cpp @@ -7,12 +7,31 @@ namespace { // Convenience keywords for GMock using ::testing::_; + using ::testing::InSequence; using ::testing::Return; using ::testing::StrictMock; // Test fixture(s) for this file class WinApiUtilsMocked: public BaseTest { public: + void + setupExpectCallForValidPaths(int number_of_calls, InSequence & /* To ensure that sequence is created outside this scope */) { + for (int i = 1; i <= number_of_calls; ++i) { + EXPECT_CALL(m_layer, getMonitorDevicePath(_)) + .Times(1) + .WillOnce(Return("Path" + std::to_string(i))) + .RetiresOnSaturation(); + EXPECT_CALL(m_layer, getDeviceId(_)) + .Times(1) + .WillOnce(Return("DeviceId" + std::to_string(i))) + .RetiresOnSaturation(); + EXPECT_CALL(m_layer, getDisplayName(_)) + .Times(1) + .WillOnce(Return("DisplayName" + std::to_string(i))) + .RetiresOnSaturation(); + } + } + StrictMock m_layer; }; @@ -299,7 +318,7 @@ TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, ActivePath, MustBeActiveIsTrue) { .Times(1) .WillOnce(Return("DisplayName")); - const auto result { display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, true) }; + const auto result { display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, display_device::ValidatedPathType::Active) }; ASSERT_TRUE(result); EXPECT_EQ(result->m_device_path, "DevicePath"); @@ -317,7 +336,7 @@ TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, ActivePath, MustBeActiveIsFalse) { .Times(1) .WillOnce(Return("DisplayName")); - const auto result { display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, false) }; + const auto result { display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, display_device::ValidatedPathType::Any) }; ASSERT_TRUE(result); EXPECT_EQ(result->m_device_path, "DevicePath"); @@ -335,7 +354,7 @@ TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, InactivePath, MustBeActiveIsTrue) .Times(1) .WillOnce(Return("")); - EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, true), std::nullopt); + EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, display_device::ValidatedPathType::Active), std::nullopt); } TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, InactivePath, MustBeActiveIsFalse) { @@ -349,7 +368,7 @@ TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, InactivePath, MustBeActiveIsFalse) .Times(1) .WillOnce(Return("DisplayName")); - const auto result { display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_INACTIVE_PATH, false) }; + const auto result { display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_INACTIVE_PATH, display_device::ValidatedPathType::Any) }; ASSERT_TRUE(result); EXPECT_EQ(result->m_device_path, "DevicePath"); @@ -364,7 +383,7 @@ TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, ActivePath, EmptyDeviceId) { .Times(1) .WillOnce(Return("")); - EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, true), std::nullopt); + EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, display_device::ValidatedPathType::Active), std::nullopt); } TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, ActivePath, EmptyDevicePath) { @@ -372,11 +391,11 @@ TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, ActivePath, EmptyDevicePath) { .Times(1) .WillOnce(Return("")); - EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, true), std::nullopt); + EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_ACTIVE_PATH, display_device::ValidatedPathType::Active), std::nullopt); } TEST_F_S_MOCKED(GetDeviceInfo, AvailablePath, InactivePath) { - EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_INACTIVE_PATH, true), std::nullopt); + EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, AVAILABLE_AND_INACTIVE_PATH, display_device::ValidatedPathType::Active), std::nullopt); } TEST_F_S_MOCKED(GetDeviceInfo, UnavailablePath, ActivePath) { @@ -385,7 +404,61 @@ TEST_F_S_MOCKED(GetDeviceInfo, UnavailablePath, ActivePath) { path.targetInfo.targetAvailable = FALSE; path.flags = DISPLAYCONFIG_PATH_ACTIVE; - EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, path, false), std::nullopt); + EXPECT_EQ(display_device::win_utils::getDeviceInfoForValidPath(m_layer, path, display_device::ValidatedPathType::Any), std::nullopt); +} + +TEST_F_S_MOCKED(GetActivePath, InstantMatch) { + EXPECT_CALL(m_layer, getMonitorDevicePath(_)) + .Times(2) + .WillRepeatedly(Return("Path1")); + EXPECT_CALL(m_layer, getDisplayName(_)) + .Times(2) + .WillRepeatedly(Return("DisplayNameX")); + EXPECT_CALL(m_layer, getDeviceId(_)) + .Times(2) + .WillRepeatedly(Return("DeviceId1")); + + auto *path { display_device::win_utils::getActivePath(m_layer, "DeviceId1", const_cast &>(PATHS_WITH_SOURCE_IDS)) }; + auto *const_path { display_device::win_utils::getActivePath(m_layer, "DeviceId1", PATHS_WITH_SOURCE_IDS) }; + + EXPECT_EQ(path, const_path); + EXPECT_EQ(path, &PATHS_WITH_SOURCE_IDS.at(0)); +} + +TEST_F_S_MOCKED(GetActivePath, SecondMatch) { + EXPECT_CALL(m_layer, getMonitorDevicePath(_)) + .Times(4) + .WillOnce(Return("Path1")) + .WillOnce(Return("Path2")) + .WillOnce(Return("Path1")) + .WillOnce(Return("Path2")); + EXPECT_CALL(m_layer, getDisplayName(_)) + .Times(4) + .WillRepeatedly(Return("DisplayNameX")); + EXPECT_CALL(m_layer, getDeviceId(_)) + .Times(4) + .WillOnce(Return("DeviceId1")) + .WillOnce(Return("DeviceId2")) + .WillOnce(Return("DeviceId1")) + .WillOnce(Return("DeviceId2")); + + auto *path { display_device::win_utils::getActivePath(m_layer, "DeviceId2", const_cast &>(PATHS_WITH_SOURCE_IDS)) }; + auto *const_path { display_device::win_utils::getActivePath(m_layer, "DeviceId2", PATHS_WITH_SOURCE_IDS) }; + + EXPECT_EQ(path, const_path); + EXPECT_EQ(path, &PATHS_WITH_SOURCE_IDS.at(1)); +} + +TEST_F_S_MOCKED(GetActivePath, NoMatch) { + EXPECT_CALL(m_layer, getMonitorDevicePath(_)) + .Times(4) + .WillOnce(Return("")); + + auto *path { display_device::win_utils::getActivePath(m_layer, "DeviceId1", const_cast &>(PATHS_WITH_SOURCE_IDS)) }; + auto *const_path { display_device::win_utils::getActivePath(m_layer, "DeviceId1", PATHS_WITH_SOURCE_IDS) }; + + EXPECT_EQ(path, const_path); + EXPECT_EQ(path, nullptr); } TEST_F_S_MOCKED(CollectSourceDataForMatchingPaths) { @@ -702,3 +775,119 @@ TEST_F_S_MOCKED(MakePathsForNewTopology, EmptyList) { const std::vector expected_paths {}; EXPECT_EQ(display_device::win_utils::makePathsForNewTopology(new_topology, EXPECTED_SOURCE_INDEX_DATA, PATHS_WITH_SOURCE_IDS), expected_paths); } + +TEST_F_S_MOCKED(GetAllDeviceIdsAndMatchingDuplicates) { + auto pam_no_modes { ut_consts::PAM_4_ACTIVE_WITH_2_DUPLICATES }; + + InSequence sequence; + EXPECT_CALL(m_layer, queryDisplayConfig(display_device::QueryType::Active)) + .Times(1) + .WillOnce(Return(pam_no_modes)); + + // DeviceId1 iterations + { + // Outer loop + setupExpectCallForValidPaths(1, sequence); + + // Inner loop + setupExpectCallForValidPaths(4, sequence); + } + + // DeviceId2 iterations + { + // Outer loop + setupExpectCallForValidPaths(2, sequence); + + // Inner loop + setupExpectCallForValidPaths(4, sequence); + } + + EXPECT_EQ(display_device::win_utils::getAllDeviceIdsAndMatchingDuplicates(m_layer, { "DeviceId1", "DeviceId2" }), (std::set { "DeviceId1", "DeviceId2", "DeviceId3" })); +} + +TEST_F_S_MOCKED(GetAllDeviceIdsAndMatchingDuplicates, FailedToQueryDevices) { + EXPECT_CALL(m_layer, queryDisplayConfig(display_device::QueryType::Active)) + .Times(1) + .WillOnce(Return(ut_consts::PAM_NULL)); + + EXPECT_EQ(display_device::win_utils::getAllDeviceIdsAndMatchingDuplicates(m_layer, { "DeviceId2" }), std::set {}); +} + +TEST_F_S_MOCKED(GetAllDeviceIdsAndMatchingDuplicates, EmptyDeviceIdInProvidedList) { + // InSequence sequence; + // setupExpectCallFor4ActivePathsAndModes(display_device::QueryType::Active, sequence); + EXPECT_CALL(m_layer, queryDisplayConfig(display_device::QueryType::Active)) + .Times(1) + .WillOnce(Return(ut_consts::PAM_4_ACTIVE_WITH_2_DUPLICATES)); + + EXPECT_EQ(display_device::win_utils::getAllDeviceIdsAndMatchingDuplicates(m_layer, { "" }), std::set {}); +} + +TEST_F_S_MOCKED(GetAllDeviceIdsAndMatchingDuplicates, FailedToFindActivePath) { + EXPECT_CALL(m_layer, queryDisplayConfig(display_device::QueryType::Active)) + .Times(1) + .WillOnce(Return(ut_consts::PAM_4_ACTIVE_WITH_2_DUPLICATES)); + EXPECT_CALL(m_layer, getMonitorDevicePath(_)) + .Times(4) + .WillOnce(Return("")); + + EXPECT_EQ(display_device::win_utils::getAllDeviceIdsAndMatchingDuplicates(m_layer, { "DeviceId2" }), std::set {}); +} + +TEST_F_S_MOCKED(GetAllDeviceIdsAndMatchingDuplicates, NoSourceModeFound) { + auto pam_no_modes { ut_consts::PAM_4_ACTIVE_WITH_2_DUPLICATES }; + pam_no_modes->m_modes.clear(); + + InSequence sequence; + EXPECT_CALL(m_layer, queryDisplayConfig(display_device::QueryType::Active)) + .Times(1) + .WillOnce(Return(pam_no_modes)); + setupExpectCallForValidPaths(2, sequence); + + EXPECT_EQ(display_device::win_utils::getAllDeviceIdsAndMatchingDuplicates(m_layer, { "DeviceId2" }), std::set {}); +} + +TEST_F_S_MOCKED(GetAllDeviceIdsAndMatchingDuplicates, IncompleteListOfSources) { + auto pam_no_modes { ut_consts::PAM_4_ACTIVE_WITH_2_DUPLICATES }; + pam_no_modes->m_modes.resize(2); + + InSequence sequence; + EXPECT_CALL(m_layer, queryDisplayConfig(display_device::QueryType::Active)) + .Times(1) + .WillOnce(Return(pam_no_modes)); + setupExpectCallForValidPaths(2, sequence); + EXPECT_CALL(m_layer, getMonitorDevicePath(_)) + .Times(1) + .WillOnce(Return("")) + .RetiresOnSaturation(); + setupExpectCallForValidPaths(1, sequence); + + EXPECT_EQ(display_device::win_utils::getAllDeviceIdsAndMatchingDuplicates(m_layer, { "DeviceId1" }), std::set {}); +} + +TEST_F_S_MOCKED(FuzzyCompareRefreshRates) { + EXPECT_EQ(display_device::win_utils::fuzzyCompareRefreshRates(display_device::Rational { 60, 1 }, display_device::Rational { 5985, 100 }), true); + EXPECT_EQ(display_device::win_utils::fuzzyCompareRefreshRates(display_device::Rational { 60, 1 }, display_device::Rational { 5920, 100 }), true); + EXPECT_EQ(display_device::win_utils::fuzzyCompareRefreshRates(display_device::Rational { 60, 1 }, display_device::Rational { 5900, 100 }), false); + EXPECT_EQ(display_device::win_utils::fuzzyCompareRefreshRates(display_device::Rational { 60, 0 }, display_device::Rational { 5985, 100 }), false); + EXPECT_EQ(display_device::win_utils::fuzzyCompareRefreshRates(display_device::Rational { 60, 1 }, display_device::Rational { 5985, 0 }), false); +} + +TEST_F_S_MOCKED(FuzzyCompareModes) { + EXPECT_EQ(display_device::win_utils::fuzzyCompareModes( + display_device::DisplayMode { 1920, 1080, { 60, 1 } }, + display_device::DisplayMode { 1920, 1080, { 60, 1 } }), + true); + EXPECT_EQ(display_device::win_utils::fuzzyCompareModes( + display_device::DisplayMode { 123, 1080, { 60, 1 } }, + display_device::DisplayMode { 1920, 1080, { 60, 1 } }), + false); + EXPECT_EQ(display_device::win_utils::fuzzyCompareModes( + display_device::DisplayMode { 1920, 123, { 60, 1 } }, + display_device::DisplayMode { 1920, 1080, { 60, 1 } }), + false); + EXPECT_EQ(display_device::win_utils::fuzzyCompareModes( + display_device::DisplayMode { 1920, 1080, { 60, 1 } }, + display_device::DisplayMode { 1920, 1080, { 50, 1 } }), + false); +} From c7aafaa2bef41ef9892e7f761689d5a4bc51fa8d Mon Sep 17 00:00:00 2001 From: FrogTheFrog Date: Sun, 19 May 2024 11:41:29 +0300 Subject: [PATCH 2/3] typo --- src/common/include/displaydevice/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/include/displaydevice/types.h b/src/common/include/displaydevice/types.h index 00d9c0a..c31816d 100644 --- a/src/common/include/displaydevice/types.h +++ b/src/common/include/displaydevice/types.h @@ -10,7 +10,7 @@ namespace display_device { }; /** - * @brief Floating point is stored in a "numerator/denominator" form. + * @brief Floating point stored in a "numerator/denominator" form. */ struct Rational { unsigned int m_numerator; From 3cc9a7e3ee532354344f12d288d14b83ddee9426 Mon Sep 17 00:00:00 2001 From: FrogTheFrog Date: Sun, 19 May 2024 15:41:14 +0300 Subject: [PATCH 3/3] increase coverage --- tests/unit/windows/utils/mockwinapilayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/windows/utils/mockwinapilayer.cpp b/tests/unit/windows/utils/mockwinapilayer.cpp index f03231e..c6ced01 100644 --- a/tests/unit/windows/utils/mockwinapilayer.cpp +++ b/tests/unit/windows/utils/mockwinapilayer.cpp @@ -68,7 +68,7 @@ namespace { data.m_modes.push_back({}); data.m_modes.back().infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE; data.m_modes.back().sourceMode = {}; // Set the union - data.m_modes.back().sourceMode.position = { 3842, 0 }; + data.m_modes.back().sourceMode.position = { 0, 1081 }; data.m_modes.back().sourceMode.width = 1920; data.m_modes.back().sourceMode.height = 1080; }