#include #include #include #include #include #include #include #include #include namespace { auto touch_event(luma::touch_phase phase, std::chrono::steady_clock::time_point time, std::int32_t id = 1) -> luma::touch_event { return {.points = {{.id = id, .phase = phase}}, .time = time}; } struct touch_sample { std::bool_t pressed; std::uint64_t milliseconds; }; } BOOST_AUTO_TEST_CASE(zero_and_pico_share_timestamped_gesture_transitions) { auto sequences = std::vector>{ {{true, 0}, {false, 30}}, {{true, 0}, {false, 30}, {true, 200}, {false, 300}}, {{true, 0}, {false, 40}, {true, 180}, {false, 260}}, {{true, 0}, {false, 40}, {true, 180}, {false, 261}}, {{true, 0}, {false, 40}, {true, 261}, {false, 300}}, {{true, 50}, {true, 749}, {true, 750}, {true, 900}, {false, 910}}, {{true, 0}, {false, 40}, {true, 180}, {false, 880}}, {{true, 0}, {false, 699}}, {{true, 0}, {false, 700}}, }; auto expected = std::vector>{ {"next"}, {"next", "next"}, {"previous"}, {"next", "next"}, {"next", "next"}, {"select"}, {"select"}, {"next"}, {"select"}, }; for (auto index = std::size_t{}; index < sequences.size(); ++index) { auto io = boost::asio::io_context{}; auto zero_commands = std::vector{}; auto pico_commands = std::vector{}; auto zero = vamp_zero::touch_input{io.get_executor(), [&](auto command) { zero_commands.emplace_back(vamp_protocol::page_command_name(command)); }}; auto pico = vamp_protocol::gesture_state{}; auto base = std::chrono::steady_clock::now() - std::chrono::seconds{10}; auto down = std::bool_t{false}; for (auto sample : sequences[index]) { auto phase = sample.pressed ? (down ? luma::touch_phase::MOVE : luma::touch_phase::DOWN) : luma::touch_phase::UP; BOOST_TEST(zero.handle(touch_event(phase, base + std::chrono::milliseconds{sample.milliseconds}))); if (auto command = pico.update(sample.pressed, sample.milliseconds)) { pico_commands.emplace_back(vamp_protocol::page_command_name(*command)); } down = sample.pressed; } if (auto command = pico.update(false, 2000U)) { pico_commands.emplace_back(vamp_protocol::page_command_name(*command)); } io.run(); BOOST_TEST(zero_commands == expected[index], boost::test_tools::per_element()); BOOST_TEST(pico_commands == expected[index], boost::test_tools::per_element()); } } BOOST_AUTO_TEST_CASE(shared_gesture_deadline_and_reset_discard_pending_actions) { auto gesture = vamp_protocol::gesture_state{}; BOOST_TEST(!gesture.pending_deadline()); BOOST_TEST(!gesture.update(true, 0U)); BOOST_TEST(!gesture.update(false, 30U)); BOOST_REQUIRE(gesture.pending_deadline()); BOOST_TEST(*gesture.pending_deadline() == 251U); BOOST_TEST(!gesture.update(false, 250U)); BOOST_TEST(!gesture.update(true, 250U)); BOOST_TEST(!gesture.pending_deadline()); gesture.reset(); BOOST_TEST(!gesture.pending_deadline()); BOOST_TEST(!gesture.update(false, 2000U)); } BOOST_AUTO_TEST_CASE(zero_touch_timer_emits_a_single_tap_without_further_input) { auto io = boost::asio::io_context{}; auto commands = std::vector{}; auto touch = vamp_zero::touch_input{io.get_executor(), [&](auto command) { commands.emplace_back(vamp_protocol::page_command_name(command)); }}; auto now = std::chrono::steady_clock::now(); touch.handle(touch_event(luma::touch_phase::DOWN, now)); touch.handle(touch_event(luma::touch_phase::UP, now)); io.poll(); BOOST_TEST(commands.empty()); io.run(); BOOST_REQUIRE_EQUAL(commands.size(), 1U); BOOST_TEST(commands.front() == "next"); } BOOST_AUTO_TEST_CASE(zero_touch_filters_other_fingers_and_cancels_pending_actions) { auto io = boost::asio::io_context{}; auto commands = std::vector{}; auto touch = vamp_zero::touch_input{io.get_executor(), [&](auto command) { commands.emplace_back(vamp_protocol::page_command_name(command)); }}; auto base = std::chrono::steady_clock::now() - std::chrono::seconds{10}; BOOST_TEST(touch.handle(touch_event(luma::touch_phase::DOWN, base))); BOOST_TEST(!touch.handle(touch_event(luma::touch_phase::DOWN, base, 2))); BOOST_TEST(!touch.handle(touch_event(luma::touch_phase::UP, base, 2))); BOOST_TEST(touch.handle(touch_event(luma::touch_phase::UP, base + std::chrono::milliseconds{30}))); BOOST_TEST(touch.handle(touch_event(luma::touch_phase::DOWN, base + std::chrono::milliseconds{200}))); BOOST_TEST(touch.handle(touch_event(luma::touch_phase::CANCEL, base + std::chrono::milliseconds{210}))); BOOST_TEST(!touch.handle(touch_event(luma::touch_phase::UP, base + std::chrono::milliseconds{220}))); io.run(); BOOST_TEST(commands.empty()); } BOOST_AUTO_TEST_CASE(zero_touch_stale_timer_cannot_consume_newer_gesture) { auto io = boost::asio::io_context{}; auto commands = std::vector{}; auto touch = vamp_zero::touch_input{io.get_executor(), [&](auto command) { commands.emplace_back(vamp_protocol::page_command_name(command)); }}; auto base = std::chrono::steady_clock::now() - std::chrono::seconds{10}; touch.handle(touch_event(luma::touch_phase::DOWN, base)); touch.handle(touch_event(luma::touch_phase::UP, base + std::chrono::milliseconds{30})); boost::asio::post(io, [&] { touch.handle(touch_event(luma::touch_phase::DOWN, base + std::chrono::milliseconds{200})); touch.handle(touch_event(luma::touch_phase::UP, base + std::chrono::milliseconds{220})); }); io.run(); BOOST_REQUIRE_EQUAL(commands.size(), 1U); BOOST_TEST(commands.front() == "previous"); } BOOST_AUTO_TEST_CASE(zero_touch_stop_and_destruction_suppress_queued_commands) { for (auto hold : {false, true}) { auto io = boost::asio::io_context{}; auto commands = std::vector{}; { auto touch = vamp_zero::touch_input{io.get_executor(), [&](auto command) { commands.emplace_back(vamp_protocol::page_command_name(command)); }}; auto base = std::chrono::steady_clock::now() - std::chrono::seconds{10}; touch.handle(touch_event(luma::touch_phase::DOWN, base)); touch.handle(touch_event(luma::touch_phase::UP, base + std::chrono::milliseconds{hold ? 700 : 30})); touch.stop(); BOOST_TEST(!touch.handle(touch_event(luma::touch_phase::DOWN, base))); } io.run(); BOOST_TEST(commands.empty()); } } BOOST_AUTO_TEST_CASE(zero_touch_reset_discards_old_score_gestures_but_accepts_new_input) { auto io = boost::asio::io_context{}; auto commands = std::vector{}; auto touch = vamp_zero::touch_input{io.get_executor(), [&](auto command) { commands.emplace_back(vamp_protocol::page_command_name(command)); }}; auto base = std::chrono::steady_clock::now() - std::chrono::seconds{10}; touch.handle(touch_event(luma::touch_phase::DOWN, base)); touch.handle(touch_event(luma::touch_phase::UP, base + std::chrono::milliseconds{700})); touch.reset(); io.run(); BOOST_TEST(commands.empty()); io.restart(); touch.handle(touch_event(luma::touch_phase::DOWN, base)); touch.handle(touch_event(luma::touch_phase::UP, base + std::chrono::milliseconds{30})); io.run(); BOOST_REQUIRE_EQUAL(commands.size(), 1U); BOOST_TEST(commands.front() == "next"); }