// Copyright (c) 2011 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/js_sync_manager_observer.h" #include <cstddef> #include "base/basictypes.h" #include "base/values.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/js_arg_list.h" #include "chrome/browser/sync/js_test_util.h" #include "chrome/browser/sync/sessions/session_state.h" #include "chrome/browser/sync/syncable/model_type.h" #include "chrome/test/sync/engine/test_user_share.h" #include "testing/gtest/include/gtest/gtest.h" namespace browser_sync { namespace { using ::testing::InSequence; using ::testing::StrictMock; class JsSyncManagerObserverTest : public testing::Test { protected: JsSyncManagerObserverTest() : sync_manager_observer_(&mock_router_) {} StrictMock<MockJsEventRouter> mock_router_; JsSyncManagerObserver sync_manager_observer_; }; TEST_F(JsSyncManagerObserverTest, NoArgNotifiations) { InSequence dummy; EXPECT_CALL(mock_router_, RouteJsEvent("onInitializationComplete", HasArgs(JsArgList()), NULL)); EXPECT_CALL(mock_router_, RouteJsEvent("onPassphraseFailed", HasArgs(JsArgList()), NULL)); EXPECT_CALL(mock_router_, RouteJsEvent("onStopSyncingPermanently", HasArgs(JsArgList()), NULL)); EXPECT_CALL(mock_router_, RouteJsEvent("onClearServerDataSucceeded", HasArgs(JsArgList()), NULL)); EXPECT_CALL(mock_router_, RouteJsEvent("onClearServerDataFailed", HasArgs(JsArgList()), NULL)); sync_manager_observer_.OnInitializationComplete(); sync_manager_observer_.OnPassphraseFailed(); sync_manager_observer_.OnStopSyncingPermanently(); sync_manager_observer_.OnClearServerDataSucceeded(); sync_manager_observer_.OnClearServerDataFailed(); } TEST_F(JsSyncManagerObserverTest, OnChangesComplete) { InSequence dummy; for (int i = syncable::FIRST_REAL_MODEL_TYPE; i < syncable::MODEL_TYPE_COUNT; ++i) { const std::string& model_type_str = syncable::ModelTypeToString(syncable::ModelTypeFromInt(i)); ListValue expected_args; expected_args.Append(Value::CreateStringValue(model_type_str)); EXPECT_CALL(mock_router_, RouteJsEvent("onChangesComplete", HasArgsAsList(expected_args), NULL)); } for (int i = syncable::FIRST_REAL_MODEL_TYPE; i < syncable::MODEL_TYPE_COUNT; ++i) { sync_manager_observer_.OnChangesComplete(syncable::ModelTypeFromInt(i)); } } TEST_F(JsSyncManagerObserverTest, OnSyncCycleCompleted) { std::string download_progress_markers[syncable::MODEL_TYPE_COUNT]; sessions::SyncSessionSnapshot snapshot(sessions::SyncerStatus(), sessions::ErrorCounters(), 100, false, syncable::ModelTypeBitSet(), download_progress_markers, false, true, 100, 5, false, sessions::SyncSourceInfo()); ListValue expected_args; expected_args.Append(snapshot.ToValue()); EXPECT_CALL(mock_router_, RouteJsEvent("onSyncCycleCompleted", HasArgsAsList(expected_args), NULL)); sync_manager_observer_.OnSyncCycleCompleted(&snapshot); } TEST_F(JsSyncManagerObserverTest, OnAuthError) { GoogleServiceAuthError error(GoogleServiceAuthError::TWO_FACTOR); ListValue expected_args; expected_args.Append(error.ToValue()); EXPECT_CALL(mock_router_, RouteJsEvent("onAuthError", HasArgsAsList(expected_args), NULL)); sync_manager_observer_.OnAuthError(error); } TEST_F(JsSyncManagerObserverTest, OnPassphraseRequired) { InSequence dummy; ListValue true_args, false_args; true_args.Append(Value::CreateBooleanValue(true)); false_args.Append(Value::CreateBooleanValue(false)); EXPECT_CALL(mock_router_, RouteJsEvent("onPassphraseRequired", HasArgsAsList(false_args), NULL)); EXPECT_CALL(mock_router_, RouteJsEvent("onPassphraseRequired", HasArgsAsList(true_args), NULL)); sync_manager_observer_.OnPassphraseRequired(false); sync_manager_observer_.OnPassphraseRequired(true); } TEST_F(JsSyncManagerObserverTest, SensitiveNotifiations) { ListValue redacted_args; redacted_args.Append(Value::CreateStringValue("<redacted>")); EXPECT_CALL(mock_router_, RouteJsEvent("onUpdatedToken", HasArgsAsList(redacted_args), NULL)); EXPECT_CALL(mock_router_, RouteJsEvent("onPassphraseAccepted", HasArgsAsList(redacted_args), NULL)); sync_manager_observer_.OnUpdatedToken("sensitive_token"); sync_manager_observer_.OnPassphraseAccepted("sensitive_token"); } TEST_F(JsSyncManagerObserverTest, OnEncryptionComplete) { ListValue expected_args; ListValue* encrypted_type_values = new ListValue(); syncable::ModelTypeSet encrypted_types; expected_args.Append(encrypted_type_values); for (int i = syncable::FIRST_REAL_MODEL_TYPE; i < syncable::MODEL_TYPE_COUNT; ++i) { syncable::ModelType type = syncable::ModelTypeFromInt(i); encrypted_types.insert(type); encrypted_type_values->Append(Value::CreateStringValue( syncable::ModelTypeToString(type))); } EXPECT_CALL(mock_router_, RouteJsEvent("onEncryptionComplete", HasArgsAsList(expected_args), NULL)); sync_manager_observer_.OnEncryptionComplete(encrypted_types); } TEST_F(JsSyncManagerObserverTest, OnMigrationNeededForTypes) { ListValue expected_args; ListValue* type_values = new ListValue(); syncable::ModelTypeSet types; expected_args.Append(type_values); for (int i = syncable::FIRST_REAL_MODEL_TYPE; i < syncable::MODEL_TYPE_COUNT; ++i) { syncable::ModelType type = syncable::ModelTypeFromInt(i); types.insert(type); type_values->Append(Value::CreateStringValue( syncable::ModelTypeToString(type))); } EXPECT_CALL(mock_router_, RouteJsEvent("onMigrationNeededForTypes", HasArgsAsList(expected_args), NULL)); sync_manager_observer_.OnMigrationNeededForTypes(types); } namespace { // Makes a node of the given model type. Returns the id of the // newly-created node. int64 MakeNode(sync_api::UserShare* share, syncable::ModelType model_type) { sync_api::WriteTransaction trans(share); sync_api::ReadNode root_node(&trans); root_node.InitByRootLookup(); sync_api::WriteNode node(&trans); EXPECT_TRUE(node.InitUniqueByCreation( model_type, root_node, syncable::ModelTypeToString(model_type))); node.SetIsFolder(false); return node.GetId(); } } // namespace TEST_F(JsSyncManagerObserverTest, OnChangesApplied) { InSequence dummy; TestUserShare test_user_share; test_user_share.SetUp(); // We don't test with passwords as that requires additional setup. // Build a list of example ChangeRecords. sync_api::SyncManager::ChangeRecord changes[syncable::MODEL_TYPE_COUNT]; for (int i = syncable::AUTOFILL_PROFILE; i < syncable::MODEL_TYPE_COUNT; ++i) { changes[i].id = MakeNode(test_user_share.user_share(), syncable::ModelTypeFromInt(i)); switch (i % 3) { case 0: changes[i].action = sync_api::SyncManager::ChangeRecord::ACTION_ADD; break; case 1: changes[i].action = sync_api::SyncManager::ChangeRecord::ACTION_UPDATE; break; default: changes[i].action = sync_api::SyncManager::ChangeRecord::ACTION_DELETE; break; } { sync_api::ReadTransaction trans(test_user_share.user_share()); sync_api::ReadNode node(&trans); EXPECT_TRUE(node.InitByIdLookup(changes[i].id)); changes[i].specifics = node.GetEntry()->Get(syncable::SPECIFICS); } } // For each i, we call OnChangesApplied() with the first arg equal // to i cast to ModelType and the second argument with the changes // starting from changes[i]. // Set expectations for each data type. for (int i = syncable::AUTOFILL_PROFILE; i < syncable::MODEL_TYPE_COUNT; ++i) { const std::string& model_type_str = syncable::ModelTypeToString(syncable::ModelTypeFromInt(i)); ListValue expected_args; expected_args.Append(Value::CreateStringValue(model_type_str)); ListValue* expected_changes = new ListValue(); expected_args.Append(expected_changes); for (int j = i; j < syncable::MODEL_TYPE_COUNT; ++j) { sync_api::ReadTransaction trans(test_user_share.user_share()); expected_changes->Append(changes[j].ToValue(&trans)); } EXPECT_CALL(mock_router_, RouteJsEvent("onChangesApplied", HasArgsAsList(expected_args), NULL)); } // Fire OnChangesApplied() for each data type. for (int i = syncable::AUTOFILL_PROFILE; i < syncable::MODEL_TYPE_COUNT; ++i) { sync_api::ReadTransaction trans(test_user_share.user_share()); sync_manager_observer_.OnChangesApplied(syncable::ModelTypeFromInt(i), &trans, &changes[i], syncable::MODEL_TYPE_COUNT - i); } test_user_share.TearDown(); } } // namespace } // namespace browser_sync