From c4139e48f77347618f023141b2dc9d47516df631 Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 5 Nov 2020 19:55:35 -0800 Subject: [PATCH] Add Millis and Cycles types to enable polymorphic Run, PressKey, ReleaseKey, etc. --- testing/Cycles.h | 29 ++++++++ testing/ExpectedKeyboardReport.cpp | 2 +- testing/ExpectedKeyboardReport.h | 8 ++- testing/Millis.h | 111 +++++++++++++++++++++++++++++ testing/VirtualDeviceTest.cpp | 80 +++++++++++++++++++-- testing/VirtualDeviceTest.h | 39 +++++++++- 6 files changed, 255 insertions(+), 14 deletions(-) create mode 100644 testing/Cycles.h create mode 100644 testing/Millis.h diff --git a/testing/Cycles.h b/testing/Cycles.h new file mode 100644 index 00000000..93633c5e --- /dev/null +++ b/testing/Cycles.h @@ -0,0 +1,29 @@ +/* -*- mode: c++ -*- + * Copyright (C) 2020 Keyboard.io, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include + +namespace kaleidoscope { +namespace testing { + +struct Cycles { + uint32_t value; +}; + +} // namespace testing +} // namespace kaleidoscope diff --git a/testing/ExpectedKeyboardReport.cpp b/testing/ExpectedKeyboardReport.cpp index 152a5af5..ad4e3508 100644 --- a/testing/ExpectedKeyboardReport.cpp +++ b/testing/ExpectedKeyboardReport.cpp @@ -31,7 +31,7 @@ const std::set & ExpectedKeyboardReport::Keycodes() const { return keycodes_; } -uint32_t ExpectedKeyboardReport::Timestamp() const { +Millis ExpectedKeyboardReport::Timestamp() const { return timestamp_; } diff --git a/testing/ExpectedKeyboardReport.h b/testing/ExpectedKeyboardReport.h index 85ef298c..a87cfb20 100644 --- a/testing/ExpectedKeyboardReport.h +++ b/testing/ExpectedKeyboardReport.h @@ -21,23 +21,25 @@ #include #include +#include "testing/Millis.h" + namespace kaleidoscope { namespace testing { class ExpectedKeyboardReport { public: - ExpectedKeyboardReport(uint32_t timestamp, + ExpectedKeyboardReport(Millis timestamp, const std::set & keycodes, std::string message = ""); const std::set & Keycodes() const; - uint32_t Timestamp() const; + Millis Timestamp() const; const std::string & Message() const; private: - uint32_t timestamp_; + Millis timestamp_; std::set keycodes_; std::string failure_message_; }; diff --git a/testing/Millis.h b/testing/Millis.h new file mode 100644 index 00000000..77fc7b0f --- /dev/null +++ b/testing/Millis.h @@ -0,0 +1,111 @@ +/* -*- mode: c++ -*- + * Copyright (C) 2020 Keyboard.io, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include + +namespace kaleidoscope { +namespace testing { + +struct Millis { + uint32_t value; + + constexpr bool operator==(const Millis& other) const { + return this->value == other.value; + } + constexpr bool operator!=(const Millis& other) const { + return !(*this == other); + } + constexpr bool operator<(const Millis& other) const { + return this->value < other.value; + } + constexpr bool operator>(const Millis& other) const { + return this->value > other.value; + } + constexpr bool operator<=(const Millis& other) const { + return this->value <= other.value; + } + constexpr bool operator>=(const Millis& other) const { + return this->value >= other.value; + } + + Millis operator+(const Millis& other) { + return Millis{this->value + other.value}; + } + Millis operator-(const Millis& other) { + return Millis{this->value - other.value}; + } + Millis operator+(const uint32_t raw) { + return Millis{this->value + raw}; + } + Millis operator-(const uint32_t raw) { + return Millis{this->value - raw}; + } + + Millis& operator=(const uint32_t raw) { + this->value = raw; + return *this; + } + Millis& operator+=(const uint32_t raw) { + this->value += raw; + return *this; + } + Millis& operator-=(const uint32_t raw) { + this->value -= raw; + return *this; + } + + constexpr bool operator<(uint32_t raw) const { + return this->value < raw; + } + constexpr bool operator>(uint32_t raw) const { + return this->value > raw; + } + constexpr bool operator<=(uint32_t raw) const { + return this->value <= raw; + } + constexpr bool operator>=(uint32_t raw) const { + return this->value >= raw; + } + + operator size_t() { + return value; + } +}; + +constexpr bool operator<(uint32_t x, const Millis& y) { + return x < y.value; +} +constexpr bool operator>(uint32_t x, const Millis& y) { + return x > y.value; +} +constexpr bool operator<=(uint32_t x, const Millis& y) { + return x <= y.value; +} +constexpr bool operator>=(uint32_t x, const Millis& y) { + return x >= y.value; +} + +constexpr Millis operator+(uint32_t x, const Millis& y) { + return Millis{x + y.value}; +} +constexpr Millis operator-(uint32_t x, const Millis& y) { + return Millis{x - y.value}; +} + +} // namespace testing +} // namespace kaleidoscope diff --git a/testing/VirtualDeviceTest.cpp b/testing/VirtualDeviceTest.cpp index 80d3a0d4..59b9ad92 100644 --- a/testing/VirtualDeviceTest.cpp +++ b/testing/VirtualDeviceTest.cpp @@ -31,6 +31,24 @@ std::unique_ptr VirtualDeviceTest::RunCycle() { return State::Snapshot(); } +// ============================================================================= +void VirtualDeviceTest::Run(Cycles n) { + sim_.RunCycles(n.value); +} + +void VirtualDeviceTest::Run(Millis t) { + sim_.RunForMillis(t); +} + +void VirtualDeviceTest::RunUntil(Millis end) { + uint32_t t0 = Runtime.millisAtCycleStart(); + Run(Millis{end - t0}); +} + +void VirtualDeviceTest::RunFrom(Millis start, Millis t) { + RunUntil(Millis{start + t}); +} + // ============================================================================= void VirtualDeviceTest::LoadState() { output_state_ = State::Snapshot(); @@ -47,24 +65,44 @@ const HIDState* VirtualDeviceTest::HIDReports() const { return output_state_->HIDReports(); } -uint32_t VirtualDeviceTest::ReportTimestamp(size_t index) const { +Millis VirtualDeviceTest::ReportTimestamp(size_t index) const { uint32_t t = output_state_->HIDReports()->Keyboard(index).Timestamp(); - return t; + return Millis{t}; } // ----------------------------------------------------------------------------- -uint32_t VirtualDeviceTest::EventTimestamp(size_t index) const { - return input_timestamps_[index]; +Millis VirtualDeviceTest::EventTimestamp(size_t index) const { + return Millis{input_timestamps_[index]}; } // ============================================================================= void VirtualDeviceTest::PressKey(KeyAddr addr) { sim_.Press(addr); - input_timestamps_.push_back(Runtime.millisAtCycleStart()); + input_timestamps_.push_back(Millis{Runtime.millisAtCycleStart()}); } void VirtualDeviceTest::ReleaseKey(KeyAddr addr) { sim_.Release(addr); - input_timestamps_.push_back(Runtime.millisAtCycleStart()); + input_timestamps_.push_back(Millis{Runtime.millisAtCycleStart()}); +} + +// ----------------------------------------------------------------------------- +void VirtualDeviceTest::PressKey(Cycles n, KeyAddr addr) { + sim_.RunCycles(n.value); + PressKey(addr); +} +void VirtualDeviceTest::ReleaseKey(Cycles n, KeyAddr addr) { + sim_.RunCycles(n.value); + ReleaseKey(addr); +} + +// ----------------------------------------------------------------------------- +void VirtualDeviceTest::PressKey(Millis t, KeyAddr addr) { + sim_.RunForMillis(t); + PressKey(addr); +} +void VirtualDeviceTest::ReleaseKey(Millis t, KeyAddr addr) { + sim_.RunForMillis(t); + ReleaseKey(addr); } @@ -86,7 +124,7 @@ void VirtualDeviceTest::ExpectReport(Keycodes keys, void VirtualDeviceTest::ExpectReport(AddKeycodes added_keys, RemoveKeycodes removed_keys, std::string description) { - uint32_t report_timestamp = Runtime.millisAtCycleStart(); + Millis report_timestamp{Runtime.millisAtCycleStart()}; for (Key key : added_keys) { AddToReport(key); } @@ -109,6 +147,34 @@ void VirtualDeviceTest::ExpectReport(RemoveKeycodes removed_keys, ExpectReport(AddKeycodes{}, removed_keys, description); } +// ----------------------------------------------------------------------------- +void VirtualDeviceTest::ExpectReport(Cycles n, + AddKeycodes added_keys, + std::string description) { + sim_.RunCycles(n.value); + ExpectReport(added_keys, description); +} +void VirtualDeviceTest::ExpectReport(Cycles n, + RemoveKeycodes removed_keys, + std::string description) { + sim_.RunCycles(n.value); + ExpectReport(removed_keys, description); +} + +// ----------------------------------------------------------------------------- +void VirtualDeviceTest::ExpectReport(Millis t, + AddKeycodes added_keys, + std::string description) { + sim_.RunForMillis(t); + ExpectReport(added_keys, description); +} +void VirtualDeviceTest::ExpectReport(Millis t, + RemoveKeycodes removed_keys, + std::string description) { + sim_.RunForMillis(t); + ExpectReport(removed_keys, description); +} + // ============================================================================= void VirtualDeviceTest::ClearReport() { current_keyboard_keycodes_.clear(); diff --git a/testing/VirtualDeviceTest.h b/testing/VirtualDeviceTest.h index d2c17620..b5c61ba1 100644 --- a/testing/VirtualDeviceTest.h +++ b/testing/VirtualDeviceTest.h @@ -18,6 +18,8 @@ #include +#include "testing/Cycles.h" +#include "testing/Millis.h" #include "testing/ExpectedKeyboardReport.h" #include "testing/SimHarness.h" #include "testing/State.h" @@ -59,6 +61,24 @@ class VirtualDeviceTest : public ::testing::Test { SimHarness sim_; + // --------------------------------------------------------------------------- + // Utility functions to advance time in the simulator. + + // Run the simulator `n` cycles + void Run(Cycles n); + + // Run the simulator `t` (virtual) milliseconds + void Run(Millis t); + + // Run the simulator until the (virtual) clock reaches `end` milliseconds + void RunUntil(Millis end); + + // Run the simulator until the (virtual) clock advances `t` milliseconds past + // `start`. This is most useful when testing timeouts based on an input event + // that is not the most recent one (e.g. a timeout following a keypress, but + // with a key release in between). + void RunFrom(Millis start, Millis t); + // --------------------------------------------------------------------------- // A representation of the set of observed HID reports accumulated by the // simulator as a testcase executes. It starts out empty, and a call to @@ -79,21 +99,28 @@ class VirtualDeviceTest : public ::testing::Test { const HIDState* HIDReports() const; // Get the timestamp of a logged Keyboard HID report - uint32_t ReportTimestamp(size_t index) const; + Millis ReportTimestamp(size_t index) const; // --------------------------------------------------------------------------- // A vector of timestamps for input events. Calls to `PressKey()` & // `ReleaseKey()` append timestamps to it when called. - std::vector input_timestamps_ = {}; + std::vector input_timestamps_ = {}; // Get the timestamp of a logged input event from `output_state_` - uint32_t EventTimestamp(size_t index) const; + Millis EventTimestamp(size_t index) const; // --------------------------------------------------------------------------- // Press/release a keyswitch & log the input event timestamp void PressKey(KeyAddr addr); void ReleaseKey(KeyAddr addr); + // Run for `n` cycles, then press/release a keyswitch + void PressKey(Cycles n, KeyAddr addr); + void ReleaseKey(Cycles n, KeyAddr addr); + + // Run for `t` milliseconds, then press/release a keyswitch + void PressKey(Millis t, KeyAddr addr); + void ReleaseKey(Millis t, KeyAddr addr); // --------------------------------------------------------------------------- // The following functions all add an expected value of HID report to the @@ -112,6 +139,12 @@ class VirtualDeviceTest : public ::testing::Test { void ExpectReport(AddKeycodes added_keys, std::string description); void ExpectReport(RemoveKeycodes removed_keys, std::string description); + void ExpectReport(Cycles n, AddKeycodes added_keys, std::string description); + void ExpectReport(Cycles n, RemoveKeycodes removed_keys, std::string description); + + void ExpectReport(Millis t, AddKeycodes added_keys, std::string description); + void ExpectReport(Millis t, RemoveKeycodes removed_keys, std::string description); + // --------------------------------------------------------------------------- std::set current_keyboard_keycodes_ = {}; // Manage the set of keycodes expected in the next report