1 #include "config_parser.hpp"
2
3 #include <phosphor-logging/elog-errors.hpp>
4 #include <stdplus/fd/atomic.hpp>
5 #include <stdplus/fd/fmt.hpp>
6 #include <stdplus/gtest/tmp.hpp>
7 #include <stdplus/print.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9
10 #include <exception>
11 #include <format>
12 #include <fstream>
13 #include <stdexcept>
14
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17
18 namespace phosphor
19 {
20 namespace network
21 {
22 namespace config
23 {
24
25 using testing::ElementsAre;
26 using std::literals::string_view_literals::operator""sv;
27
TEST(TestConvert,iCaseEq)28 TEST(TestConvert, iCaseEq)
29 {
30 EXPECT_TRUE(icaseeq("VaL", "val"));
31 EXPECT_TRUE(icaseeq("[ab1", "[ab1"));
32 }
33
TEST(TestConvert,ParseBool)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
TEST(TestTypeChecking,Section)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
TEST(TestTypeChecking,Value)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
TEST(TestTypeChecking,Key)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 = std::format("{}/eth0.network", CaseTmpDir());
81 Parser parser;
82
WriteSampleFile()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
ValidateSectionMap()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
TEST_F(TestConfigParser,EmptyObject)113 TEST_F(TestConfigParser, EmptyObject)
114 {
115 EXPECT_FALSE(parser.getFileExists());
116 EXPECT_TRUE(parser.getFilename().empty());
117 EXPECT_EQ(0, parser.getWarnings().size());
118 EXPECT_EQ(SectionMap(), parser.map);
119 }
120
TEST_F(TestConfigParser,ReadDirectory)121 TEST_F(TestConfigParser, ReadDirectory)
122 {
123 parser.setFile("/");
124 EXPECT_FALSE(parser.getFileExists());
125 EXPECT_EQ("/", parser.getFilename());
126 EXPECT_EQ(1, parser.getWarnings().size());
127 EXPECT_EQ(SectionMap(), parser.map);
128 }
129
TEST_F(TestConfigParser,ReadConfigDataMissingFile)130 TEST_F(TestConfigParser, ReadConfigDataMissingFile)
131 {
132 parser.setFile("/no-such-path");
133 EXPECT_FALSE(parser.getFileExists());
134 EXPECT_EQ("/no-such-path", parser.getFilename());
135 EXPECT_EQ(1, parser.getWarnings().size());
136 EXPECT_EQ(SectionMap(), parser.map);
137 }
138
TEST_F(TestConfigParser,ReadConfigDataFromFile)139 TEST_F(TestConfigParser, ReadConfigDataFromFile)
140 {
141 WriteSampleFile();
142 parser.setFile(filename);
143 EXPECT_TRUE(parser.getFileExists());
144 EXPECT_EQ(filename, parser.getFilename());
145 EXPECT_EQ(4, parser.getWarnings().size());
146 ValidateSectionMap();
147
148 const auto& map = parser.map;
149
150 EXPECT_EQ("eth0", *map.getLastValueString("Match", "Name"));
151 EXPECT_EQ("yes", *map.getLastValueString("Network", "DHCP"));
152 EXPECT_EQ(nullptr, map.getLastValueString("Match", "BadKey"));
153 EXPECT_EQ(nullptr, map.getLastValueString("BadSec", "Name"));
154 EXPECT_EQ(nullptr, map.getLastValueString("BadSec", "Name"));
155
156 EXPECT_THAT(map.getValueStrings("Match", "Name"), ElementsAre("eth0"));
157 EXPECT_THAT(map.getValueStrings("DHCP", "ClientIdentifier"),
158 ElementsAre("mac"));
159 EXPECT_THAT(map.getValueStrings("Network", "DHCP"),
160 ElementsAre("true", "false #hi", "yes"));
161 EXPECT_THAT(map.getValueStrings(" SEC ", "'DHCP#'"),
162 ElementsAre("\"#hi\""));
163 EXPECT_THAT(map.getValueStrings("Blah", "nil"), ElementsAre());
164 EXPECT_THAT(map.getValueStrings("Network", "nil"), ElementsAre());
165 }
166
TEST_F(TestConfigParser,WriteConfigFile)167 TEST_F(TestConfigParser, WriteConfigFile)
168 {
169 WriteSampleFile();
170 parser.setFile(filename);
171 EXPECT_EQ(4, parser.getWarnings().size());
172 ValidateSectionMap();
173
174 parser.writeFile();
175
176 parser.setFile(filename);
177 EXPECT_EQ(0, parser.getWarnings().size());
178 ValidateSectionMap();
179 }
180
TEST_F(TestConfigParser,Perf)181 TEST_F(TestConfigParser, Perf)
182 {
183 GTEST_SKIP();
184 stdplus::fd::AtomicWriter file(std::format("{}/tmp.XXXXXX", CaseTmpDir()),
185 0600);
186 stdplus::fd::FormatBuffer out(file);
187 std::string obj(500, 'a');
188 std::string kv(500 * 70, 'b');
189 for (size_t i = 0; i < 500; ++i)
190 {
191 out.appends("["sv, std::string_view{obj}.substr(0, i + 1), "[\n"sv);
192 for (size_t j = 0; j < 70; j++)
193 {
194 auto sv = std::string_view(kv).substr(0, i * 70 + j + 1);
195 out.appends(sv, "="sv, sv, "\n"sv);
196 }
197 }
198 out.flush();
199 file.commit();
200
201 auto start = std::chrono::steady_clock::now();
202 parser.setFile(filename);
203 stdplus::print("Duration: {}\n", std::chrono::steady_clock::now() - start);
204 // Make sure this test isn't enabled
205 EXPECT_FALSE(true);
206 }
207
208 } // namespace config
209 } // namespace network
210 } // namespace phosphor
211