// Тесты таблиц шаблонов: состав против PROTOCOL.md §8.2, типы, RO, диапазоны. #include "doctest/doctest.h" #include #include #include "fgl-aircon/templates.hpp" using fgl::aircon::Prop; using fgl::aircon::prop_info_count; using fgl::aircon::prop_info_find; using fgl::aircon::prop_info_find_by_name; using fgl::aircon::Template; using fgl::aircon::template_detect; namespace { // Список имён свойств шаблона (для сверки с протоколом). std::string names_joined(Template t) { std::string out; for (auto* p = fgl::aircon::prop_info_begin(t); p != fgl::aircon::prop_info_begin(t) + prop_info_count(t); p++) { if (!out.empty()) out += ","; out += p->name; } return out; } bool has(Template t, const char* name) { return prop_info_find_by_name(t, name) != nullptr; } } // namespace TEST_CASE("Шаблон A: состав против PROTOCOL §8.2 (все 33)") { const char* expected[] = { "operation_mode", "fan_speed", "adjust_temperature", "af_vertical_direction", "af_vertical_swing", "af_horizontal_direction", "af_horizontal_swing", "outdoor_low_noise", "indoor_fan_control", "human_det_auto_save", "min_heat", "powerful_mode", "coil_dry_mode", "economy_mode", "master_timer_on_off_1", "master_timer_on_off_2", "error_code", "demand_control", "filter_sign_reset_display", "op_status", "device_name", "building_name", "wifi_led_enable", "service_contact_name", "service_contact_phone", "service_contact_email", "af_horizontal_num_dir", "af_vertical_num_dir", "device_capabilities", "display_temperature", "get_prop", "human_det", "refresh"}; for (const char* n : expected) { CAPTURE(n); CHECK(has(Template::kA, n)); } CHECK(prop_info_count(Template::kA) == 33); } TEST_CASE("Шаблон B: состав (20) и отличия от A") { CHECK(prop_info_count(Template::kB) == 20); CHECK(has(Template::kB, "af_vertical_move_step1")); CHECK(has(Template::kB, "af_horizontal_move_step1")); CHECK_FALSE(has(Template::kB, "af_vertical_direction")); CHECK_FALSE(has(Template::kB, "display_temperature")); CHECK_FALSE(has(Template::kB, "powerful_mode")); CHECK_FALSE(has(Template::kB, "min_heat")); } TEST_CASE("Шаблон F: = A + monitor1 + filter_sign_reset (без display-варианта)") { CHECK(prop_info_count(Template::kF) == 34); CHECK(has(Template::kF, "monitor1")); CHECK(has(Template::kF, "filter_sign_reset")); CHECK_FALSE(has(Template::kF, "filter_sign_reset_display")); CHECK(has(Template::kF, "display_temperature")); } TEST_CASE("template_detect: oem_model → шаблон (PROTOCOL §8.1)") { CHECK(template_detect("AP-WC1E") == Template::kA); CHECK(template_detect("AP-WA3E") == Template::kA); CHECK(template_detect("AP-WB2E") == Template::kB); CHECK(template_detect("AP-WF4E") == Template::kF); CHECK(template_detect("UNKNOWN-MODEL") == Template::kA); // как legacy CHECK(template_detect(nullptr) == Template::kA); } TEST_CASE("Атрибуты свойств: base_type/read_only/диапазоны") { auto* om = prop_info_find_by_name(Template::kA, "operation_mode"); REQUIRE(om != nullptr); CHECK(std::string(om->base_type) == "integer"); CHECK_FALSE(om->read_only); CHECK(om->raw_min == 0); CHECK(om->raw_max == 6); auto* err = prop_info_find_by_name(Template::kA, "error_code"); REQUIRE(err != nullptr); CHECK(err->read_only); CHECK(err->raw_max == 4095); auto* eco = prop_info_find_by_name(Template::kA, "economy_mode"); REQUIRE(eco != nullptr); CHECK(std::string(eco->base_type) == "boolean"); CHECK(eco->kind == fgl::aircon::ValueKind::kBool); auto* dn = prop_info_find_by_name(Template::kA, "device_name"); REQUIRE(dn != nullptr); CHECK(dn->kind == fgl::aircon::ValueKind::kString); CHECK(std::string(dn->base_type) == "string"); // prop_info_find по enum согласован с find_by_name. auto* by_enum = prop_info_find(Template::kA, Prop::FanSpeed); REQUIRE(by_enum != nullptr); CHECK(std::string(by_enum->name) == "fan_speed"); CHECK(prop_info_find(Template::kB, Prop::AfVerticalDirection) == nullptr); CHECK(prop_info_find(Template::kA, Prop::kCount) == nullptr); CHECK(prop_info_find_by_name(Template::kA, "no_such") == nullptr); CHECK(prop_info_find_by_name(Template::kA, nullptr) == nullptr); } TEST_CASE("Таблицы: без дублей имен и enum'ов внутри шаблона") { for (Template t : {Template::kA, Template::kB, Template::kF}) { size_t n = prop_info_count(t); for (size_t i = 0; i < n; i++) { for (size_t j = i + 1; j < n; j++) { auto* a = fgl::aircon::prop_info_begin(t) + i; auto* b = fgl::aircon::prop_info_begin(t) + j; CHECK_FALSE(std::string(a->name) == std::string(b->name)); CHECK_FALSE(a->prop == b->prop); } } } } TEST_CASE("af_*_swing: base_type integer (PROTOCOL §8.3), kind bool в API") { auto* vs = prop_info_find_by_name(Template::kA, "af_vertical_swing"); REQUIRE(vs != nullptr); CHECK(std::string(vs->base_type) == "integer"); CHECK(vs->kind == fgl::aircon::ValueKind::kBool); }