| Index: components/sync_driver/non_blocking_data_type_controller_unittest.cc
|
| diff --git a/components/sync_driver/non_blocking_data_type_controller_unittest.cc b/components/sync_driver/non_blocking_data_type_controller_unittest.cc
|
| index 43c4fa4d25cbe47ab9868db4231e5b817bd13428..b1c8dc6b1d8667e215d64cc213b3e97c5082185f 100644
|
| --- a/components/sync_driver/non_blocking_data_type_controller_unittest.cc
|
| +++ b/components/sync_driver/non_blocking_data_type_controller_unittest.cc
|
| @@ -2,6 +2,10 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include <list>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| #include "base/memory/ref_counted.h"
|
| #include "base/memory/weak_ptr.h"
|
| #include "base/sequenced_task_runner.h"
|
| @@ -14,22 +18,74 @@
|
|
|
| namespace {
|
|
|
| +// A class that pretends to be the sync backend.
|
| +class MockSyncCore {
|
| + public:
|
| + void Connect(
|
| + syncer::ModelType type,
|
| + const scoped_refptr<base::SingleThreadTaskRunner>& model_task_runner,
|
| + const base::WeakPtr<syncer::NonBlockingTypeProcessor>& type_processor) {
|
| + enabled_types_.Put(type);
|
| + model_task_runner->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&syncer::NonBlockingTypeProcessor::OnConnect,
|
| + type_processor,
|
| + base::WeakPtr<syncer::NonBlockingTypeProcessorCore>(),
|
| + scoped_refptr<base::SequencedTaskRunner>()));
|
| + }
|
| +
|
| + void Disconnect(syncer::ModelType type) {
|
| + DCHECK(enabled_types_.Has(type));
|
| + enabled_types_.Remove(type);
|
| + }
|
| +
|
| + private:
|
| + std::list<base::Closure> tasks_;
|
| + syncer::ModelTypeSet enabled_types_;
|
| +};
|
| +
|
| +// A proxy to the MockSyncCore that implements SyncCoreProxy.
|
| class MockSyncCoreProxy : public syncer::SyncCoreProxy {
|
| public:
|
| - MockSyncCoreProxy() {}
|
| + MockSyncCoreProxy(
|
| + MockSyncCore* sync_core,
|
| + const scoped_refptr<base::TestSimpleTaskRunner>& model_task_runner,
|
| + const scoped_refptr<base::TestSimpleTaskRunner>& sync_task_runner)
|
| + : mock_sync_core_(sync_core),
|
| + model_task_runner_(model_task_runner),
|
| + sync_task_runner_(sync_task_runner) {}
|
| virtual ~MockSyncCoreProxy() {}
|
|
|
| virtual void ConnectTypeToCore(
|
| syncer::ModelType type,
|
| base::WeakPtr<syncer::NonBlockingTypeProcessor> type_processor) OVERRIDE {
|
| - type_processor->OnConnect(
|
| - base::WeakPtr<syncer::NonBlockingTypeProcessorCore>(),
|
| - scoped_refptr<base::SequencedTaskRunner>());
|
| + // Normally we'd use MessageLoopProxy::current() as the TaskRunner argument
|
| + // to Connect(). That won't work here in this test, so we use the
|
| + // model_task_runner_ that was injected for this purpose instead.
|
| + sync_task_runner_->PostTask(FROM_HERE,
|
| + base::Bind(&MockSyncCore::Connect,
|
| + base::Unretained(mock_sync_core_),
|
| + type,
|
| + model_task_runner_,
|
| + type_processor));
|
| + }
|
| +
|
| + virtual void Disconnect(syncer::ModelType type) OVERRIDE {
|
| + sync_task_runner_->PostTask(FROM_HERE,
|
| + base::Bind(&MockSyncCore::Disconnect,
|
| + base::Unretained(mock_sync_core_),
|
| + type));
|
| }
|
|
|
| virtual scoped_ptr<SyncCoreProxy> Clone() const OVERRIDE {
|
| - return scoped_ptr<SyncCoreProxy>(new MockSyncCoreProxy());
|
| + return scoped_ptr<SyncCoreProxy>(new MockSyncCoreProxy(
|
| + mock_sync_core_, model_task_runner_, sync_task_runner_));
|
| }
|
| +
|
| + private:
|
| + MockSyncCore* mock_sync_core_;
|
| + scoped_refptr<base::TestSimpleTaskRunner> model_task_runner_;
|
| + scoped_refptr<base::TestSimpleTaskRunner> sync_task_runner_;
|
| };
|
|
|
| class NonBlockingDataTypeControllerTest : public testing::Test {
|
| @@ -37,48 +93,75 @@ class NonBlockingDataTypeControllerTest : public testing::Test {
|
| NonBlockingDataTypeControllerTest()
|
| : processor_(syncer::DICTIONARY),
|
| model_thread_(new base::TestSimpleTaskRunner()),
|
| - controller_(syncer::DICTIONARY, true) {}
|
| + sync_thread_(new base::TestSimpleTaskRunner()),
|
| + controller_(syncer::DICTIONARY, true),
|
| + mock_core_proxy_(&mock_sync_core_, model_thread_, sync_thread_),
|
| + auto_run_tasks_(true) {}
|
|
|
| virtual ~NonBlockingDataTypeControllerTest() {}
|
|
|
| // Connects the processor to the NonBlockingDataTypeController.
|
| void InitProcessor() {
|
| - controller_.InitializeProcessor(
|
| - model_thread_, processor_.AsWeakPtr());
|
| - RunQueuedTasks();
|
| + controller_.InitializeProcessor(model_thread_, processor_.AsWeakPtrForUI());
|
| + if (auto_run_tasks_) {
|
| + RunAllTasks();
|
| + }
|
| }
|
|
|
| // Connects the sync backend to the NonBlockingDataTypeController.
|
| void InitSync() {
|
| controller_.InitializeSyncCoreProxy(mock_core_proxy_.Clone());
|
| - RunQueuedTasks();
|
| + if (auto_run_tasks_) {
|
| + RunAllTasks();
|
| + }
|
| }
|
|
|
| // Disconnects the sync backend from the NonBlockingDataTypeController.
|
| void UninitializeSync() {
|
| controller_.ClearSyncCoreProxy();
|
| - RunQueuedTasks();
|
| + if (auto_run_tasks_) {
|
| + RunAllTasks();
|
| + }
|
| }
|
|
|
| // Toggles the user's preference for syncing this type.
|
| void SetIsPreferred(bool preferred) {
|
| controller_.SetIsPreferred(preferred);
|
| - RunQueuedTasks();
|
| + if (auto_run_tasks_) {
|
| + RunAllTasks();
|
| + }
|
| + }
|
| +
|
| + // These threads can ping-pong for a bit so we run the model thread twice.
|
| + void RunAllTasks() {
|
| + RunQueuedModelThreadTasks();
|
| + RunQueuedSyncThreadTasks();
|
| + RunQueuedModelThreadTasks();
|
| }
|
|
|
| // The processor pretends to run tasks on a different thread.
|
| // This function runs any posted tasks.
|
| - void RunQueuedTasks() {
|
| - model_thread_->RunUntilIdle();
|
| + void RunQueuedModelThreadTasks() { model_thread_->RunUntilIdle(); }
|
| +
|
| + // Processes any pending connect or disconnect requests and sends
|
| + // responses synchronously.
|
| + void RunQueuedSyncThreadTasks() { sync_thread_->RunUntilIdle(); }
|
| +
|
| + void SetAutoRunTasks(bool auto_run_tasks) {
|
| + auto_run_tasks_ = auto_run_tasks;
|
| }
|
|
|
| protected:
|
| - MockSyncCoreProxy mock_core_proxy_;
|
| -
|
| syncer::NonBlockingTypeProcessor processor_;
|
| scoped_refptr<base::TestSimpleTaskRunner> model_thread_;
|
| + scoped_refptr<base::TestSimpleTaskRunner> sync_thread_;
|
|
|
| browser_sync::NonBlockingDataTypeController controller_;
|
| +
|
| + MockSyncCore mock_sync_core_;
|
| + MockSyncCoreProxy mock_core_proxy_;
|
| +
|
| + bool auto_run_tasks_;
|
| };
|
|
|
| // Initialization when the user has disabled syncing for this type.
|
| @@ -224,4 +307,103 @@ TEST_F(NonBlockingDataTypeControllerTest, TogglePreferenceWithoutBackend) {
|
| EXPECT_TRUE(processor_.IsPreferred());
|
| }
|
|
|
| +// Turns off auto-task-running to test the effects of delaying a connection
|
| +// response.
|
| +//
|
| +// This is mostly a test of the test framework. It's not very interesting on
|
| +// its own, but it provides a useful "control" against some of the more
|
| +// complicated race tests below.
|
| +TEST_F(NonBlockingDataTypeControllerTest, DelayedConnect) {
|
| + SetAutoRunTasks(false);
|
| +
|
| + SetIsPreferred(true);
|
| + InitProcessor();
|
| + InitSync();
|
| +
|
| + // Allow the model to emit the request.
|
| + RunQueuedModelThreadTasks();
|
| +
|
| + // That should result in a request to connect, but it won't be
|
| + // executed right away.
|
| + EXPECT_FALSE(processor_.IsConnected());
|
| + EXPECT_TRUE(processor_.IsPreferred());
|
| +
|
| + // Let the sync thread process the request and the model thread handle its
|
| + // response.
|
| + RunQueuedSyncThreadTasks();
|
| + RunQueuedModelThreadTasks();
|
| +
|
| + EXPECT_TRUE(processor_.IsConnected());
|
| + EXPECT_TRUE(processor_.IsPreferred());
|
| +}
|
| +
|
| +// Send Disable signal while a connection request is in progress.
|
| +TEST_F(NonBlockingDataTypeControllerTest, DisableRacesWithOnConnect) {
|
| + SetAutoRunTasks(false);
|
| +
|
| + SetIsPreferred(true);
|
| + InitProcessor();
|
| + InitSync();
|
| +
|
| + // Allow the model to emit the request.
|
| + RunQueuedModelThreadTasks();
|
| +
|
| + // That should result in a request to connect, but it won't be
|
| + // executed right away.
|
| + EXPECT_FALSE(processor_.IsConnected());
|
| + EXPECT_TRUE(processor_.IsPreferred());
|
| +
|
| + // Send and execute a disable signal before the OnConnect callback returns.
|
| + SetIsPreferred(false);
|
| +
|
| + // Now we let sync process the initial request and the disable request,
|
| + // both of which should be sitting in its queue.
|
| + RunQueuedSyncThreadTasks();
|
| +
|
| + // Let the model thread process any responses received from the sync thread.
|
| + // A plausible error would be that the sync thread returns a "connection OK"
|
| + // message, and this message overrides the request to disable that arrived
|
| + // from the UI thread earlier. We need to make sure that doesn't happen.
|
| + RunQueuedModelThreadTasks();
|
| +
|
| + EXPECT_FALSE(processor_.IsPreferred());
|
| + EXPECT_FALSE(processor_.IsConnected());
|
| +}
|
| +
|
| +// Send a request to enable, then disable, then re-enable the data type.
|
| +//
|
| +// To make it more interesting, we stall the sync thread until all three
|
| +// requests have been passed to the model thread.
|
| +TEST_F(NonBlockingDataTypeControllerTest, EnableDisableEnableRace) {
|
| + SetAutoRunTasks(false);
|
| +
|
| + SetIsPreferred(true);
|
| + InitProcessor();
|
| + InitSync();
|
| + RunQueuedModelThreadTasks();
|
| +
|
| + // That was the first enable.
|
| + EXPECT_FALSE(processor_.IsConnected());
|
| + EXPECT_TRUE(processor_.IsPreferred());
|
| +
|
| + // Now disable.
|
| + SetIsPreferred(false);
|
| + RunQueuedModelThreadTasks();
|
| + EXPECT_FALSE(processor_.IsPreferred());
|
| +
|
| + // And re-enable.
|
| + SetIsPreferred(true);
|
| + RunQueuedModelThreadTasks();
|
| + EXPECT_TRUE(processor_.IsPreferred());
|
| +
|
| + // The sync thread has three messages related to those enables and
|
| + // disables sittin in its queue. Let's allow it to process them.
|
| + RunQueuedSyncThreadTasks();
|
| +
|
| + // Let the model thread process any messages from the sync thread.
|
| + RunQueuedModelThreadTasks();
|
| + EXPECT_TRUE(processor_.IsPreferred());
|
| + EXPECT_TRUE(processor_.IsConnected());
|
| +}
|
| +
|
| } // namespace
|
|
|