shill: add a class for holding a set of callbacks.

This will be used to allow Devices to register interest
in suspend/resume/terminate events.

BUG=chromium-os:22407
TEST=new unit tests

Change-Id: I466499291cebb57a0d1f8befeef31c78c075cd83
Reviewed-on: https://gerrit.chromium.org/gerrit/14028
Tested-by: mukesh agrawal <quiche@chromium.org>
Reviewed-by: Thieu Le <thieule@chromium.org>
Commit-Ready: mukesh agrawal <quiche@chromium.org>
diff --git a/Makefile b/Makefile
index 163628a..86bec91 100644
--- a/Makefile
+++ b/Makefile
@@ -74,6 +74,7 @@
 SHILL_OBJS = \
 	async_connection.o \
 	byte_string.o \
+	callback_list.o \
 	cellular.o \
 	cellular_capability.o \
 	cellular_capability_cdma.o \
@@ -157,6 +158,7 @@
 TEST_OBJS = \
 	async_connection_unittest.o \
 	byte_string_unittest.o \
+	callback_list_unittest.o \
 	cellular_capability_cdma_unittest.o \
 	cellular_capability_gsm_unittest.o \
 	cellular_service_unittest.o \
@@ -183,6 +185,7 @@
 	mock_adaptors.o \
 	mock_ares.o \
 	mock_async_connection.o \
+	mock_callback.o \
 	mock_connection.o \
 	mock_control.o \
 	mock_dbus_properties_proxy.o \
diff --git a/callback_list.cc b/callback_list.cc
new file mode 100644
index 0000000..d441989
--- /dev/null
+++ b/callback_list.cc
@@ -0,0 +1,55 @@
+// 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 "callback_list.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/stl_util-inl.h>
+
+using std::map;
+using std::string;
+
+namespace shill {
+
+CallbackList::CallbackList() {}
+
+CallbackList::~CallbackList() {
+  for (CallbackMap::iterator it = callbacks_.begin();
+       it != callbacks_.end();
+       ++it) {
+    delete it->second;
+  }
+}
+
+void CallbackList::Add(const string &name, Callback *callback) {
+  DCHECK(!ContainsKey(callbacks_, name));
+  callbacks_[name] = callback;
+}
+
+void CallbackList::Remove(const string &name) {
+  DCHECK(ContainsKey(callbacks_, name));
+  CallbackMap::iterator it = callbacks_.find(name);
+  if (it != callbacks_.end()) {
+    delete it->second;
+    callbacks_.erase(it);
+  }
+}
+
+bool CallbackList::Run() const {
+  bool ret = true;
+  for (CallbackMap::const_iterator it = callbacks_.begin();
+       it != callbacks_.end();
+       ++it) {
+    VLOG(2) << "Running callback " << it->first;
+    bool res;
+    res = it->second->Run();
+    VLOG(2) << "Callback " << it->first << " returned " << res;
+    ret = ret && res;
+  }
+  return ret;
+}
+
+}  // namespace shill
diff --git a/callback_list.h b/callback_list.h
new file mode 100644
index 0000000..0e87168
--- /dev/null
+++ b/callback_list.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef SHILL_CALLBACK_LIST_
+#define SHILL_CALLBACK_LIST_
+
+#include <map>
+#include <string>
+
+#include <base/basictypes.h>
+#include <base/callback_old.h>
+#include <base/scoped_ptr.h>
+
+namespace shill {
+
+typedef CallbackWithReturnValue<bool>::Type Callback;
+
+class CallbackList {
+ public:
+  CallbackList();
+  virtual ~CallbackList();
+
+  void Add(const std::string &name, Callback *callback);  // takes ownership
+  void Remove(const std::string &name);
+  // Run all callbacks, returning false if any of the callbacks return false
+  // (and returning true otherwise). The order in which the callbacks run
+  // is unspecified.
+  bool Run() const;
+
+ private:
+  // Can't use scoped_ptr, because it is non-copyable.
+  typedef std::map<const std::string, Callback *> CallbackMap;
+  CallbackMap callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_CALLBACK_LIST_
diff --git a/callback_list_unittest.cc b/callback_list_unittest.cc
new file mode 100644
index 0000000..f7695f3
--- /dev/null
+++ b/callback_list_unittest.cc
@@ -0,0 +1,88 @@
+// 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/callback_list.h"
+
+#include <base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "mock_callback.h"
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::Test;
+
+namespace shill {
+
+class CallbackListTest : public Test {
+ protected:
+  CallbackList callbacks_;
+};
+
+TEST_F(CallbackListTest, AllTrue) {
+  // Note that the EXPECT_CALLs cause gmock to verify that the
+  // CallbackList dtor cleanups up callbacks.
+  MockCallback *callback1 = new MockCallback();
+  callbacks_.Add("callback1", callback1);
+  EXPECT_CALL(*callback1, Run())
+      .WillOnce(Return(true));
+
+  MockCallback *callback2 = new MockCallback();
+  callbacks_.Add("callback2", callback2);
+  EXPECT_CALL(*callback2, Run())
+      .WillOnce(Return(true));
+
+  EXPECT_TRUE(callbacks_.Run());
+}
+
+
+TEST_F(CallbackListTest, AllFalse) {
+  // Note that the EXPECT_CALLs verify that Run() doesn't
+  // short-circuit on the first callback failure. Also, we have both
+  // callbacks return false, so we catch short-circuiting regardless
+  // of the order in which Run() executes the callbacks.
+  MockCallback *callback1 = new MockCallback();
+  callbacks_.Add("callback1", callback1);
+  EXPECT_CALL(*callback1, Run())
+      .WillOnce(Return(false));
+
+  MockCallback *callback2 = new MockCallback();
+  callbacks_.Add("callback2", callback2);
+  EXPECT_CALL(*callback2, Run())
+      .WillOnce(Return(false));
+
+  EXPECT_FALSE(callbacks_.Run());
+}
+
+TEST_F(CallbackListTest, MixedReturnValues) {
+  MockCallback *callback1 = new MockCallback();
+  callbacks_.Add("callback1", callback1);
+  EXPECT_CALL(*callback1, Run())
+      .WillOnce(Return(true));
+
+  MockCallback *callback2 = new MockCallback();
+  callbacks_.Add("callback2", callback2);
+  EXPECT_CALL(*callback2, Run())
+      .WillOnce(Return(false));
+
+  EXPECT_FALSE(callbacks_.Run());
+}
+
+TEST_F(CallbackListTest, Remove) {
+  MockCallback *callback = new MockCallback();
+  callbacks_.Add("callback", callback);
+  EXPECT_CALL(*callback, Run())
+      .WillOnce(Return(false));
+  EXPECT_FALSE(callbacks_.Run());
+
+  EXPECT_CALL(*callback, Run())
+      .Times(0);
+  // By virtue of the EXPECT_CALLs on |callback|, we implicitly tests
+  // that Remove does not leak objects.
+  callbacks_.Remove("callback");
+  EXPECT_TRUE(callbacks_.Run());
+}
+
+}  // namespace shill
diff --git a/mock_callback.cc b/mock_callback.cc
new file mode 100644
index 0000000..b54a69a
--- /dev/null
+++ b/mock_callback.cc
@@ -0,0 +1,15 @@
+// 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/mock_callback.h"
+
+namespace shill {
+
+MockCallback::MockCallback() :
+    CallbackWithReturnValueImpl<
+  EmptyClass, bool (EmptyClass::*)(), bool>(NULL, NULL) {}
+
+MockCallback::~MockCallback() {}
+
+}  // namespace shill
diff --git a/mock_callback.h b/mock_callback.h
new file mode 100644
index 0000000..79c715b
--- /dev/null
+++ b/mock_callback.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef SHILL_MOCK_CALLBACK_
+#define SHILL_MOCK_CALLBACK_
+
+#include <base/basictypes.h>
+#include <base/callback_old.h>
+#include <gmock/gmock.h>
+
+namespace shill {
+
+class EmptyClass {};
+
+class MockCallback
+    : public CallbackWithReturnValueImpl<
+  EmptyClass, bool (EmptyClass::*)(), bool> {
+ public:
+  MockCallback();
+  virtual ~MockCallback();
+
+  MOCK_METHOD0(Run, bool());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_CALLBACK_