Supporting a button library.
This commit is contained in:
289
libraries/button/src/test_button.cpp
Normal file
289
libraries/button/src/test_button.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Button Driver Tests
|
||||
*
|
||||
* Auto-generated by: anvil add button
|
||||
* These tests verify the Button driver mock and simulation without
|
||||
* any hardware. They run alongside your unit and system tests.
|
||||
*
|
||||
* To run: ./test.sh (all tests)
|
||||
* ./test.sh --system (skips these -- use no filter to include)
|
||||
* ./test.sh --unit (skips these -- use no filter to include)
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "mock_arduino.h"
|
||||
#include "sim_hal.h"
|
||||
#include "button.h"
|
||||
#include "button_digital.h"
|
||||
#include "button_mock.h"
|
||||
#include "button_sim.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mock: basic functionality
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class ButtonMockTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
mock_arduino_reset();
|
||||
}
|
||||
|
||||
ButtonMock btn;
|
||||
};
|
||||
|
||||
TEST_F(ButtonMockTest, DefaultsToNotPressed) {
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonMockTest, SetPressedReturnsTrue) {
|
||||
btn.setPressed(true);
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonMockTest, SetReleasedReturnsFalse) {
|
||||
btn.setPressed(true);
|
||||
btn.setPressed(false);
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonMockTest, DefaultRawIsHigh) {
|
||||
EXPECT_EQ(btn.readState(), HIGH);
|
||||
}
|
||||
|
||||
TEST_F(ButtonMockTest, SetRawReturnsExactValue) {
|
||||
btn.setRaw(LOW);
|
||||
EXPECT_EQ(btn.readState(), LOW);
|
||||
}
|
||||
|
||||
TEST_F(ButtonMockTest, ReadCountTracking) {
|
||||
EXPECT_EQ(btn.readCount(), 0);
|
||||
btn.isPressed();
|
||||
btn.isPressed();
|
||||
btn.readState();
|
||||
EXPECT_EQ(btn.readCount(), 3);
|
||||
}
|
||||
|
||||
TEST_F(ButtonMockTest, ReadCountReset) {
|
||||
btn.isPressed();
|
||||
btn.isPressed();
|
||||
btn.resetReadCount();
|
||||
EXPECT_EQ(btn.readCount(), 0);
|
||||
}
|
||||
|
||||
TEST_F(ButtonMockTest, PressAndRawAreIndependent) {
|
||||
// setPressed controls isPressed(), setRaw controls readState()
|
||||
btn.setPressed(true);
|
||||
btn.setRaw(HIGH);
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
EXPECT_EQ(btn.readState(), HIGH);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Digital: real implementation via SimHal
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class ButtonDigitalTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
mock_arduino_reset();
|
||||
}
|
||||
|
||||
SimHal hal;
|
||||
};
|
||||
|
||||
TEST_F(ButtonDigitalTest, ActiveLowPressedWhenLow) {
|
||||
ButtonDigital btn(&hal, 2, true); // active-low
|
||||
hal.setPin(2, LOW);
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonDigitalTest, ActiveLowReleasedWhenHigh) {
|
||||
ButtonDigital btn(&hal, 2, true); // active-low
|
||||
hal.setPin(2, HIGH);
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonDigitalTest, ActiveHighPressedWhenHigh) {
|
||||
ButtonDigital btn(&hal, 2, false); // active-high
|
||||
hal.setPin(2, HIGH);
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonDigitalTest, ActiveHighReleasedWhenLow) {
|
||||
ButtonDigital btn(&hal, 2, false); // active-high
|
||||
hal.setPin(2, LOW);
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonDigitalTest, ReadStateReturnsRawPin) {
|
||||
ButtonDigital btn(&hal, 2, true);
|
||||
hal.setPin(2, LOW);
|
||||
EXPECT_EQ(btn.readState(), LOW);
|
||||
|
||||
hal.setPin(2, HIGH);
|
||||
EXPECT_EQ(btn.readState(), HIGH);
|
||||
}
|
||||
|
||||
TEST_F(ButtonDigitalTest, DifferentPin) {
|
||||
ButtonDigital btn(&hal, 7, true);
|
||||
hal.setPin(7, LOW);
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonDigitalTest, DefaultIsActiveLow) {
|
||||
ButtonDigital btn(&hal, 2); // active_low defaults to true
|
||||
hal.setPin(2, LOW);
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Simulation: bounce and determinism
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class ButtonSimTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
mock_arduino_reset();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ButtonSimTest, DefaultsToNotPressed) {
|
||||
ButtonSim btn;
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, PressWithNoBounceIsImmediate) {
|
||||
ButtonSim btn(0); // no bounce
|
||||
btn.press();
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, ReleaseWithNoBounceIsImmediate) {
|
||||
ButtonSim btn(0);
|
||||
btn.press();
|
||||
btn.release();
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, BounceSettlesAfterEnoughReads) {
|
||||
ButtonSim btn(5); // 5 bouncy reads
|
||||
btn.press();
|
||||
|
||||
// Read through the bounce window
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
btn.isPressed(); // may or may not bounce
|
||||
}
|
||||
|
||||
// After bounce window, should be stable
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, ReleaseBouncesSettleToReleased) {
|
||||
ButtonSim btn(3);
|
||||
btn.press();
|
||||
// Clear press bounce
|
||||
for (int i = 0; i < 10; ++i) btn.isPressed();
|
||||
|
||||
btn.release();
|
||||
// Read through release bounce
|
||||
for (int i = 0; i < 3; ++i) btn.isPressed();
|
||||
|
||||
// Settled to released
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, DeterministicWithSameSeed) {
|
||||
ButtonSim s1(5);
|
||||
ButtonSim s2(5);
|
||||
s1.setSeed(99);
|
||||
s2.setSeed(99);
|
||||
|
||||
s1.press();
|
||||
s2.press();
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
EXPECT_EQ(s1.isPressed(), s2.isPressed())
|
||||
<< "Reading " << i << " should match with same seed";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, DifferentSeedsDifferentBounce) {
|
||||
ButtonSim s1(10);
|
||||
ButtonSim s2(10);
|
||||
s1.setSeed(1);
|
||||
s2.setSeed(2);
|
||||
|
||||
s1.press();
|
||||
s2.press();
|
||||
|
||||
// At least one reading during bounce should differ
|
||||
bool any_differ = false;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (s1.isPressed() != s2.isPressed()) {
|
||||
any_differ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(any_differ);
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, NoBounceZeroReads) {
|
||||
ButtonSim btn(0);
|
||||
btn.press();
|
||||
// With zero bounce, every read should be stable
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
EXPECT_TRUE(btn.isPressed()) << "Read " << i << " should be pressed";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, ReadStateMatchesIsPressed) {
|
||||
ButtonSim btn(0);
|
||||
btn.press();
|
||||
// Active-low convention: pressed -> LOW
|
||||
EXPECT_EQ(btn.readState(), LOW);
|
||||
|
||||
btn.release();
|
||||
EXPECT_EQ(btn.readState(), HIGH);
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, DoublePressSameState) {
|
||||
ButtonSim btn(0);
|
||||
btn.press();
|
||||
btn.press(); // second press is a no-op
|
||||
EXPECT_TRUE(btn.isPressed());
|
||||
}
|
||||
|
||||
TEST_F(ButtonSimTest, DoubleReleaseSameState) {
|
||||
ButtonSim btn(0);
|
||||
btn.release(); // already released, no-op
|
||||
EXPECT_FALSE(btn.isPressed());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Polymorphism: all impls work through Button pointer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST(ButtonPolymorphismTest, AllImplsWorkThroughBasePointer) {
|
||||
mock_arduino_reset();
|
||||
SimHal hal;
|
||||
hal.setDigital(2, LOW); // pressed for active-low
|
||||
|
||||
ButtonDigital digital_btn(&hal, 2, true);
|
||||
ButtonMock mock_btn;
|
||||
ButtonSim sim_btn(0);
|
||||
|
||||
mock_btn.setPressed(true);
|
||||
sim_btn.press();
|
||||
|
||||
Button* buttons[] = { &digital_btn, &mock_btn, &sim_btn };
|
||||
|
||||
for (auto* b : buttons) {
|
||||
bool pressed = b->isPressed();
|
||||
int raw = b->readState();
|
||||
|
||||
EXPECT_TRUE(pressed);
|
||||
(void)raw; // just verify it compiles and runs
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user