Fix identityref bug when it's a typedef

When identityref was a typedef, YangSchema::validIdentities was not
working at all.

Change-Id: Ib94db4ac0d61a0eb716ae0c1e022fade3e52ef86
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 0ace2f7..6507f08 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -144,20 +144,61 @@
 }
 
 namespace {
-std::set<enum_> enumValues(const libyang::S_Type& typeArg)
+enum class ResolveMode {
+    Enum,
+    Identity
+};
+/** @brief Resolves a typedef to a type which defines values.
+ * When we need allowed values of a type and that type is a typedef, we need to recurse into the typedef until we find a
+ * type which defines values. These values are the allowed values.
+ * Example:
+ *
+ * typedef MyOtherEnum {
+ *   type enumeration {
+ *     enum "A";
+ *     enum "B";
+ *   }
+ * }
+ *
+ * typedef MyEnum {
+ *   type MyOtherEnum;
+ * }
+ *
+ * If `toResolve` points to MyEnum, then just doing ->enums()->enm() returns nothing and that means that this particular
+ * typedef (MyEnum) did not say which values are allowed. So, we need to dive into the parent enum (MyOtherEnum) with
+ * ->der()->type(). This typedef (MyOtherEnum) DID specify allowed values and enums()->enm() WILL contain them. These
+ *  values are the only relevant values and we don't care about other parent typedefs. We return these values to the
+ *  caller.
+ *
+ *  For enums, this function simply returns all allowed enums.
+ *  For identities, this function returns which bases `toResolve` has.
+ */
+template <ResolveMode TYPE>
+auto resolveTypedef(const libyang::S_Type& toResolve)
 {
-    auto type = typeArg;
-    auto enm = type->info()->enums()->enm();
-    // The enum can be a derived type and enm() only returns values,
-    // if that specific typedef changed the possible values. So we go
-    // up the hierarchy until we find a typedef that defined these values.
-    while (enm.empty()) {
+    auto type = toResolve;
+    auto getValuesFromType = [] (const libyang::S_Type& type) {
+        if constexpr (TYPE == ResolveMode::Identity) {
+            return type->info()->ident()->ref();
+        } else {
+            return type->info()->enums()->enm();
+        }
+    };
+    auto values = getValuesFromType(type);
+    while (values.empty()) {
         type = type->der()->type();
-        enm = type->info()->enums()->enm();
+        values = getValuesFromType(type);
     }
 
+    return values;
+}
+
+std::set<enum_> enumValues(const libyang::S_Type& type)
+{
+    auto values = resolveTypedef<ResolveMode::Enum>(type);
+
     std::vector<libyang::S_Type_Enum> enabled;
-    std::copy_if(enm.begin(), enm.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
+    std::copy_if(values.begin(), values.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
         auto iffeatures = it->iffeature();
         return std::all_of(iffeatures.begin(), iffeatures.end(), [](auto it) { return it->value(); });
     });
@@ -171,10 +212,7 @@
 {
     std::set<identityRef_> identSet;
 
-    // auto topLevelModule = leaf->module();
-
-    auto info = type->info();
-    for (auto base : info->ident()->ref()) { // Iterate over all bases
+    for (auto base : resolveTypedef<ResolveMode::Identity>(type)) { // Iterate over all bases
         identSet.emplace(base->module()->name(), base->name());
         // Iterate over derived identities (this is recursive!)
         for (auto derived : base->der()->schema()) {
diff --git a/tests/yang.cpp b/tests/yang.cpp
index 1720d79..37b473d 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -81,6 +81,16 @@
         base "pizza";
     }
 
+    typedef foodTypedef {
+        type identityref {
+            base food;
+        }
+    }
+
+    leaf leafFoodTypedef {
+        type foodTypedef;
+    }
+
     container a {
         container a2 {
             container a3 {
@@ -754,6 +764,19 @@
                 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));
         }
@@ -801,7 +824,8 @@
                         {"example-schema"s, "dummyLeaf"},
                         {"example-schema"s, "addresses"},
                         {"example-schema"s, "subLeaf"},
-                        {"example-schema"s, "flagBits"}};
+                        {"example-schema"s, "flagBits"},
+                        {"example-schema"s, "leafFoodTypedef"}};
                 }
 
                 SECTION("example-schema:a")
@@ -864,6 +888,7 @@
                         {"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"},
@@ -930,6 +955,7 @@
                         {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"},