1 #include "config_parser.hpp" 2 3 #include <fmt/chrono.h> 4 #include <fmt/compile.h> 5 #include <fmt/format.h> 6 7 #include <exception> 8 #include <fstream> 9 #include <phosphor-logging/elog-errors.hpp> 10 #include <stdexcept> 11 #include <stdplus/fd/atomic.hpp> 12 #include <stdplus/fd/fmt.hpp> 13 #include <stdplus/gtest/tmp.hpp> 14 #include <xyz/openbmc_project/Common/error.hpp> 15 16 #include <gmock/gmock.h> 17 #include <gtest/gtest.h> 18 19 namespace phosphor 20 { 21 namespace network 22 { 23 namespace config 24 { 25 26 using testing::ElementsAre; 27 28 TEST(TestConvert, iCaseEq) 29 { 30 EXPECT_TRUE(icaseeq("VaL", "val")); 31 EXPECT_TRUE(icaseeq("[ab1", "[ab1")); 32 } 33 34 TEST(TestConvert, ParseBool) 35 { 36 EXPECT_TRUE(parseBool("tRue").value()); 37 EXPECT_FALSE(parseBool("tru").has_value()); 38 EXPECT_TRUE(parseBool("t").value()); 39 EXPECT_TRUE(parseBool("Yes").value()); 40 EXPECT_FALSE(parseBool("ye").has_value()); 41 EXPECT_TRUE(parseBool("y").value()); 42 EXPECT_TRUE(parseBool("oN").value()); 43 44 EXPECT_FALSE(parseBool("fAlse").value()); 45 EXPECT_FALSE(parseBool("fal").has_value()); 46 EXPECT_FALSE(parseBool("f").value()); 47 EXPECT_FALSE(parseBool("No").value()); 48 EXPECT_FALSE(parseBool("n").value()); 49 EXPECT_FALSE(parseBool("oFf").value()); 50 } 51 52 TEST(TestTypeChecking, Section) 53 { 54 Section(""); 55 Section("fds#1!'\""); 56 EXPECT_THROW(Section("fds]sf"), std::invalid_argument); 57 EXPECT_THROW(Section("g\ng"), std::invalid_argument); 58 } 59 60 TEST(TestTypeChecking, Value) 61 { 62 Value(""); 63 Value("=fds1!'\"#="); 64 Value("fds]sf'' #"); 65 EXPECT_THROW(Value("g\ng"), std::invalid_argument); 66 } 67 68 TEST(TestTypeChecking, Key) 69 { 70 Key(""); 71 Key("fds1!'\"#"); 72 Key("fds]sf'' #"); 73 EXPECT_THROW(Key("fds]sf'='"), std::invalid_argument); 74 EXPECT_THROW(Key("g\ng"), std::invalid_argument); 75 } 76 77 class TestConfigParser : public stdplus::gtest::TestWithTmp 78 { 79 public: 80 std::string filename = fmt::format("{}/eth0.network", CaseTmpDir()); 81 Parser parser; 82 83 void WriteSampleFile() 84 { 85 std::ofstream filestream(filename); 86 filestream << "\n\n\n\nBad=key\n[Match]\n # K=v \nName =eth0\n" 87 << "[Network\nDHCP=true\n[DHCP]\nClientIdentifier= mac\n" 88 << "[Network] a\nDHCP=false #hi\n\n\nDHCP = yes \n" 89 << " [ SEC ] \n'DHCP#'=\"#hi\"\nDHCP#=ho\n[Network]\n" 90 << "Key=val\nAddress=::/0\n[]\n=\nKey"; 91 filestream.close(); 92 } 93 94 void ValidateSectionMap() 95 { 96 EXPECT_THAT( 97 parser.map, 98 testing::ContainerEq(SectionMap(SectionMapInt{ 99 {"Match", {{{"Name", {"eth0"}}}}}, 100 {"Network", 101 { 102 {{"DHCP", {"true"}}}, 103 {{"DHCP", {"false #hi", "yes"}}}, 104 {{"Key", {"val"}}, {"Address", {"::/0"}}}, 105 }}, 106 {"DHCP", {{{"ClientIdentifier", {"mac"}}}}}, 107 {" SEC ", {{{"'DHCP#'", {"\"#hi\""}}, {"DHCP#", {"ho"}}}}}, 108 {"", {{{"", {""}}}}}, 109 }))); 110 } 111 }; 112 113 TEST_F(TestConfigParser, EmptyObject) 114 { 115 EXPECT_TRUE(parser.getFilename().empty()); 116 EXPECT_EQ(0, parser.getWarnings().size()); 117 EXPECT_EQ(SectionMap(), parser.map); 118 } 119 120 TEST_F(TestConfigParser, ReadDirectory) 121 { 122 parser.setFile("/"); 123 EXPECT_EQ("/", parser.getFilename()); 124 EXPECT_EQ(1, parser.getWarnings().size()); 125 EXPECT_EQ(SectionMap(), parser.map); 126 } 127 128 TEST_F(TestConfigParser, ReadConfigDataMissingFile) 129 { 130 parser.setFile("/no-such-path"); 131 EXPECT_EQ("/no-such-path", parser.getFilename()); 132 EXPECT_EQ(1, parser.getWarnings().size()); 133 EXPECT_EQ(SectionMap(), parser.map); 134 } 135 136 TEST_F(TestConfigParser, ReadConfigDataFromFile) 137 { 138 WriteSampleFile(); 139 parser.setFile(filename); 140 EXPECT_EQ(filename, parser.getFilename()); 141 EXPECT_EQ(4, parser.getWarnings().size()); 142 ValidateSectionMap(); 143 144 const auto& map = parser.map; 145 146 EXPECT_EQ("eth0", *map.getLastValueString("Match", "Name")); 147 EXPECT_EQ("yes", *map.getLastValueString("Network", "DHCP")); 148 EXPECT_EQ(nullptr, map.getLastValueString("Match", "BadKey")); 149 EXPECT_EQ(nullptr, map.getLastValueString("BadSec", "Name")); 150 EXPECT_EQ(nullptr, map.getLastValueString("BadSec", "Name")); 151 152 EXPECT_THAT(map.getValueStrings("Match", "Name"), ElementsAre("eth0")); 153 EXPECT_THAT(map.getValueStrings("DHCP", "ClientIdentifier"), 154 ElementsAre("mac")); 155 EXPECT_THAT(map.getValueStrings("Network", "DHCP"), 156 ElementsAre("true", "false #hi", "yes")); 157 EXPECT_THAT(map.getValueStrings(" SEC ", "'DHCP#'"), 158 ElementsAre("\"#hi\"")); 159 EXPECT_THAT(map.getValueStrings("Blah", "nil"), ElementsAre()); 160 EXPECT_THAT(map.getValueStrings("Network", "nil"), ElementsAre()); 161 } 162 163 TEST_F(TestConfigParser, WriteConfigFile) 164 { 165 WriteSampleFile(); 166 parser.setFile(filename); 167 EXPECT_EQ(4, parser.getWarnings().size()); 168 ValidateSectionMap(); 169 170 parser.writeFile(); 171 172 parser.setFile(filename); 173 EXPECT_EQ(0, parser.getWarnings().size()); 174 ValidateSectionMap(); 175 } 176 177 TEST_F(TestConfigParser, Perf) 178 { 179 GTEST_SKIP(); 180 stdplus::fd::AtomicWriter file(fmt::format("{}/tmp.XXXXXX", CaseTmpDir()), 181 0600); 182 stdplus::fd::FormatBuffer out(file); 183 for (size_t i = 0; i < 500; ++i) 184 { 185 out.append(FMT_COMPILE("[{:a>{}}]\n"), "", i + 1); 186 for (size_t j = 0; j < 70; j++) 187 { 188 const size_t es = i * 70 + j + 1; 189 out.append(FMT_COMPILE("{:b>{}}={:c>{}}\n"), "", es, "", es); 190 } 191 } 192 out.flush(); 193 file.commit(); 194 195 auto start = std::chrono::steady_clock::now(); 196 parser.setFile(filename); 197 fmt::print("Duration: {}\n", std::chrono::steady_clock::now() - start); 198 // Make sure this test isn't enabled 199 EXPECT_FALSE(true); 200 } 201 202 } // namespace config 203 } // namespace network 204 } // namespace phosphor 205