shill: Implement GetEntry on Profile

Implement "GetEntry" on profile_dbus_adaptor.  This change adds
the ability to look up a Service in the Manager by its
StorageIdentifier and then to return its properties to the caller.
If the Service does not exist in the manager, we have to read
directly out of the Profile's store and build a DBus property dict
out of its contents.  This is a pretty gross method, and I've already
created a bug to remove this as soon as we can diverge from the
flimflam Profile API.  crosbug.com/25813

BUG=chromium-os:25542
TEST=New unit tests + Manual: chrome://settings/internet now lists
both visible and unavailable networks under "Remembered Networks"
and clikcing on "Forget Network" purges the network from the profile.

Change-Id: Ib2f0ab772e40a1f615206a7b985be446fc7facde
Reviewed-on: https://gerrit.chromium.org/gerrit/15200
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/profile_dbus_property_exporter_unittest.cc b/profile_dbus_property_exporter_unittest.cc
new file mode 100644
index 0000000..a5d14cb
--- /dev/null
+++ b/profile_dbus_property_exporter_unittest.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/profile_dbus_property_exporter.h"
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+
+#include "shill/error.h"
+#include "shill/mock_store.h"
+#include "shill/service.h"
+#include "shill/wifi_service.h"
+
+using std::string;
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::StrEq;
+using testing::StrNe;
+using testing::StrictMock;
+
+namespace shill {
+
+class ProfileDBusPropertyExporterTest : public testing::Test {
+ public:
+  ProfileDBusPropertyExporterTest() {}
+
+  virtual void SetUp() {
+    SetUpWithEntryName("entry_name");
+  }
+
+  void SetUpWithEntryName(const string &entry_name) {
+    entry_name_ = entry_name;
+    storage_.reset(new StrictMock<MockStore>());
+    EXPECT_CALL(*storage_.get(), ContainsGroup(StrEq(entry_name_)))
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*storage_.get(), ContainsGroup(StrNe(entry_name_)))
+        .WillRepeatedly(Return(false));
+    exporter_.reset(new ProfileDBusPropertyExporter(storage_.get(),
+                                                    entry_name_));
+  }
+
+  void ExpectBoolProperty(const string &key, bool value) {
+    EXPECT_CALL(*storage_.get(), GetBool(StrEq(entry_name_), StrEq(key), _))
+        .WillOnce(DoAll(SetArgumentPointee<2>(value), Return(true)));
+  }
+
+  void ExpectStringProperty(const string &key, const string &value) {
+    EXPECT_CALL(*storage_.get(), GetString(StrEq(entry_name_), StrEq(key), _))
+        .WillOnce(DoAll(SetArgumentPointee<2>(value), Return(true)));
+  }
+
+  void ExpectUnknownProperties() {
+    EXPECT_CALL(*storage_.get(), GetBool(StrEq(entry_name_), _, _))
+        .WillRepeatedly(Return(false));
+    EXPECT_CALL(*storage_.get(), GetString(StrEq(entry_name_), _, _))
+        .WillRepeatedly(Return(false));
+  }
+
+  bool GetBoolProperty(
+      ProfileDBusPropertyExporter::PropertyList *props, const string key) {
+    return (*props)[key].reader().get_bool();
+  }
+
+  string GetStringProperty(
+      ProfileDBusPropertyExporter::PropertyList *props, const string key) {
+    return (*props)[key].reader().get_string();
+  }
+
+ protected:
+  string entry_name_;
+  scoped_ptr<MockStore> storage_;
+  scoped_ptr<ProfileDBusPropertyExporter> exporter_;
+};
+
+TEST_F(ProfileDBusPropertyExporterTest, LoadWrongGroup) {
+  ProfileDBusPropertyExporter exporter(storage_.get(), "not_entry_name");
+  ProfileDBusPropertyExporter::PropertyList props;
+  Error e;
+  EXPECT_FALSE(exporter.LoadServiceProperties(&props, &e));
+  EXPECT_EQ(Error::kNotFound, e.type());
+}
+
+TEST_F(ProfileDBusPropertyExporterTest, InvalidTechnology) {
+  SetUpWithEntryName("unknown_technology");
+  ProfileDBusPropertyExporter::PropertyList props;
+  Error e;
+  EXPECT_FALSE(exporter_->LoadServiceProperties(&props, &e));
+  EXPECT_EQ(Error::kInternalError, e.type());
+}
+
+TEST_F(ProfileDBusPropertyExporterTest, MinimalProperties) {
+  SetUpWithEntryName("ethernet_service");
+  ExpectUnknownProperties();
+  ProfileDBusPropertyExporter::PropertyList props;
+  Error e;
+  EXPECT_TRUE(exporter_->LoadServiceProperties(&props, &e));
+  EXPECT_EQ(1, props.size());
+  EXPECT_EQ(flimflam::kTypeEthernet,
+            GetStringProperty(&props, flimflam::kTypeProperty));
+}
+
+TEST_F(ProfileDBusPropertyExporterTest, OverrideTypeProperty) {
+  SetUpWithEntryName("ethernet_service");
+  const string service_type = "special_type";
+  ExpectUnknownProperties();
+  ExpectStringProperty(Service::kStorageType, service_type);
+  ProfileDBusPropertyExporter::PropertyList props;
+  Error e;
+  EXPECT_TRUE(exporter_->LoadServiceProperties(&props, &e));
+  EXPECT_EQ(1, props.size());
+  EXPECT_EQ(service_type, GetStringProperty(&props, flimflam::kTypeProperty));
+}
+
+TEST_F(ProfileDBusPropertyExporterTest, AllServiceProperties) {
+  SetUpWithEntryName("ethernet_service");
+  const bool auto_connect(true);
+  ExpectBoolProperty(Service::kStorageAutoConnect, auto_connect);
+  const string error("no carrier");
+  ExpectStringProperty(Service::kStorageError, error);
+  const string guid("guid");
+  ExpectStringProperty(Service::kStorageGUID, guid);
+  const string name("fastnet");
+  ExpectStringProperty(Service::kStorageName, name);
+  const string type("100baset");
+  ExpectStringProperty(Service::kStorageType, type);
+  const string uidata("ui data");
+  ExpectStringProperty(Service::kStorageUIData, uidata);
+
+  ProfileDBusPropertyExporter::PropertyList props;
+  Error e;
+  EXPECT_TRUE(exporter_->LoadServiceProperties(&props, &e));
+  EXPECT_EQ(auto_connect, GetBoolProperty(&props,
+                                          flimflam::kAutoConnectProperty));
+  EXPECT_EQ(error, GetStringProperty(&props, flimflam::kErrorProperty));
+  EXPECT_EQ(guid, GetStringProperty(&props, flimflam::kGuidProperty));
+  EXPECT_EQ(name, GetStringProperty(&props, flimflam::kNameProperty));
+  EXPECT_EQ(type, GetStringProperty(&props, flimflam::kTypeProperty));
+  EXPECT_EQ(uidata, GetStringProperty(&props, flimflam::kUIDataProperty));
+}
+
+TEST_F(ProfileDBusPropertyExporterTest, MinimalWiFiServiceProperties) {
+  SetUpWithEntryName("wifi_address_ssid_superfly_unbreakable_crypto");
+  ExpectUnknownProperties();
+  ProfileDBusPropertyExporter::PropertyList props;
+  Error e;
+  EXPECT_TRUE(exporter_->LoadServiceProperties(&props, &e));
+  EXPECT_EQ("superfly", GetStringProperty(&props, flimflam::kModeProperty));
+  EXPECT_EQ("unbreakable_crypto",
+            GetStringProperty(&props, flimflam::kSecurityProperty));
+}
+
+TEST_F(ProfileDBusPropertyExporterTest, AllWiFiServiceProperties) {
+  SetUpWithEntryName("wifi_service");
+  ExpectUnknownProperties();
+  const bool hidden_ssid(true);
+  ExpectBoolProperty(WiFiService::kStorageHiddenSSID, hidden_ssid);
+  const string mode("superfly");
+  ExpectStringProperty(WiFiService::kStorageMode, mode);
+  const string security("unbreakablecrypto");
+  ExpectStringProperty(WiFiService::kStorageSecurity, security);
+
+  ProfileDBusPropertyExporter::PropertyList props;
+  Error e;
+  EXPECT_TRUE(exporter_->LoadServiceProperties(&props, &e));
+  EXPECT_EQ(hidden_ssid, GetBoolProperty(&props, flimflam::kWifiHiddenSsid));
+  EXPECT_EQ(mode, GetStringProperty(&props, flimflam::kModeProperty));
+  EXPECT_EQ(security, GetStringProperty(&props, flimflam::kSecurityProperty));
+}
+
+}  // namespace shill