diff --git a/CMakeLists.txt b/CMakeLists.txt index 1819720..235bbea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,7 @@ if(ENABLE_TEST) src/test/test1_ParseConfig.cpp src/test/test2_MatchKeySequence.cpp src/test/test3_Stage.cpp + src/test/test4_Fuzz.cpp ) endif(ENABLE_TEST) diff --git a/src/runtime/Stage.cpp b/src/runtime/Stage.cpp index f863162..96c399d 100644 --- a/src/runtime/Stage.cpp +++ b/src/runtime/Stage.cpp @@ -98,6 +98,7 @@ KeySequence Stage::apply_input(const KeyEvent event) { for (auto& output : m_output_down) output.suppressed = false; + m_sequence_might_match = false; while (has_non_optional(m_sequence)) { // find first mapping which matches or might match sequence for (const auto& mapping : m_mappings) { @@ -108,7 +109,6 @@ KeySequence Stage::apply_input(const KeyEvent event) { m_sequence_might_match = true; return std::move(m_output_buffer); } - m_sequence_might_match = false; if (result == MatchResult::match) { apply_output(get_output(mapping)); @@ -211,9 +211,7 @@ void Stage::update_output(const KeyEvent& event, KeyCode trigger) { it->suppressed = true; } } - else { - assert(event.state == KeyState::Down); - + else if (event.state == KeyState::Down) { // reapply temporarily released auto reapplied = false; for (auto& output : m_output_down) diff --git a/src/test/test4_Fuzz.cpp b/src/test/test4_Fuzz.cpp new file mode 100644 index 0000000..1d3b782 --- /dev/null +++ b/src/test/test4_Fuzz.cpp @@ -0,0 +1,56 @@ + +#include "test.h" +#include "config/ParseConfig.h" +#include "runtime/Stage.h" +#include +#include + +namespace { + Stage create_stage(const char* string) { + static auto parse_config = ParseConfig(); + auto stream = std::stringstream(string); + auto config = parse_config(stream); + auto mappings = std::vector(); + for (auto& command : config.commands) + mappings.push_back({ + std::move(command.input), + std::move(command.default_mapping) + }); + return Stage(std::move(mappings), { }); + } +} // namespace + +//-------------------------------------------------------------------- + +TEST_CASE("Fuzz #1", "[Fuzz]") { + auto config = R"( + Ext = IntlBackslash + Ext >> + Ext{W{K}} >> 1 + Ext{W{L}} >> 2 + Ext{W{J}} >> 3 + Ext{W{Any}} >> + )"; + Stage stage = create_stage(config); + + auto keys = std::vector(); + for (auto k : { "IntlBackslash", "W", "K", "L", "J", "I" }) + keys.push_back(parse_input(k).front().key); + auto pressed = std::set(); + + auto rand = std::mt19937(0); + auto dist = std::uniform_int_distribution(0, keys.size() - 1); + for (auto i = 0; i < 1000; i++) { + const auto key = keys[dist(rand)]; + if (auto it = pressed.find(key); it != end(pressed)) { + pressed.erase(it); + stage.apply_input({ key, KeyState::Up }); + } + else { + pressed.insert(key); + stage.apply_input({ key, KeyState::Down }); + } + } +} + +//--------------------------------------------------------------------