|
|
|
/* -*- 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
|
|
|
#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 <memory>
|
|
|
|
|
|
|
|
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<Key> {
|
|
|
|
public:
|
|
|
|
AddKeycodes(std::initializer_list<Key> list) : std::set<Key>(list) {}
|
|
|
|
};
|
|
|
|
class RemoveKeycodes : public std::set<Key> {
|
|
|
|
public:
|
|
|
|
RemoveKeycodes(std::initializer_list<Key> list) : std::set<Key>(list) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class Keycodes : public std::set<Key> {
|
|
|
|
public:
|
|
|
|
Keycodes(std::initializer_list<Key> list) : std::set<Key>(list) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// The base class for testcases
|
|
|
|
class VirtualDeviceTest : public ::testing::Test {
|
|
|
|
protected:
|
|
|
|
void SetUp();
|
|
|
|
|
|
|
|
std::unique_ptr<State> 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<State> 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<Millis> 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<ExpectedKeyboardReport> 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<uint8_t> 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(); \
|
|
|
|
} \
|
|
|
|
|
|
|
|
|