|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + * |
| 6 | + * Multi-stage tests (see ESP-IDF unit test guide): |
| 7 | + * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/unit-tests.html#multi-stage-test-cases |
| 8 | + * |
| 9 | + * On device: run sub-test (1), wait for reboot, run the same menu entry and sub-test (2). |
| 10 | + * In CI/QEMU: pytest calls run_all_single_board_cases() which runs both stages automatically. |
| 11 | + */ |
| 12 | + |
| 13 | +#include <string.h> |
| 14 | +#include <unity.h> |
| 15 | +#include <esp_system.h> |
| 16 | +#include <esp_matter.h> |
| 17 | +#include <esp_matter_core.h> |
| 18 | +#include <esp_matter_data_model.h> |
| 19 | +#include <esp_matter_cluster.h> |
| 20 | +#include <nvs_flash.h> |
| 21 | + |
| 22 | +namespace esp_matter::attribute { |
| 23 | +esp_err_t get_val_internal(attribute_t *attribute, esp_matter_attr_val_t *val); |
| 24 | +esp_err_t store_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, |
| 25 | + const esp_matter_attr_val_t &val); |
| 26 | +esp_err_t erase_val_in_nvs(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id); |
| 27 | +} // namespace esp_matter::attribute |
| 28 | + |
| 29 | +using namespace esp_matter; |
| 30 | + |
| 31 | +static constexpr uint32_t k_cluster_id = 0xFFF2; |
| 32 | +static constexpr uint32_t k_attribute_id = 0x10001; |
| 33 | +static constexpr uint16_t k_max_size = 32; |
| 34 | + |
| 35 | +static esp_err_t init_nvs() |
| 36 | +{ |
| 37 | + esp_err_t err = nvs_flash_init(); |
| 38 | + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { |
| 39 | + TEST_ASSERT_EQUAL(ESP_OK, nvs_flash_erase()); |
| 40 | + err = nvs_flash_init(); |
| 41 | + } |
| 42 | + TEST_ASSERT_EQUAL(ESP_OK, err); |
| 43 | + return esp_matter_nvs_init(); |
| 44 | +} |
| 45 | + |
| 46 | +static cluster_t *add_test_cluster(endpoint_t *endpoint) |
| 47 | +{ |
| 48 | + cluster_t *cluster = cluster::create(endpoint, k_cluster_id, CLUSTER_FLAG_SERVER); |
| 49 | + TEST_ASSERT_NOT_NULL(cluster); |
| 50 | + cluster::global::attribute::create_feature_map(cluster, 0); |
| 51 | + cluster::global::attribute::create_cluster_revision(cluster, 1); |
| 52 | + return cluster; |
| 53 | +} |
| 54 | + |
| 55 | +static void check_max_and_stored(attribute_t *attr) |
| 56 | +{ |
| 57 | + esp_matter_attr_val_t val; |
| 58 | + TEST_ASSERT_EQUAL(ESP_OK, attribute::get_val_internal(attr, &val)); |
| 59 | + TEST_ASSERT_EQUAL(k_max_size, val.val.a.max); |
| 60 | + TEST_ASSERT_EQUAL_STRING("stored", reinterpret_cast<char *>(val.val.a.b)); |
| 61 | +} |
| 62 | + |
| 63 | +static void attr_create_max_before_reboot() |
| 64 | +{ |
| 65 | + TEST_ASSERT_EQUAL(ESP_OK, init_nvs()); |
| 66 | + |
| 67 | + if (node::get() != nullptr) { |
| 68 | + node::destroy_raw(); |
| 69 | + } |
| 70 | + |
| 71 | + node::config_t node_config; |
| 72 | + node_t *node = node::create(&node_config, nullptr, nullptr); |
| 73 | + TEST_ASSERT_NOT_NULL(node); |
| 74 | + |
| 75 | + endpoint_t *ep = endpoint::create(node, ENDPOINT_FLAG_NONE, nullptr); |
| 76 | + TEST_ASSERT_NOT_NULL(ep); |
| 77 | + uint16_t ep_id = endpoint::get_id(ep); |
| 78 | + TEST_ASSERT_EQUAL(1, ep_id); |
| 79 | + |
| 80 | + cluster_t *cluster = add_test_cluster(ep); |
| 81 | + attribute::erase_val_in_nvs(ep_id, k_cluster_id, k_attribute_id); |
| 82 | + |
| 83 | + char stored[] = "stored"; |
| 84 | + esp_matter_attr_val_t nvs_val = esp_matter_char_str(stored, strlen(stored)); |
| 85 | + TEST_ASSERT_EQUAL(ESP_OK, attribute::store_val_in_nvs(ep_id, k_cluster_id, k_attribute_id, nvs_val)); |
| 86 | + |
| 87 | + char empty[] = ""; |
| 88 | + esp_matter_attr_val_t def = esp_matter_char_str(empty, 0); |
| 89 | + attribute_t *attr = attribute::create(cluster, k_attribute_id, ATTRIBUTE_FLAG_NONVOLATILE, def, k_max_size); |
| 90 | + TEST_ASSERT_NOT_NULL(attr); |
| 91 | + check_max_and_stored(attr); |
| 92 | + |
| 93 | + esp_restart(); |
| 94 | +} |
| 95 | + |
| 96 | +static void attr_create_max_after_reboot() |
| 97 | +{ |
| 98 | + TEST_ASSERT_EQUAL(ESP_RST_SW, esp_reset_reason()); |
| 99 | + TEST_ASSERT_EQUAL(ESP_OK, init_nvs()); |
| 100 | + |
| 101 | + if (node::get() != nullptr) { |
| 102 | + node::destroy_raw(); |
| 103 | + } |
| 104 | + |
| 105 | + node::config_t node_config; |
| 106 | + node_t *node = node::create(&node_config, nullptr, nullptr); |
| 107 | + TEST_ASSERT_NOT_NULL(node); |
| 108 | + |
| 109 | + endpoint_t *ep = endpoint::create(node, ENDPOINT_FLAG_NONE, nullptr); |
| 110 | + TEST_ASSERT_NOT_NULL(ep); |
| 111 | + TEST_ASSERT_EQUAL(1, endpoint::get_id(ep)); |
| 112 | + |
| 113 | + cluster_t *cluster = add_test_cluster(ep); |
| 114 | + |
| 115 | + char empty[] = ""; |
| 116 | + esp_matter_attr_val_t def = esp_matter_char_str(empty, 0); |
| 117 | + attribute_t *attr = attribute::create(cluster, k_attribute_id, ATTRIBUTE_FLAG_NONVOLATILE, def, k_max_size); |
| 118 | + TEST_ASSERT_NOT_NULL(attr); |
| 119 | + check_max_and_stored(attr); |
| 120 | + |
| 121 | + attribute::erase_val_in_nvs(1, k_cluster_id, k_attribute_id); |
| 122 | + cluster::destroy(cluster); |
| 123 | +} |
| 124 | + |
| 125 | +TEST_CASE_MULTIPLE_STAGES("attribute::create preserves max after reboot", |
| 126 | + "[attribute][create][reset=SW_CPU_RESET]", |
| 127 | + attr_create_max_before_reboot, attr_create_max_after_reboot); |
0 commit comments