Index: chrome/browser/sync/glue/sync_backend_host_unittest.cc |
diff --git a/chrome/browser/sync/glue/sync_backend_host_unittest.cc b/chrome/browser/sync/glue/sync_backend_host_unittest.cc |
index 14774a4568c71b0aca9d7a5216047e0c390b546b..075726b8c67664c9cde6f7fb7bf58c6001eb95c5 100644 |
--- a/chrome/browser/sync/glue/sync_backend_host_unittest.cc |
+++ b/chrome/browser/sync/glue/sync_backend_host_unittest.cc |
@@ -8,6 +8,8 @@ |
#include "base/memory/scoped_ptr.h" |
#include "base/message_loop.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/test/test_timeouts.h" |
#include "chrome/browser/sync/invalidations/invalidator_storage.h" |
#include "chrome/browser/sync/sync_prefs.h" |
#include "chrome/test/base/testing_profile.h" |
@@ -16,6 +18,8 @@ |
#include "net/url_request/test_url_fetcher_factory.h" |
#include "sync/internal_api/public/base/model_type.h" |
#include "sync/internal_api/public/engine/model_safe_worker.h" |
+#include "sync/internal_api/public/sync_manager_factory.h" |
+#include "sync/internal_api/public/test/fake_sync_manager.h" |
#include "sync/internal_api/public/util/experiments.h" |
#include "sync/protocol/encryption.pb.h" |
#include "sync/protocol/sync_protocol_error.h" |
@@ -24,11 +28,27 @@ |
#include "testing/gtest/include/gtest/gtest.h" |
using content::BrowserThread; |
+using syncer::FakeSyncManager; |
+using syncer::SyncManager; |
+using ::testing::InvokeWithoutArgs; |
+using ::testing::_; |
namespace browser_sync { |
namespace { |
+ACTION_P(Signal, event) { |
+ event->Signal(); |
+} |
+ |
+void SignalEvent(base::WaitableEvent* event) { |
+ event->Signal(); |
+} |
+ |
+static void QuitMessageLoop() { |
+ MessageLoop::current()->Quit(); |
+} |
+ |
class MockSyncFrontend : public SyncFrontend { |
public: |
virtual ~MockSyncFrontend() {} |
@@ -56,21 +76,103 @@ class MockSyncFrontend : public SyncFrontend { |
MOCK_METHOD0(OnSyncConfigureRetry, void()); |
}; |
-} // namespace |
+class FakeSyncManagerFactory : public syncer::SyncManagerFactory { |
+ public: |
+ FakeSyncManagerFactory() : manager_(NULL) {} |
+ virtual ~FakeSyncManagerFactory() {} |
+ |
+ // Takes ownership of |manager|. |
+ void SetSyncManager(FakeSyncManager* manager) { |
+ DCHECK(!manager_.get()); |
+ manager_.reset(manager); |
+ } |
+ |
+ // Passes ownership of |manager_|. |
+ // SyncManagerFactory implementation. |
+ virtual scoped_ptr<SyncManager> CreateSyncManager(std::string name) OVERRIDE { |
+ DCHECK(manager_.get()); |
+ return manager_.Pass(); |
+ } |
+ |
+ private: |
+ scoped_ptr<SyncManager> manager_; |
+}; |
+ |
+class TestSyncBackendHost : public SyncBackendHost { |
+ public: |
+ TestSyncBackendHost( |
+ const std::string& name, |
+ Profile* profile, |
+ const base::WeakPtr<SyncPrefs>& sync_prefs, |
+ const base::WeakPtr<InvalidatorStorage>& invalidator_storage) |
+ : SyncBackendHost(name, profile, sync_prefs, invalidator_storage) { |
+ StartSyncThread(); |
+ } |
+ virtual ~TestSyncBackendHost() {} |
+ |
+ void PumpSyncLoop() { |
rlarocque
2012/07/04 06:39:35
I'm amazed that there isn't a better way to do thi
Nicolas Zea
2012/07/09 18:40:45
How would MessageLoop::Quit help? The issue is tha
rlarocque
2012/07/09 19:22:58
My point is that it shouldn't be the test's respon
Nicolas Zea
2012/07/09 19:42:50
We do these kind of checks anywhere a single actio
|
+ DCHECK(sync_loop()); |
+ base::WaitableEvent done(true, false); |
+ sync_loop()->PostTask(FROM_HERE, base::Bind(&SignalEvent, &done)); |
+ done.TimedWait(base::TimeDelta::FromMilliseconds( |
+ TestTimeouts::action_timeout_ms())); |
+ if (!done.IsSignaled()) |
+ FAIL() << "Timed out waiting for sync loop."; |
+ } |
+ |
+ void PostToSyncLoop(const base::Closure& closure) { |
+ DCHECK(sync_loop()); |
+ sync_loop()->PostTask(FROM_HERE, closure); |
+ } |
+}; |
class SyncBackendHostTest : public testing::Test { |
protected: |
SyncBackendHostTest() |
: ui_thread_(BrowserThread::UI, &ui_loop_), |
- io_thread_(BrowserThread::IO) {} |
+ io_thread_(BrowserThread::IO), |
+ fake_manager_(NULL) {} |
virtual ~SyncBackendHostTest() {} |
- virtual void SetUp() { |
+ virtual void SetUp() OVERRIDE { |
io_thread_.StartIOThread(); |
+ profile_.reset(new TestingProfile()); |
+ profile_->CreateRequestContext(); |
+ sync_prefs_.reset(new SyncPrefs(profile_->GetPrefs())); |
+ invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); |
+ backend_.reset(new TestSyncBackendHost( |
+ profile_->GetDebugName(), |
+ profile_.get(), |
+ sync_prefs_->AsWeakPtr(), |
+ invalidator_storage_->AsWeakPtr())); |
+ credentials_.email = "user@example.com"; |
+ credentials_.sync_token = "sync_token"; |
+ |
+ // The sync manager must be created on the sync loop in order to allow |
+ // thread safety enforcement. |
+ PostToSyncLoop(base::Bind(&SyncBackendHostTest::BuildSyncManager, |
+ base::Unretained(this))); |
+ PumpSyncLoop(); |
+ fake_sync_manager_factory_.SetSyncManager(fake_manager_); |
+ |
+ // NOTE: We can't include Passwords or Typed URLs due to the Sync Backend |
+ // Registrar removing them if it can't find their model workers. |
+ enabled_types_.Put(syncer::BOOKMARKS); |
+ enabled_types_.Put(syncer::NIGORI); |
+ enabled_types_.Put(syncer::PREFERENCES); |
+ enabled_types_.Put(syncer::SESSIONS); |
+ enabled_types_.Put(syncer::SEARCH_ENGINES); |
+ enabled_types_.Put(syncer::AUTOFILL); |
} |
- virtual void TearDown() { |
+ virtual void TearDown() OVERRIDE { |
+ backend_->StopSyncingForShutdown(); |
+ backend_->Shutdown(false); |
+ backend_.reset(); |
+ sync_prefs_.reset(); |
+ invalidator_storage_.reset(); |
+ profile_.reset(); |
// Pump messages posted by the sync core thread (which may end up |
// posting on the IO thread). |
ui_loop_.RunAllPending(); |
@@ -79,44 +181,168 @@ class SyncBackendHostTest : public testing::Test { |
ui_loop_.RunAllPending(); |
} |
- private: |
+ // Synchronously initializes the backend. |
+ void InitializeBackend(syncer::ModelTypeSet enabled_types) { |
+ EXPECT_CALL(mock_frontend_, OnBackendInitialized(_, true)). |
+ WillOnce(InvokeWithoutArgs(QuitMessageLoop)); |
+ backend_->Initialize(&mock_frontend_, |
+ syncer::WeakHandle<syncer::JsEventHandler>(), |
+ GURL(""), |
+ enabled_types, |
+ credentials_, |
+ true, |
+ &fake_sync_manager_factory_, |
+ &handler_, |
+ NULL); |
+ ui_loop_.PostDelayedTask( |
+ FROM_HERE, |
+ ui_loop_.QuitClosure(), |
+ base::TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms())); |
+ ui_loop_.Run(); |
+ } |
+ |
+ // Synchronously configures the backend's datatypes. |
+ void ConfigureDataTypes(syncer::ModelTypeSet types_to_add, |
+ syncer::ModelTypeSet types_to_remove, |
+ BackendDataTypeConfigurer::NigoriState nigori_state) { |
+ backend_->ConfigureDataTypes( |
+ syncer::CONFIGURE_REASON_RECONFIGURATION, |
+ types_to_add, |
+ types_to_remove, |
+ nigori_state, |
+ base::Bind(&SyncBackendHostTest::DownloadReady, |
+ base::Unretained(this)), |
+ base::Bind(&SyncBackendHostTest::OnDownloadRetry, |
+ base::Unretained(this))); |
+ ui_loop_.PostDelayedTask( |
+ FROM_HERE, |
+ ui_loop_.QuitClosure(), |
+ base::TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms())); |
+ ui_loop_.Run(); |
+ } |
+ |
+ protected: |
+ // Note: should run on the sync thread. |
+ void BuildSyncManager() { |
+ DCHECK(!fake_manager_); |
+ fake_manager_ = new FakeSyncManager("name"); |
+ } |
+ |
+ void PumpSyncLoop() { |
+ backend_->PumpSyncLoop(); |
+ } |
+ |
+ void PostToSyncLoop(const base::Closure& closure) { |
+ backend_->PostToSyncLoop(closure); |
+ } |
+ |
+ void DownloadReady(syncer::ModelTypeSet types) { |
+ MessageLoop::current()->Quit(); |
+ } |
+ |
+ void OnDownloadRetry() { |
+ NOTIMPLEMENTED(); |
+ } |
+ |
MessageLoop ui_loop_; |
content::TestBrowserThread ui_thread_; |
content::TestBrowserThread io_thread_; |
+ MockSyncFrontend mock_frontend_; |
+ syncer::SyncCredentials credentials_; |
+ syncer::TestUnrecoverableErrorHandler handler_; |
+ scoped_ptr<TestingProfile> profile_; |
+ scoped_ptr<SyncPrefs> sync_prefs_; |
+ scoped_ptr<InvalidatorStorage> invalidator_storage_; |
+ scoped_ptr<TestSyncBackendHost> backend_; |
+ FakeSyncManagerFactory fake_sync_manager_factory_; |
+ FakeSyncManager* fake_manager_; |
+ syncer::ModelTypeSet enabled_types_; |
}; |
+// Test basic initialization with no initial types (first time initialization). |
+// Only the nigori should be configured. |
TEST_F(SyncBackendHostTest, InitShutdown) { |
- std::string k_mock_url = "http://www.example.com"; |
- net::FakeURLFetcherFactory test_factory_; |
- test_factory_.SetFakeResponse(k_mock_url + "/time?command=get_time", "", |
- false); |
- |
- TestingProfile profile; |
- profile.CreateRequestContext(); |
- |
- SyncPrefs sync_prefs(profile.GetPrefs()); |
- InvalidatorStorage invalidator_storage(profile.GetPrefs()); |
- SyncBackendHost backend(profile.GetDebugName(), |
- &profile, sync_prefs.AsWeakPtr(), |
- invalidator_storage.AsWeakPtr()); |
- |
- MockSyncFrontend mock_frontend; |
- syncer::SyncCredentials credentials; |
- credentials.email = "user@example.com"; |
- credentials.sync_token = "sync_token"; |
- syncer::TestUnrecoverableErrorHandler handler; |
- backend.Initialize(&mock_frontend, |
- syncer::WeakHandle<syncer::JsEventHandler>(), |
- GURL(k_mock_url), |
+ InitializeBackend(syncer::ModelTypeSet()); |
+ |
+ EXPECT_TRUE(fake_manager_->InitialSyncEndedTypes().Has(syncer::NIGORI)); |
+ EXPECT_TRUE(fake_manager_->GetTypesWithEmptyProgressMarkerToken( |
+ syncer::ModelTypeSet(syncer::NIGORI)).Empty()); |
+} |
+ |
+// Test first time sync scenario. All types should be properly configured. |
+TEST_F(SyncBackendHostTest, FirstTimeSync) { |
+ InitializeBackend(syncer::ModelTypeSet()); |
+ ConfigureDataTypes(enabled_types_, |
+ syncer::ModelTypeSet(), |
+ BackendDataTypeConfigurer::WITH_NIGORI); |
+ |
+ EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().HasAll( |
+ enabled_types_)); |
+ EXPECT_TRUE(fake_manager_->InitialSyncEndedTypes().Equals(enabled_types_)); |
+ EXPECT_TRUE(fake_manager_->GetTypesWithEmptyProgressMarkerToken( |
+ enabled_types_).Empty()); |
+} |
+ |
+// Test the restart after setting up sync scenario. No enabled types should be |
+// downloaded or cleaned. |
+TEST_F(SyncBackendHostTest, Restart) { |
+ syncer::ModelTypeSet all_but_nigori = enabled_types_; |
+ fake_manager_->set_progress_marker_types( |
+ enabled_types_); |
+ fake_manager_->set_initial_sync_ended_types(enabled_types_); |
+ InitializeBackend(enabled_types_); |
+ ConfigureDataTypes(enabled_types_, |
+ syncer::ModelTypeSet(), |
+ BackendDataTypeConfigurer::WITH_NIGORI); |
+ |
+ EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty()); |
+ EXPECT_TRUE(Intersection(fake_manager_->GetAndResetCleanedTypes(), |
+ enabled_types_).Empty()); |
+ EXPECT_TRUE(fake_manager_->InitialSyncEndedTypes().Equals(enabled_types_)); |
+ EXPECT_TRUE(fake_manager_->GetTypesWithEmptyProgressMarkerToken( |
+ enabled_types_).Empty()); |
+} |
+ |
+// Test a sync restart scenario where the nigori had never finished configuring. |
+// The nigori should be cleaned up, then reconfigured properly. |
+TEST_F(SyncBackendHostTest, PartialNigori) { |
+ // Set sync manager behavior before passing it down. All types have progress |
+ // markers, but nigori is missing initial sync ended. |
+ syncer::ModelTypeSet all_but_nigori = enabled_types_; |
+ all_but_nigori.Remove(syncer::NIGORI); |
+ fake_manager_->set_progress_marker_types( |
+ enabled_types_); |
+ fake_manager_->set_initial_sync_ended_types(all_but_nigori); |
+ InitializeBackend(enabled_types_); |
+ |
+ // Nigori should have been cleaned up, then configured. |
+ EXPECT_TRUE(Intersection(fake_manager_->GetAndResetCleanedTypes(), |
+ enabled_types_). |
+ Equals(syncer::ModelTypeSet(syncer::NIGORI))); |
+ EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Equals( |
+ syncer::ModelTypeSet(syncer::NIGORI))); |
+ EXPECT_TRUE(fake_manager_->InitialSyncEndedTypes().Has(syncer::NIGORI)); |
+} |
+ |
+// Test the behavior when we lose the sync db. Although we already have types |
+// enabled, we should re-download all of them because we lost their data. |
+TEST_F(SyncBackendHostTest, LostDB) { |
+ // Don't set any progress marker or initial_sync_ended types before |
+ // initializing. |
+ InitializeBackend(enabled_types_); |
+ ConfigureDataTypes(enabled_types_, |
syncer::ModelTypeSet(), |
- credentials, |
- true, |
- &handler, |
- NULL); |
- backend.StopSyncingForShutdown(); |
- backend.Shutdown(false); |
+ BackendDataTypeConfigurer::WITH_NIGORI); |
+ |
+ EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().HasAll( |
+ enabled_types_)); |
+ EXPECT_TRUE(Intersection(fake_manager_->GetAndResetCleanedTypes(), |
+ enabled_types_).Empty()); |
+ EXPECT_TRUE(fake_manager_->InitialSyncEndedTypes().Equals(enabled_types_)); |
+ EXPECT_TRUE(fake_manager_->GetTypesWithEmptyProgressMarkerToken( |
+ enabled_types_).Empty()); |
} |
-// TODO(akalin): Write more SyncBackendHost unit tests. |
+} // namespace |
} // namespace browser_sync |