diff --git a/test_partitioned_vector.cpp b/test_partitioned_vector.cpp deleted file mode 100644 index 15d6db18e3..0000000000 --- a/test_partitioned_vector.cpp +++ /dev/null @@ -1,378 +0,0 @@ -#include -#include -#include -#include -#include - -// Forward declare tests vector -struct Test { - std::string name; - void (*func)(); -}; -std::vector tests; - -// Minimal test framework -#define TEST(name) \ - void test_##name(); \ - struct test_##name##_registrar { \ - test_##name##_registrar() { tests.push_back({#name, test_##name}); } \ - } test_##name##_instance; \ - void test_##name() - -#define ASSERT(cond) \ - do { \ - if (!(cond)) { \ - std::cerr << "FAILED: " #cond " at " << __FILE__ << ":" << __LINE__ << std::endl; \ - exit(1); \ - } \ - } while (0) -#define ASSERT_EQ(a, b) ASSERT((a) == (b)) - -// Mock classes matching ESPHome structure -const uint8_t COMPONENT_STATE_MASK = 0x07; -const uint8_t COMPONENT_STATE_LOOP = 0x02; -const uint8_t COMPONENT_STATE_LOOP_DONE = 0x04; -const uint8_t COMPONENT_STATE_FAILED = 0x03; - -class Component { - protected: - uint8_t component_state_ = COMPONENT_STATE_LOOP; - int id_; - int loop_count_ = 0; - - public: - Component(int id) : id_(id) {} - virtual ~Component() = default; - - virtual void call() { loop_count_++; } - - int get_id() const { return id_; } - int get_loop_count() const { return loop_count_; } - uint8_t get_state() const { return component_state_ & COMPONENT_STATE_MASK; } - - void set_state(uint8_t state) { component_state_ = (component_state_ & ~COMPONENT_STATE_MASK) | state; } -}; - -class Application { - public: - std::vector looping_components_; - uint16_t looping_components_active_end_ = 0; - uint16_t current_loop_index_ = 0; - bool in_loop_ = false; - - void add_component(Component *c) { - looping_components_.push_back(c); - looping_components_active_end_ = looping_components_.size(); - } - - void loop() { - in_loop_ = true; - for (current_loop_index_ = 0; current_loop_index_ < looping_components_active_end_; current_loop_index_++) { - looping_components_[current_loop_index_]->call(); - } - in_loop_ = false; - } - - void disable_component_loop(Component *component) { - for (uint16_t i = 0; i < looping_components_active_end_; i++) { - if (looping_components_[i] == component) { - looping_components_active_end_--; - if (i != looping_components_active_end_) { - std::swap(looping_components_[i], looping_components_[looping_components_active_end_]); - - if (in_loop_ && i == current_loop_index_) { - current_loop_index_--; - } - } - return; - } - } - } - - void enable_component_loop(Component *component) { - const uint16_t size = looping_components_.size(); - for (uint16_t i = 0; i < size; i++) { - if (looping_components_[i] == component) { - if (i < looping_components_active_end_) { - return; // Already active - } - - if (i != looping_components_active_end_) { - std::swap(looping_components_[i], looping_components_[looping_components_active_end_]); - } - looping_components_active_end_++; - return; - } - } - } - - // Helper methods for testing - std::vector get_active_ids() const { - std::vector ids; - for (uint16_t i = 0; i < looping_components_active_end_; i++) { - ids.push_back(looping_components_[i]->get_id()); - } - return ids; - } - - bool is_component_active(Component *c) const { - for (uint16_t i = 0; i < looping_components_active_end_; i++) { - if (looping_components_[i] == c) - return true; - } - return false; - } -}; - -// Test basic functionality -TEST(basic_loop) { - Application app; - std::vector> components; - - for (int i = 0; i < 5; i++) { - components.push_back(std::make_unique(i)); - app.add_component(components.back().get()); - } - - app.loop(); - - for (const auto &c : components) { - ASSERT_EQ(c->get_loop_count(), 1); - } -} - -TEST(disable_component) { - Application app; - std::vector> components; - - for (int i = 0; i < 5; i++) { - components.push_back(std::make_unique(i)); - app.add_component(components.back().get()); - } - - // Disable component 2 - app.disable_component_loop(components[2].get()); - - app.loop(); - - // Components 0,1,3,4 should have been called - ASSERT_EQ(components[0]->get_loop_count(), 1); - ASSERT_EQ(components[1]->get_loop_count(), 1); - ASSERT_EQ(components[2]->get_loop_count(), 0); // Disabled - ASSERT_EQ(components[3]->get_loop_count(), 1); - ASSERT_EQ(components[4]->get_loop_count(), 1); - - // Verify partitioning - ASSERT_EQ(app.looping_components_active_end_, 4); - ASSERT(!app.is_component_active(components[2].get())); -} - -TEST(enable_component) { - Application app; - std::vector> components; - - for (int i = 0; i < 5; i++) { - components.push_back(std::make_unique(i)); - app.add_component(components.back().get()); - } - - // Disable then re-enable - app.disable_component_loop(components[2].get()); - app.enable_component_loop(components[2].get()); - - app.loop(); - - // All should have been called - for (const auto &c : components) { - ASSERT_EQ(c->get_loop_count(), 1); - } - - ASSERT_EQ(app.looping_components_active_end_, 5); -} - -TEST(multiple_disable_enable) { - Application app; - std::vector> components; - - for (int i = 0; i < 10; i++) { - components.push_back(std::make_unique(i)); - app.add_component(components.back().get()); - } - - // Disable multiple - app.disable_component_loop(components[1].get()); - app.disable_component_loop(components[5].get()); - app.disable_component_loop(components[7].get()); - - ASSERT_EQ(app.looping_components_active_end_, 7); - - app.loop(); - - // Check counts - int active_count = 0; - for (const auto &c : components) { - if (c->get_loop_count() == 1) - active_count++; - } - ASSERT_EQ(active_count, 7); - - // Re-enable one - app.enable_component_loop(components[5].get()); - ASSERT_EQ(app.looping_components_active_end_, 8); - - app.loop(); - - ASSERT_EQ(components[5]->get_loop_count(), 1); -} - -// Test reentrant behavior -class SelfDisablingComponent : public Component { - Application *app_; - - public: - SelfDisablingComponent(int id, Application *app) : Component(id), app_(app) {} - - void call() override { - Component::call(); - if (loop_count_ == 2) { - app_->disable_component_loop(this); - } - } -}; - -TEST(reentrant_disable) { - Application app; - std::vector> components; - - // Add regular components - for (int i = 0; i < 3; i++) { - components.push_back(std::make_unique(i)); - app.add_component(components.back().get()); - } - - // Add self-disabling component - auto self_disable = std::make_unique(3, &app); - app.add_component(self_disable.get()); - - // Add more regular components - for (int i = 4; i < 6; i++) { - components.push_back(std::make_unique(i)); - app.add_component(components.back().get()); - } - - // First loop - all active - app.loop(); - ASSERT_EQ(app.looping_components_active_end_, 6); - - // Second loop - self-disabling component disables itself - app.loop(); - ASSERT_EQ(app.looping_components_active_end_, 5); - ASSERT_EQ(self_disable->get_loop_count(), 2); - - // Third loop - self-disabling component should not be called - app.loop(); - ASSERT_EQ(self_disable->get_loop_count(), 2); // Still 2 -} - -// Test edge cases -TEST(disable_already_disabled) { - Application app; - auto comp = std::make_unique(0); - app.add_component(comp.get()); - - app.disable_component_loop(comp.get()); - ASSERT_EQ(app.looping_components_active_end_, 0); - - // Disable again - should be no-op - app.disable_component_loop(comp.get()); - ASSERT_EQ(app.looping_components_active_end_, 0); -} - -TEST(enable_already_enabled) { - Application app; - auto comp = std::make_unique(0); - app.add_component(comp.get()); - - ASSERT_EQ(app.looping_components_active_end_, 1); - - // Enable again - should be no-op - app.enable_component_loop(comp.get()); - ASSERT_EQ(app.looping_components_active_end_, 1); -} - -TEST(disable_last_component) { - Application app; - auto comp = std::make_unique(0); - app.add_component(comp.get()); - - app.disable_component_loop(comp.get()); - ASSERT_EQ(app.looping_components_active_end_, 0); - - app.loop(); // Should not crash with empty active set -} - -// Test that mimics real ESPHome component behavior -class MockSNTPComponent : public Component { - Application *app_; - bool time_synced_ = false; - - public: - MockSNTPComponent(int id, Application *app) : Component(id), app_(app) {} - - void call() override { - Component::call(); - - // Simulate time sync after 3 calls - if (loop_count_ >= 3 && !time_synced_) { - time_synced_ = true; - std::cout << " SNTP: Time synced, disabling loop" << std::endl; - set_state(COMPONENT_STATE_LOOP_DONE); - app_->disable_component_loop(this); - } - } - - bool is_synced() const { return time_synced_; } -}; - -TEST(real_world_sntp) { - Application app; - - // Regular components - std::vector> components; - for (int i = 0; i < 5; i++) { - components.push_back(std::make_unique(i)); - app.add_component(components.back().get()); - } - - // SNTP component - auto sntp = std::make_unique(5, &app); - app.add_component(sntp.get()); - - // Run 5 iterations - for (int i = 0; i < 5; i++) { - app.loop(); - } - - // SNTP should have disabled itself after 3 calls - ASSERT_EQ(sntp->get_loop_count(), 3); - ASSERT(sntp->is_synced()); - ASSERT_EQ(app.looping_components_active_end_, 5); // SNTP removed - - // Regular components should have 5 calls each - for (const auto &c : components) { - ASSERT_EQ(c->get_loop_count(), 5); - } -} - -int main() { - std::cout << "Running partitioned vector tests...\n" << std::endl; - - for (const auto &test : tests) { - std::cout << "Running test: " << test.name << std::endl; - test.func(); - std::cout << " ✓ PASSED" << std::endl; - } - - std::cout << "\nAll " << tests.size() << " tests passed!" << std::endl; - return 0; -} \ No newline at end of file diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 53c29dec14..525e3541b3 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -9,7 +9,6 @@ import logging import os from pathlib import Path import platform -import pty import signal import socket import sys @@ -48,6 +47,9 @@ if platform.system() == "Windows": ) +import pty # not available on Windows + + @pytest.fixture(scope="module", autouse=True) def enable_aioesphomeapi_debug_logging(): """Enable debug logging for aioesphomeapi to help diagnose connection issues.""" diff --git a/tests/integration/fixtures/loop_disable_enable.yaml b/tests/integration/fixtures/loop_disable_enable.yaml index 0d70dac363..17010f7c34 100644 --- a/tests/integration/fixtures/loop_disable_enable.yaml +++ b/tests/integration/fixtures/loop_disable_enable.yaml @@ -37,7 +37,7 @@ loop_test_component: # Interval to re-enable the self_disable_10 component after some time interval: - - interval: 2s + - interval: 0.5s then: - if: condition: