mirror of
https://github.com/esphome/esphome.git
synced 2026-10-08 20:06:03 +00:00
165 lines
5.5 KiB
C++
165 lines
5.5 KiB
C++
#include <cmath>
|
|
#include <vector>
|
|
#include "gtest/gtest.h"
|
|
#include "esphome/components/espectre/espectre.h"
|
|
|
|
#ifdef ESPECTRE_SDK_TEST_DOUBLE
|
|
|
|
namespace esphome::espectre::testing {
|
|
|
|
class ESPectreTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
component_.set_motion_binary_sensor(&motion_);
|
|
component_.set_calibrating_binary_sensor(&calibrating_);
|
|
component_.set_movement_sensor(&movement_);
|
|
}
|
|
|
|
void make_ready_(bool motion = true) {
|
|
sdk_->snapshot_.ready_to_publish = true;
|
|
sdk_->snapshot_.motion_state = motion ? ::espectre::MotionState::MOTION : ::espectre::MotionState::IDLE;
|
|
sdk_->snapshot_.movement_metric = 0.8f;
|
|
component_.loop();
|
|
}
|
|
|
|
ESPectreComponent component_;
|
|
::espectre::RuntimeFrontendController *sdk_{::espectre::RuntimeFrontendController::instance};
|
|
binary_sensor::BinarySensor motion_;
|
|
binary_sensor::BinarySensor calibrating_;
|
|
sensor::Sensor movement_;
|
|
};
|
|
|
|
TEST_F(ESPectreTest, StartupDoesNotPublishFalseMotion) {
|
|
component_.setup();
|
|
component_.loop();
|
|
EXPECT_FALSE(motion_.has_state());
|
|
EXPECT_FALSE(movement_.has_state());
|
|
EXPECT_TRUE(calibrating_.has_state());
|
|
}
|
|
|
|
TEST_F(ESPectreTest, SetupHandsScanResultsAndPersistenceToEspHome) {
|
|
component_.setup();
|
|
EXPECT_TRUE(sdk_->config_at_setup_.wifi_scan_results_managed_externally);
|
|
EXPECT_FALSE(sdk_->config_at_setup_.persist_runtime_overrides);
|
|
}
|
|
|
|
TEST_F(ESPectreTest, ReadinessLossAndRecovery) {
|
|
component_.setup();
|
|
std::vector<optional<bool>> published_states;
|
|
motion_.add_full_state_callback(
|
|
[&published_states](optional<bool>, optional<bool> current) { published_states.push_back(current); });
|
|
for (bool motion : {false, true}) {
|
|
published_states.clear();
|
|
make_ready_(motion);
|
|
ASSERT_TRUE(motion_.has_state());
|
|
EXPECT_EQ(motion_.state, motion);
|
|
EXPECT_FLOAT_EQ(movement_.state, 0.8f);
|
|
|
|
sdk_->snapshot_.ready_to_publish = false;
|
|
sdk_->snapshot_.calibrating = true;
|
|
component_.loop();
|
|
EXPECT_FALSE(motion_.has_state());
|
|
EXPECT_TRUE(std::isnan(movement_.state));
|
|
EXPECT_TRUE(calibrating_.state);
|
|
|
|
sdk_->snapshot_.calibrating = false;
|
|
make_ready_(motion);
|
|
EXPECT_TRUE(motion_.has_state());
|
|
EXPECT_EQ(motion_.state, motion);
|
|
EXPECT_FLOAT_EQ(movement_.state, 0.8f);
|
|
EXPECT_EQ(published_states, (std::vector<optional<bool>>{motion, nullopt, motion}));
|
|
}
|
|
}
|
|
|
|
TEST_F(ESPectreTest, TelemetryUsesFinalReadiness) {
|
|
component_.setup();
|
|
make_ready_();
|
|
std::vector<float> published_states;
|
|
movement_.add_on_state_callback([&published_states](float state) { published_states.push_back(state); });
|
|
sdk_->loop_hook = [this]() {
|
|
sdk_->listener->on_live_telemetry(sdk_->snapshot_);
|
|
sdk_->snapshot_.movement_metric = 0.9f;
|
|
sdk_->snapshot_.ready_to_publish = false;
|
|
};
|
|
component_.loop();
|
|
EXPECT_FALSE(motion_.has_state());
|
|
EXPECT_TRUE(std::isnan(movement_.state));
|
|
ASSERT_EQ(published_states.size(), 1u);
|
|
EXPECT_TRUE(std::isnan(published_states.front()));
|
|
}
|
|
|
|
TEST_F(ESPectreTest, EntityCallbackQueuesRecalibration) {
|
|
component_.setup();
|
|
movement_.add_on_state_callback([this](float) { component_.recalibrate(); });
|
|
make_ready_();
|
|
EXPECT_EQ(sdk_->recalibration_calls, 0u);
|
|
component_.loop();
|
|
EXPECT_EQ(sdk_->recalibration_calls, 1u);
|
|
}
|
|
|
|
TEST_F(ESPectreTest, CalibrationFailureWarnsOnlyBeforeFirstSuccess) {
|
|
component_.setup();
|
|
sdk_->listener->on_calibration_finished(sdk_->snapshot_, false);
|
|
EXPECT_TRUE(component_.status_has_warning());
|
|
EXPECT_FALSE(component_.is_failed());
|
|
sdk_->listener->on_calibration_finished(sdk_->snapshot_, true);
|
|
EXPECT_FALSE(component_.status_has_warning());
|
|
sdk_->listener->on_calibration_finished(sdk_->snapshot_, false);
|
|
EXPECT_FALSE(component_.status_has_warning());
|
|
}
|
|
|
|
TEST_F(ESPectreTest, RuntimeFaultStopsAndWaitsToRestart) {
|
|
component_.setup();
|
|
make_ready_();
|
|
sdk_->listener->on_runtime_fault("test fault");
|
|
component_.loop();
|
|
EXPECT_FALSE(component_.is_failed());
|
|
EXPECT_TRUE(component_.status_has_error());
|
|
EXPECT_TRUE(sdk_->shutdown_called);
|
|
EXPECT_FALSE(motion_.has_state());
|
|
EXPECT_TRUE(std::isnan(movement_.state));
|
|
|
|
component_.loop();
|
|
EXPECT_EQ(sdk_->setup_calls, 1u);
|
|
}
|
|
|
|
TEST_F(ESPectreTest, FailedSetupStopsAndWaitsToRestart) {
|
|
sdk_->setup_result = false;
|
|
component_.setup();
|
|
EXPECT_FALSE(component_.is_failed());
|
|
EXPECT_TRUE(component_.status_has_error());
|
|
EXPECT_TRUE(sdk_->shutdown_called);
|
|
}
|
|
|
|
TEST_F(ESPectreTest, TrafficModeRequestAppliesInLoop) {
|
|
component_.setup();
|
|
component_.request_traffic_generator_mode(::espectre::TrafficGeneratorMode::EXTERNAL_HOST);
|
|
EXPECT_EQ(sdk_->config_.traffic_generator_mode, ::espectre::TrafficGeneratorMode::PING);
|
|
component_.loop();
|
|
EXPECT_EQ(sdk_->config_.traffic_generator_mode, ::espectre::TrafficGeneratorMode::EXTERNAL_HOST);
|
|
}
|
|
|
|
TEST_F(ESPectreTest, TrafficModeRequestByName) {
|
|
component_.setup();
|
|
component_.request_traffic_generator_mode("unknown");
|
|
component_.loop();
|
|
EXPECT_EQ(sdk_->config_.traffic_generator_mode, ::espectre::TrafficGeneratorMode::PING);
|
|
component_.request_traffic_generator_mode("dns_tcp");
|
|
component_.loop();
|
|
EXPECT_EQ(sdk_->config_.traffic_generator_mode, ::espectre::TrafficGeneratorMode::DNS_TCP);
|
|
}
|
|
|
|
TEST_F(ESPectreTest, ShutdownInvalidatesEntities) {
|
|
component_.setup();
|
|
make_ready_();
|
|
component_.on_shutdown();
|
|
EXPECT_TRUE(sdk_->shutdown_called);
|
|
EXPECT_FALSE(motion_.has_state());
|
|
EXPECT_FALSE(calibrating_.has_state());
|
|
EXPECT_TRUE(std::isnan(movement_.state));
|
|
}
|
|
|
|
} // namespace esphome::espectre::testing
|
|
|
|
#endif // ESPECTRE_SDK_TEST_DOUBLE
|