| /* |
| * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/ |
| * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/ |
| * |
| * Written by Václav Kubernát <kubervac@fit.cvut.cz> |
| * |
| */ |
| |
| #include "trompeloeil_doctest.hpp" |
| #include <experimental/iterator> |
| #include "leaf_data_helpers.hpp" |
| #include "pretty_printers.hpp" |
| #include "yang_schema.hpp" |
| |
| const char* second_schema = R"( |
| module second-schema { |
| namespace "http://example.com/nevim"; |
| prefix second; |
| |
| import example-schema { |
| prefix "example"; |
| } |
| |
| identity pineapple { |
| base "example:fruit"; |
| } |
| |
| augment /example:a { |
| container augmentedContainer { |
| } |
| } |
| |
| container bla { |
| container bla2 { |
| } |
| } |
| } |
| )"; |
| |
| const char* included_submodule = R"( |
| submodule sub-module { |
| yang-version 1.1; |
| |
| belongs-to example-schema { |
| prefix sub; |
| } |
| |
| leaf subLeaf { |
| type string; |
| } |
| } |
| )"; |
| |
| const char* example_schema = R"( |
| module example-schema { |
| yang-version 1.1; |
| namespace "http://example.com/example-sports"; |
| prefix coze; |
| |
| include sub-module; |
| |
| identity drink { |
| } |
| |
| identity voda { |
| base "drink"; |
| } |
| |
| identity food { |
| } |
| |
| identity fruit { |
| base "food"; |
| } |
| |
| identity pizza { |
| base "food"; |
| } |
| |
| identity hawaii { |
| base "pizza"; |
| } |
| |
| typedef foodTypedef { |
| type identityref { |
| base food; |
| } |
| } |
| |
| leaf leafFoodTypedef { |
| type foodTypedef; |
| } |
| |
| container a { |
| container a2 { |
| container a3 { |
| presence true; |
| } |
| } |
| |
| leaf leafa { |
| type string; |
| } |
| } |
| |
| container b { |
| container b2 { |
| presence true; |
| container b3 { |
| } |
| } |
| } |
| |
| leaf leafString { |
| type string; |
| } |
| |
| leaf leafDecimal { |
| type decimal64 { |
| fraction-digits 5; |
| } |
| } |
| |
| leaf leafBool { |
| type boolean; |
| } |
| |
| leaf leafInt8 { |
| type int8; |
| } |
| |
| leaf leafUint8 { |
| type uint8; |
| } |
| |
| leaf leafInt16 { |
| type int16; |
| } |
| |
| leaf leafUint16 { |
| type uint16; |
| } |
| |
| leaf leafInt32 { |
| description "A 32-bit integer leaf."; |
| type int32; |
| } |
| |
| leaf leafUint32 { |
| type uint32; |
| } |
| |
| leaf leafInt64 { |
| type int64; |
| } |
| |
| leaf leafUint64 { |
| type uint64; |
| default 9001; |
| } |
| |
| leaf leafEnum { |
| type enumeration { |
| enum lol; |
| enum data; |
| enum coze; |
| } |
| } |
| |
| typedef enumTypedef { |
| type enumeration { |
| enum lol; |
| enum data; |
| enum coze; |
| } |
| default data; |
| } |
| |
| typedef enumTypedefRestricted { |
| type enumTypedef { |
| enum lol; |
| enum data; |
| } |
| description "This is a restricted enum typedef."; |
| } |
| |
| leaf leafEnumTypedef { |
| type enumTypedef; |
| } |
| |
| leaf leafEnumTypedefRestricted { |
| type enumTypedef { |
| enum data; |
| enum coze; |
| } |
| } |
| |
| leaf leafEnumTypedefRestricted2 { |
| type enumTypedefRestricted; |
| } |
| |
| leaf foodIdentLeaf { |
| type identityref { |
| base "food"; |
| } |
| } |
| |
| leaf pizzaIdentLeaf { |
| type identityref { |
| base "pizza"; |
| } |
| } |
| |
| leaf foodDrinkIdentLeaf { |
| type identityref { |
| base "food"; |
| base "drink"; |
| } |
| } |
| |
| list _list { |
| key number; |
| |
| leaf number { |
| type int32; |
| } |
| |
| container contInList { |
| presence true; |
| } |
| } |
| |
| list twoKeyList { |
| key "name number"; |
| |
| leaf number { |
| type int32; |
| } |
| |
| leaf name { |
| type string; |
| } |
| } |
| |
| grouping arithmeticFlags { |
| leaf carry { |
| type boolean; |
| } |
| leaf zero { |
| type boolean; |
| } |
| } |
| |
| grouping flags { |
| leaf direction { |
| type boolean; |
| } |
| leaf interrupt { |
| type boolean; |
| } |
| |
| uses arithmeticFlags; |
| } |
| |
| uses flags; |
| |
| choice interface { |
| case caseLoopback { |
| container loopback { |
| leaf ip { |
| type string; |
| } |
| } |
| } |
| |
| case caseEthernet { |
| container ethernet { |
| leaf ip { |
| type string; |
| } |
| } |
| } |
| } |
| |
| feature bigPizzas; |
| |
| leaf pizzaSize { |
| type enumeration { |
| enum large { |
| if-feature "bigPizzas"; |
| } |
| enum medium; |
| enum small; |
| } |
| } |
| |
| leaf length { |
| type int32; |
| units "m"; |
| } |
| |
| leaf wavelength { |
| type decimal64 { |
| fraction-digits 10; |
| } |
| units "nm"; |
| } |
| |
| typedef seconds { |
| type int32; |
| units "s"; |
| } |
| |
| leaf duration { |
| type seconds; |
| } |
| |
| leaf another-duration { |
| type seconds; |
| units "vt"; |
| } |
| |
| leaf activeNumber { |
| type leafref { |
| path "/_list/number"; |
| } |
| } |
| |
| rpc myRpc {} |
| |
| rpc rpcOneOutput { |
| output { |
| leaf ahoj { |
| type string; |
| } |
| } |
| } |
| |
| rpc rpcOneInput { |
| input { |
| leaf ahoj { |
| type string; |
| } |
| } |
| } |
| |
| leaf numberOrString { |
| type union { |
| type int32; |
| type string; |
| } |
| description "Can be an int32 or a string."; |
| } |
| |
| list portSettings { |
| key "port"; |
| leaf port { |
| type enumeration { |
| enum eth0; |
| enum eth1; |
| enum eth2; |
| } |
| } |
| action shutdown { |
| output { |
| leaf success { |
| type boolean; |
| } |
| } |
| } |
| } |
| |
| feature weirdPortNames; |
| |
| list portMapping { |
| key "port"; |
| leaf port { |
| type enumeration { |
| enum WEIRD { |
| if-feature "weirdPortNames"; |
| } |
| enum utf2; |
| enum utf3; |
| } |
| } |
| } |
| |
| leaf activeMappedPort { |
| type leafref { |
| path "../portMapping/port"; |
| } |
| } |
| |
| leaf activePort { |
| type union { |
| type enumeration { |
| enum wlan0; |
| enum wlan1; |
| } |
| type leafref { |
| path "../portSettings/port"; |
| } |
| type leafref { |
| path "../activeMappedPort"; |
| } |
| type empty; |
| } |
| } |
| |
| leaf dummyLeaf { |
| type empty; |
| } |
| |
| leaf clockSpeed { |
| type int64; |
| config false; |
| } |
| |
| container systemStats { |
| config false; |
| leaf upTime { |
| type uint64; |
| } |
| } |
| |
| leaf obsoleteLeaf { |
| type int32; |
| status obsolete; |
| } |
| |
| leaf deprecatedLeaf { |
| type int32; |
| status deprecated; |
| } |
| |
| typedef deprecatedType { |
| type int32; |
| status deprecated; |
| } |
| |
| leaf obsoleteLeafWithDeprecatedType { |
| type deprecatedType; |
| status obsolete; |
| } |
| |
| typedef obsoleteType { |
| type int32; |
| status obsolete; |
| } |
| |
| leaf obsoleteLeafWithObsoleteType { |
| type deprecatedType; |
| status obsolete; |
| } |
| |
| leaf-list addresses { |
| type string; |
| } |
| |
| leaf flagBits { |
| type bits { |
| bit carry; |
| bit sign; |
| bit overflow; |
| } |
| } |
| })"; |
| |
| TEST_CASE("yangschema") |
| { |
| using namespace std::string_literals; |
| using namespace std::string_view_literals; |
| YangSchema ys; |
| ys.registerModuleCallback([]([[maybe_unused]] auto modName, auto, auto subModule, auto) { |
| if (modName != "example-schema"sv) { |
| throw std::logic_error("unrecognized module "s + modName); |
| } |
| if (subModule == nullptr) { |
| return example_schema; |
| } |
| if (subModule == "sub-module"sv) { |
| return included_submodule; |
| } |
| |
| throw std::logic_error("unrecognized submodule "s + subModule); |
| }); |
| ys.addSchemaString(second_schema); |
| |
| schemaPath_ path{Scope::Absolute, {}}; |
| ModuleNodePair node; |
| |
| SECTION("positive") |
| { |
| SECTION("isModule") |
| { |
| REQUIRE(ys.isModule("example-schema")); |
| } |
| |
| SECTION("listHasKey") |
| { |
| std::string key; |
| |
| SECTION("_list") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, list_{"_list"}); |
| SECTION("number") |
| { |
| key = "number"; |
| } |
| } |
| |
| SECTION("twoKeyList") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, list_{"twoKeyList"}); |
| SECTION("number") |
| { |
| key = "number"; |
| } |
| SECTION("name") |
| { |
| key = "name"; |
| } |
| } |
| |
| REQUIRE(ys.listHasKey(path, key)); |
| } |
| SECTION("listKeys") |
| { |
| std::set<std::string> set; |
| |
| SECTION("_list") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, list_{"_list"}); |
| set = {"number"}; |
| } |
| |
| SECTION("twoKeyList") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, list_{"twoKeyList"}); |
| set = {"number", "name"}; |
| } |
| |
| REQUIRE(ys.listKeys(path) == set); |
| } |
| SECTION("leafType") |
| { |
| yang::LeafDataType type; |
| std::optional<std::string> expectedDescription; |
| |
| SECTION("leafString") |
| { |
| node.first = "example-schema"; |
| node.second = "leafString"; |
| type = yang::String{}; |
| } |
| |
| SECTION("leafDecimal") |
| { |
| node.first = "example-schema"; |
| node.second = "leafDecimal"; |
| type = yang::Decimal{}; |
| } |
| |
| SECTION("leafBool") |
| { |
| node.first = "example-schema"; |
| node.second = "leafBool"; |
| type = yang::Bool{}; |
| } |
| |
| SECTION("leafInt8") |
| { |
| node.first = "example-schema"; |
| node.second = "leafInt8"; |
| type = yang::Int8{}; |
| } |
| |
| SECTION("leafUint8") |
| { |
| node.first = "example-schema"; |
| node.second = "leafUint8"; |
| type = yang::Uint8{}; |
| } |
| |
| SECTION("leafInt16") |
| { |
| node.first = "example-schema"; |
| node.second = "leafInt16"; |
| type = yang::Int16{}; |
| } |
| |
| SECTION("leafUint16") |
| { |
| node.first = "example-schema"; |
| node.second = "leafUint16"; |
| type = yang::Uint16{}; |
| } |
| |
| SECTION("leafInt32") |
| { |
| node.first = "example-schema"; |
| node.second = "leafInt32"; |
| type = yang::Int32{}; |
| } |
| |
| SECTION("leafUint32") |
| { |
| node.first = "example-schema"; |
| node.second = "leafUint32"; |
| type = yang::Uint32{}; |
| } |
| |
| SECTION("leafInt64") |
| { |
| node.first = "example-schema"; |
| node.second = "leafInt64"; |
| type = yang::Int64{}; |
| } |
| |
| SECTION("leafUint64") |
| { |
| node.first = "example-schema"; |
| node.second = "leafUint64"; |
| type = yang::Uint64{}; |
| } |
| |
| SECTION("leafEnum") |
| { |
| node.first = "example-schema"; |
| node.second = "leafEnum"; |
| type = createEnum({"lol", "data", "coze"}); |
| } |
| |
| SECTION("leafEnumTypedef") |
| { |
| node.first = "example-schema"; |
| node.second = "leafEnumTypedef"; |
| type = createEnum({"lol", "data", "coze"}); |
| } |
| |
| SECTION("leafEnumTypedefRestricted") |
| { |
| node.first = "example-schema"; |
| node.second = "leafEnumTypedefRestricted"; |
| type = createEnum({"data", "coze"}); |
| } |
| |
| SECTION("leafEnumTypedefRestricted2") |
| { |
| node.first = "example-schema"; |
| node.second = "leafEnumTypedefRestricted2"; |
| type = createEnum({"lol", "data"}); |
| expectedDescription = "This is a restricted enum typedef."; |
| } |
| |
| SECTION("pizzaSize") |
| { |
| node.first = "example-schema"; |
| node.second = "pizzaSize"; |
| |
| SECTION("bigPizzas disabled") |
| { |
| type = createEnum({"small", "medium"}); |
| } |
| SECTION("bigPizzas enabled") |
| { |
| ys.enableFeature("example-schema", "bigPizzas"); |
| type = createEnum({"small", "medium", "large"}); |
| } |
| } |
| |
| SECTION("foodIdentLeaf") |
| { |
| node.first = "example-schema"; |
| node.second = "foodIdentLeaf"; |
| type = yang::IdentityRef{{{"second-schema", "pineapple"}, |
| {"example-schema", "food"}, |
| {"example-schema", "pizza"}, |
| {"example-schema", "hawaii"}, |
| {"example-schema", "fruit"}}}; |
| } |
| |
| SECTION("pizzaIdentLeaf") |
| { |
| node.first = "example-schema"; |
| node.second = "pizzaIdentLeaf"; |
| |
| type = yang::IdentityRef{{ |
| {"example-schema", "pizza"}, |
| {"example-schema", "hawaii"}, |
| }}; |
| } |
| |
| SECTION("foodDrinkIdentLeaf") |
| { |
| node.first = "example-schema"; |
| node.second = "foodDrinkIdentLeaf"; |
| |
| type = yang::IdentityRef{{ |
| {"example-schema", "food"}, |
| {"example-schema", "drink"}, |
| {"example-schema", "fruit"}, |
| {"example-schema", "hawaii"}, |
| {"example-schema", "pizza"}, |
| {"example-schema", "voda"}, |
| {"second-schema", "pineapple"}, |
| }}; |
| } |
| |
| SECTION("activeNumber") |
| { |
| node.first = "example-schema"; |
| node.second = "activeNumber"; |
| type.emplace<yang::LeafRef>( |
| "/example-schema:_list/number", |
| std::make_unique<yang::TypeInfo>(ys.leafType("/example-schema:_list/number")) |
| ); |
| } |
| |
| SECTION("activePort") |
| { |
| node.first = "example-schema"; |
| node.second = "activePort"; |
| |
| yang::Enum enums({}); |
| SECTION("weird ports disabled") |
| { |
| enums = createEnum({"utf2", "utf3"}); |
| } |
| SECTION("weird ports enabled") |
| { |
| ys.enableFeature("example-schema", "weirdPortNames"); |
| enums = createEnum({"WEIRD", "utf2", "utf3"}); |
| } |
| |
| type = yang::Union{{ |
| yang::TypeInfo{createEnum({"wlan0", "wlan1"})}, |
| yang::TypeInfo{yang::LeafRef{ |
| "/example-schema:portSettings/port", |
| std::make_unique<yang::TypeInfo>(createEnum({"eth0", "eth1", "eth2"})) |
| }}, |
| yang::TypeInfo{yang::LeafRef{ |
| "/example-schema:activeMappedPort", |
| std::make_unique<yang::TypeInfo>(yang::LeafRef{ |
| "/example-schema:portMapping/port", |
| std::make_unique<yang::TypeInfo>(enums) |
| }) |
| }}, |
| yang::TypeInfo{yang::Empty{}}, |
| }}; |
| } |
| |
| SECTION("addresses") |
| { |
| node.first = "example-schema"; |
| node.second = "addresses"; |
| type.emplace<yang::String>(); |
| } |
| |
| SECTION("flagBits") |
| { |
| node.first = "example-schema"; |
| node.second = "flagBits"; |
| type = yang::Bits{{"carry", "sign", "overflow"}}; |
| } |
| |
| SECTION("foodTypedef") |
| { |
| node.first = "example-schema"; |
| node.second = "leafFoodTypedef"; |
| type = yang::IdentityRef{{ |
| {"example-schema", "food"}, |
| {"example-schema", "fruit"}, |
| {"example-schema", "hawaii"}, |
| {"example-schema", "pizza"}, |
| {"second-schema", "pineapple"}, |
| }}; |
| } |
| |
| |
| REQUIRE(ys.leafType(path, node) == yang::TypeInfo(type, std::nullopt, expectedDescription)); |
| } |
| SECTION("availableNodes") |
| { |
| // TODO: merge "path" and "module" sections and add recursive versions to the path section |
| SECTION("paths") |
| { |
| std::set<ModuleNodePair> set; |
| |
| using namespace std::string_literals; |
| SECTION("<root>") |
| { |
| set = {{"example-schema"s, "a"}, {"example-schema"s, "b"}, {"example-schema"s, "leafString"}, |
| {"example-schema"s, "leafDecimal"}, {"example-schema"s, "leafBool"}, |
| {"example-schema"s, "leafInt8"}, {"example-schema"s, "leafUint8"}, |
| {"example-schema"s, "leafInt16"}, {"example-schema"s, "leafUint16"}, |
| {"example-schema"s, "leafInt32"}, {"example-schema"s, "leafUint32"}, |
| {"example-schema"s, "leafInt64"}, {"example-schema"s, "leafUint64"}, |
| {"example-schema"s, "leafEnum"}, {"example-schema"s, "leafEnumTypedef"}, |
| {"example-schema"s, "leafEnumTypedefRestricted"}, {"example-schema"s, "leafEnumTypedefRestricted2"}, |
| {"example-schema"s, "foodIdentLeaf"}, {"example-schema"s, "pizzaIdentLeaf"}, {"example-schema"s, "foodDrinkIdentLeaf"}, |
| {"example-schema"s, "_list"}, {"example-schema"s, "twoKeyList"}, {"second-schema"s, "bla"}, |
| {"example-schema"s, "carry"}, {"example-schema"s, "zero"}, {"example-schema"s, "direction"}, |
| {"example-schema"s, "interrupt"}, |
| {"example-schema"s, "ethernet"}, {"example-schema"s, "loopback"}, |
| {"example-schema"s, "pizzaSize"}, |
| {"example-schema"s, "length"}, {"example-schema"s, "wavelength"}, |
| {"example-schema"s, "duration"}, {"example-schema"s, "another-duration"}, |
| {"example-schema"s, "activeNumber"}, |
| {"example-schema"s, "numberOrString"}, |
| {"example-schema"s, "portSettings"}, |
| {"example-schema"s, "portMapping"}, |
| {"example-schema"s, "activeMappedPort"}, |
| {"example-schema"s, "activePort"}, |
| {"example-schema"s, "clockSpeed"}, |
| {"example-schema"s, "deprecatedLeaf"}, |
| {"example-schema"s, "obsoleteLeaf"}, |
| {"example-schema"s, "obsoleteLeafWithDeprecatedType"}, |
| {"example-schema"s, "obsoleteLeafWithObsoleteType"}, |
| {"example-schema"s, "myRpc"}, |
| {"example-schema"s, "rpcOneOutput"}, |
| {"example-schema"s, "rpcOneInput"}, |
| {"example-schema"s, "systemStats"}, |
| {"example-schema"s, "dummyLeaf"}, |
| {"example-schema"s, "addresses"}, |
| {"example-schema"s, "subLeaf"}, |
| {"example-schema"s, "flagBits"}, |
| {"example-schema"s, "leafFoodTypedef"}}; |
| } |
| |
| SECTION("example-schema:a") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("a")); |
| set = { |
| {boost::none, "a2"}, |
| {boost::none, "leafa"}, |
| {"second-schema"s, "augmentedContainer"} |
| }; |
| } |
| |
| SECTION("example-schema:ethernet") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("ethernet")); |
| set = {{boost::none, "ip"}}; |
| } |
| |
| SECTION("second-schema:bla") |
| { |
| path.m_nodes.emplace_back(module_{"second-schema"}, container_("bla")); |
| set = {{boost::none, "bla2"}}; |
| } |
| |
| REQUIRE(ys.availableNodes(path, Recursion::NonRecursive) == set); |
| } |
| |
| SECTION("modules") |
| { |
| std::string module; |
| std::set<ModuleNodePair> expectedNonRecursive; |
| std::set<ModuleNodePair> expectedRecursive; |
| using namespace std::string_literals; |
| SECTION("example-schema") |
| { |
| module = "example-schema"; |
| expectedNonRecursive = { |
| {"example-schema"s, "_list"}, |
| {"example-schema"s, "a"}, |
| {"example-schema"s, "activeMappedPort"}, |
| {"example-schema"s, "activeNumber"}, |
| {"example-schema"s, "activePort"}, |
| {"example-schema"s, "addresses"}, |
| {"example-schema"s, "another-duration"}, |
| {"example-schema"s, "b"}, |
| {"example-schema"s, "carry"}, |
| {"example-schema"s, "clockSpeed"}, |
| {"example-schema"s, "deprecatedLeaf"}, |
| {"example-schema"s, "direction"}, |
| {"example-schema"s, "dummyLeaf"}, |
| {"example-schema"s, "duration"}, |
| {"example-schema"s, "ethernet"}, |
| {"example-schema"s, "foodDrinkIdentLeaf"}, |
| {"example-schema"s, "foodIdentLeaf"}, |
| {"example-schema"s, "flagBits"}, |
| {"example-schema"s, "interrupt"}, |
| {"example-schema"s, "leafBool"}, |
| {"example-schema"s, "leafDecimal"}, |
| {"example-schema"s, "leafEnum"}, |
| {"example-schema"s, "leafEnumTypedef"}, |
| {"example-schema"s, "leafEnumTypedefRestricted"}, |
| {"example-schema"s, "leafEnumTypedefRestricted2"}, |
| {"example-schema"s, "leafFoodTypedef"}, |
| {"example-schema"s, "leafInt16"}, |
| {"example-schema"s, "leafInt32"}, |
| {"example-schema"s, "leafInt64"}, |
| {"example-schema"s, "leafInt8"}, |
| {"example-schema"s, "leafString"}, |
| {"example-schema"s, "leafUint16"}, |
| {"example-schema"s, "leafUint32"}, |
| {"example-schema"s, "leafUint64"}, |
| {"example-schema"s, "leafUint8"}, |
| {"example-schema"s, "length"}, |
| {"example-schema"s, "loopback"}, |
| {"example-schema"s, "myRpc"}, |
| {"example-schema"s, "rpcOneOutput"}, |
| {"example-schema"s, "rpcOneInput"}, |
| {"example-schema"s, "numberOrString"}, |
| {"example-schema"s, "obsoleteLeaf"}, |
| {"example-schema"s, "obsoleteLeafWithDeprecatedType"}, |
| {"example-schema"s, "obsoleteLeafWithObsoleteType"}, |
| {"example-schema"s, "pizzaIdentLeaf"}, |
| {"example-schema"s, "pizzaSize"}, |
| {"example-schema"s, "portMapping"}, |
| {"example-schema"s, "portSettings"}, |
| {"example-schema"s, "systemStats"}, |
| {"example-schema"s, "twoKeyList"}, |
| {"example-schema"s, "wavelength"}, |
| {"example-schema"s, "zero"}, |
| {"example-schema"s, "subLeaf"} |
| }; |
| expectedRecursive = { |
| {boost::none, "/example-schema:_list"}, |
| {boost::none, "/example-schema:_list/contInList"}, |
| {boost::none, "/example-schema:_list/number"}, |
| {boost::none, "/example-schema:a"}, |
| {boost::none, "/example-schema:addresses"}, |
| {boost::none, "/example-schema:a/a2"}, |
| {boost::none, "/example-schema:a/a2/a3"}, |
| {boost::none, "/example-schema:a/leafa"}, |
| {boost::none, "/example-schema:a/second-schema:augmentedContainer"}, |
| {boost::none, "/example-schema:activeMappedPort"}, |
| {boost::none, "/example-schema:activeNumber"}, |
| {boost::none, "/example-schema:activePort"}, |
| {boost::none, "/example-schema:another-duration"}, |
| {boost::none, "/example-schema:b"}, |
| {boost::none, "/example-schema:b/b2"}, |
| {boost::none, "/example-schema:b/b2/b3"}, |
| {boost::none, "/example-schema:carry"}, |
| {boost::none, "/example-schema:clockSpeed"}, |
| {boost::none, "/example-schema:deprecatedLeaf"}, |
| {boost::none, "/example-schema:direction"}, |
| {boost::none, "/example-schema:duration"}, |
| {boost::none, "/example-schema:dummyLeaf"}, |
| {boost::none, "/example-schema:flagBits"}, |
| {boost::none, "/example-schema:foodDrinkIdentLeaf"}, |
| {boost::none, "/example-schema:foodDrinkIdentLeaf"}, |
| {boost::none, "/example-schema:foodIdentLeaf"}, |
| {boost::none, "/example-schema:interface/caseEthernet/ethernet"}, |
| {boost::none, "/example-schema:interface/caseEthernet/ethernet/ip"}, |
| {boost::none, "/example-schema:interface/caseLoopback/loopback"}, |
| {boost::none, "/example-schema:interface/caseLoopback/loopback/ip"}, |
| {boost::none, "/example-schema:interrupt"}, |
| {boost::none, "/example-schema:leafBool"}, |
| {boost::none, "/example-schema:leafDecimal"}, |
| {boost::none, "/example-schema:leafEnum"}, |
| {boost::none, "/example-schema:leafEnumTypedef"}, |
| {boost::none, "/example-schema:leafEnumTypedefRestricted"}, |
| {boost::none, "/example-schema:leafEnumTypedefRestricted2"}, |
| {boost::none, "/example-schema:leafFoodTypedef"}, |
| {boost::none, "/example-schema:leafInt16"}, |
| {boost::none, "/example-schema:leafInt32"}, |
| {boost::none, "/example-schema:leafInt64"}, |
| {boost::none, "/example-schema:leafInt8"}, |
| {boost::none, "/example-schema:leafString"}, |
| {boost::none, "/example-schema:leafUint16"}, |
| {boost::none, "/example-schema:leafUint32"}, |
| {boost::none, "/example-schema:leafUint64"}, |
| {boost::none, "/example-schema:leafUint8"}, |
| {boost::none, "/example-schema:length"}, |
| {boost::none, "/example-schema:myRpc"}, |
| {boost::none, "/example-schema:myRpc/input"}, |
| {boost::none, "/example-schema:myRpc/output"}, |
| {boost::none, "/example-schema:rpcOneOutput"}, |
| {boost::none, "/example-schema:rpcOneOutput/input"}, |
| {boost::none, "/example-schema:rpcOneOutput/output"}, |
| {boost::none, "/example-schema:rpcOneOutput/output/ahoj"}, |
| {boost::none, "/example-schema:rpcOneInput"}, |
| {boost::none, "/example-schema:rpcOneInput/input"}, |
| {boost::none, "/example-schema:rpcOneInput/input/ahoj"}, |
| {boost::none, "/example-schema:rpcOneInput/output"}, |
| {boost::none, "/example-schema:numberOrString"}, |
| {boost::none, "/example-schema:obsoleteLeaf"}, |
| {boost::none, "/example-schema:obsoleteLeafWithDeprecatedType"}, |
| {boost::none, "/example-schema:obsoleteLeafWithObsoleteType"}, |
| {boost::none, "/example-schema:pizzaIdentLeaf"}, |
| {boost::none, "/example-schema:pizzaSize"}, |
| {boost::none, "/example-schema:portMapping"}, |
| {boost::none, "/example-schema:portMapping/port"}, |
| {boost::none, "/example-schema:portSettings"}, |
| {boost::none, "/example-schema:portSettings/port"}, |
| {boost::none, "/example-schema:portSettings/shutdown"}, |
| {boost::none, "/example-schema:portSettings/shutdown/input"}, |
| {boost::none, "/example-schema:portSettings/shutdown/output"}, |
| {boost::none, "/example-schema:portSettings/shutdown/output/success"}, |
| {boost::none, "/example-schema:systemStats"}, |
| {boost::none, "/example-schema:systemStats/upTime"}, |
| {boost::none, "/example-schema:subLeaf"}, |
| {boost::none, "/example-schema:twoKeyList"}, |
| {boost::none, "/example-schema:twoKeyList/name"}, |
| {boost::none, "/example-schema:twoKeyList/number"}, |
| {boost::none, "/example-schema:wavelength"}, |
| {boost::none, "/example-schema:zero"} |
| }; |
| } |
| SECTION("second-schema") |
| { |
| module = "second-schema"; |
| expectedNonRecursive = { |
| {"second-schema"s, "bla"} |
| }; |
| expectedRecursive = { |
| {boost::none, "/second-schema:bla"}, |
| {boost::none, "/second-schema:bla/bla2"} |
| }; |
| } |
| |
| REQUIRE(ys.availableNodes(module_{module}, Recursion::NonRecursive) == expectedNonRecursive); |
| REQUIRE(ys.availableNodes(module_{module}, Recursion::Recursive) == expectedRecursive); |
| } |
| } |
| SECTION("nodeType") |
| { |
| yang::NodeTypes expected; |
| SECTION("leafInt32") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("leafInt32")); |
| expected = yang::NodeTypes::Leaf; |
| } |
| |
| SECTION("a") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("a")); |
| expected = yang::NodeTypes::Container; |
| } |
| |
| SECTION("a/a2/a3") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("a")); |
| path.m_nodes.emplace_back(container_("a2")); |
| path.m_nodes.emplace_back(container_("a3")); |
| expected = yang::NodeTypes::PresenceContainer; |
| } |
| |
| SECTION("_list") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, list_("_list")); |
| expected = yang::NodeTypes::List; |
| } |
| |
| SECTION("subLeaf") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("subLeaf")); |
| expected = yang::NodeTypes::Leaf; |
| } |
| |
| REQUIRE(ys.nodeType(pathToSchemaString(path, Prefixes::WhenNeeded)) == expected); |
| } |
| |
| SECTION("description") |
| { |
| std::optional<std::string> expected; |
| SECTION("leafInt32") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("leafInt32")); |
| expected = "A 32-bit integer leaf."; |
| } |
| |
| SECTION("leafString") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("leafString")); |
| } |
| |
| SECTION("numberOrString") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("numberOrString")); |
| expected = "Can be an int32 or a string."; |
| } |
| |
| REQUIRE(ys.description(pathToSchemaString(path, Prefixes::WhenNeeded)) == expected); |
| } |
| |
| SECTION("status") |
| { |
| REQUIRE(ys.status("/example-schema:leafUint64") == yang::Status::Current); |
| REQUIRE(ys.status("/example-schema:obsoleteLeaf") == yang::Status::Obsolete); |
| REQUIRE(ys.status("/example-schema:deprecatedLeaf") == yang::Status::Deprecated); |
| REQUIRE(ys.status("/example-schema:obsoleteLeafWithDeprecatedType") == yang::Status::Obsolete); |
| REQUIRE(ys.status("/example-schema:obsoleteLeafWithObsoleteType") == yang::Status::Obsolete); |
| } |
| |
| SECTION("units") |
| { |
| yang::LeafDataType expectedType; |
| std::optional<std::string> expectedUnits; |
| SECTION("length") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("length")); |
| expectedType.emplace<yang::Int32>(); |
| expectedUnits = "m"; |
| } |
| |
| SECTION("wavelength") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("wavelength")); |
| expectedType.emplace<yang::Decimal>(); |
| expectedUnits = "nm"; |
| } |
| |
| SECTION("leafInt32") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("leafInt32")); |
| expectedType.emplace<yang::Int32>(); |
| } |
| |
| SECTION("duration") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("duration")); |
| expectedType.emplace<yang::Int32>(); |
| expectedUnits = "s"; |
| } |
| |
| SECTION("another-duration") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("another-duration")); |
| expectedType.emplace<yang::Int32>(); |
| expectedUnits = "vt"; |
| } |
| auto nodeType = ys.nodeType(pathToSchemaString(path, Prefixes::WhenNeeded)); |
| REQUIRE((nodeType == yang::NodeTypes::Leaf || nodeType == yang::NodeTypes::LeafList)); |
| REQUIRE(ys.leafType(pathToSchemaString(path, Prefixes::WhenNeeded)) == yang::TypeInfo{expectedType, expectedUnits}); |
| } |
| |
| SECTION("type description") |
| { |
| yang::LeafDataType expectedType = createEnum({"lol", "data"}); |
| std::optional<std::string> expectedDescription; |
| |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("leafEnumTypedefRestricted2")); |
| expectedDescription = "This is a restricted enum typedef."; |
| REQUIRE(ys.leafType(pathToSchemaString(path, Prefixes::WhenNeeded)) == yang::TypeInfo{expectedType, std::nullopt, expectedDescription}); |
| } |
| |
| SECTION("nodeType") |
| { |
| yang::NodeTypes expected; |
| SECTION("leafInt32") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, leaf_("leafInt32")); |
| expected = yang::NodeTypes::Leaf; |
| } |
| |
| SECTION("a") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("a")); |
| expected = yang::NodeTypes::Container; |
| } |
| |
| SECTION("a/a2/a3") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("a")); |
| path.m_nodes.emplace_back(container_("a2")); |
| path.m_nodes.emplace_back(container_("a3")); |
| expected = yang::NodeTypes::PresenceContainer; |
| } |
| |
| SECTION("_list") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, list_("_list")); |
| expected = yang::NodeTypes::List; |
| } |
| |
| REQUIRE(ys.nodeType(pathToSchemaString(path, Prefixes::WhenNeeded)) == expected); |
| } |
| |
| SECTION("leafrefPath") |
| { |
| REQUIRE(ys.leafrefPath("/example-schema:activeNumber") == "/example-schema:_list/number"); |
| } |
| |
| SECTION("isConfig") |
| { |
| REQUIRE(ys.isConfig("/example-schema:leafInt32")); |
| REQUIRE_FALSE(ys.isConfig("/example-schema:clockSpeed")); |
| REQUIRE_FALSE(ys.isConfig("/example-schema:systemStats")); |
| REQUIRE_FALSE(ys.isConfig("/example-schema:systemStats/upTime")); |
| } |
| |
| SECTION("defaultValue") |
| { |
| REQUIRE(ys.defaultValue("/example-schema:leafUint64") == "9001"); |
| REQUIRE(ys.defaultValue("/example-schema:leafEnumTypedefRestricted") == "data"); |
| REQUIRE(ys.defaultValue("/example-schema:leafInt32") == std::nullopt); |
| } |
| |
| SECTION("leafTypeName") |
| { |
| REQUIRE(ys.leafTypeName("/example-schema:leafEnumTypedefRestricted") == "enumTypedef"); |
| REQUIRE(ys.leafTypeName("/example-schema:leafInt32") == std::nullopt); |
| } |
| |
| SECTION("dataPathToSchemaPath") |
| { |
| REQUIRE(ys.dataPathToSchemaPath("/example-schema:portSettings[port='eth0']") == "/example-schema:portSettings"); |
| REQUIRE(ys.dataPathToSchemaPath("/example-schema:portSettings[port='eth0']/shutdown") == "/example-schema:portSettings/shutdown"); |
| } |
| |
| SECTION("has input nodes") |
| { |
| bool expected; |
| SECTION("example-schema:myRpc") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, rpcNode_{"myRpc"}); |
| expected = false; |
| } |
| |
| SECTION("example-schema:rpcOneInput") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, rpcNode_{"rpcOneInput"}); |
| expected = true; |
| } |
| |
| SECTION("example-schema:rpcOneOutput") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, rpcNode_{"rpcOneOutput"}); |
| expected = false; |
| } |
| |
| REQUIRE(ys.hasInputNodes(pathToSchemaString(path, Prefixes::WhenNeeded)) == expected); |
| } |
| } |
| |
| SECTION("negative") |
| { |
| SECTION("nonexistent nodes") |
| { |
| SECTION("example-schema:coze") |
| { |
| node.first = "example-schema"; |
| node.second = "coze"; |
| } |
| |
| SECTION("example-schema:a/nevim") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("a")); |
| node.second = "nevim"; |
| } |
| |
| SECTION("modul:a/nevim") |
| { |
| path.m_nodes.emplace_back(module_{"modul"}, container_("a")); |
| node.second = "nevim"; |
| } |
| |
| REQUIRE_THROWS(ys.nodeType(path, node)); |
| } |
| |
| SECTION("nodetype-specific methods called with different nodetypes") |
| { |
| path.m_nodes.emplace_back(module_{"example-schema"}, container_("a")); |
| path.m_nodes.emplace_back(container_("a2")); |
| |
| REQUIRE(!ys.listHasKey(path, "chacha")); |
| } |
| |
| SECTION("nonexistent module") |
| { |
| REQUIRE(!ys.isModule("notAModule")); |
| } |
| |
| SECTION("grouping is not a node") |
| { |
| SECTION("example-schema:arithmeticFlags") |
| { |
| node.first = "example-schema"; |
| node.second = "arithmeticFlags"; |
| } |
| |
| SECTION("example-schema:flags") |
| { |
| node.first = "example-schema"; |
| node.second = "startAndStop"; |
| } |
| |
| REQUIRE_THROWS(ys.nodeType(path, node)); |
| } |
| |
| SECTION("choice is not a node") |
| { |
| SECTION("example-schema:interface") |
| { |
| node.first = "example-schema"; |
| node.second = "interface"; |
| } |
| |
| REQUIRE_THROWS(ys.nodeType(path, node)); |
| } |
| |
| SECTION("case is not a node") |
| { |
| SECTION("example-schema:caseLoopback") |
| { |
| node.first = "example-schema"; |
| node.second = "caseLoopback"; |
| } |
| |
| SECTION("example-schema:caseEthernet") |
| { |
| node.first = "example-schema"; |
| node.second = "caseEthernet"; |
| } |
| |
| REQUIRE_THROWS(ys.nodeType(path, node)); |
| } |
| |
| SECTION("enableFeature - non existing module") |
| { |
| REQUIRE_THROWS_AS(ys.enableFeature("non-existing", "just-no"), std::runtime_error); |
| } |
| |
| SECTION("enableFeature - non existing feature") |
| { |
| REQUIRE_THROWS_AS(ys.enableFeature("example-schema", "just-no"), std::runtime_error); |
| } |
| } |
| } |