Skip to content

Commit

Permalink
Initial support for focus serial protocol testing, along with a test …
Browse files Browse the repository at this point in the history
…for the version command.

It wasn't supposed to fail, but the test caught a bug
  • Loading branch information
obra committed Dec 9, 2024
1 parent 04104c0 commit 29666ec
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 0 deletions.
57 changes: 57 additions & 0 deletions testing/SimHarness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,63 @@ uint8_t SimHarness::CycleTime() const {
return millis_per_cycle_;
}

// Serial support implementation
void SimHarness::ProcessSerialInput() {
// This will be called by RunCycle() to process any pending serial input
// The actual processing is handled by the virtual HardwareSerial implementation
}

void SimHarness::SendSerialData(const uint8_t *data, size_t length) {
// Get direct access to the virtual device's serial port
auto &serial = Serial;
serial.injectInput(data, length);
}

std::vector<uint8_t> SimHarness::GetSerialOutput() {
// Get direct access to the virtual device's serial port
auto &serial = Serial;
return serial.getOutputBuffer();
}

// Focus protocol helper implementations
std::string SimHarness::SendFocusCommand(const std::string &command) {
// Ensure command ends with newline
std::string cmd = command;
if (cmd.empty() || cmd.back() != '\n') {
cmd += '\n';
}

// Send command
SendString(cmd);

// Run cycles until we get a complete response
// (ends with \r\n.\r\n)
std::string response;
size_t max_cycles = 100; // Prevent infinite loops
size_t cycles = 0;

while (cycles++ < max_cycles) {
RunCycle();
response = GetSerialOutputAsString();
if (IsFocusResponse(response)) break;
}

return StripFocusTerminator(response);
}

bool SimHarness::IsFocusResponse(const std::string &response) {
static const std::string terminator = "\r\n.\r\n";
return response.length() >= terminator.length() &&
response.substr(response.length() - terminator.length()) == terminator;
}

std::string SimHarness::StripFocusTerminator(const std::string &response) {
static const std::string terminator = "\r\n.\r\n";
if (IsFocusResponse(response)) {
return response.substr(0, response.length() - terminator.length());
}
return response;
}

} // namespace testing
} // namespace kaleidoscope
19 changes: 19 additions & 0 deletions testing/SimHarness.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include <cstddef> // for size_t
#include <cstdint> // for uint8_t
#include <vector>
#include <string>

#include "kaleidoscope/KeyAddr.h" // for KeyAddr
#include "testing/gtest.h" // IWYU pragma: keep
Expand All @@ -37,6 +39,23 @@ class SimHarness {
void SetCycleTime(uint8_t millis);
uint8_t CycleTime() const;

// Serial support
void ProcessSerialInput();
void SendString(const std::string &str) {
SendSerialData(reinterpret_cast<const uint8_t *>(str.c_str()), str.length());
}
void SendSerialData(const uint8_t *data, size_t length);
std::vector<uint8_t> GetSerialOutput();
std::string GetSerialOutputAsString() {
auto output = GetSerialOutput();
return std::string(output.begin(), output.end());
}

// Focus protocol helpers
std::string SendFocusCommand(const std::string &command);
static bool IsFocusResponse(const std::string &response);
static std::string StripFocusTerminator(const std::string &response);

private:
uint8_t millis_per_cycle_ = 1;
};
Expand Down
44 changes: 44 additions & 0 deletions tests/simulator/serial/focus_version/focus_version.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// -*- mode: c++ -*-
// Copyright 2016 Keyboardio, inc. <[email protected]>
// See "LICENSE" for license details

#include "Kaleidoscope.h"
#include "Kaleidoscope-FocusSerial.h"

#define KALEIDOSCOPE_FIRMWARE_VERSION "0.1.2"

#include "Kaleidoscope-FirmwareVersion.h"
// *INDENT-OFF*
// *INDENT-OFF*

KEYMAPS(
[0] = KEYMAP_STACKED
(
XXX ,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX
,XXX

,XXX ,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX ,XXX ,XXX ,XXX
,XXX ,XXX ,XXX ,XXX
,XXX
)
) // KEYMAPS(

// *INDENT-ON*


KALEIDOSCOPE_INIT_PLUGINS(Focus, FirmwareVersion);

void setup() {
Kaleidoscope.setup();
}

void loop() {
Kaleidoscope.loop();
}
6 changes: 6 additions & 0 deletions tests/simulator/serial/focus_version/sketch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"cpu": {
"fqbn": "keyboardio:virtual:model01",
"port": ""
}
}
1 change: 1 addition & 0 deletions tests/simulator/serial/focus_version/sketch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_fqbn: keyboardio:virtual:model01
28 changes: 28 additions & 0 deletions tests/simulator/serial/focus_version/test/testcase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "testing/setup-googletest.h"
#include "Kaleidoscope.h"
#include <iostream>
#include "gmock/gmock.h" // For matchers like Eq()

SETUP_GOOGLETEST();

namespace kaleidoscope {
namespace testing {
namespace {

class FocusVersionCommand : public VirtualDeviceTest {};

TEST_F(FocusVersionCommand, SendVersionCommand) {
// Run a cycle to initialize
RunCycle();

// Send version command and get response using new helper
auto response = sim_.SendFocusCommand("version");

// Response should be exactly the version number
EXPECT_THAT(response, ::testing::Eq("0.1.2"))
<< "Expected version response '0.1.2', got: '" << response << "'";
}

} // namespace
} // namespace testing
} // namespace kaleidoscope

0 comments on commit 29666ec

Please sign in to comment.