#include #include "mock_arduino.h" #include "hal.h" #include "sim_hal.h" #include "tmp36_sim.h" #include "{{PROJECT_NAME}}_app.h" // ============================================================================ // System Tests -- exercise WeatherApp with simulated sensor and hardware // // These tests use SimHal (simulated GPIO, timing, serial) and Tmp36Sim // (simulated analog sensor with noise). No mocking expectations -- we // observe real behavior through SimHal's inspection API. // ============================================================================ class WeatherSystemTest : public ::testing::Test { protected: void SetUp() override { mock_arduino_reset(); sim_.setMillis(0); } SimHal sim_; Tmp36Sim sensor_{25.0f}; // 25 C base temperature }; TEST_F(WeatherSystemTest, StartsAndPrintsToSerial) { WeatherApp app(&sim_, &sensor_); app.begin(); std::string output = sim_.serialOutput(); EXPECT_NE(output.find("WeatherApp started"), std::string::npos); EXPECT_NE(output.find("Temperature:"), std::string::npos); } TEST_F(WeatherSystemTest, InitialReadingIsReasonable) { Tmp36Sim exact_sensor(25.0f, 0.0f); // zero noise WeatherApp app(&sim_, &exact_sensor); app.begin(); EXPECT_NEAR(app.lastCelsius(), 25.0f, 1.0f); EXPECT_EQ(app.readCount(), 1); } TEST_F(WeatherSystemTest, ReadsAtTwoSecondIntervals) { WeatherApp app(&sim_, &sensor_); app.begin(); EXPECT_EQ(app.readCount(), 1); // 1 second -- no new reading sim_.advanceMillis(1000); app.update(); EXPECT_EQ(app.readCount(), 1); // 2 seconds -- new reading sim_.advanceMillis(1000); app.update(); EXPECT_EQ(app.readCount(), 2); // 4 seconds -- another reading sim_.advanceMillis(2000); app.update(); EXPECT_EQ(app.readCount(), 3); } TEST_F(WeatherSystemTest, FiveMinuteRun) { WeatherApp app(&sim_, &sensor_); app.begin(); // Run 5 minutes at 100ms resolution for (int i = 0; i < 3000; ++i) { sim_.advanceMillis(100); app.update(); } // 5 minutes = 300 seconds / 2 second interval = 150 readings + 1 initial EXPECT_EQ(app.readCount(), 151); } TEST_F(WeatherSystemTest, TemperatureChangeMidRun) { Tmp36Sim sensor(20.0f, 0.0f); // start at 20 C, no noise WeatherApp app(&sim_, &sensor); app.begin(); EXPECT_NEAR(app.lastCelsius(), 20.0f, 1.0f); // Change temperature and wait for next reading sensor.setBaseTemperature(35.0f); sim_.advanceMillis(2000); app.update(); EXPECT_NEAR(app.lastCelsius(), 35.0f, 1.0f); } TEST_F(WeatherSystemTest, SerialOutputContainsFahrenheit) { Tmp36Sim exact_sensor(0.0f, 0.0f); // 0 C = 32 F WeatherApp app(&sim_, &exact_sensor); app.begin(); std::string output = sim_.serialOutput(); EXPECT_NE(output.find("32"), std::string::npos) << "Should contain 32 F for 0 C: " << output; } TEST_F(WeatherSystemTest, NoisyReadingsStayInRange) { Tmp36Sim noisy_sensor(25.0f, 2.0f); // +/- 2 C noise noisy_sensor.setSeed(42); WeatherApp app(&sim_, &noisy_sensor); for (int i = 0; i < 20; ++i) { sim_.setMillis(i * 2000); if (i == 0) app.begin(); else app.update(); float c = app.lastCelsius(); EXPECT_GE(c, 20.0f) << "Reading " << i << " too low: " << c; EXPECT_LE(c, 30.0f) << "Reading " << i << " too high: " << c; } }