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_