config_parser: Split up sections

We can't always combine sections together in network files as sections
like

[Address]
Address=::1/128
Peer=fe80::1
[Address]
Address=::2/128
Peer=fe80::2

Require that they are grouped accordingly. Rewrite the storage logic of
the config parser to support this logical organization.

Change-Id: I34ae1523202f8770fe3dcac010fb6226dd28b9ec
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/test/test_config_parser.cpp b/test/test_config_parser.cpp
index 896d61e..909ad7f 100644
--- a/test/test_config_parser.cpp
+++ b/test/test_config_parser.cpp
@@ -65,12 +65,31 @@
                    << "Key=val\nAddress=::/0\n[]\n=\nKey";
         filestream.close();
     }
+
+    void ValidateSectionMap()
+    {
+        EXPECT_THAT(
+            parser.getMap(),
+            testing::ContainerEq(SectionMap(SectionMapInt{
+                {"Match", {{{"Name", {"eth0"}}}}},
+                {"Network",
+                 {
+                     {{"DHCP", {"true"}}},
+                     {{"DHCP", {"false #hi", "yes"}}},
+                     {{"Key", {"val"}}, {"Address", {"::/0"}}},
+                 }},
+                {"DHCP", {{{"ClientIdentifier", {"mac"}}}}},
+                {" SEC ", {{{"'DHCP#'", {"\"#hi\""}}, {"DHCP#", {"ho"}}}}},
+                {"", {{{"", {""}}}}},
+            })));
+    }
 };
 
 TEST_F(TestConfigParser, EmptyObject)
 {
     EXPECT_TRUE(parser.getFilename().empty());
     EXPECT_EQ(0, parser.getWarnings().size());
+    EXPECT_EQ(SectionMap(), parser.getMap());
 }
 
 TEST_F(TestConfigParser, ReadDirectory)
@@ -78,6 +97,7 @@
     parser.setFile("/");
     EXPECT_EQ("/", parser.getFilename());
     EXPECT_EQ(1, parser.getWarnings().size());
+    EXPECT_EQ(SectionMap(), parser.getMap());
 }
 
 TEST_F(TestConfigParser, ReadConfigDataMissingFile)
@@ -85,6 +105,7 @@
     parser.setFile("/no-such-path");
     EXPECT_EQ("/no-such-path", parser.getFilename());
     EXPECT_EQ(1, parser.getWarnings().size());
+    EXPECT_EQ(SectionMap(), parser.getMap());
 }
 
 TEST_F(TestConfigParser, ReadConfigDataFromFile)
@@ -93,15 +114,25 @@
     parser.setFile(filename);
     EXPECT_EQ(filename, parser.getFilename());
     EXPECT_EQ(4, parser.getWarnings().size());
+    ValidateSectionMap();
 
-    EXPECT_THAT(parser.getValues("Match", "Name"), ElementsAre("eth0"));
-    EXPECT_THAT(parser.getValues("DHCP", "ClientIdentifier"),
+    const auto& map = parser.getMap();
+
+    EXPECT_EQ("eth0", *map.getLastValueString("Match", "Name"));
+    EXPECT_EQ("yes", *map.getLastValueString("Network", "DHCP"));
+    EXPECT_EQ(nullptr, map.getLastValueString("Match", "BadKey"));
+    EXPECT_EQ(nullptr, map.getLastValueString("BadSec", "Name"));
+    EXPECT_EQ(nullptr, map.getLastValueString("BadSec", "Name"));
+
+    EXPECT_THAT(map.getValueStrings("Match", "Name"), ElementsAre("eth0"));
+    EXPECT_THAT(map.getValueStrings("DHCP", "ClientIdentifier"),
                 ElementsAre("mac"));
-    EXPECT_THAT(parser.getValues("Network", "DHCP"),
+    EXPECT_THAT(map.getValueStrings("Network", "DHCP"),
                 ElementsAre("true", "false #hi", "yes"));
-    EXPECT_THAT(parser.getValues(" SEC ", "'DHCP#'"), ElementsAre("\"#hi\""));
-    EXPECT_THAT(parser.getValues("Blah", "nil"), ElementsAre());
-    EXPECT_THAT(parser.getValues("Network", "nil"), ElementsAre());
+    EXPECT_THAT(map.getValueStrings(" SEC ", "'DHCP#'"),
+                ElementsAre("\"#hi\""));
+    EXPECT_THAT(map.getValueStrings("Blah", "nil"), ElementsAre());
+    EXPECT_THAT(map.getValueStrings("Network", "nil"), ElementsAre());
 }
 
 TEST_F(TestConfigParser, Perf)
diff --git a/test/test_ethernet_interface.cpp b/test/test_ethernet_interface.cpp
index e9ed3fd..e340d7b 100644
--- a/test/test_ethernet_interface.cpp
+++ b/test/test_ethernet_interface.cpp
@@ -165,7 +165,7 @@
     fs::path filePath = confDir;
     filePath /= "00-bmc-test0.network";
     config::Parser parser(filePath.string());
-    EXPECT_EQ(servers, parser.getValues("Network", "DNS"));
+    EXPECT_EQ(servers, parser.getMap().getValueStrings("Network", "DNS"));
 }
 
 TEST_F(TestEthernetInterface, getDynamicNameServers)
@@ -184,7 +184,7 @@
     fs::path filePath = confDir;
     filePath /= "00-bmc-test0.network";
     config::Parser parser(filePath.string());
-    EXPECT_EQ(servers, parser.getValues("Network", "NTP"));
+    EXPECT_EQ(servers, parser.getMap().getValueStrings("Network", "NTP"));
 }
 
 TEST_F(TestEthernetInterface, addGateway)
diff --git a/test/test_vlan_interface.cpp b/test/test_vlan_interface.cpp
index 5ee1cf9..728199a 100644
--- a/test/test_vlan_interface.cpp
+++ b/test/test_vlan_interface.cpp
@@ -102,27 +102,25 @@
     fs::path filePath = confDir;
     filePath /= "test0.50.netdev";
 
-    config::Parser parser(filePath.string());
-
-    EXPECT_EQ(parser.getValues("NetDev", "Name"),
-              (config::ValueList{"test0.50"}));
-    EXPECT_EQ(parser.getValues("NetDev", "Kind"), (config::ValueList{"vlan"}));
-    EXPECT_EQ(parser.getValues("VLAN", "Id"), (config::ValueList{"50"}));
+    config::Parser parser(filePath);
+    EXPECT_EQ(parser.getMap(),
+              config::SectionMap(config::SectionMapInt{
+                  {"NetDev",
+                   {
+                       {{"Name", {"test0.50"}}, {"Kind", {"vlan"}}},
+                   }},
+                  {"VLAN", {{{"Id", {"50"}}}}},
+              }));
 }
 
 TEST_F(TestVlanInterface, deleteVLAN)
 {
     createVlan(50);
     deleteVlan("test0.50");
-    bool fileFound = false;
 
     fs::path filePath = confDir;
     filePath /= "test0.50.netdev";
-    if (fs::is_regular_file(filePath.string()))
-    {
-        fileFound = true;
-    }
-    EXPECT_EQ(fileFound, false);
+    EXPECT_FALSE(fs::is_regular_file(filePath));
 }
 
 TEST_F(TestVlanInterface, createMultipleVLAN)
@@ -132,19 +130,27 @@
 
     fs::path filePath = confDir;
     filePath /= "test0.50.netdev";
-    config::Parser parser(filePath.string());
-    EXPECT_EQ(parser.getValues("NetDev", "Name"),
-              (config::ValueList{"test0.50"}));
-    EXPECT_EQ(parser.getValues("NetDev", "Kind"), (config::ValueList{"vlan"}));
-    EXPECT_EQ(parser.getValues("VLAN", "Id"), (config::ValueList{"50"}));
+    config::Parser parser(filePath);
+    EXPECT_EQ(parser.getMap(),
+              config::SectionMap(config::SectionMapInt{
+                  {"NetDev",
+                   {
+                       {{"Name", {"test0.50"}}, {"Kind", {"vlan"}}},
+                   }},
+                  {"VLAN", {{{"Id", {"50"}}}}},
+              }));
 
     filePath = confDir;
     filePath /= "test0.60.netdev";
-    parser.setFile(filePath.string());
-    EXPECT_EQ(parser.getValues("NetDev", "Name"),
-              (config::ValueList{"test0.60"}));
-    EXPECT_EQ(parser.getValues("NetDev", "Kind"), (config::ValueList{"vlan"}));
-    EXPECT_EQ(parser.getValues("VLAN", "Id"), (config::ValueList{"60"}));
+    parser.setFile(filePath);
+    EXPECT_EQ(parser.getMap(),
+              config::SectionMap(config::SectionMapInt{
+                  {"NetDev",
+                   {
+                       {{"Name", {"test0.60"}}, {"Kind", {"vlan"}}},
+                   }},
+                  {"VLAN", {{{"Id", {"60"}}}}},
+              }));
 
     deleteVlan("test0.50");
     deleteVlan("test0.60");