#include "unity.h" #include #include #include #include "hid/hid_report.hpp" void setUp() {} void tearDown() {} namespace { using xone::hid::gamepad_input; using xone::hid::make_input_report; using xone::hid::parse_output_report; using xone::hid::xbox_one_report_descriptor; // GIP bitmask with every button but the guide pressed. constexpr std::uint16_t k_gip_all_buttons = 0x0004 | // start 0x0008 | // select 0x0010 | // a 0x0020 | // b 0x0040 | // x 0x0080 | // y 0x0100 | // dpad up 0x0200 | // dpad down 0x0400 | // dpad left 0x0800 | // dpad right 0x1000 | // bumper left 0x2000 | // bumper right 0x4000 | // stick left 0x8000; // stick right } // namespace void test_input_report_idle(void) { auto report = make_input_report(gamepad_input{}); TEST_ASSERT_EQUAL_UINT8(1, report[0]); for (std::size_t i = 1; i < report.size(); ++i) TEST_ASSERT_EQUAL_UINT8(0, report[i]); } void test_input_report_all_buttons(void) { gamepad_input in; in.buttons = k_gip_all_buttons; in.guide_down = true; auto report = make_input_report(in); // Every HID button bit (0..13) set: LE16 0x3FFF. TEST_ASSERT_EQUAL_UINT8(0xff, report[1]); TEST_ASSERT_EQUAL_UINT8(0x3f, report[2]); TEST_ASSERT_EQUAL_UINT8(1, report[3]); } void test_input_report_button_remap(void) { // GIP A (bit 4) -> HID bit 0. gamepad_input in; in.buttons = 0x0010; auto report = make_input_report(in); TEST_ASSERT_EQUAL_UINT8(0x01, report[1]); TEST_ASSERT_EQUAL_UINT8(0x00, report[2]); // GIP Select (bit 3) -> HID Back (bit 8). in.buttons = 0x0008; report = make_input_report(in); TEST_ASSERT_EQUAL_UINT8(0x00, report[1]); TEST_ASSERT_EQUAL_UINT8(0x01, report[2]); // GIP Dpad right (bit 11) -> HID bit 13. in.buttons = 0x0800; report = make_input_report(in); TEST_ASSERT_EQUAL_UINT8(0x00, report[1]); TEST_ASSERT_EQUAL_UINT8(0x20, report[2]); } void test_input_report_triggers(void) { gamepad_input in; in.trigger_left = 511; in.trigger_right = 512; auto report = make_input_report(in); TEST_ASSERT_EQUAL_UINT8(127, report[4]); TEST_ASSERT_EQUAL_UINT8(128, report[5]); in.trigger_left = 0; in.trigger_right = 1023; report = make_input_report(in); TEST_ASSERT_EQUAL_UINT8(0, report[4]); TEST_ASSERT_EQUAL_UINT8(255, report[5]); // Values above the 10-bit range clamp to full. in.trigger_left = 0xffff; in.trigger_right = 0; report = make_input_report(in); TEST_ASSERT_EQUAL_UINT8(255, report[4]); TEST_ASSERT_EQUAL_UINT8(0, report[5]); } void test_input_report_sticks(void) { gamepad_input in; in.stick_left_x = -1; in.stick_left_y = 32767; in.stick_right_x = -32768; in.stick_right_y = 0; auto report = make_input_report(in); TEST_ASSERT_EQUAL_UINT8(0xff, report[6]); TEST_ASSERT_EQUAL_UINT8(0xff, report[7]); TEST_ASSERT_EQUAL_UINT8(0xff, report[8]); TEST_ASSERT_EQUAL_UINT8(0x7f, report[9]); TEST_ASSERT_EQUAL_UINT8(0x00, report[10]); TEST_ASSERT_EQUAL_UINT8(0x80, report[11]); } void test_output_report_parse(void) { std::array report{}; report[0] = 1; report[2] = 0x80; report[3] = 0xc0; auto motors = parse_output_report(report); TEST_ASSERT_EQUAL_UINT8(0x80, motors.left); TEST_ASSERT_EQUAL_UINT8(0xc0, motors.right); } void test_output_report_rejects_malformed(void) { std::array report{}; report[0] = 2; // wrong report ID report[2] = 0x80; auto motors = parse_output_report(report); TEST_ASSERT_EQUAL_UINT8(0, motors.left); TEST_ASSERT_EQUAL_UINT8(0, motors.right); std::array short_report{}; short_report[0] = 1; short_report[2] = 0x80; motors = parse_output_report(short_report); TEST_ASSERT_EQUAL_UINT8(0, motors.left); TEST_ASSERT_EQUAL_UINT8(0, motors.right); } void test_descriptor_sanity(void) { auto desc = xbox_one_report_descriptor(); TEST_ASSERT_EQUAL_INT(167, static_cast(desc.size())); // Generic Desktop / Game Pad / Application collection. TEST_ASSERT_EQUAL_UINT8(0x05, desc[0]); TEST_ASSERT_EQUAL_UINT8(0x01, desc[1]); TEST_ASSERT_EQUAL_UINT8(0x09, desc[2]); TEST_ASSERT_EQUAL_UINT8(0x04, desc[3]); TEST_ASSERT_EQUAL_UINT8(0xa1, desc[4]); TEST_ASSERT_EQUAL_UINT8(0x01, desc[5]); // Ends with End Collection. TEST_ASSERT_EQUAL_UINT8(0xc0, desc.back()); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_input_report_idle); RUN_TEST(test_input_report_all_buttons); RUN_TEST(test_input_report_button_remap); RUN_TEST(test_input_report_triggers); RUN_TEST(test_input_report_sticks); RUN_TEST(test_output_report_parse); RUN_TEST(test_output_report_rejects_malformed); RUN_TEST(test_descriptor_sanity); return UNITY_END(); }