From 04fa990b0ced02a7011ad4801d8c956cbc1f65ab Mon Sep 17 00:00:00 2001 From: vperus Date: Sun, 7 Nov 2021 14:56:33 +0200 Subject: [PATCH 1/3] [input_common] Add completion test for CalibrationConfigurationJob --- src/input_common/helpers/udp_protocol.h | 21 +-- src/tests/CMakeLists.txt | 3 +- .../calibration_configuration_job.cpp | 136 ++++++++++++++++++ 3 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 src/tests/input_common/calibration_configuration_job.cpp diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h index bcba12c58e..2d5d54ddbd 100644 --- a/src/input_common/helpers/udp_protocol.h +++ b/src/input_common/helpers/udp_protocol.h @@ -54,6 +54,18 @@ struct Message { template constexpr Type GetMessageType(); +template +Message CreateMessage(const u32 magic, const T data, const u32 sender_id) { + boost::crc_32_type crc; + Header header{ + magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType(), + }; + Message message{header, data}; + crc.process_bytes(&message, sizeof(Message)); + message.header.crc = crc.checksum(); + return message; +} + namespace Request { enum RegisterFlags : u8 { @@ -101,14 +113,7 @@ static_assert(std::is_trivially_copyable_v, */ template Message Create(const T data, const u32 client_id = 0) { - boost::crc_32_type crc; - Header header{ - CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType(), - }; - Message message{header, data}; - crc.process_bytes(&message, sizeof(Message)); - message.header.crc = crc.checksum(); - return message; + return CreateMessage(CLIENT_MAGIC, data, client_id); } } // namespace Request diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c4c012f3d8..4a20c0768e 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -10,11 +10,12 @@ add_executable(tests core/network/network.cpp tests.cpp video_core/buffer_base.cpp + input_common/calibration_configuration_job.cpp ) create_target_directory_groups(tests) -target_link_libraries(tests PRIVATE common core) +target_link_libraries(tests PRIVATE common core input_common) target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) add_test(NAME tests COMMAND tests) diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp new file mode 100644 index 0000000000..8c77d81e9b --- /dev/null +++ b/src/tests/input_common/calibration_configuration_job.cpp @@ -0,0 +1,136 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include + +#include "input_common/drivers/udp_client.h" +#include "input_common/helpers/udp_protocol.h" + +class FakeCemuhookServer { +public: + FakeCemuhookServer() + : socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {} + + ~FakeCemuhookServer() { + is_running = false; + boost::system::error_code error_code; + socket.shutdown(boost::asio::socket_base::shutdown_both, error_code); + socket.close(); + if (handler.joinable()) { + handler.join(); + } + } + + u16 GetPort() { + return socket.local_endpoint().port(); + } + + std::string GetHost() { + return socket.local_endpoint().address().to_string(); + } + + void Run(const std::vector touch_movement_path) { + constexpr size_t HeaderSize = sizeof(InputCommon::CemuhookUDP::Header); + constexpr size_t PadDataSize = + sizeof(InputCommon::CemuhookUDP::Message); + + REQUIRE(touch_movement_path.size() > 0); + is_running = true; + handler = std::thread([touch_movement_path, this]() { + auto current_touch_position = touch_movement_path.begin(); + while (is_running) { + boost::asio::ip::udp::endpoint sender_endpoint; + boost::system::error_code error_code; + auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer), + sender_endpoint, 0, error_code); + + if (received_size < HeaderSize) { + continue; + } + + InputCommon::CemuhookUDP::Header header{}; + std::memcpy(&header, receive_buffer.data(), HeaderSize); + switch (header.type) { + case InputCommon::CemuhookUDP::Type::PadData: { + InputCommon::CemuhookUDP::Response::PadData pad_data{}; + pad_data.touch[0] = *current_touch_position; + const auto pad_message = InputCommon::CemuhookUDP::CreateMessage( + InputCommon::CemuhookUDP::SERVER_MAGIC, pad_data, 0); + std::memcpy(send_buffer.data(), &pad_message, PadDataSize); + socket.send_to(boost::asio::buffer(send_buffer, PadDataSize), sender_endpoint, + 0, error_code); + + bool can_advance = + std::next(current_touch_position) != touch_movement_path.end(); + if (can_advance) { + std::advance(current_touch_position, 1); + } + break; + } + case InputCommon::CemuhookUDP::Type::PortInfo: + case InputCommon::CemuhookUDP::Type::Version: + default: + break; + } + } + }); + } + +private: + boost::asio::io_service io_service; + boost::asio::ip::udp::socket socket; + std::array send_buffer; + std::array receive_buffer; + bool is_running = false; + std::thread handler; +}; + +TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") { + Common::Event complete_event; + FakeCemuhookServer server; + server.Run({{ + .is_active = 1, + .x = 0, + .y = 0, + }, + { + .is_active = 1, + .x = 200, + .y = 200, + }}); + + InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status{}; + u16 min_x{}; + u16 min_y{}; + u16 max_x{}; + u16 max_y{}; + InputCommon::CemuhookUDP::CalibrationConfigurationJob job( + server.GetHost(), server.GetPort(), + [&status, + &complete_event](InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status_) { + status = status_; + if (status == + InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed) { + complete_event.Set(); + } + }, + [&](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) { + min_x = min_x_; + min_y = min_y_; + max_x = max_x_; + max_y = max_y_; + }); + + complete_event.WaitUntil(std::chrono::system_clock::now() + std::chrono::seconds(10)); + REQUIRE(status == InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed); + REQUIRE(min_x == 0); + REQUIRE(min_y == 0); + REQUIRE(max_x == 200); + REQUIRE(max_y == 200); +} From 660c6bec22973ad3d4d5d53011c3d87f1f77e1df Mon Sep 17 00:00:00 2001 From: vperus Date: Mon, 29 Nov 2021 16:35:54 +0200 Subject: [PATCH 2/3] Revert of b01aa72 Caused worker_thread to be stuck in Stage1Completed state until job's destruction. --- src/input_common/drivers/udp_client.cpp | 72 +++++++++++++------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index fdee0f2d53..730e8b2f3f 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -536,42 +536,46 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( std::function data_callback) { std::thread([=, this] { + constexpr u16 CALIBRATION_THRESHOLD = 100; + + u16 min_x{UINT16_MAX}; + u16 min_y{UINT16_MAX}; + u16 max_x{}; + u16 max_y{}; + Status current_status{Status::Initialized}; - SocketCallback callback{ - [](Response::Version) {}, [](Response::PortInfo) {}, - [&](Response::PadData data) { - static constexpr u16 CALIBRATION_THRESHOLD = 100; - static constexpr u16 MAX_VALUE = UINT16_MAX; + SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, + [&](Response::PadData data) { + if (current_status == Status::Initialized) { + // Receiving data means the communication is ready now + current_status = Status::Ready; + status_callback(current_status); + } + if (data.touch[0].is_active == 0) { + return; + } + LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, + data.touch[0].y); + min_x = std::min(min_x, static_cast(data.touch[0].x)); + min_y = std::min(min_y, static_cast(data.touch[0].y)); + if (current_status == Status::Ready) { + // First touch - min data (min_x/min_y) + current_status = Status::Stage1Completed; + status_callback(current_status); + } + if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && + data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { + // Set the current position as max value and finishes + // configuration + max_x = data.touch[0].x; + max_y = data.touch[0].y; + current_status = Status::Completed; + data_callback(min_x, min_y, max_x, max_y); + status_callback(current_status); - if (current_status == Status::Initialized) { - // Receiving data means the communication is ready now - current_status = Status::Ready; - status_callback(current_status); - } - const auto& touchpad_0 = data.touch[0]; - if (touchpad_0.is_active == 0) { - return; - } - LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y); - const u16 min_x = std::min(MAX_VALUE, static_cast(touchpad_0.x)); - const u16 min_y = std::min(MAX_VALUE, static_cast(touchpad_0.y)); - if (current_status == Status::Ready) { - // First touch - min data (min_x/min_y) - current_status = Status::Stage1Completed; - status_callback(current_status); - } - if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD && - touchpad_0.y - min_y > CALIBRATION_THRESHOLD) { - // Set the current position as max value and finishes configuration - const u16 max_x = touchpad_0.x; - const u16 max_y = touchpad_0.y; - current_status = Status::Completed; - data_callback(min_x, min_y, max_x, max_y); - status_callback(current_status); - - complete_event.Set(); - } - }}; + complete_event.Set(); + } + }}; Socket socket{host, port, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; complete_event.Wait(); From 11f4bf8a9afe109aaed0e53133781694a735e225 Mon Sep 17 00:00:00 2001 From: vperus Date: Fri, 17 Dec 2021 20:51:47 +0200 Subject: [PATCH 3/3] [input_common] Move variable declaration closer to usage MSVC supplied with VS2022 generates "warning C4189: 'CALIBRATION_THRESHOLD': local variable is initialized but not referenced" which is treated as an error. Circumvent it by moving constexpr variable directly into body of lambda function. --- src/input_common/drivers/udp_client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 730e8b2f3f..d6172527c1 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -536,8 +536,6 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( std::function data_callback) { std::thread([=, this] { - constexpr u16 CALIBRATION_THRESHOLD = 100; - u16 min_x{UINT16_MAX}; u16 min_y{UINT16_MAX}; u16 max_x{}; @@ -546,6 +544,8 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( Status current_status{Status::Initialized}; SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) { + constexpr u16 CALIBRATION_THRESHOLD = 100; + if (current_status == Status::Initialized) { // Receiving data means the communication is ready now current_status = Status::Ready;