AU: Don't use network on expensive connection types

Specifically:

- FlimFlam proxy class to query the current network status and find
out if it's expensive.
- Dbus Interface, so dbus can be mocked.
- Libcurl change to redirect the URL if we are on an expensive
connection. This may seem hacky, but the reason I avoided retooling
the whole class is that we may decide that some network usage is
okay on pricy connections. Perhaps it's okay to throttle. So, for
now this is a more minimal change.

BUG=chromium-os:2397
TEST=Unit tests, tested that flimflam proxy works on the device.

Review URL: http://codereview.chromium.org/4029002

Change-Id: Ic4dcde1ca863bda890bc46a55c552e2b32d9433d
diff --git a/flimflam_proxy_unittest.cc b/flimflam_proxy_unittest.cc
new file mode 100644
index 0000000..e1caa01
--- /dev/null
+++ b/flimflam_proxy_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2010 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 <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/flimflam_proxy.h"
+#include "update_engine/mock_dbus_interface.h"
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrEq;
+
+namespace chromeos_update_engine {
+
+template<typename T, void F(T)>
+class ScopedRelease {
+ public:
+  ScopedRelease(T obj) : obj_(obj) {}
+  ~ScopedRelease() {
+    F(obj_);
+  }
+
+ private:
+  T obj_;
+};
+
+class FlimFlamProxyTest : public ::testing::Test {
+ protected:
+  void TestWithServiceType(
+      const char* service_type, NetworkConnectionType expected_type);
+};
+
+void FlimFlamProxyTest::TestWithServiceType(
+    const char* service_type,
+    NetworkConnectionType expected_type) {
+  int number = 1;
+  DBusGConnection* kMockSystemBus =
+      reinterpret_cast<DBusGConnection*>(number++);
+  DBusGProxy* kMockFlimFlamManagerProxy =
+      reinterpret_cast<DBusGProxy*>(number++);
+  DBusGProxy* kMockFlimFlamServiceProxy =
+      reinterpret_cast<DBusGProxy*>(number++);
+  ASSERT_NE(kMockSystemBus, reinterpret_cast<DBusGConnection*>(NULL));
+  const char* kServicePath = "/foo/service";
+  const char kGetPropertiesMethod[] = "GetProperties";
+  
+  MockDbusGlib dbus_iface;
+  
+  EXPECT_CALL(dbus_iface,
+              ProxyNewForNameOwner(kMockSystemBus,
+                                   StrEq(kFlimFlamDbusService),
+                                   StrEq(kFlimFlamDbusManagerPath),
+                                   StrEq(kFlimFlamDbusManagerInterface),
+                                   _))
+      .WillOnce(Return(kMockFlimFlamManagerProxy));
+  EXPECT_CALL(dbus_iface,
+              ProxyNewForNameOwner(kMockSystemBus,
+                                   StrEq(kFlimFlamDbusService),
+                                   StrEq(kServicePath),
+                                   StrEq(kFlimFlamDbusServiceInterface),
+                                   _))
+      .WillOnce(Return(kMockFlimFlamServiceProxy));
+      
+  EXPECT_CALL(dbus_iface, ProxyUnref(kMockFlimFlamManagerProxy));
+  EXPECT_CALL(dbus_iface, ProxyUnref(kMockFlimFlamServiceProxy));
+
+  EXPECT_CALL(dbus_iface, BusGet(DBUS_BUS_SYSTEM, _))
+      .Times(2)
+      .WillRepeatedly(Return(kMockSystemBus));
+  
+  // Set up return value for first dbus call (to Manager object).
+  GHashTable* manager_hash_table = g_hash_table_new(g_str_hash, g_str_equal);
+  ScopedRelease<GHashTable*, g_hash_table_unref> manager_hash_table_unref(
+      manager_hash_table);
+  
+  GArray* array = g_array_new(FALSE, FALSE, sizeof(const char*));
+  ASSERT_TRUE(array != NULL);
+  
+  EXPECT_EQ(array, g_array_append_val(array, kServicePath));
+  GValue array_value = {0, {{0}}};
+  EXPECT_EQ(&array_value, g_value_init(&array_value, G_TYPE_ARRAY));
+  g_value_take_boxed(&array_value, array);
+  g_hash_table_insert(manager_hash_table,
+                      const_cast<char*>("Services"),
+                      &array_value);
+
+  // Set up return value for second dbus call (to Service object).
+
+  GHashTable* service_hash_table = g_hash_table_new(g_str_hash, g_str_equal);
+  ScopedRelease<GHashTable*, g_hash_table_unref> service_hash_table_unref(
+      service_hash_table);
+      
+  GValue service_type_value = {0, {{0}}};
+  EXPECT_EQ(&service_type_value,
+            g_value_init(&service_type_value, G_TYPE_STRING));
+  g_value_set_static_string(&service_type_value, service_type);
+  
+  g_hash_table_insert(service_hash_table,
+                      const_cast<char*>("Type"),
+                      &service_type_value);
+
+  EXPECT_CALL(dbus_iface, ProxyCall(kMockFlimFlamManagerProxy,
+                                    StrEq(kGetPropertiesMethod),
+                                    _,
+                                    G_TYPE_INVALID,
+                                    dbus_g_type_get_map("GHashTable",
+                                                        G_TYPE_STRING,
+                                                        G_TYPE_VALUE),
+                                    _,
+                                    G_TYPE_INVALID))
+      .WillOnce(DoAll(SetArgumentPointee<5>(manager_hash_table), Return(TRUE)));
+
+  EXPECT_CALL(dbus_iface, ProxyCall(kMockFlimFlamServiceProxy,
+                                    StrEq(kGetPropertiesMethod),
+                                    _,
+                                    G_TYPE_INVALID,
+                                    dbus_g_type_get_map("GHashTable",
+                                                        G_TYPE_STRING,
+                                                        G_TYPE_VALUE),
+                                    _,
+                                    G_TYPE_INVALID))
+      .WillOnce(DoAll(SetArgumentPointee<5>(service_hash_table), Return(TRUE)));
+
+  NetworkConnectionType type;
+  
+  EXPECT_TRUE(FlimFlamProxy::GetConnectionType(&dbus_iface, &type));
+  EXPECT_EQ(expected_type, type);
+}
+
+TEST_F(FlimFlamProxyTest, SimpleTest) {
+  TestWithServiceType(kFlimFlamNetTypeEthernet, kNetEthernet);
+  TestWithServiceType(kFlimFlamNetTypeWifi, kNetWifi);
+  TestWithServiceType(kFlimFlamNetTypeWimax, kNetWimax);
+  TestWithServiceType(kFlimFlamNetTypeBluetooth, kNetBluetooth);
+  TestWithServiceType(kFlimFlamNetTypeCellular, kNetCellular);
+}
+
+TEST_F(FlimFlamProxyTest, UnknownTest) {
+  TestWithServiceType("foo", kNetUnknown);
+}
+
+TEST_F(FlimFlamProxyTest, ExpensiveConnectionsTest) {
+  EXPECT_FALSE(FlimFlamProxy::IsExpensiveConnectionType(kNetEthernet));
+  EXPECT_FALSE(FlimFlamProxy::IsExpensiveConnectionType(kNetWifi));
+  EXPECT_TRUE(FlimFlamProxy::IsExpensiveConnectionType(kNetWimax));
+  EXPECT_TRUE(FlimFlamProxy::IsExpensiveConnectionType(kNetBluetooth));
+  EXPECT_TRUE(FlimFlamProxy::IsExpensiveConnectionType(kNetCellular));
+}
+
+TEST_F(FlimFlamProxyTest, StringForConnectionTypeTest) {
+  EXPECT_EQ(kFlimFlamNetTypeEthernet,
+            FlimFlamProxy::StringForConnectionType(kNetEthernet));
+  EXPECT_EQ(kFlimFlamNetTypeWifi,
+            FlimFlamProxy::StringForConnectionType(kNetWifi));
+  EXPECT_EQ(kFlimFlamNetTypeWimax,
+            FlimFlamProxy::StringForConnectionType(kNetWimax));
+  EXPECT_EQ(kFlimFlamNetTypeBluetooth,
+            FlimFlamProxy::StringForConnectionType(kNetBluetooth));
+  EXPECT_EQ(kFlimFlamNetTypeCellular,
+            FlimFlamProxy::StringForConnectionType(kNetCellular));
+  EXPECT_EQ("Unknown", FlimFlamProxy::StringForConnectionType(kNetUnknown));
+  EXPECT_EQ("Unknown", FlimFlamProxy::StringForConnectionType(
+      static_cast<NetworkConnectionType>(999999)));
+}
+
+}  // namespace chromeos_update_engine