shill: Add persistent store support for getting/setting string lists.

BUG=chromium-os:17144
TEST=unit tests

Change-Id: Ib85935f3e53b606757aa50786d94975b1fe5109c
Reviewed-on: http://gerrit.chromium.org/gerrit/3486
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
diff --git a/crypto_provider_unittest.cc b/crypto_provider_unittest.cc
index 316b94f..b84162a 100644
--- a/crypto_provider_unittest.cc
+++ b/crypto_provider_unittest.cc
@@ -49,20 +49,20 @@
 
   provider_.set_key_matter_file(FilePath("/some/non/existent/file"));
   provider_.Init();
-  EXPECT_EQ(1, provider_.cryptos_.size());
+  ASSERT_EQ(1, provider_.cryptos_.size());
   EXPECT_EQ(CryptoROT47::kID, provider_.cryptos_[0]->GetID());
 
   ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   provider_.set_key_matter_file(InitKeyMatterFile(temp_dir.path()));
   provider_.Init();
-  EXPECT_EQ(2, provider_.cryptos_.size());
+  ASSERT_EQ(2, provider_.cryptos_.size());
   EXPECT_EQ(CryptoDESCBC::kID, provider_.cryptos_[0]->GetID());
   EXPECT_EQ(CryptoROT47::kID, provider_.cryptos_[1]->GetID());
 
   provider_.set_key_matter_file(FilePath("/other/missing/file"));
   provider_.Init();
-  EXPECT_EQ(1, provider_.cryptos_.size());
+  ASSERT_EQ(1, provider_.cryptos_.size());
   EXPECT_EQ(CryptoROT47::kID, provider_.cryptos_[0]->GetID());
 }
 
diff --git a/glib.cc b/glib.cc
index 998aa43..95980c3 100644
--- a/glib.cc
+++ b/glib.cc
@@ -75,6 +75,14 @@
   return g_key_file_get_string(key_file, group_name, key, error);
 }
 
+gchar **GLib::KeyFileGetStringList(GKeyFile *key_file,
+                                   const gchar *group_name,
+                                   const gchar *key,
+                                   gsize *length,
+                                   GError **error) {
+  return g_key_file_get_string_list(key_file, group_name, key, length, error);
+}
+
 gboolean GLib::KeyFileHasGroup(GKeyFile *key_file,
                                const gchar *group_name) {
   return g_key_file_has_group(key_file, group_name);
@@ -118,6 +126,14 @@
   g_key_file_set_string(key_file, group_name, key, string);
 }
 
+void GLib::KeyFileSetStringList(GKeyFile *key_file,
+                                const gchar *group_name,
+                                const gchar *key,
+                                const gchar * const list[],
+                                gsize length) {
+  g_key_file_set_string_list(key_file, group_name, key, list, length);
+}
+
 gchar *GLib::KeyFileToData(GKeyFile *key_file,
                            gsize *length,
                            GError **error) {
diff --git a/glib.h b/glib.h
index e65eb7d..7016f0a 100644
--- a/glib.h
+++ b/glib.h
@@ -49,6 +49,12 @@
                                   const gchar *group_name,
                                   const gchar *key,
                                   GError **error);
+  // g_key_file_get_string_list
+  virtual gchar **KeyFileGetStringList(GKeyFile *key_file,
+                                       const gchar *group_name,
+                                       const gchar *key,
+                                       gsize *length,
+                                       GError **error);
   // g_key_file_has_group
   virtual gboolean KeyFileHasGroup(GKeyFile *key_file,
                                    const gchar *group_name);
@@ -83,6 +89,12 @@
                                 const gchar *group_name,
                                 const gchar *key,
                                 const gchar *string);
+  // g_key_file_set_string_list
+  virtual void KeyFileSetStringList(GKeyFile *key_file,
+                                    const gchar *group_name,
+                                    const gchar *key,
+                                    const gchar * const list[],
+                                    gsize length);
   // g_key_file_to_data
   virtual gchar *KeyFileToData(GKeyFile *key_file,
                                gsize *length,
diff --git a/key_file_store.cc b/key_file_store.cc
index cce5ea7..1fa52fb 100644
--- a/key_file_store.cc
+++ b/key_file_store.cc
@@ -9,6 +9,7 @@
 
 using std::set;
 using std::string;
+using std::vector;
 
 namespace shill {
 
@@ -196,6 +197,46 @@
   return true;
 }
 
+bool KeyFileStore::GetStringList(const string &group,
+                                 const string &key,
+                                 vector<string> *value) {
+  CHECK(key_file_);
+  gsize length = 0;
+  GError *error = NULL;
+  gchar **data = glib_->KeyFileGetStringList(key_file_,
+                                             group.c_str(),
+                                             key.c_str(),
+                                             &length,
+                                             &error);
+  if (!data) {
+    LOG(ERROR) << "Failed to lookup (" << group << ":" << key << "): "
+               << glib_->ConvertErrorToMessage(error);
+    return false;
+  }
+  if (value) {
+    value->assign(data, data + length);
+  }
+  glib_->Strfreev(data);
+  return true;
+}
+
+bool KeyFileStore::SetStringList(const string &group,
+                                 const string &key,
+                                 const vector<string> &value) {
+  CHECK(key_file_);
+  vector<const char *> list;
+  for (vector<string>::const_iterator it = value.begin();
+       it != value.end(); ++it) {
+    list.push_back(it->c_str());
+  }
+  glib_->KeyFileSetStringList(key_file_,
+                              group.c_str(),
+                              key.c_str(),
+                              list.data(),
+                              list.size());
+  return true;
+}
+
 bool KeyFileStore::GetCryptedString(const string &group,
                                     const string &key,
                                     string *value) {
diff --git a/key_file_store.h b/key_file_store.h
index 3e88de2..b448256 100644
--- a/key_file_store.h
+++ b/key_file_store.h
@@ -19,7 +19,7 @@
 // of the key file format.
 class KeyFileStore : public StoreInterface {
  public:
-  KeyFileStore(GLib *glib);
+  explicit KeyFileStore(GLib *glib);
   virtual ~KeyFileStore();
 
   void set_path(const FilePath &path) { path_ = path; }
@@ -50,13 +50,18 @@
   virtual bool SetInt(const std::string &group,
                       const std::string &key,
                       int value);
+  virtual bool GetStringList(const std::string &group,
+                             const std::string &key,
+                             std::vector<std::string> *value);
+  virtual bool SetStringList(const std::string &group,
+                             const std::string &key,
+                             const std::vector<std::string> &value);
   virtual bool GetCryptedString(const std::string &group,
                                 const std::string &key,
                                 std::string *value);
   virtual bool SetCryptedString(const std::string &group,
                                 const std::string &key,
                                 const std::string &value);
-
  private:
   FRIEND_TEST(KeyFileStoreTest, OpenClose);
   FRIEND_TEST(KeyFileStoreTest, OpenFail);
diff --git a/key_file_store_unittest.cc b/key_file_store_unittest.cc
index a206060..907ec06 100644
--- a/key_file_store_unittest.cc
+++ b/key_file_store_unittest.cc
@@ -12,6 +12,7 @@
 
 using std::set;
 using std::string;
+using std::vector;
 using testing::Test;
 
 namespace shill {
@@ -294,6 +295,121 @@
             ReadKeyFile());
 }
 
+TEST_F(KeyFileStoreTest, GetStringList) {
+  static const char kGroup[] = "string-lists";
+  static const char kKeyEmpty[] = "empty";
+  static const char kKeyEmptyValue[] = "empty-value";
+  static const char kKeyValueEmpty[] = "value-empty";
+  static const char kKeyValueEmptyValue[] = "value-empty-value";
+  static const char kKeyValues[] = "values";
+  static const char kValue[] = "value";
+  static const char kValue2[] = "value2";
+  static const char kValue3[] = "value3";
+  WriteKeyFile(base::StringPrintf("[%s]\n"
+                                  "%s=\n"
+                                  "%s=;%s\n"
+                                  "%s=%s;;\n"
+                                  "%s=%s;;%s\n"
+                                  "%s=%s;%s;%s\n",
+                                  kGroup,
+                                  kKeyEmpty,
+                                  kKeyEmptyValue, kValue,
+                                  kKeyValueEmpty, kValue,
+                                  kKeyValueEmptyValue, kValue, kValue2,
+                                  kKeyValues, kValue, kValue2, kValue3));
+  ASSERT_TRUE(store_.Open());
+
+  vector<string> value;
+
+  EXPECT_TRUE(store_.GetStringList(kGroup, kKeyValues, &value));
+  ASSERT_EQ(3, value.size());
+  EXPECT_EQ(kValue, value[0]);
+  EXPECT_EQ(kValue2, value[1]);
+  EXPECT_EQ(kValue3, value[2]);
+
+  EXPECT_TRUE(store_.GetStringList(kGroup, kKeyEmptyValue, &value));
+  ASSERT_EQ(2, value.size());
+  EXPECT_EQ("", value[0]);
+  EXPECT_EQ(kValue, value[1]);
+
+  EXPECT_TRUE(store_.GetStringList(kGroup, kKeyValueEmpty, &value));
+  ASSERT_EQ(2, value.size());
+  EXPECT_EQ(kValue, value[0]);
+  EXPECT_EQ("", value[1]);
+
+  EXPECT_TRUE(store_.GetStringList(kGroup, kKeyEmpty, &value));
+  ASSERT_EQ(0, value.size());
+
+  EXPECT_TRUE(store_.GetStringList(kGroup, kKeyValueEmptyValue, &value));
+  ASSERT_EQ(3, value.size());
+  EXPECT_EQ(kValue, value[0]);
+  EXPECT_EQ("", value[1]);
+  EXPECT_EQ(kValue2, value[2]);
+
+  EXPECT_FALSE(store_.GetStringList("unknown-string-lists", kKeyEmpty, &value));
+  EXPECT_FALSE(store_.GetStringList(kGroup, "some-key", &value));
+  EXPECT_TRUE(store_.GetStringList(kGroup, kKeyValues, NULL));
+  ASSERT_TRUE(store_.Close());
+}
+
+TEST_F(KeyFileStoreTest, SetStringList) {
+  static const char kGroup[] = "strings";
+  static const char kKeyEmpty[] = "e";
+  static const char kKeyEmptyValue[] = "ev";
+  static const char kKeyValueEmpty[] = "ve";
+  static const char kKeyValueEmptyValue[] = "vev";
+  static const char kKeyValues[] = "v";
+  static const char kValue[] = "abc";
+  static const char kValue2[] = "pqr";
+  static const char kValue3[] = "xyz";
+  ASSERT_TRUE(store_.Open());
+  {
+    vector<string> value;
+    ASSERT_TRUE(store_.SetStringList(kGroup, kKeyEmpty, value));
+  }
+  {
+    vector<string> value;
+    value.push_back("");
+    value.push_back(kValue);
+    ASSERT_TRUE(store_.SetStringList(kGroup, kKeyEmptyValue, value));
+  }
+  {
+    vector<string> value;
+    value.push_back(kValue);
+    value.push_back("");
+    ASSERT_TRUE(store_.SetStringList(kGroup, kKeyValueEmpty, value));
+  }
+  {
+    vector<string> value;
+    value.push_back(kValue);
+    value.push_back("");
+    value.push_back(kValue2);
+    ASSERT_TRUE(store_.SetStringList(kGroup, kKeyValueEmptyValue, value));
+  }
+  {
+    vector<string> value;
+    value.push_back(kValue);
+    value.push_back(kValue2);
+    value.push_back(kValue3);
+    ASSERT_TRUE(store_.SetStringList(kGroup, kKeyValues, value));
+  }
+  ASSERT_TRUE(store_.Close());
+  EXPECT_EQ(base::StringPrintf("\n"
+                               "[%s]\n"
+                               "%s=\n"
+                               "%s=;%s;\n"
+                               "%s=%s;;\n"
+                               "%s=%s;;%s;\n"
+                               "%s=%s;%s;%s;\n",
+                               kGroup,
+                               kKeyEmpty,
+                               kKeyEmptyValue, kValue,
+                               kKeyValueEmpty, kValue,
+                               kKeyValueEmptyValue, kValue, kValue2,
+                               kKeyValues, kValue, kValue2, kValue3),
+            ReadKeyFile());
+}
+
 TEST_F(KeyFileStoreTest, GetCryptedString) {
   static const char kGroup[] = "crypto-group";
   static const char kKey[] = "secret";
@@ -329,6 +445,7 @@
   static const char kGroupC[] = "triangle";
   static const char kGroupX[] = "pentagon";
   static const char kKeyString[] = "color";
+  static const char kKeyStringList[] = "alternative-colors";
   static const char kKeyInt[] = "area";
   static const char kKeyBool[] = "visible";
   static const char kValueStringA[] = "blue";
@@ -340,9 +457,11 @@
   const int kValueIntBNew = 333;
   WriteKeyFile(base::StringPrintf("[%s]\n"
                                   "%s=%s\n"
+                                  "%s=%s;%s\n"
                                   "%s=%d\n"
                                   "[%s]\n"
                                   "%s=%s\n"
+                                  "%s=%s;%s\n"
                                   "%s=%d\n"
                                   "%s=true\n"
                                   "[%s]\n"
@@ -350,9 +469,11 @@
                                   "%s=false\n",
                                   kGroupA,
                                   kKeyString, kValueStringA,
+                                  kKeyStringList, kValueStringB, kValueStringC,
                                   kKeyInt, kValueIntA,
                                   kGroupB,
                                   kKeyString, kValueStringB,
+                                  kKeyStringList, kValueStringA, kValueStringC,
                                   kKeyInt, kValueIntB,
                                   kKeyBool,
                                   kGroupC,
@@ -382,6 +503,18 @@
     EXPECT_EQ(kValueStringC, value);
   }
   {
+    vector<string> value;
+    EXPECT_TRUE(store_.GetStringList(kGroupB, kKeyStringList, &value));
+    ASSERT_EQ(2, value.size());
+    EXPECT_EQ(kValueStringA, value[0]);
+    EXPECT_EQ(kValueStringC, value[1]);
+    EXPECT_TRUE(store_.GetStringList(kGroupA, kKeyStringList, &value));
+    ASSERT_EQ(2, value.size());
+    EXPECT_EQ(kValueStringB, value[0]);
+    EXPECT_EQ(kValueStringC, value[1]);
+    EXPECT_FALSE(store_.GetStringList(kGroupC, kKeyStringList, &value));
+  }
+  {
     int value = 0;
     EXPECT_TRUE(store_.GetInt(kGroupB, kKeyInt, &value));
     EXPECT_EQ(kValueIntB, value);
@@ -414,6 +547,9 @@
   EXPECT_TRUE(store_.SetBool(kGroupB, kKeyBool, false));
   EXPECT_TRUE(store_.SetInt(kGroupB, kKeyInt, kValueIntBNew));
   EXPECT_TRUE(store_.SetString(kGroupC, kKeyString, kValueStringCNew));
+  store_.SetStringList(kGroupB,
+                       kKeyStringList,
+                       vector<string>(1, kValueStringB));
 
   EXPECT_TRUE(store_.DeleteKey(kGroupB, kKeyString));
   EXPECT_FALSE(store_.DeleteKey(kGroupB, kKeyString));
@@ -425,7 +561,14 @@
     EXPECT_TRUE(store_.GetString(kGroupC, kKeyString, &value));
     EXPECT_EQ(kValueStringCNew, value);
   }
-
+  {
+    vector<string> value;
+    EXPECT_TRUE(store_.GetStringList(kGroupB, kKeyStringList, &value));
+    ASSERT_EQ(1, value.size());
+    EXPECT_EQ(kValueStringB, value[0]);
+    EXPECT_FALSE(store_.GetStringList(kGroupA, kKeyStringList, &value));
+    EXPECT_FALSE(store_.GetStringList(kGroupC, kKeyStringList, &value));
+  }
   {
     int value = 0;
     EXPECT_TRUE(store_.GetInt(kGroupB, kKeyInt, &value));
@@ -433,7 +576,6 @@
     EXPECT_FALSE(store_.GetInt(kGroupA, kKeyInt, &value));
     EXPECT_FALSE(store_.GetInt(kGroupC, kKeyInt, &value));
   }
-
   {
     bool value = false;
     EXPECT_TRUE(store_.GetBool(kGroupB, kKeyBool, &value));
@@ -446,6 +588,7 @@
   ASSERT_TRUE(store_.Close());
   EXPECT_EQ(base::StringPrintf("\n"
                                "[%s]\n"
+                               "%s=%s;\n"
                                "%s=%d\n"
                                "%s=false\n"
                                "\n"
@@ -453,6 +596,7 @@
                                "%s=%s\n"
                                "%s=false\n",
                                kGroupB,
+                               kKeyStringList, kValueStringB,
                                kKeyInt, kValueIntBNew,
                                kKeyBool,
                                kGroupC,
diff --git a/mock_glib.h b/mock_glib.h
index 8bf7218..46d714b 100644
--- a/mock_glib.h
+++ b/mock_glib.h
@@ -34,6 +34,11 @@
                                          const gchar *group_name,
                                          const gchar *key,
                                          GError **error));
+  MOCK_METHOD5(KeyFileGetStringList, gchar **(GKeyFile *key_file,
+                                              const gchar *group_name,
+                                              const gchar *key,
+                                              gsize *length,
+                                              GError **error));
   MOCK_METHOD2(KeyFileHasGroup, gboolean(GKeyFile *key_file,
                                          const gchar *group_name));
   MOCK_METHOD4(KeyFileLoadFromFile, gboolean(GKeyFile *key_file,
@@ -60,6 +65,11 @@
                                       const gchar *group_name,
                                       const gchar *key,
                                       const gchar *string));
+  MOCK_METHOD5(KeyFileSetStringList, void(GKeyFile *key_file,
+                                          const gchar *group_name,
+                                          const gchar *key,
+                                          const gchar * const list[],
+                                          gsize length));
   MOCK_METHOD3(KeyFileToData, gchar *(GKeyFile *key_file,
                                       gsize *length,
                                       GError **error));
diff --git a/store_interface.h b/store_interface.h
index 46a71cc..b03d3c0 100644
--- a/store_interface.h
+++ b/store_interface.h
@@ -7,6 +7,7 @@
 
 #include <set>
 #include <string>
+#include <vector>
 
 namespace shill {
 
@@ -78,6 +79,19 @@
                       const std::string &key,
                       int value) = 0;
 
+  // Gets a string list |value| associated with |group|:|key|. Returns true on
+  // success and false on failure (including when |group|:|key| is not present
+  // in the store).
+  virtual bool GetStringList(const std::string &group,
+                             const std::string &key,
+                             std::vector<std::string> *value) = 0;
+
+  // Associates |group|:|key| with a string list |value|. Returns true on
+  // success, false otherwise.
+  virtual bool SetStringList(const std::string &group,
+                             const std::string &key,
+                             const std::vector<std::string> &value) = 0;
+
   // Gets and decrypts string |value| associated with |group|:|key|. Returns
   // true on success and false on failure (including when |group|:|key| is not
   // present in the store).