/* -*- mode: c++ -*- * Copyright (C) 2020 Eric Paniagua (epaniagua@google.com) * * 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 "testing/Cycles.h" #include "testing/Millis.h" #include "testing/ExpectedKeyboardReport.h" #include "testing/SimHarness.h" #include "testing/State.h" // Out of order due to macro conflicts. #include "testing/fix-macros.h" #include "gtest/gtest.h" #include 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) {} }; class Keycodes : public std::set { public: Keycodes(std::initializer_list list) : std::set(list) {} }; // ----------------------------------------------------------------------------- // The base class for testcases class VirtualDeviceTest : public ::testing::Test { protected: void SetUp(); std::unique_ptr RunCycle(); 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 // `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 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_ = {}; // Get the timestamp of a logged input event from `output_state_` 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 // 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(Keycodes added_keys, std::string description); 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 void ClearReport(); void AddToReport(Key key); void RemoveFromReport(Key key); }; } // namespace testing } // namespace kaleidoscope // XXX This is a horrible hack and this should be a function, but I (Jesse) // can't quite figure out the right way to get ASSERT_EQ and ElementsAreArray // into a method in VirtualDeviceTest #define CHECK_EXPECTED_REPORTS() \ LoadState(); \ ASSERT_EQ(HIDReports()->Keyboard().size(), expected_reports_.size()); \ \ for (auto i = 0; i < expected_reports_.size(); ++i) { \ EXPECT_THAT(HIDReports()->Keyboard(i).ActiveKeycodes(), \ ::testing::ElementsAreArray(expected_reports_[i].Keycodes())) \ << expected_reports_[i].Message(); \ } \