summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-30 04:23:00 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-30 04:23:00 +0000
commitf48d4e96e5de24da68e2a755e74b6cae63cd0843 (patch)
tree0b29d24d1fec62d28a4535dd586c002e9f39c186
parent9365e0553abbefbb54cd6f82586f2a1c4790e522 (diff)
downloadchromium_src-f48d4e96e5de24da68e2a755e74b6cae63cd0843.zip
chromium_src-f48d4e96e5de24da68e2a755e74b6cae63cd0843.tar.gz
chromium_src-f48d4e96e5de24da68e2a755e74b6cae63cd0843.tar.bz2
Added NetworkChangeObserverProxy class.
This class forwards notifications from a NetworkChangeNotifier on one thread to an observer on another. This will be used to enable the sync threads to listen to NCN notifications from the IO thread. BUG=42606 TEST=unittests Review URL: http://codereview.chromium.org/1704014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46037 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/sync/net/network_change_observer_proxy.cc131
-rw-r--r--chrome/browser/sync/net/network_change_observer_proxy.h113
-rw-r--r--chrome/browser/sync/net/network_change_observer_proxy_unittest.cc351
-rw-r--r--chrome/chrome.gyp13
-rw-r--r--chrome/chrome_tests.gypi32
5 files changed, 640 insertions, 0 deletions
diff --git a/chrome/browser/sync/net/network_change_observer_proxy.cc b/chrome/browser/sync/net/network_change_observer_proxy.cc
new file mode 100644
index 0000000..b40a530
--- /dev/null
+++ b/chrome/browser/sync/net/network_change_observer_proxy.cc
@@ -0,0 +1,131 @@
+// 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.
+
+// Some notes that may help anyone trying to reason about this code:
+//
+// - The source thread must be guaranteed to outlive the target
+// thread. This is so that it is guaranteed that any task posted to
+// the source thread will eventually be run. In particular, we want
+// to make sure that Detach() causes us to eventually be removed as
+// an observer.
+//
+// Note that this implies that any task posted to the target thread
+// from the source thread may not run. But we post only
+// TargetObserverOnIPAddressChanged() tasks on the target thread,
+// which we can safely drop.
+//
+// - The source NetworkChangeNotifier must be guaranteed to outlive
+// the target thread. This is so that it is guaranteed that any
+// task posted to the source thread can safely access the source
+// NetworkChangeNotifier.
+//
+// - Ref-counting this class is necessary, although as a consequence
+// we can't make any guarantees about which thread will destroy an
+// instance. Earlier versions of this class tried to get away
+// without ref-counting. One version deleted the class in
+// Unobserve(); this didn't work because there may still be
+// TargetObserverOnIPAddressChanged() tasks on the target thread.
+// An attempt to fix this was to post a DeleteTask on the target
+// thread from Unobserve(), but this meant that there would be no
+// way of knowing when on the target thread the instance would be
+// deleted. Indeed, as mentioned above, any tasks posted on the
+// target thread may not run, so this introduced the possibility of
+// a memory leak.
+//
+// - It is important that all posted tasks that work with a proxy be
+// RunnableMethods so that the ref-counting guarantees that the
+// proxy is still valid when the task runs.
+
+#include "chrome/browser/sync/net/network_change_observer_proxy.h"
+
+#include <cstddef>
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+
+namespace browser_sync {
+
+NetworkChangeObserverProxy::NetworkChangeObserverProxy(
+ MessageLoop* source_message_loop,
+ net::NetworkChangeNotifier* source_network_change_notifier,
+ MessageLoop* target_message_loop)
+ : source_message_loop_(source_message_loop),
+ source_network_change_notifier_(source_network_change_notifier),
+ target_message_loop_(target_message_loop),
+ target_observer_(NULL) {
+ DCHECK(source_message_loop_);
+ DCHECK(source_network_change_notifier_);
+ DCHECK(target_message_loop_);
+ DCHECK_NE(source_message_loop_, target_message_loop_);
+ DCHECK_EQ(MessageLoop::current(), target_message_loop_);
+}
+
+NetworkChangeObserverProxy::~NetworkChangeObserverProxy() {
+ MessageLoop* current_message_loop = MessageLoop::current();
+ // We can be deleted on either the source or target thread, so the
+ // best we can do is check that we're on either.
+ DCHECK((current_message_loop == source_message_loop_) ||
+ (current_message_loop == target_message_loop_));
+ // Even though only the target thread uses target_observer_, it
+ // should still be unset even if we're on the source thread; posting
+ // a task is effectively a memory barrier.
+ DCHECK(!target_observer_);
+}
+
+void NetworkChangeObserverProxy::Observe() {
+ DCHECK_EQ(MessageLoop::current(), source_message_loop_);
+ source_network_change_notifier_->AddObserver(this);
+}
+
+// The Task from which this was called may hold the last reference to
+// us (this is how we can get deleted on the source thread).
+void NetworkChangeObserverProxy::Unobserve() {
+ DCHECK_EQ(MessageLoop::current(), source_message_loop_);
+ source_network_change_notifier_->RemoveObserver(this);
+}
+
+void NetworkChangeObserverProxy::Attach(
+ net::NetworkChangeNotifier::Observer* target_observer) {
+ DCHECK_EQ(MessageLoop::current(), target_message_loop_);
+ DCHECK(!target_observer_);
+ target_observer_ = target_observer;
+ DCHECK(target_observer_);
+ source_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &NetworkChangeObserverProxy::Observe));
+}
+
+void NetworkChangeObserverProxy::Detach() {
+ DCHECK_EQ(MessageLoop::current(), target_message_loop_);
+ DCHECK(target_observer_);
+ target_observer_ = NULL;
+ source_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &NetworkChangeObserverProxy::Unobserve));
+}
+
+// Although we may get this event after Detach() has been called on
+// the target thread, we know that Unobserve() hasn't been called yet.
+// But we know that it has been posted, so it at least holds a
+// reference to us.
+void NetworkChangeObserverProxy::OnIPAddressChanged() {
+ DCHECK_EQ(MessageLoop::current(), source_message_loop_);
+ target_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &NetworkChangeObserverProxy::TargetObserverOnIPAddressChanged));
+}
+
+// The Task from which this was called may hold the last reference to
+// us (this is how we can get deleted on the target thread).
+void NetworkChangeObserverProxy::TargetObserverOnIPAddressChanged() {
+ DCHECK_EQ(MessageLoop::current(), target_message_loop_);
+ if (target_observer_) {
+ target_observer_->OnIPAddressChanged();
+ }
+}
+
+} // 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
new file mode 100644
index 0000000..24eff05
--- /dev/null
+++ b/chrome/browser/sync/net/network_change_observer_proxy.h
@@ -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.
+
+#ifndef CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_OBSERVER_PROXY_H_
+#define CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_OBSERVER_PROXY_H_
+
+// NetworkChangeObserverProxy is a class that listens to a
+// NetworkChangeNotifier on one thread (the source thread, which is
+// usually the Chrome IO thread) and emits events to a target observer
+// on another thread (the target thread).
+//
+// How to use:
+//
+// In the target thread, create the observer proxy:
+//
+// NetworkChangeObserverProxy* proxy =
+// new NetworkChangeObserverProxy(source_thread->message_loop(),
+// source_network_change_notifier,
+// MessageLoop::current());
+//
+// Both source_thread and source_network_change_notifier must be
+// guaranteed to outlive the target (current) thread.
+//
+// Then, attach the target observer:
+//
+// proxy->Attach(target_observer);
+//
+// target_observer will then begin to receive events on the target
+// thread.
+//
+// If you call Attach(), you *must* call Detach() before releasing:
+//
+// proxy->Detach();
+// proxy->Release(); // omit if proxy is a scoped_refptr
+// proxy = NULL;
+//
+// The proxy may be destroyed on either the source or the target
+// thread (depending on which one ends up holding the last reference).
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "net/base/network_change_notifier.h"
+
+namespace browser_sync {
+
+class NetworkChangeObserverProxy
+ : public base::RefCountedThreadSafe<NetworkChangeObserverProxy>,
+ private net::NetworkChangeNotifier::Observer {
+ public:
+ // All public methods (including the constructor) must be called on
+ // the target thread.
+
+ // Does not take ownership of any arguments.
+ NetworkChangeObserverProxy(
+ MessageLoop* source_message_loop,
+ net::NetworkChangeNotifier* source_network_change_notifier,
+ MessageLoop* target_message_loop);
+
+ // After this method is called, |target_observer| will start
+ // receiving events on the target thread. Once called, Detach()
+ // must be called before releasing and you cannot call Attach()
+ // again until you have done so. Does not take ownership of
+ // |target_observer|.
+ void Attach(net::NetworkChangeNotifier::Observer* target_observer);
+
+ // After this method is called, the target observer will stop
+ // receiving events. You can call Attach() again after this method
+ // is called.
+ void Detach();
+
+ protected:
+ // May be called on either the source or the target thread. Marked
+ // protected instead of private so that this class can be subclassed
+ // for unit tests.
+ virtual ~NetworkChangeObserverProxy();
+
+ private:
+ friend class base::RefCountedThreadSafe<NetworkChangeObserverProxy>;
+
+ // Adds ourselves as an observer of
+ // |source_network_change_notifier_|. Must be called on the source
+ // thread.
+ void Observe();
+
+ // Removes ourselves as an observer of
+ // |source_network_change_notifier_|. Must be called on the source
+ // thread.
+ void Unobserve();
+
+ // net::NetworkChangeNotifier::Observer implementation.
+ //
+ // Called on the source thread. Posts
+ // TargetObserverOnIPAddressChanged() on the target thread.
+ virtual void OnIPAddressChanged();
+
+ // Called on the target thread. Invokes OnIPAddressChanged() on
+ // |target_observer_|.
+ void TargetObserverOnIPAddressChanged();
+
+ MessageLoop* const source_message_loop_;
+ net::NetworkChangeNotifier* const source_network_change_notifier_;
+ MessageLoop* const target_message_loop_;
+ // |target_observer_| is used only by the target thread.
+ net::NetworkChangeNotifier::Observer* target_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeObserverProxy);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_OBSERVER_PROXY_H_
diff --git a/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc b/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc
new file mode 100644
index 0000000..adb0f81
--- /dev/null
+++ b/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc
@@ -0,0 +1,351 @@
+// 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_observer_proxy.h"
+
+#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/thread.h"
+#include "base/waitable_event.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*) {}
+};
+
+template <>
+struct RunnableMethodTraits<net::MockNetworkChangeNotifier> {
+ void RetainCallee(net::MockNetworkChangeNotifier*) {}
+ void ReleaseCallee(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
+// it was deleted.
+class DeleteCheckingNetworkChangeObserverProxy
+ : public NetworkChangeObserverProxy {
+ public:
+ // *deleting_message_loop_ must be NULL. It is set to a non-NULL
+ // *value when this object is deleted.
+ DeleteCheckingNetworkChangeObserverProxy(
+ MessageLoop* source_message_loop,
+ net::NetworkChangeNotifier* source_network_change_notifier,
+ MessageLoop* target_message_loop,
+ MessageLoop** deleting_message_loop)
+ : NetworkChangeObserverProxy(source_message_loop,
+ source_network_change_notifier,
+ target_message_loop),
+ deleting_message_loop_(deleting_message_loop) {
+ CHECK(deleting_message_loop_);
+ EXPECT_TRUE(*deleting_message_loop_ == NULL);
+ }
+
+ private:
+ virtual ~DeleteCheckingNetworkChangeObserverProxy() {
+ *deleting_message_loop_ = MessageLoop::current();
+ }
+
+ MessageLoop** deleting_message_loop_;
+
+ 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) {}
+
+ virtual ~NetworkChangeObserverProxyTest() {
+ 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();
+
+ proxy_deleting_message_loop_ = NULL;
+ proxy_ = new DeleteCheckingNetworkChangeObserverProxy(
+ source_message_loop_, &source_network_change_notifier_,
+ &target_message_loop_, &proxy_deleting_message_loop_);
+ }
+
+ // On TearDown, proxy_ must be released and both source and target
+ // must be pumped.
+ virtual void TearDown() {
+ EXPECT_TRUE(proxy_ == NULL);
+ EXPECT_TRUE(proxy_deleting_message_loop_ != NULL);
+ source_thread_blocker_->Unblock();
+ source_thread_blocker_.reset();
+ source_message_loop_ = NULL;
+ source_thread_.Stop();
+ }
+
+ // Pump any events posted on the source thread.
+ void PumpSource() {
+ source_thread_blocker_->Unblock();
+ source_thread_blocker_->Block();
+ }
+
+ // 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();
+ }
+ }
+
+ // Trigger an "IP address changed" event on the source network
+ // change notifier on the source thread.
+ void OnIPAddressChanged() {
+ source_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ &source_network_change_notifier_,
+ &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.
+ 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_;
+
+ MessageLoop* proxy_deleting_message_loop_;
+ scoped_refptr<NetworkChangeObserverProxy> proxy_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeObserverProxyTest);
+};
+
+// Make sure nothing blows up if we don't attach the proxy.
+TEST_F(NetworkChangeObserverProxyTest, DoNothing) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ proxy_ = NULL;
+ // No need to pump.
+
+ EXPECT_EQ(proxy_deleting_message_loop_, &target_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, AttachDetach) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ proxy_->Attach(&target_observer_);
+ proxy_->Detach();
+ proxy_ = NULL;
+ PumpSource();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, source_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, AttachDetachReleaseAfterPump) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ proxy_->Attach(&target_observer_);
+ proxy_->Detach();
+ PumpSource();
+ proxy_ = NULL;
+
+ EXPECT_EQ(proxy_deleting_message_loop_, &target_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, Basic) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(1);
+
+ proxy_->Attach(&target_observer_);
+ OnIPAddressChanged();
+ PumpSource();
+ PumpTarget();
+ proxy_->Detach();
+ proxy_ = NULL;
+ PumpSource();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, source_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, Multiple) {
+ const int kTimes = 5;
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(kTimes);
+
+ proxy_->Attach(&target_observer_);
+ for (int i = 0; i < kTimes; ++i) {
+ OnIPAddressChanged();
+ }
+ PumpSource();
+ PumpTarget();
+ proxy_->Detach();
+ proxy_ = NULL;
+ PumpSource();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, source_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, MultipleAttachDetach) {
+ const int kTimes = 5;
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(kTimes);
+
+ for (int i = 0; i < kTimes; ++i) {
+ proxy_->Attach(&target_observer_);
+ OnIPAddressChanged();
+ PumpSource();
+ PumpTarget();
+ proxy_->Detach();
+ }
+ proxy_ = NULL;
+ PumpSource();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, source_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, IgnoresEventPostedAfterDetach) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ proxy_->Attach(&target_observer_);
+ OnIPAddressChanged();
+ proxy_->Detach();
+ proxy_ = NULL;
+ PumpSource();
+ PumpTarget();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, &target_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, IgnoresEventPostedBeforeDetach) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ proxy_->Attach(&target_observer_);
+ OnIPAddressChanged();
+ PumpSource();
+ proxy_->Detach();
+ proxy_ = NULL;
+ PumpTarget();
+ PumpSource();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, source_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, IgnoresEventAfterDetach) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ proxy_->Attach(&target_observer_);
+ proxy_->Detach();
+ proxy_ = NULL;
+ OnIPAddressChanged();
+ PumpSource();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, source_message_loop_);
+}
+
+TEST_F(NetworkChangeObserverProxyTest, IgnoresEventBeforeAttach) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ OnIPAddressChanged();
+ PumpSource();
+ proxy_->Attach(&target_observer_);
+ proxy_->Detach();
+ proxy_ = NULL;
+ PumpSource();
+
+ EXPECT_EQ(proxy_deleting_message_loop_, source_message_loop_);
+}
+
+} // namespace
+
+} // namespace browser_sync
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 49bb392..35d7319 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1024,6 +1024,19 @@
}],
],
},
+ {
+ # TODO(akalin): Consider moving this into its own file.
+ 'target_name': 'sync_net',
+ 'type': '<(library)',
+ 'sources': [
+ 'browser/sync/net/network_change_observer_proxy.cc',
+ 'browser/sync/net/network_change_observer_proxy.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../net/net.gyp:net_base',
+ ],
+ },
],
'conditions': [
['OS=="mac"',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 3b4cc66..0a9af8d 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1774,6 +1774,38 @@
],
},
{
+ # TODO(akalin): Add this to all.gyp.
+ # TODO(akalin): Consider moving this into its own file.
+ 'target_name': 'sync_net_unit_tests',
+ 'type': 'executable',
+ 'sources': [
+ # TODO(akalin): Write our own test suite and runner.
+ '../base/test/run_all_unittests.cc',
+ '../base/test/test_suite.h',
+ 'browser/sync/net/network_change_observer_proxy_unittest.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ 'sync_net',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ ],
+ # TODO(akalin): Remove this once we have our own test suite and
+ # runner.
+ 'conditions': [
+ ['OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
+ 'dependencies': [
+ # Needed to handle the #include chain:
+ # base/test/test_suite.h
+ # gtk/gtk.h
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ],
+ },
+ {
'target_name': 'sync_integration_tests',
'type': 'executable',
'dependencies': [