From 78e9ec429177df93a148d0cfac20be0d2ed7572f Mon Sep 17 00:00:00 2001 From: Michael Richters Date: Thu, 5 Nov 2020 19:48:39 -0800 Subject: [PATCH] Add event queue functions to testing infrastructure This change adds a set of functions to the `VirtualDeviceTest` class to make it possible to write simpler testcases involving timed keyswitch press and release events along with corresponding keyboard HID reports. Other outputs (Consumer & System Control HID reports, LEDs, et cetera) are not yet included. --- testing/ExpectedKeyboardReport.cpp | 43 +++++++++++++++++ testing/ExpectedKeyboardReport.h | 46 ++++++++++++++++++ testing/VirtualDeviceTest.cpp | 71 ++++++++++++++++++++++++++++ testing/VirtualDeviceTest.h | 75 ++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 testing/ExpectedKeyboardReport.cpp create mode 100644 testing/ExpectedKeyboardReport.h diff --git a/testing/ExpectedKeyboardReport.cpp b/testing/ExpectedKeyboardReport.cpp new file mode 100644 index 00000000..e962d561 --- /dev/null +++ b/testing/ExpectedKeyboardReport.cpp @@ -0,0 +1,43 @@ +/* -*- 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 . + */ + +#include "testing/ExpectedKeyboardReport.h" + +namespace kaleidoscope { +namespace testing { + +ExpectedKeyboardReport::ExpectedKeyboardReport(uint32_t timestamp, + const std::set &keycodes, + std::string message) { + timestamp_ = timestamp; + keycodes_ = std::set(keycodes); + failure_message_ = message; +} + +const std::set & ExpectedKeyboardReport::Keycodes() const { + return keycodes_; +} + +uint32_t ExpectedKeyboardReport::Timestamp() const { + return timestamp_; +} + +const std::string & ExpectedKeyboardReport::Message() const { + return failure_message_; +} + +} // namespace testing +} // namespace kaleidoscope diff --git a/testing/ExpectedKeyboardReport.h b/testing/ExpectedKeyboardReport.h new file mode 100644 index 00000000..85ef298c --- /dev/null +++ b/testing/ExpectedKeyboardReport.h @@ -0,0 +1,46 @@ +/* -*- 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 +#include +#include +#include + +namespace kaleidoscope { +namespace testing { + +class ExpectedKeyboardReport { + public: + ExpectedKeyboardReport(uint32_t timestamp, + const std::set & keycodes, + std::string message = ""); + + const std::set & Keycodes() const; + + uint32_t Timestamp() const; + + const std::string & Message() const; + + private: + uint32_t timestamp_; + std::set keycodes_; + std::string failure_message_; +}; + +} // namespace testing +} // namespace kaleidoscope diff --git a/testing/VirtualDeviceTest.cpp b/testing/VirtualDeviceTest.cpp index fc731de3..1d209faf 100644 --- a/testing/VirtualDeviceTest.cpp +++ b/testing/VirtualDeviceTest.cpp @@ -31,5 +31,76 @@ std::unique_ptr VirtualDeviceTest::RunCycle() { return State::Snapshot(); } +// ============================================================================= +void VirtualDeviceTest::LoadState() { + output_state_ = State::Snapshot(); +} + +void VirtualDeviceTest::ClearState() { + output_state_ = nullptr; + input_timestamps_.clear(); + expected_reports_.clear(); +} + +const HIDState* VirtualDeviceTest::HIDReports() const { + if (output_state_ == nullptr) return nullptr; + return output_state_->HIDReports(); +} + +uint32_t VirtualDeviceTest::ReportTimestamp(size_t index) const { + uint32_t t = output_state_->HIDReports()->Keyboard(index).Timestamp(); + return t; +} + +// ----------------------------------------------------------------------------- +uint32_t VirtualDeviceTest::EventTimestamp(size_t index) const { + return input_timestamps_[index]; +} + +// ============================================================================= +void VirtualDeviceTest::PressKey(KeyAddr addr) { + sim_.Press(addr); + input_timestamps_.push_back(Runtime.millisAtCycleStart()); +} +void VirtualDeviceTest::ReleaseKey(KeyAddr addr) { + sim_.Release(addr); + input_timestamps_.push_back(Runtime.millisAtCycleStart()); +} + +// ============================================================================= +void VirtualDeviceTest::ExpectReport(AddKeycodes added_keys, + RemoveKeycodes removed_keys, + std::string description) { + uint32_t report_timestamp = Runtime.millisAtCycleStart(); + for (Key key : added_keys) { + AddToReport(key); + } + for (Key key : removed_keys) { + RemoveFromReport(key); + } + ExpectedKeyboardReport new_report(report_timestamp, + current_keyboard_keycodes_, + description); + expected_reports_.push_back(new_report); +} + +// ----------------------------------------------------------------------------- +void VirtualDeviceTest::ExpectReport(AddKeycodes added_keys, + std::string description) { + ExpectReport(added_keys, RemoveKeycodes{}, description); +} +void VirtualDeviceTest::ExpectReport(RemoveKeycodes removed_keys, + std::string description) { + ExpectReport(AddKeycodes{}, removed_keys, description); +} + +// ============================================================================= +void VirtualDeviceTest::AddToReport(Key key) { + current_keyboard_keycodes_.insert(key.getKeyCode()); +} +void VirtualDeviceTest::RemoveFromReport(Key key) { + current_keyboard_keycodes_.erase(key.getKeyCode()); +} + } // namespace testing } // namespace kaleidoscope diff --git a/testing/VirtualDeviceTest.h b/testing/VirtualDeviceTest.h index 9ffeef31..b6fc22aa 100644 --- a/testing/VirtualDeviceTest.h +++ b/testing/VirtualDeviceTest.h @@ -18,6 +18,7 @@ #include +#include "testing/ExpectedKeyboardReport.h" #include "testing/SimHarness.h" #include "testing/State.h" @@ -29,6 +30,21 @@ namespace kaleidoscope { namespace testing { +// ----------------------------------------------------------------------------- +// Utility classes for use in `PressKey()`/`ReleaseKey()` & `ExpectReport()` +// method invocations. These make those calls simpler by providing +// differentiated types for those polymorphic functions. +class AddKeycodes : public std::set { + public: + AddKeycodes(std::initializer_list list) : std::set(list) {} +}; +class RemoveKeycodes : public std::set { + public: + RemoveKeycodes(std::initializer_list list) : std::set(list) {} +}; + +// ----------------------------------------------------------------------------- +// The base class for testcases class VirtualDeviceTest : public ::testing::Test { protected: void SetUp(); @@ -36,6 +52,65 @@ class VirtualDeviceTest : public ::testing::Test { std::unique_ptr RunCycle(); SimHarness sim_; + + // --------------------------------------------------------------------------- + // 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 + // `LoadState()` is required in order to load those reports into this + // `output_state_` variable so that they can be used. + std::unique_ptr output_state_ = nullptr; + + // Load any accumulated observed HID reports from the simulator (since the + // previous call to this function). + void LoadState(); + + // Clear previous state. This should be used at the beginning of each + // `TEST_F()` call to clear out any accumulated state in the input and output + // queues. + void ClearState(); + + // Get a pointer to the current list of observed HID reports + const HIDState* HIDReports() const; + + // Get the timestamp of a logged Keyboard HID report + uint32_t 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_ = {}; + + // Get the timestamp of a logged input event from `output_state_` + uint32_t EventTimestamp(size_t index) const; + + // --------------------------------------------------------------------------- + // Press/release a keyswitch & log the input event timestamp + void PressKey(KeyAddr addr); + void ReleaseKey(KeyAddr addr); + + + // --------------------------------------------------------------------------- + // The following functions all add an expected value of HID report to the + // queue. They specify modifications to the current (expected) report, and a + // message to display if verification fails. Some versions allow multiple + // keycode changes in a single report; others only a single keycode. Some run + // the simulator for a specified number of milliseconds or cycles first. These + // expected-value reports are all stored in a vector: + std::vector expected_reports_ = {}; + + void ExpectReport(AddKeycodes added_keys, + RemoveKeycodes removed_keys, + std::string description); + + void ExpectReport(AddKeycodes added_keys, std::string description); + void ExpectReport(RemoveKeycodes removed_keys, std::string description); + + // --------------------------------------------------------------------------- + std::set current_keyboard_keycodes_ = {}; + // Add to/remove from the set of keycodes expected in the next report + void AddToReport(Key key); + void RemoveFromReport(Key key); + }; } // namespace testing