#include "test_support.h" #include #include #include #include #include "../../switch2_commands.h" namespace { bool switch2_classifies_captured_report_selection_requests() { // Given: captured common and Pro report-selection request vectors. static constexpr uint8_t select_05[] = { 0x03, 0x91, 0x00, 0x0A, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, }; static constexpr uint8_t select_09[] = { 0x03, 0x91, 0x00, 0x0A, 0x00, 0x04, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, }; // When/Then: each captured vector selects only its represented report. CHECK(switch2_classify_vendor_request(select_05, sizeof(select_05)) == Switch2VendorCommand::SelectReport05); CHECK(switch2_classify_vendor_request(select_09, sizeof(select_09)) == Switch2VendorCommand::SelectReport09); return true; } bool switch2_builds_exact_report_selection_ack() { // Given: the two supported report-selection classifications. static constexpr uint8_t expected[] = { 0x03, 0x01, 0x00, 0x0A, 0x00, 0xF8, 0x00, 0x00, }; std::array output{}; // When: either response is built. Then: both equal the captured ACK. CHECK(switch2_build_vendor_response( Switch2VendorCommand::SelectReport05, output.data(), output.size()) == sizeof(expected)); CHECK(std::memcmp(output.data(), expected, sizeof(expected)) == 0); output.fill(0); CHECK(switch2_build_vendor_response( Switch2VendorCommand::SelectReport09, output.data(), output.size()) == sizeof(expected)); CHECK(std::memcmp(output.data(), expected, sizeof(expected)) == 0); return true; } bool switch2_classifies_captured_and_opaque_usb_init_requests() { // Given: the pinned vector and another opaque host-address payload. static constexpr uint8_t captured[] = { 0x03, 0x91, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static constexpr uint8_t opaque_address[] = { 0x03, 0x91, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0x01, 0xA5, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, }; // When/Then: both structurally valid requests initialize USB. CHECK(switch2_classify_vendor_request(captured, sizeof(captured)) == Switch2VendorCommand::InitializeUsb); CHECK(switch2_classify_vendor_request(opaque_address, sizeof(opaque_address)) == Switch2VendorCommand::InitializeUsb); return true; } bool switch2_builds_exact_usb_init_ack() { // Given: the captured initialization acknowledgement. static constexpr uint8_t expected[] = { 0x03, 0x01, 0x00, 0x0D, 0x00, 0xF8, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, }; std::array output{}; // When: the initialization response is built. Then: every byte is exact. CHECK(switch2_build_vendor_response( Switch2VendorCommand::InitializeUsb, output.data(), output.size()) == sizeof(expected)); CHECK(std::memcmp(output.data(), expected, sizeof(expected)) == 0); return true; } bool switch2_rejects_malformed_header_and_length_fields() { // Given: a structurally valid selection request as the mutation baseline. static constexpr uint8_t valid[] = { 0x03, 0x91, 0x00, 0x0A, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, }; // When/Then: truncation, extension, and every fixed header violation reject. CHECK(switch2_classify_vendor_request(nullptr, 0) == Switch2VendorCommand::Unsupported); for (std::size_t length = 0; length < sizeof(valid); ++length) { CHECK(switch2_classify_vendor_request(valid, length) == Switch2VendorCommand::Unsupported); } std::array extended{}; std::memcpy(extended.data(), valid, sizeof(valid)); CHECK(switch2_classify_vendor_request(extended.data(), extended.size()) == Switch2VendorCommand::Unsupported); for (uint8_t offset : {0, 1, 2, 4, 6, 7}) { std::array malformed{}; std::memcpy(malformed.data(), valid, sizeof(valid)); ++malformed[offset]; CHECK(switch2_classify_vendor_request(malformed.data(), malformed.size()) == Switch2VendorCommand::Unsupported); } for (uint8_t declared_length : {0x03, 0x05}) { std::array malformed{}; std::memcpy(malformed.data(), valid, sizeof(valid)); malformed[5] = declared_length; CHECK(switch2_classify_vendor_request(malformed.data(), malformed.size()) == Switch2VendorCommand::Unsupported); } return true; } bool switch2_rejects_unsupported_commands_and_payloads() { // Given: valid framing mutated to unsupported subcommands and payloads. std::array request = { 0x03, 0x91, 0x00, 0x0A, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, }; // When/Then: no command outside the captured subset is classified. request[3] = 0x03; CHECK(switch2_classify_vendor_request(request.data(), request.size()) == Switch2VendorCommand::Unsupported); request[3] = 0x0A; request[8] = 0x08; CHECK(switch2_classify_vendor_request(request.data(), request.size()) == Switch2VendorCommand::Unsupported); request[8] = 0x05; request[9] = 0x01; CHECK(switch2_classify_vendor_request(request.data(), request.size()) == Switch2VendorCommand::Unsupported); std::array init = { 0x03, 0x91, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, }; CHECK(switch2_classify_vendor_request(init.data(), init.size()) == Switch2VendorCommand::Unsupported); return true; } bool switch2_response_builder_rejects_unsupported_or_small_outputs() { // Given: a sentinel output buffer and every unsupported capacity. std::array output{}; // When/Then: unsupported, null, and short outputs produce no bytes or writes. output.fill(0xA5); CHECK(switch2_build_vendor_response( Switch2VendorCommand::Unsupported, output.data(), output.size()) == 0); for (uint8_t byte : output) CHECK(byte == 0xA5); CHECK(switch2_build_vendor_response( Switch2VendorCommand::InitializeUsb, nullptr, output.size()) == 0); for (std::size_t capacity = 0; capacity < 8; ++capacity) { output.fill(0xA5); CHECK(switch2_build_vendor_response( Switch2VendorCommand::SelectReport05, output.data(), capacity) == 0); for (uint8_t byte : output) CHECK(byte == 0xA5); } for (std::size_t capacity = 0; capacity < 12; ++capacity) { output.fill(0xA5); CHECK(switch2_build_vendor_response( Switch2VendorCommand::InitializeUsb, output.data(), capacity) == 0); for (uint8_t byte : output) CHECK(byte == 0xA5); } return true; } } // namespace void run_switch2_command_tests(TestRunner& runner) { runner.run("Switch 2 captured report selections", switch2_classifies_captured_report_selection_requests); runner.run("Switch 2 report selection ACK", switch2_builds_exact_report_selection_ack); runner.run("Switch 2 captured USB initialization", switch2_classifies_captured_and_opaque_usb_init_requests); runner.run("Switch 2 USB initialization ACK", switch2_builds_exact_usb_init_ack); runner.run("Switch 2 malformed command framing", switch2_rejects_malformed_header_and_length_fields); runner.run("Switch 2 unsupported commands", switch2_rejects_unsupported_commands_and_payloads); runner.run("Switch 2 response capacity", switch2_response_builder_rejects_unsupported_or_small_outputs); }