284 lines
6.8 KiB
Cheetah
284 lines
6.8 KiB
Cheetah
/*
|
|
* test_button_app.cpp -- Button application example tests.
|
|
*
|
|
* THIS FILE IS MANAGED BY ANVIL and will be updated by `anvil refresh`.
|
|
* Do not edit -- put your own tests in test_unit.cpp and test_system.cpp.
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <gmock/gmock.h>
|
|
|
|
#include "mock_arduino.h"
|
|
#include "hal.h"
|
|
#include "mock_hal.h"
|
|
#include "sim_hal.h"
|
|
#include "button_mock.h"
|
|
#include "button_sim.h"
|
|
#include "{{PROJECT_NAME}}_app.h"
|
|
|
|
using ::testing::_;
|
|
using ::testing::AnyNumber;
|
|
using ::testing::Return;
|
|
using ::testing::HasSubstr;
|
|
|
|
// ============================================================================
|
|
// Unit Tests -- verify ButtonApp behavior with mock button
|
|
// ============================================================================
|
|
|
|
class ButtonUnitTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
ON_CALL(mock_, millis()).WillByDefault(Return(0));
|
|
EXPECT_CALL(mock_, serialBegin(_)).Times(AnyNumber());
|
|
EXPECT_CALL(mock_, serialPrint(_)).Times(AnyNumber());
|
|
EXPECT_CALL(mock_, serialPrintln(_)).Times(AnyNumber());
|
|
EXPECT_CALL(mock_, millis()).Times(AnyNumber());
|
|
EXPECT_CALL(mock_, pinMode(_, _)).Times(AnyNumber());
|
|
}
|
|
|
|
::testing::NiceMock<MockHal> mock_;
|
|
ButtonMock btn_;
|
|
};
|
|
|
|
TEST_F(ButtonUnitTest, BeginPrintsReadyMessage) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
|
|
EXPECT_CALL(mock_, serialBegin(115200)).Times(1);
|
|
EXPECT_CALL(mock_, serialPrintln(HasSubstr("ButtonApp ready"))).Times(1);
|
|
|
|
app.begin();
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, BeginSetsPinMode) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
|
|
EXPECT_CALL(mock_, pinMode(2, INPUT_PULLUP)).Times(1);
|
|
|
|
app.begin();
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, NoPressNoCount) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
app.begin();
|
|
|
|
// Button not pressed, update should not increment count
|
|
btn_.setPressed(false);
|
|
app.update();
|
|
app.update();
|
|
app.update();
|
|
|
|
EXPECT_EQ(app.pressCount(), 0);
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, SinglePressIncrementsCount) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
btn_.setPressed(false);
|
|
app.begin();
|
|
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
|
|
EXPECT_EQ(app.pressCount(), 1);
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, HoldDoesNotRepeat) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
btn_.setPressed(false);
|
|
app.begin();
|
|
|
|
btn_.setPressed(true);
|
|
app.update(); // rising edge -> count = 1
|
|
app.update(); // still pressed -> no increment
|
|
app.update(); // still pressed -> no increment
|
|
|
|
EXPECT_EQ(app.pressCount(), 1);
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, ReleaseAndPressAgain) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
btn_.setPressed(false);
|
|
app.begin();
|
|
|
|
// First press
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
EXPECT_EQ(app.pressCount(), 1);
|
|
|
|
// Release
|
|
btn_.setPressed(false);
|
|
app.update();
|
|
|
|
// Second press
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
EXPECT_EQ(app.pressCount(), 2);
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, PrintsMessageOnPress) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
btn_.setPressed(false);
|
|
app.begin();
|
|
|
|
EXPECT_CALL(mock_, serialPrintln(HasSubstr("Button pressed!"))).Times(1);
|
|
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, PrintsCountInMessage) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
btn_.setPressed(false);
|
|
app.begin();
|
|
|
|
EXPECT_CALL(mock_, serialPrintln(HasSubstr("count: 1"))).Times(1);
|
|
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, DoesNotPrintOnRelease) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
btn_.setPressed(false);
|
|
app.begin();
|
|
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
|
|
// Release should not trigger another print
|
|
EXPECT_CALL(mock_, serialPrintln(HasSubstr("Button pressed!"))).Times(0);
|
|
|
|
btn_.setPressed(false);
|
|
app.update();
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, TenPresses) {
|
|
ButtonApp app(&mock_, &btn_);
|
|
btn_.setPressed(false);
|
|
app.begin();
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
btn_.setPressed(false);
|
|
app.update();
|
|
}
|
|
|
|
EXPECT_EQ(app.pressCount(), 10);
|
|
}
|
|
|
|
TEST_F(ButtonUnitTest, StartupWithButtonAlreadyPressed) {
|
|
// If the button is held during startup, begin() reads it as pressed.
|
|
// The first update() should NOT trigger a press (no rising edge).
|
|
btn_.setPressed(true);
|
|
|
|
ButtonApp app(&mock_, &btn_);
|
|
app.begin();
|
|
|
|
app.update(); // still pressed, no edge
|
|
EXPECT_EQ(app.pressCount(), 0);
|
|
|
|
// Release and press again -- THIS should trigger
|
|
btn_.setPressed(false);
|
|
app.update();
|
|
btn_.setPressed(true);
|
|
app.update();
|
|
EXPECT_EQ(app.pressCount(), 1);
|
|
}
|
|
|
|
// ============================================================================
|
|
// System Tests -- exercise ButtonApp with simulated button and hardware
|
|
// ============================================================================
|
|
|
|
class ButtonSystemTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
mock_arduino_reset();
|
|
sim_.setMillis(0);
|
|
}
|
|
|
|
SimHal sim_;
|
|
ButtonSim btn_{0}; // no bounce for predictable tests
|
|
};
|
|
|
|
TEST_F(ButtonSystemTest, StartsAndPrintsToSerial) {
|
|
ButtonApp app(&sim_, &btn_);
|
|
app.begin();
|
|
|
|
std::string output = sim_.serialOutput();
|
|
EXPECT_NE(output.find("ButtonApp ready"), std::string::npos);
|
|
}
|
|
|
|
TEST_F(ButtonSystemTest, PressShowsInSerialOutput) {
|
|
ButtonApp app(&sim_, &btn_);
|
|
app.begin();
|
|
|
|
btn_.press();
|
|
app.update();
|
|
|
|
std::string output = sim_.serialOutput();
|
|
EXPECT_NE(output.find("Button pressed!"), std::string::npos);
|
|
EXPECT_NE(output.find("count: 1"), std::string::npos);
|
|
}
|
|
|
|
TEST_F(ButtonSystemTest, MultiplePressesCountCorrectly) {
|
|
ButtonApp app(&sim_, &btn_);
|
|
app.begin();
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
btn_.press();
|
|
app.update();
|
|
btn_.release();
|
|
app.update();
|
|
}
|
|
|
|
EXPECT_EQ(app.pressCount(), 5);
|
|
|
|
std::string output = sim_.serialOutput();
|
|
EXPECT_NE(output.find("count: 5"), std::string::npos);
|
|
}
|
|
|
|
TEST_F(ButtonSystemTest, HoldOnlyCountsOnce) {
|
|
ButtonApp app(&sim_, &btn_);
|
|
app.begin();
|
|
|
|
btn_.press();
|
|
for (int i = 0; i < 100; ++i) {
|
|
app.update();
|
|
}
|
|
|
|
EXPECT_EQ(app.pressCount(), 1);
|
|
}
|
|
|
|
TEST_F(ButtonSystemTest, RapidPressReleaseCycle) {
|
|
ButtonApp app(&sim_, &btn_);
|
|
app.begin();
|
|
|
|
// Simulate rapid button mashing
|
|
for (int i = 0; i < 50; ++i) {
|
|
btn_.press();
|
|
app.update();
|
|
btn_.release();
|
|
app.update();
|
|
}
|
|
|
|
EXPECT_EQ(app.pressCount(), 50);
|
|
}
|
|
|
|
TEST_F(ButtonSystemTest, BouncyButtonWithSettling) {
|
|
ButtonSim bouncy_btn(5); // 5 reads of bounce
|
|
bouncy_btn.setSeed(42);
|
|
ButtonApp app(&sim_, &bouncy_btn);
|
|
app.begin();
|
|
|
|
bouncy_btn.press();
|
|
|
|
// During bounce, we may get spurious edges.
|
|
// After settling, one press should eventually register.
|
|
for (int i = 0; i < 20; ++i) {
|
|
app.update();
|
|
}
|
|
|
|
// At least one press should have registered
|
|
EXPECT_GE(app.pressCount(), 1);
|
|
}
|