#include #include #include "utils/config_expand.h" #include "utils/config_schema.h" #include "utils/simple_json.h" namespace rk3588 { namespace { TEST(ConfigExpandTest, ExpandEmptyConfig) { SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson("{}", root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); EXPECT_TRUE(expanded.IsObject()); } TEST(ConfigExpandTest, PassthroughGraphs) { const char* json = R"({ "graphs": [ { "name": "test", "nodes": [{"id": "n1", "type": "input_file", "role": "source"}], "edges": [] } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); const SimpleJson* graphs = expanded.Find("graphs"); ASSERT_NE(graphs, nullptr); EXPECT_TRUE(graphs->IsArray()); EXPECT_EQ(graphs->AsArray().size(), 1u); EXPECT_EQ(graphs->AsArray()[0].ValueOr("name", ""), "test"); } TEST(ConfigExpandTest, ExpandTemplate) { const char* json = R"({ "templates": { "simple": { "nodes": [ {"id": "src", "type": "input_file", "role": "source", "path": "${path}"} ], "edges": [] } }, "instances": [ { "name": "cam1", "template": "simple", "params": {"path": "/video/test.mp4"} } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); const SimpleJson* graphs = expanded.Find("graphs"); ASSERT_NE(graphs, nullptr); EXPECT_EQ(graphs->AsArray().size(), 1u); const SimpleJson& g = graphs->AsArray()[0]; EXPECT_EQ(g.ValueOr("name", ""), "cam1"); const SimpleJson* nodes = g.Find("nodes"); ASSERT_NE(nodes, nullptr); EXPECT_EQ(nodes->AsArray().size(), 1u); // Node ID should be prefixed with instance name EXPECT_EQ(nodes->AsArray()[0].ValueOr("id", ""), "cam1_src"); // Placeholder should be replaced EXPECT_EQ(nodes->AsArray()[0].ValueOr("path", ""), "/video/test.mp4"); } TEST(ConfigExpandTest, ExpandMultipleInstances) { const char* json = R"({ "templates": { "t": { "nodes": [{"id": "n", "type": "test", "role": "source"}], "edges": [] } }, "instances": [ {"name": "i1", "template": "t", "params": {}}, {"name": "i2", "template": "t", "params": {}} ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); const SimpleJson* graphs = expanded.Find("graphs"); ASSERT_NE(graphs, nullptr); EXPECT_EQ(graphs->AsArray().size(), 2u); EXPECT_EQ(graphs->AsArray()[0].ValueOr("name", ""), "i1"); EXPECT_EQ(graphs->AsArray()[1].ValueOr("name", ""), "i2"); } TEST(ConfigExpandTest, ExpandWithOverride) { const char* json = R"({ "templates": { "t": { "nodes": [ {"id": "n1", "type": "test", "role": "source", "enable": true}, {"id": "n2", "type": "filter", "role": "filter"} ], "edges": [["n1", "n2"]] } }, "instances": [ { "name": "inst", "template": "t", "params": {}, "override": { "nodes": { "n1": {"enable": false} } } } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); const SimpleJson* graphs = expanded.Find("graphs"); ASSERT_NE(graphs, nullptr); const SimpleJson* nodes = graphs->AsArray()[0].Find("nodes"); ASSERT_NE(nodes, nullptr); // Find the overridden node bool found = false; for (const auto& n : nodes->AsArray()) { if (n.ValueOr("id", "") == "inst_n1") { found = true; EXPECT_FALSE(n.ValueOr("enable", true)); break; } } EXPECT_TRUE(found); } TEST(ConfigExpandTest, EdgeIdPrefixing) { const char* json = R"({ "templates": { "t": { "nodes": [ {"id": "src", "type": "source", "role": "source"}, {"id": "sink", "type": "sink", "role": "sink"} ], "edges": [["src", "sink"]] } }, "instances": [ {"name": "g1", "template": "t", "params": {}} ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); const SimpleJson* graphs = expanded.Find("graphs"); ASSERT_NE(graphs, nullptr); const SimpleJson* edges = graphs->AsArray()[0].Find("edges"); ASSERT_NE(edges, nullptr); EXPECT_EQ(edges->AsArray().size(), 1u); // Edge endpoints should be prefixed const SimpleJson& edge = edges->AsArray()[0]; EXPECT_TRUE(edge.IsArray()); EXPECT_EQ(edge.AsArray()[0].AsString(), "g1_src"); EXPECT_EQ(edge.AsArray()[1].AsString(), "g1_sink"); } TEST(ConfigExpandTest, QueueConfigPreserved) { const char* json = R"({ "queue": {"size": 16, "strategy": "drop_newest"}, "graphs": [ { "name": "test", "nodes": [], "edges": [] } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); const SimpleJson* queue = expanded.Find("queue"); ASSERT_NE(queue, nullptr); EXPECT_EQ(queue->ValueOr("size", 0), 16); EXPECT_EQ(queue->ValueOr("strategy", ""), "drop_newest"); } TEST(ConfigExpandTest, GlobalConfigPreserved) { const char* json = R"({ "global": {"metrics_port": 8080, "log_level": "debug"}, "graphs": [] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); const SimpleJson* global = expanded.Find("global"); ASSERT_NE(global, nullptr); EXPECT_EQ(global->ValueOr("metrics_port", 0), 8080); EXPECT_EQ(global->ValueOr("log_level", ""), "debug"); } TEST(ConfigExpandTest, MissingTemplateError) { const char* json = R"({ "templates": {}, "instances": [ {"name": "inst", "template": "nonexistent", "params": {}} ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_FALSE(ExpandRootConfig(root, expanded, err)); EXPECT_FALSE(err.empty()); } // Schema validation tests TEST(ConfigSchemaTest, ValidGraphConfig) { const char* json = R"({ "graphs": [ { "name": "valid", "nodes": [ {"id": "n1", "type": "input_file", "role": "source"}, {"id": "n2", "type": "output", "role": "sink"} ], "edges": [["n1", "n2"]] } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); EXPECT_TRUE(ValidateExpandedRootConfig(expanded, err)) << err; } TEST(ConfigSchemaTest, MissingGraphsArray) { const char* json = R"({})"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); // Should pass since empty graphs is allowed after expand } TEST(ConfigSchemaTest, NodeMissingId) { const char* json = R"({ "graphs": [ { "name": "test", "nodes": [{"type": "input", "role": "source"}], "edges": [] } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); EXPECT_FALSE(ValidateExpandedRootConfig(expanded, err)); } TEST(ConfigSchemaTest, NodeMissingType) { const char* json = R"({ "graphs": [ { "name": "test", "nodes": [{"id": "n1", "role": "source"}], "edges": [] } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); EXPECT_FALSE(ValidateExpandedRootConfig(expanded, err)); } TEST(ConfigSchemaTest, DuplicateNodeId) { const char* json = R"({ "graphs": [ { "name": "test", "nodes": [ {"id": "same", "type": "t1", "role": "source"}, {"id": "same", "type": "t2", "role": "sink"} ], "edges": [] } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); EXPECT_FALSE(ValidateExpandedRootConfig(expanded, err)); } TEST(ConfigSchemaTest, EdgeReferencesUnknownNode) { const char* json = R"({ "graphs": [ { "name": "test", "nodes": [{"id": "n1", "type": "t", "role": "source"}], "edges": [["n1", "unknown"]] } ] })"; SimpleJson root; std::string err; EXPECT_TRUE(ParseSimpleJson(json, root, err)); SimpleJson expanded; EXPECT_TRUE(ExpandRootConfig(root, expanded, err)); EXPECT_FALSE(ValidateExpandedRootConfig(expanded, err)); } } // namespace } // namespace rk3588