#include #include "mock_arduino.h" #include "hal.h" #include "sim_hal.h" #include "{{PROJECT_NAME}}_app.h" // ============================================================================ // System Tests -- exercise full application logic against simulated hardware // // Unlike unit tests (which verify exact call sequences), system tests // run the real application code against SimHal and inspect observable // outputs: pin states, serial messages, timing behavior. // ============================================================================ class BlinkAppSystemTest : public ::testing::Test { protected: void SetUp() override { mock_arduino_reset(); sim_.setMillis(0); } SimHal sim_; }; TEST_F(BlinkAppSystemTest, SetupConfiguresPins) { BlinkApp app(&sim_); app.begin(); EXPECT_EQ(sim_.getPinMode(13), OUTPUT); EXPECT_EQ(sim_.getPinMode(2), INPUT_PULLUP); } TEST_F(BlinkAppSystemTest, LedTogglesAtCorrectInterval) { BlinkApp app(&sim_); app.begin(); // LED should be off initially EXPECT_EQ(sim_.getPin(13), LOW); // Advance past one slow interval (500ms) sim_.advanceMillis(500); app.update(); EXPECT_EQ(sim_.getPin(13), HIGH); // Advance another interval sim_.advanceMillis(500); app.update(); EXPECT_EQ(sim_.getPin(13), LOW); } TEST_F(BlinkAppSystemTest, LedDoesNotToggleTooEarly) { BlinkApp app(&sim_); app.begin(); sim_.advanceMillis(499); app.update(); EXPECT_EQ(sim_.getPin(13), LOW); } TEST_F(BlinkAppSystemTest, ButtonTogglesFastMode) { BlinkApp app(&sim_, 13, 2); sim_.setPin(2, HIGH); // Button not pressed (INPUT_PULLUP) app.begin(); EXPECT_FALSE(app.fastMode()); // Press button sim_.setPin(2, LOW); app.update(); EXPECT_TRUE(app.fastMode()); EXPECT_EQ(app.interval(), BlinkApp::FAST_INTERVAL_MS); // Release and press again -> back to slow sim_.setPin(2, HIGH); app.update(); sim_.setPin(2, LOW); app.update(); EXPECT_FALSE(app.fastMode()); EXPECT_EQ(app.interval(), BlinkApp::SLOW_INTERVAL_MS); } TEST_F(BlinkAppSystemTest, FastModeBlinksFaster) { BlinkApp app(&sim_, 13, 2); sim_.setPin(2, HIGH); app.begin(); // Switch to fast mode sim_.setPin(2, LOW); app.update(); sim_.setPin(2, HIGH); app.update(); EXPECT_TRUE(app.fastMode()); // Clear log, then count toggles over 1 second sim_.clearGpioLog(); for (int i = 0; i < 8; ++i) { sim_.advanceMillis(125); app.update(); } // In 1 second at 125ms intervals, we expect 8 toggles int high_count = sim_.countWrites(13, HIGH); int low_count = sim_.countWrites(13, LOW); EXPECT_EQ(high_count + low_count, 8); } TEST_F(BlinkAppSystemTest, SerialOutputOnStartup) { BlinkApp app(&sim_); app.begin(); std::string output = sim_.serialOutput(); EXPECT_NE(output.find("BlinkApp started"), std::string::npos); } TEST_F(BlinkAppSystemTest, SerialOutputOnModeChange) { BlinkApp app(&sim_, 13, 2); sim_.setPin(2, HIGH); app.begin(); sim_.clearSerialOutput(); // Press button sim_.setPin(2, LOW); app.update(); std::string output = sim_.serialOutput(); EXPECT_NE(output.find("FAST"), std::string::npos); } // ============================================================================ // Timing accuracy test -- verifies blink over a longer duration // ============================================================================ TEST_F(BlinkAppSystemTest, TenSecondBlinkCount) { BlinkApp app(&sim_); app.begin(); sim_.clearGpioLog(); // Run for 10 seconds at 10ms resolution for (int i = 0; i < 1000; ++i) { sim_.advanceMillis(10); app.update(); } // At 500ms intervals over 10s, expect 20 toggles int toggles = sim_.countWrites(13, HIGH) + sim_.countWrites(13, LOW); EXPECT_EQ(toggles, 20); }