components/esp_matter/test: add jsontlv roundtrip and validation tests

Signed-off-by: Zhibin (Ryan) Wen <wenzhibin@espressif.com>
This commit is contained in:
Zhibin (Ryan) Wen
2026-03-31 15:23:05 +08:00
parent 95e523db22
commit e48dc7139b
3 changed files with 157 additions and 0 deletions
@@ -1,6 +1,7 @@
list(APPEND srcs_list "attribute_get_val.cpp")
list(APPEND srcs_list "attribute_get_val_type.cpp")
list(APPEND srcs_list "attribute_report.cpp")
list(APPEND srcs_list "jsontlv.cpp")
idf_component_register(SRCS ${srcs_list}
INCLUDE_DIRS "."
+149
View File
@@ -0,0 +1,149 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cJSON.h>
#include <esp_err.h>
#include <json_to_tlv.h>
#include <lib/core/TLV.h>
#include <tlv_to_json.h>
#include <unity.h>
static constexpr size_t k_tlv_buffer_size = 1024;
static esp_err_t roundtrip_json_tree(const char *input_json, cJSON **output_json)
{
if (output_json == nullptr) {
return ESP_ERR_INVALID_ARG;
}
*output_json = nullptr;
uint8_t buffer[k_tlv_buffer_size] = { 0 };
chip::TLV::TLVWriter writer;
writer.Init(buffer, sizeof(buffer));
esp_err_t err = esp_matter::json_to_tlv(input_json, writer, chip::TLV::AnonymousTag());
if (err != ESP_OK) {
return err;
}
chip::TLV::TLVReader reader;
reader.Init(buffer, writer.GetLengthWritten());
return esp_matter::tlv_to_json(reader, output_json);
}
static void expect_roundtrip(const char *input_json, const char *expected_json)
{
cJSON *actual_json = nullptr;
cJSON *expected_json_tree = cJSON_Parse(expected_json);
TEST_ASSERT_NOT_NULL(expected_json_tree);
esp_err_t err = roundtrip_json_tree(input_json, &actual_json);
TEST_ASSERT_EQUAL(ESP_OK, err);
TEST_ASSERT_NOT_NULL(actual_json);
char *actual_printed = cJSON_PrintUnformatted(actual_json);
char *expected_printed = cJSON_PrintUnformatted(expected_json_tree);
TEST_ASSERT_NOT_NULL(actual_printed);
TEST_ASSERT_NOT_NULL(expected_printed);
TEST_ASSERT_EQUAL_STRING(expected_printed, actual_printed);
cJSON_free(actual_printed);
cJSON_free(expected_printed);
cJSON_Delete(actual_json);
cJSON_Delete(expected_json_tree);
}
static void expect_json_to_tlv_failure(const char *input_json)
{
uint8_t buffer[k_tlv_buffer_size] = { 0 };
chip::TLV::TLVWriter writer;
writer.Init(buffer, sizeof(buffer));
esp_err_t err = esp_matter::json_to_tlv(input_json, writer, chip::TLV::AnonymousTag());
TEST_ASSERT_TRUE(err != ESP_OK);
}
TEST_CASE("jsontlv roundtrip scalar values", "[jsontlv][roundtrip]")
{
expect_roundtrip(
R"({"5:STR":"chip","2:I16":-1234,"4:BOOL":true,"1:U8":42,"3:NULL":null})",
R"({"1:U8":42,"2:I16":-1234,"3:NULL":null,"4:BOOL":true,"5:STR":"chip"})");
expect_roundtrip(
R"({"2:OBJ":{"4:BOOL":false,"1:U8":7},"1:ARR-U8":[3,1,2]})",
R"({"1:ARR-U8":[3,1,2],"2:OBJ":{"1:U8":7,"4:BOOL":false}})");
expect_roundtrip(
R"({"1:BYT":"AQID","2:FP":"INF","3:DFP":"-INF"})",
R"({"1:BYT":"AQID","2:FP":"INF","3:DFP":"-INF"})");
expect_roundtrip(
R"({"1:I64":"-1234567890123456789","2:U64":"12345678901234567890"})",
R"({"1:I64":"-1234567890123456789","2:U64":"12345678901234567890"})");
}
TEST_CASE("jsontlv roundtrip container and ordering cases", "[jsontlv][roundtrip]")
{
expect_roundtrip(
R"({"1:ARR-?":[]})",
R"({"1:ARR-?":[]})");
expect_roundtrip(
R"({"255:U16":65535,"1:U8":1})",
R"({"1:U8":1,"255:U16":65535})");
expect_roundtrip(
R"({"9:OBJ":{"3:STR":"c","1:STR":"a","2:STR":"b"},"2:U8":2,"1:U8":1})",
R"({"1:U8":1,"2:U8":2,"9:OBJ":{"1:STR":"a","2:STR":"b","3:STR":"c"}})");
expect_roundtrip(
R"({"7:ARR-STR":[]})",
R"({"7:ARR-?":[]})");
}
TEST_CASE("jsontlv roundtrip numeric float values", "[jsontlv][roundtrip]")
{
cJSON *json = nullptr;
esp_err_t err = roundtrip_json_tree(R"({"1:FP":1.5,"2:DFP":-2.25})", &json);
TEST_ASSERT_EQUAL(ESP_OK, err);
TEST_ASSERT_NOT_NULL(json);
cJSON *float_value = cJSON_GetObjectItemCaseSensitive(json, "1:FP");
TEST_ASSERT_NOT_NULL(float_value);
TEST_ASSERT_TRUE(cJSON_IsNumber(float_value));
TEST_ASSERT_DOUBLE_WITHIN(0.0001, 1.5, float_value->valuedouble);
cJSON *double_value = cJSON_GetObjectItemCaseSensitive(json, "2:DFP");
TEST_ASSERT_NOT_NULL(double_value);
TEST_ASSERT_TRUE(cJSON_IsNumber(double_value));
TEST_ASSERT_DOUBLE_WITHIN(0.0001, -2.25, double_value->valuedouble);
cJSON_Delete(json);
}
TEST_CASE("jsontlv rejects invalid structure and type inputs", "[jsontlv][invalid]")
{
expect_json_to_tlv_failure(R"({"1:ARR":[1,2,3]})");
expect_json_to_tlv_failure(R"({"1:BYT":"not-base64"})");
expect_json_to_tlv_failure(R"({"1":42})");
expect_json_to_tlv_failure(R"({"1:BOOL":"true"})");
expect_json_to_tlv_failure(R"({"1:FP":"NaN"})");
expect_json_to_tlv_failure(R"({"1:I64":"123aaa"})");
expect_json_to_tlv_failure(R"({"1:I64":"aaa123"})");
expect_json_to_tlv_failure(R"({"1:U64":"123aaa"})");
expect_json_to_tlv_failure(R"({"1:U64":"aaa123"})");
}
TEST_CASE("jsontlv rejects invalid numeric boundaries", "[jsontlv][invalid]")
{
expect_json_to_tlv_failure(R"({"1:U8":256})");
expect_json_to_tlv_failure(R"({"1:U32":-1})");
expect_json_to_tlv_failure(R"({"1:I8":128})");
expect_json_to_tlv_failure(R"({"4294967295:U8":1})");
expect_json_to_tlv_failure(R"({"1:U32":1.5})");
expect_json_to_tlv_failure(R"({"1:I32":2147483648})");
}
@@ -42,3 +42,10 @@ def test_get_val_type(dut: QemuDut) -> None:
def test_update_report(dut: QemuDut) -> None:
run_group(dut, 'report')
run_group(dut, 'update')
@pytest.mark.host_test
@pytest.mark.qemu
@pytest.mark.esp32c3
def test_jsontlv(dut: QemuDut) -> None:
run_group(dut, 'jsontlv')