[shill] Enable IPConfig to Store/Load itself

BUG=chromium-os:17256
TEST=unit

Change-Id: I7792156f95ecb2e17afaf4e3250ff3dbc0e3dac9
Reviewed-on: http://gerrit.chromium.org/gerrit/4602
Tested-by: Chris Masone <cmasone@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
diff --git a/dhcp_config_unittest.cc b/dhcp_config_unittest.cc
index 8746f8b..744e0ad 100644
--- a/dhcp_config_unittest.cc
+++ b/dhcp_config_unittest.cc
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "shill/dhcp_config.h"
+
 #include <base/file_util.h>
 #include <base/memory/scoped_temp_dir.h>
 #include <base/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
 
 #include "shill/dbus_adaptor.h"
-#include "shill/dhcp_config.h"
 #include "shill/dhcp_provider.h"
 #include "shill/mock_control.h"
 #include "shill/mock_dhcp_proxy.h"
diff --git a/ipconfig.cc b/ipconfig.cc
index 7e5551e..fe8d821 100644
--- a/ipconfig.cc
+++ b/ipconfig.cc
@@ -10,12 +10,15 @@
 #include "shill/adaptor_interfaces.h"
 #include "shill/control_interface.h"
 #include "shill/error.h"
+#include "shill/store_interface.h"
 
 using std::string;
 
 namespace shill {
 
 // static
+const char IPConfig::kStorageType[] = "Mode";
+// static
 const char IPConfig::kType[] = "ip";
 // static
 uint IPConfig::global_serial_ = 0;
@@ -81,6 +84,23 @@
   return false;
 }
 
+bool IPConfig::Load(StoreInterface *storage) {
+  const string id = GetStorageIdentifier();
+  if (!storage->ContainsGroup(id)) {
+    LOG(WARNING) << "IPConfig is not available in the persistent store: " << id;
+    return false;
+  }
+  string local_type;
+  storage->GetString(id, kStorageType, &local_type);
+  return local_type == type();
+}
+
+bool IPConfig::Save(StoreInterface *storage) {
+  const string id = GetStorageIdentifier();
+  storage->SetString(id, kStorageType, type());
+  return true;
+}
+
 void IPConfig::UpdateProperties(const Properties &properties, bool success) {
   properties_ = properties;
   if (update_callback_.get()) {
@@ -93,4 +113,11 @@
   update_callback_.reset(callback);
 }
 
+string IPConfig::GetStorageIdentifier() {
+  string id = adaptor_->GetRpcIdentifier();
+  for (size_t i = id.find('/'); i != string::npos; i = id.find('/', i))
+    id[i] = '_';
+  return id;
+}
+
 }  // namespace shill
diff --git a/ipconfig.h b/ipconfig.h
index 7a930ba..8feee05 100644
--- a/ipconfig.h
+++ b/ipconfig.h
@@ -22,6 +22,7 @@
 class ControlInterface;
 class Error;
 class IPConfigAdaptorInterface;
+class StoreInterface;
 
 // IPConfig superclass. Individual IP configuration types will inherit from this
 // class.
@@ -78,6 +79,9 @@
 
   PropertyStore *store() { return &store_; }
 
+  bool Load(StoreInterface *storage);
+  bool Save(StoreInterface *storage);
+
  protected:
   // Updates the IP configuration properties and notifies registered listeners
   // about the event. |success| is set to false if the IP configuration failed.
@@ -93,6 +97,7 @@
   FRIEND_TEST(IPConfigTest, UpdateCallback);
   FRIEND_TEST(IPConfigTest, UpdateProperties);
 
+  static const char kStorageType[];
   static const char kType[];
   static uint global_serial_;
 
@@ -104,6 +109,7 @@
   scoped_ptr<Callback2<const IPConfigRefPtr&, bool>::Type> update_callback_;
 
   void Init();
+  std::string GetStorageIdentifier();
 
   DISALLOW_COPY_AND_ASSIGN(IPConfig);
 };
diff --git a/ipconfig_unittest.cc b/ipconfig_unittest.cc
index 8f90607..1a7482f 100644
--- a/ipconfig_unittest.cc
+++ b/ipconfig_unittest.cc
@@ -8,7 +8,14 @@
 #include <gtest/gtest.h>
 
 #include "shill/mock_control.h"
+#include "shill/mock_store.h"
 
+using std::string;
+using testing::_;
+using testing::Return;
+using testing::SaveArg;
+using testing::SetArgumentPointee;
+using testing::StrictMock;
 using testing::Test;
 
 namespace shill {
@@ -42,6 +49,23 @@
   EXPECT_FALSE(ipconfig_->ReleaseIP());
 }
 
+TEST_F(IPConfigTest, SaveLoad) {
+  StrictMock<MockStore> storage;
+  string id, key, value;
+  EXPECT_CALL(storage, SetString(_, _, _))
+      .WillOnce(DoAll(SaveArg<0>(&id),
+                      SaveArg<1>(&key),
+                      SaveArg<2>(&value),
+                      Return(true)));
+  ASSERT_TRUE(ipconfig_->Save(&storage));
+
+  EXPECT_CALL(storage, ContainsGroup(id))
+      .WillOnce(Return(true));
+  EXPECT_CALL(storage, GetString(id, key, _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(value), Return(true)));
+  ASSERT_TRUE(ipconfig_->Load(&storage));
+}
+
 TEST_F(IPConfigTest, UpdateProperties) {
   IPConfig::Properties properties;
   properties.address = "1.2.3.4";
diff --git a/key_file_store_unittest.cc b/key_file_store_unittest.cc
index 907ec06..65fa89e 100644
--- a/key_file_store_unittest.cc
+++ b/key_file_store_unittest.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "shill/key_file_store.h"
+
 #include <base/file_util.h>
 #include <base/stl_util-inl.h>
 #include <base/stringprintf.h>
 #include <base/memory/scoped_temp_dir.h>
 #include <gtest/gtest.h>
 
-#include "shill/key_file_store.h"
-
 using std::set;
 using std::string;
 using std::vector;
diff --git a/service_unittest.cc b/service_unittest.cc
index f116db6..ba076f6 100644
--- a/service_unittest.cc
+++ b/service_unittest.cc
@@ -168,7 +168,6 @@
 
 TEST_F(ServiceTest, LoadFail) {
   StrictMock<MockStore> storage;
-  const string id = kMockServiceName;
   EXPECT_CALL(storage, ContainsGroup(kMockServiceName)).WillOnce(Return(false));
   EXPECT_FALSE(service_->Load(&storage));
 }