hyperrealm / libconfig

C/C++ library for processing configuration files
https://hyperrealm.github.io/libconfig/
GNU Lesser General Public License v2.1
1.11k stars 362 forks source link

Setting::exists returns incorrect value #229

Closed invenibo closed 9 months ago

invenibo commented 1 year ago

In certain cases, Setting::exists() returns false even if the path exists.

This test (gtest) can be used to reproduce the issue:

TEST(Test, WhenPathExists_ThenLibconfigExistsReturnsTrue)
{
    std::string configContent = "Group = {\n"
                                "   Subgroup = {\n"
                                "       NotInterestedInThisGroup = {\n"
                                "           SettingA = 1;\n"
                                "           SettingB = 2;\n"
                                "       };\n"
                                "   };\n"
                                "};";

    libconfig::Config config{};
    config.readString(configContent);
    const auto& root = config.getRoot();

    EXPECT_TRUE(root.exists("Group"));
    EXPECT_TRUE(root.exists("Group.Subgroup")); // <-- actually returns false
    EXPECT_TRUE(root.exists("Group.Subgroup.NotInterestedInThisGroup")); // <-- actually returns false

    // returned true before it was fixed in this issue https://github.com/hyperrealm/libconfig/issues/220
    EXPECT_FALSE(root.exists("Group.Subgroup.DoesNotExist"));

    // When looking up the value, it throws an exception as expected.
    EXPECT_THROW(root.lookup("Group.Subgroup.DoesNotExist"), libconfig::SettingNotFoundException);
    EXPECT_NO_THROW(root.lookup("Group.Subgroup"));
    EXPECT_NO_THROW(root.lookup("Group.Subgroup.NotInterestedInThisGroup"));
}

Using the latest commit 4f13b7f this results in the following output:

Value of: root.exists("Group.Subgroup")
  Actual: false
Expected: true
Value of: root.exists("Group.Subgroup.NotInterestedInThisGroup")
  Actual: false
Expected: true
hyperrealm commented 9 months ago

This is actually not a bug; it behaves as documented in the manual, which is that Setting::exists() takes a name and checks if there is a direct child setting with that name. It does not search paths the way Config::exists() does.