diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-04 21:42:00 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-04 21:42:00 +0000 |
commit | c11f3e61056cb914ae91428dcba3f6bc05fc7364 (patch) | |
tree | 4e563c7d04297606e565afe56e1fe80c1e015f3f /chrome/browser/sync | |
parent | b5775171a912fec03e4098944c34e8cad4b103b2 (diff) | |
download | chromium_src-c11f3e61056cb914ae91428dcba3f6bc05fc7364.zip chromium_src-c11f3e61056cb914ae91428dcba3f6bc05fc7364.tar.gz chromium_src-c11f3e61056cb914ae91428dcba3f6bc05fc7364.tar.bz2 |
Wrote NetworkChangeNotifierProxy, which lets objects that don't live on
the same thread as a NetworkChangeNotifier safely listen to notifications.
Cleaned up unit tests. Moved ThreadBlocker and MockNetworkChangeObserver
into their own files.
BUG=42606
TEST=unittests
Review URL: http://codereview.chromium.org/1909001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46395 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync')
9 files changed, 532 insertions, 90 deletions
diff --git a/chrome/browser/sync/net/mock_network_change_observer.h b/chrome/browser/sync/net/mock_network_change_observer.h new file mode 100644 index 0000000..91a5ef9 --- /dev/null +++ b/chrome/browser/sync/net/mock_network_change_observer.h @@ -0,0 +1,32 @@ +// Copyright (c) 2010 The Chromium 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 CHROME_BROWSER_SYNC_NET_MOCK_NETWORK_CHANGE_OBSERVER_H_ +#define CHROME_BROWSER_SYNC_NET_MOCK_NETWORK_CHANGE_OBSERVER_H_ + +#include "base/basictypes.h" +#include "net/base/network_change_notifier.h" +#include "testing/gmock/include/gmock/gmock.h" + +// This class is a mock net::NetworkChangeNotifier::Observer used in +// unit tests. + +namespace browser_sync { + +class MockNetworkChangeObserver + : public net::NetworkChangeNotifier::Observer { + public: + MockNetworkChangeObserver() {} + + virtual ~MockNetworkChangeObserver() {} + + MOCK_METHOD0(OnIPAddressChanged, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockNetworkChangeObserver); +}; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_NET_MOCK_NETWORK_CHANGE_OBSERVER_H_ diff --git a/chrome/browser/sync/net/network_change_notifier_proxy.cc b/chrome/browser/sync/net/network_change_notifier_proxy.cc new file mode 100644 index 0000000..7176da3 --- /dev/null +++ b/chrome/browser/sync/net/network_change_notifier_proxy.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2010 The Chromium 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 "chrome/browser/sync/net/network_change_notifier_proxy.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "chrome/browser/sync/net/network_change_observer_proxy.h" + +namespace browser_sync { + +NetworkChangeNotifierProxy::NetworkChangeNotifierProxy( + MessageLoop* source_message_loop, + net::NetworkChangeNotifier* source_network_change_notifier) + : observer_proxy_(new NetworkChangeObserverProxy( + source_message_loop, source_network_change_notifier, + MessageLoop::current())), + observer_repeater_(&observers_) { + // TODO(akalin): We get this from NonThreadSafe, which + // net::NetworkChangeNotifier inherits from. Interface classes + // really shouldn't be inheriting from NonThreadSafe; make it so + // that all the implementations of net::NetworkChangeNotifier + // inherit from NonThreadSafe directly and + // net::NetworkChangeNotifier doesn't. + DCHECK(CalledOnValidThread()); + DCHECK(observer_proxy_); + observer_proxy_->Attach(&observer_repeater_); +} + +NetworkChangeNotifierProxy::~NetworkChangeNotifierProxy() { + DCHECK(CalledOnValidThread()); + observer_proxy_->Detach(); +} + +void NetworkChangeNotifierProxy::AddObserver( + net::NetworkChangeNotifier::Observer* observer) { + DCHECK(CalledOnValidThread()); + observers_.AddObserver(observer); +} + +void NetworkChangeNotifierProxy::RemoveObserver( + net::NetworkChangeNotifier::Observer* observer) { + DCHECK(CalledOnValidThread()); + observers_.RemoveObserver(observer); +} + +NetworkChangeNotifierProxy::ObserverRepeater::ObserverRepeater( + NetworkObserverList* observers) : observers_(observers) { + DCHECK(observers_); +} + +NetworkChangeNotifierProxy::ObserverRepeater::~ObserverRepeater() {} + +void NetworkChangeNotifierProxy::ObserverRepeater::OnIPAddressChanged() { + DCHECK(CalledOnValidThread()); + FOR_EACH_OBSERVER(net::NetworkChangeNotifier::Observer, + *observers_, OnIPAddressChanged()); +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/net/network_change_notifier_proxy.h b/chrome/browser/sync/net/network_change_notifier_proxy.h new file mode 100644 index 0000000..517d705 --- /dev/null +++ b/chrome/browser/sync/net/network_change_notifier_proxy.h @@ -0,0 +1,72 @@ +// Copyright (c) 2010 The Chromium 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 CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_NOTIFIER_PROXY_H_ +#define CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_NOTIFIER_PROXY_H_ + +// NetworkChangeNotifierProxy is a class that lets observers listen to +// a NetworkChangeNotifier that lives on another thread. + +#include "base/basictypes.h" +#include "base/non_thread_safe.h" +#include "base/observer_list.h" +#include "base/ref_counted.h" +#include "net/base/network_change_notifier.h" + +class MessageLoop; + +namespace browser_sync { + +class NetworkChangeObserverProxy; + +class NetworkChangeNotifierProxy : public net::NetworkChangeNotifier { + public: + // Both source_thread and source_network_change_notifier must be + // guaranteed to outlive the current thread. Does not take + // ownership of any arguments. + NetworkChangeNotifierProxy( + MessageLoop* source_message_loop, + net::NetworkChangeNotifier* source_network_change_notifier); + + virtual ~NetworkChangeNotifierProxy(); + + // net::NetworkChangeNotifier implementation. + + virtual void AddObserver(net::NetworkChangeNotifier::Observer* observer); + + virtual void RemoveObserver(net::NetworkChangeNotifier::Observer* observer); + + private: + typedef ObserverList<net::NetworkChangeNotifier::Observer, true> + NetworkObserverList; + + // Utility class that routes received notifications to a list of + // observers. + class ObserverRepeater : public NonThreadSafe, + public net::NetworkChangeNotifier::Observer { + public: + // Does not take ownership of the given observer list. + explicit ObserverRepeater(NetworkObserverList* observers); + + virtual ~ObserverRepeater(); + + // net::NetworkChangeNotifier::Observer implementation. + virtual void OnIPAddressChanged(); + + private: + NetworkObserverList* observers_; + + DISALLOW_COPY_AND_ASSIGN(ObserverRepeater); + }; + + scoped_refptr<NetworkChangeObserverProxy> observer_proxy_; + NetworkObserverList observers_; + ObserverRepeater observer_repeater_; + + DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierProxy); +}; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_NOTIFIER_PROXY_H_ diff --git a/chrome/browser/sync/net/network_change_notifier_proxy_unittest.cc b/chrome/browser/sync/net/network_change_notifier_proxy_unittest.cc new file mode 100644 index 0000000..5e095fd7 --- /dev/null +++ b/chrome/browser/sync/net/network_change_notifier_proxy_unittest.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2010 The Chromium 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 "chrome/browser/sync/net/network_change_notifier_proxy.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/browser/sync/net/mock_network_change_observer.h" +#include "chrome/browser/sync/net/thread_blocker.h" +#include "net/base/mock_network_change_notifier.h" +#include "net/base/network_change_notifier.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +// We manage the lifetime of net::MockNetworkChangeNotifier ourselves. +template <> +struct RunnableMethodTraits<net::MockNetworkChangeNotifier> { + void RetainCallee(net::MockNetworkChangeNotifier*) {} + void ReleaseCallee(net::MockNetworkChangeNotifier*) {} +}; + +namespace browser_sync { + +namespace { + +class NetworkChangeNotifierProxyTest : public testing::Test { + protected: + NetworkChangeNotifierProxyTest() + : source_thread_("Source Thread"), + source_message_loop_(NULL) {} + + virtual ~NetworkChangeNotifierProxyTest() { + CHECK(!source_thread_blocker_.get()); + CHECK(!source_message_loop_); + } + + virtual void SetUp() { + CHECK(source_thread_.Start()); + source_message_loop_ = source_thread_.message_loop(); + source_thread_blocker_.reset(new ThreadBlocker(&source_thread_)); + source_thread_blocker_->Block(); + + notifier_proxy_.reset(new NetworkChangeNotifierProxy( + source_message_loop_, &source_network_change_notifier_)); + } + + virtual void TearDown() { + // Posts a task to the source thread. + notifier_proxy_.reset(); + source_thread_blocker_->Unblock(); + source_thread_blocker_.reset(); + source_message_loop_ = NULL; + source_thread_.Stop(); + } + + // Trigger an "IP address changed" event on the source network + // change notifier on the source thread and propagate any generated + // notifications to the target thread. + void OnIPAddressChanged() { + source_message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod( + &source_network_change_notifier_, + &net::MockNetworkChangeNotifier::NotifyIPAddressChange)); + // Pump source thread. + source_thread_blocker_->Unblock(); + source_thread_blocker_->Block(); + // Pump target thread. + target_message_loop_.RunAllPending(); + } + + // This lives on |source_thread_| but is created/destroyed in the + // main thread. As long as we make sure to not modify it from the + // main thread while |source_thread_| is running, we shouldn't need + // any special synchronization. + net::MockNetworkChangeNotifier source_network_change_notifier_; + base::Thread source_thread_; + MessageLoop* source_message_loop_; + scoped_ptr<ThreadBlocker> source_thread_blocker_; + + MessageLoop target_message_loop_; + MockNetworkChangeObserver target_observer_; + + scoped_ptr<net::NetworkChangeNotifier> notifier_proxy_; + + private: + DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierProxyTest); +}; + +TEST_F(NetworkChangeNotifierProxyTest, Basic) { + EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(1); + + notifier_proxy_->AddObserver(&target_observer_); + OnIPAddressChanged(); + notifier_proxy_->RemoveObserver(&target_observer_); +} + +TEST_F(NetworkChangeNotifierProxyTest, IgnoresEventAfterRemoveObserver) { + EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0); + + notifier_proxy_->AddObserver(&target_observer_); + notifier_proxy_->RemoveObserver(&target_observer_); + OnIPAddressChanged(); +} + +TEST_F(NetworkChangeNotifierProxyTest, IgnoresEventBeforeRemoveObserver) { + EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0); + + OnIPAddressChanged(); + notifier_proxy_->AddObserver(&target_observer_); + notifier_proxy_->RemoveObserver(&target_observer_); +} + +TEST_F(NetworkChangeNotifierProxyTest, Multiple) { + EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0); + + const int kNumObservers = 5; + MockNetworkChangeObserver extra_observers[kNumObservers]; + for (int i = 0; i < kNumObservers; ++i) { + EXPECT_CALL(extra_observers[i], OnIPAddressChanged()).Times(1); + } + + for (int i = 0; i < kNumObservers; ++i) { + notifier_proxy_->AddObserver(&extra_observers[i]); + } + OnIPAddressChanged(); + for (int i = 0; i < kNumObservers; ++i) { + notifier_proxy_->RemoveObserver(&extra_observers[i]); + } +} + +} // namespace + +} // namespace browser_sync diff --git a/chrome/browser/sync/net/network_change_observer_proxy.h b/chrome/browser/sync/net/network_change_observer_proxy.h index 24eff05..184f5cb 100644 --- a/chrome/browser/sync/net/network_change_observer_proxy.h +++ b/chrome/browser/sync/net/network_change_observer_proxy.h @@ -45,6 +45,7 @@ namespace browser_sync { +// TODO(akalin): Remove use of private inheritance. class NetworkChangeObserverProxy : public base::RefCountedThreadSafe<NetworkChangeObserverProxy>, private net::NetworkChangeNotifier::Observer { diff --git a/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc b/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc index adb0f81..ba78e89 100644 --- a/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc +++ b/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc @@ -7,28 +7,18 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/message_loop.h" -#include "base/observer_list.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "base/task.h" #include "base/thread.h" -#include "base/waitable_event.h" +#include "chrome/browser/sync/net/mock_network_change_observer.h" +#include "chrome/browser/sync/net/thread_blocker.h" #include "net/base/mock_network_change_notifier.h" #include "net/base/network_change_notifier.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace browser_sync { -class ThreadBlocker; -} // namespace browser_sync - -// We manage the lifetime of these classes ourselves. - -template <> -struct RunnableMethodTraits<browser_sync::ThreadBlocker> { - void RetainCallee(browser_sync::ThreadBlocker*) {} - void ReleaseCallee(browser_sync::ThreadBlocker*) {} -}; - +// We manage the lifetime of net::MockNetworkChangeNotifier ourselves. template <> struct RunnableMethodTraits<net::MockNetworkChangeNotifier> { void RetainCallee(net::MockNetworkChangeNotifier*) {} @@ -37,50 +27,6 @@ struct RunnableMethodTraits<net::MockNetworkChangeNotifier> { namespace browser_sync { -// This class lets you block and unblock a thread at will. -class ThreadBlocker { - public: - // The given thread must already be started and it must outlive this - // instance. - explicit ThreadBlocker(base::Thread* target_thread) - : target_message_loop_(target_thread->message_loop()), - is_blocked_(false, false), is_finished_blocking_(false, false), - is_unblocked_(false, false) { - DCHECK(target_message_loop_); - } - - // When this function returns, the target thread will be blocked - // until Unblock() is called. Each call to Block() must be matched - // by a call to Unblock(). - void Block() { - DCHECK_NE(MessageLoop::current(), target_message_loop_); - target_message_loop_->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ThreadBlocker::BlockOnTargetThread)); - while (!is_blocked_.Wait()) {} - } - - // When this function returns, the target thread is unblocked. - void Unblock() { - DCHECK_NE(MessageLoop::current(), target_message_loop_); - is_finished_blocking_.Signal(); - while (!is_unblocked_.Wait()) {} - } - - private: - void BlockOnTargetThread() { - DCHECK_EQ(MessageLoop::current(), target_message_loop_); - is_blocked_.Signal(); - while (!is_finished_blocking_.Wait()) {} - is_unblocked_.Signal(); - } - - MessageLoop* const target_message_loop_; - base::WaitableEvent is_blocked_, is_finished_blocking_, is_unblocked_; - - DISALLOW_COPY_AND_ASSIGN(ThreadBlocker); -}; - namespace { // Version of NetworkChangeObserverProxy that records on what thread @@ -113,32 +59,12 @@ class DeleteCheckingNetworkChangeObserverProxy DISALLOW_COPY_AND_ASSIGN(DeleteCheckingNetworkChangeObserverProxy); }; -// Mock observer used in the unit tests. -class MockNetworkChangeObserver - : public net::NetworkChangeNotifier::Observer { - public: - MockNetworkChangeObserver() {} - - virtual ~MockNetworkChangeObserver() {} - - MOCK_METHOD0(OnIPAddressChanged, void()); - - private: - DISALLOW_COPY_AND_ASSIGN(MockNetworkChangeObserver); -}; - -// Utility function used by PumpTarget() below. -void SetTrue(bool* b) { - *b = true; -} - class NetworkChangeObserverProxyTest : public testing::Test { protected: NetworkChangeObserverProxyTest() : source_thread_("Source Thread"), source_message_loop_(NULL), - proxy_deleting_message_loop_(NULL), - proxy_(NULL) {} + proxy_deleting_message_loop_(NULL) {} virtual ~NetworkChangeObserverProxyTest() { CHECK(!source_thread_blocker_.get()); @@ -157,7 +83,7 @@ class NetworkChangeObserverProxyTest : public testing::Test { &target_message_loop_, &proxy_deleting_message_loop_); } - // On TearDown, proxy_ must be released and both source and target + // On TearDown, |proxy_| must be released and both source and target // must be pumped. virtual void TearDown() { EXPECT_TRUE(proxy_ == NULL); @@ -177,12 +103,7 @@ class NetworkChangeObserverProxyTest : public testing::Test { // Pump any events posted on the target thread (which is just the // main test thread). void PumpTarget() { - bool done = false; - target_message_loop_.PostTask(FROM_HERE, - NewRunnableFunction(&SetTrue, &done)); - while (!done) { - target_message_loop_.RunAllPending(); - } + target_message_loop_.RunAllPending(); } // Trigger an "IP address changed" event on the source network @@ -195,10 +116,10 @@ class NetworkChangeObserverProxyTest : public testing::Test { &net::MockNetworkChangeNotifier::NotifyIPAddressChange)); } - // This lives on source_thread_ but is created/destroyed in the main - // thread. As long as we make sure to not modify it from the main - // thread while source_thread_ is running, we shouldn't need any - // special synchronization. + // This lives on |source_thread_| but is created/destroyed in the + // main thread. As long as we make sure to not modify it from the + // main thread while |source_thread_| is running, we shouldn't need + // any special synchronization. net::MockNetworkChangeNotifier source_network_change_notifier_; base::Thread source_thread_; MessageLoop* source_message_loop_; diff --git a/chrome/browser/sync/net/thread_blocker.cc b/chrome/browser/sync/net/thread_blocker.cc new file mode 100644 index 0000000..df3fafc --- /dev/null +++ b/chrome/browser/sync/net/thread_blocker.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2010 The Chromium 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 "chrome/browser/sync/net/thread_blocker.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "base/thread.h" +#include "base/waitable_event.h" + +// Since a ThreadBlocker is outlived by its target thread, we don't +// have to ref-count it. +template <> +struct RunnableMethodTraits<browser_sync::ThreadBlocker> { + void RetainCallee(browser_sync::ThreadBlocker*) {} + void ReleaseCallee(browser_sync::ThreadBlocker*) {} +}; + +namespace browser_sync { + +ThreadBlocker::ThreadBlocker(base::Thread* target_thread) + : target_message_loop_(target_thread->message_loop()), + is_blocked_(false, false), is_finished_blocking_(false, false), + is_unblocked_(false, false) { + DCHECK(target_message_loop_); +} + +void ThreadBlocker::Block() { + DCHECK_NE(MessageLoop::current(), target_message_loop_); + target_message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ThreadBlocker::BlockOnTargetThread)); + while (!is_blocked_.Wait()) {} +} + +void ThreadBlocker::Unblock() { + DCHECK_NE(MessageLoop::current(), target_message_loop_); + is_finished_blocking_.Signal(); + while (!is_unblocked_.Wait()) {} +} + +void ThreadBlocker::BlockOnTargetThread() { + DCHECK_EQ(MessageLoop::current(), target_message_loop_); + is_blocked_.Signal(); + while (!is_finished_blocking_.Wait()) {} + is_unblocked_.Signal(); +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/net/thread_blocker.h b/chrome/browser/sync/net/thread_blocker.h new file mode 100644 index 0000000..3548c5d --- /dev/null +++ b/chrome/browser/sync/net/thread_blocker.h @@ -0,0 +1,51 @@ +// Copyright (c) 2010 The Chromium 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 CHROME_BROWSER_SYNC_NET_THREAD_BLOCKER_H_ +#define CHROME_BROWSER_SYNC_NET_THREAD_BLOCKER_H_ + +// This class (mainly used for testing) lets you block and unblock a +// thread at will. +// +// TODO(akalin): Consider moving this to base/ as this class is not +// sync-specific (ask darin about it). + +#include "base/basictypes.h" +#include "base/waitable_event.h" + +class MessageLoop; + +namespace base { +class Thread; +} // namespace base + +namespace browser_sync { + +class ThreadBlocker { + public: + // The given thread must already be started and it must outlive this + // instance. + explicit ThreadBlocker(base::Thread* target_thread); + + // When this function returns, the target thread will be blocked + // until Unblock() is called. Each call to Block() must be matched + // by a call to Unblock(). + void Block(); + + // When this function returns, the target thread is unblocked. + void Unblock(); + + private: + // On the target thread, blocks until Unblock() is called. + void BlockOnTargetThread(); + + MessageLoop* const target_message_loop_; + base::WaitableEvent is_blocked_, is_finished_blocking_, is_unblocked_; + + DISALLOW_COPY_AND_ASSIGN(ThreadBlocker); +}; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_NET_THREAD_BLOCKER_H_ diff --git a/chrome/browser/sync/net/thread_blocker_unittest.cc b/chrome/browser/sync/net/thread_blocker_unittest.cc new file mode 100644 index 0000000..4587e30 --- /dev/null +++ b/chrome/browser/sync/net/thread_blocker_unittest.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2010 The Chromium 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 "chrome/browser/sync/net/thread_blocker.h" + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace browser_sync { +class Flag; +}; // namespace browser_sync + +// We manage the lifetime of browser_sync::Flag ourselves. +template <> +struct RunnableMethodTraits<browser_sync::Flag> { + void RetainCallee(browser_sync::Flag*) {} + void ReleaseCallee(browser_sync::Flag*) {} +}; + +namespace browser_sync { + +// Utility class that is basically just a thread-safe boolean. +class Flag { + public: + Flag() : flag_(false) {} + + bool IsSet() const { + AutoLock auto_lock(lock_); + return flag_; + } + + void Set() { + AutoLock auto_lock(lock_); + flag_ = true; + } + + void Unset() { + AutoLock auto_lock(lock_); + flag_ = false; + } + + private: + mutable Lock lock_; + bool flag_; + + DISALLOW_COPY_AND_ASSIGN(Flag); +}; + +namespace { + +class ThreadBlockerTest : public testing::Test { + protected: + ThreadBlockerTest() : target_thread_("Target Thread") {} + + virtual ~ThreadBlockerTest() { + CHECK(!thread_blocker_.get()); + } + + virtual void SetUp() { + CHECK(target_thread_.Start()); + thread_blocker_.reset(new ThreadBlocker(&target_thread_)); + } + + virtual void TearDown() { + target_thread_.Stop(); + thread_blocker_.reset(); + } + + base::Thread target_thread_; + scoped_ptr<ThreadBlocker> thread_blocker_; + Flag flag_; + + private: + DISALLOW_COPY_AND_ASSIGN(ThreadBlockerTest); +}; + +TEST_F(ThreadBlockerTest, Basic) { + thread_blocker_->Block(); + target_thread_.message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(&flag_, &Flag::Set)); + EXPECT_FALSE(flag_.IsSet()); + thread_blocker_->Unblock(); + // Need to block again to make sure this thread waits for the posted + // method to run. + thread_blocker_->Block(); + EXPECT_TRUE(flag_.IsSet()); + thread_blocker_->Unblock(); +} + +TEST_F(ThreadBlockerTest, SetUnset) { + thread_blocker_->Block(); + target_thread_.message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(&flag_, &Flag::Set)); + target_thread_.message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(&flag_, &Flag::Unset)); + EXPECT_FALSE(flag_.IsSet()); + thread_blocker_->Unblock(); + // Need to block again here too. + thread_blocker_->Block(); + EXPECT_FALSE(flag_.IsSet()); + thread_blocker_->Unblock(); +} + +} // namespace + +} // namespace browser_sync |