#include #include #include "hal.h" #include "mock_hal.h" #include "{{PROJECT_NAME}}_app.h" using ::testing::_; using ::testing::AnyNumber; using ::testing::Return; using ::testing::HasSubstr; // ============================================================================ // Unit Tests -- verify exact HAL interactions // ============================================================================ class BlinkAppUnitTest : public ::testing::Test { protected: void SetUp() override { ON_CALL(mock_, millis()).WillByDefault(Return(0)); ON_CALL(mock_, digitalRead(_)).WillByDefault(Return(HIGH)); EXPECT_CALL(mock_, serialBegin(_)).Times(AnyNumber()); EXPECT_CALL(mock_, serialPrintln(_)).Times(AnyNumber()); EXPECT_CALL(mock_, millis()).Times(AnyNumber()); } ::testing::NiceMock mock_; }; TEST_F(BlinkAppUnitTest, BeginConfiguresPins) { BlinkApp app(&mock_, 13, 2); EXPECT_CALL(mock_, pinMode(13, OUTPUT)).Times(1); EXPECT_CALL(mock_, pinMode(2, INPUT_PULLUP)).Times(1); EXPECT_CALL(mock_, serialBegin(115200)).Times(1); app.begin(); } TEST_F(BlinkAppUnitTest, StartsInSlowMode) { BlinkApp app(&mock_); app.begin(); EXPECT_FALSE(app.fastMode()); EXPECT_EQ(app.interval(), BlinkApp::SLOW_INTERVAL_MS); } TEST_F(BlinkAppUnitTest, TogglesLedAfterInterval) { BlinkApp app(&mock_); ON_CALL(mock_, millis()).WillByDefault(Return(0)); app.begin(); ON_CALL(mock_, millis()).WillByDefault(Return(500)); EXPECT_CALL(mock_, digitalWrite(13, _)).Times(1); app.update(); } TEST_F(BlinkAppUnitTest, DoesNotToggleBeforeInterval) { BlinkApp app(&mock_); ON_CALL(mock_, millis()).WillByDefault(Return(0)); app.begin(); ON_CALL(mock_, millis()).WillByDefault(Return(499)); EXPECT_CALL(mock_, digitalWrite(_, _)).Times(0); app.update(); } TEST_F(BlinkAppUnitTest, ButtonPressSwitchesToFastMode) { BlinkApp app(&mock_, 13, 2); ON_CALL(mock_, millis()).WillByDefault(Return(0)); ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(HIGH)); app.begin(); ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(LOW)); EXPECT_CALL(mock_, serialPrintln(HasSubstr("FAST"))).Times(1); app.update(); EXPECT_TRUE(app.fastMode()); EXPECT_EQ(app.interval(), BlinkApp::FAST_INTERVAL_MS); } TEST_F(BlinkAppUnitTest, SecondButtonPressReturnsToSlowMode) { BlinkApp app(&mock_, 13, 2); ON_CALL(mock_, millis()).WillByDefault(Return(0)); ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(HIGH)); app.begin(); // First press: fast mode ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(LOW)); app.update(); EXPECT_TRUE(app.fastMode()); // Release ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(HIGH)); app.update(); // Second press: back to slow ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(LOW)); EXPECT_CALL(mock_, serialPrintln(HasSubstr("SLOW"))).Times(1); app.update(); EXPECT_FALSE(app.fastMode()); } TEST_F(BlinkAppUnitTest, HoldingButtonDoesNotRepeatToggle) { BlinkApp app(&mock_, 13, 2); ON_CALL(mock_, millis()).WillByDefault(Return(0)); ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(HIGH)); app.begin(); ON_CALL(mock_, digitalRead(2)).WillByDefault(Return(LOW)); app.update(); EXPECT_TRUE(app.fastMode()); // Still held -- should NOT toggle again app.update(); app.update(); EXPECT_TRUE(app.fastMode()); }