1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/sync/js_sync_manager_observer.h" 6 7 #include <cstddef> 8 9 #include "base/basictypes.h" 10 #include "base/values.h" 11 #include "chrome/browser/sync/engine/syncapi.h" 12 #include "chrome/browser/sync/js_arg_list.h" 13 #include "chrome/browser/sync/js_test_util.h" 14 #include "chrome/browser/sync/sessions/session_state.h" 15 #include "chrome/browser/sync/syncable/model_type.h" 16 #include "chrome/test/sync/engine/test_user_share.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 namespace browser_sync { 20 namespace { 21 22 using ::testing::InSequence; 23 using ::testing::StrictMock; 24 25 class JsSyncManagerObserverTest : public testing::Test { 26 protected: 27 JsSyncManagerObserverTest() : sync_manager_observer_(&mock_router_) {} 28 29 StrictMock<MockJsEventRouter> mock_router_; 30 JsSyncManagerObserver sync_manager_observer_; 31 }; 32 33 TEST_F(JsSyncManagerObserverTest, NoArgNotifiations) { 34 InSequence dummy; 35 36 EXPECT_CALL(mock_router_, 37 RouteJsEvent("onInitializationComplete", 38 HasArgs(JsArgList()), NULL)); 39 EXPECT_CALL(mock_router_, 40 RouteJsEvent("onPassphraseFailed", 41 HasArgs(JsArgList()), NULL)); 42 43 EXPECT_CALL(mock_router_, 44 RouteJsEvent("onStopSyncingPermanently", 45 HasArgs(JsArgList()), NULL)); 46 EXPECT_CALL(mock_router_, 47 RouteJsEvent("onClearServerDataSucceeded", 48 HasArgs(JsArgList()), NULL)); 49 EXPECT_CALL(mock_router_, 50 RouteJsEvent("onClearServerDataFailed", 51 HasArgs(JsArgList()), NULL)); 52 53 sync_manager_observer_.OnInitializationComplete(); 54 sync_manager_observer_.OnPassphraseFailed(); 55 sync_manager_observer_.OnStopSyncingPermanently(); 56 sync_manager_observer_.OnClearServerDataSucceeded(); 57 sync_manager_observer_.OnClearServerDataFailed(); 58 } 59 60 TEST_F(JsSyncManagerObserverTest, OnChangesComplete) { 61 InSequence dummy; 62 63 for (int i = syncable::FIRST_REAL_MODEL_TYPE; 64 i < syncable::MODEL_TYPE_COUNT; ++i) { 65 const std::string& model_type_str = 66 syncable::ModelTypeToString(syncable::ModelTypeFromInt(i)); 67 ListValue expected_args; 68 expected_args.Append(Value::CreateStringValue(model_type_str)); 69 EXPECT_CALL(mock_router_, 70 RouteJsEvent("onChangesComplete", 71 HasArgsAsList(expected_args), NULL)); 72 } 73 74 for (int i = syncable::FIRST_REAL_MODEL_TYPE; 75 i < syncable::MODEL_TYPE_COUNT; ++i) { 76 sync_manager_observer_.OnChangesComplete(syncable::ModelTypeFromInt(i)); 77 } 78 } 79 80 TEST_F(JsSyncManagerObserverTest, OnSyncCycleCompleted) { 81 std::string download_progress_markers[syncable::MODEL_TYPE_COUNT]; 82 sessions::SyncSessionSnapshot snapshot(sessions::SyncerStatus(), 83 sessions::ErrorCounters(), 84 100, 85 false, 86 syncable::ModelTypeBitSet(), 87 download_progress_markers, 88 false, 89 true, 90 100, 91 5, 92 false, 93 sessions::SyncSourceInfo()); 94 ListValue expected_args; 95 expected_args.Append(snapshot.ToValue()); 96 97 EXPECT_CALL(mock_router_, 98 RouteJsEvent("onSyncCycleCompleted", 99 HasArgsAsList(expected_args), NULL)); 100 101 sync_manager_observer_.OnSyncCycleCompleted(&snapshot); 102 } 103 104 TEST_F(JsSyncManagerObserverTest, OnAuthError) { 105 GoogleServiceAuthError error(GoogleServiceAuthError::TWO_FACTOR); 106 ListValue expected_args; 107 expected_args.Append(error.ToValue()); 108 109 EXPECT_CALL(mock_router_, 110 RouteJsEvent("onAuthError", 111 HasArgsAsList(expected_args), NULL)); 112 113 sync_manager_observer_.OnAuthError(error); 114 } 115 116 TEST_F(JsSyncManagerObserverTest, OnPassphraseRequired) { 117 InSequence dummy; 118 119 ListValue true_args, false_args; 120 true_args.Append(Value::CreateBooleanValue(true)); 121 false_args.Append(Value::CreateBooleanValue(false)); 122 123 EXPECT_CALL(mock_router_, 124 RouteJsEvent("onPassphraseRequired", 125 HasArgsAsList(false_args), NULL)); 126 EXPECT_CALL(mock_router_, 127 RouteJsEvent("onPassphraseRequired", 128 HasArgsAsList(true_args), NULL)); 129 130 sync_manager_observer_.OnPassphraseRequired(false); 131 sync_manager_observer_.OnPassphraseRequired(true); 132 } 133 134 TEST_F(JsSyncManagerObserverTest, SensitiveNotifiations) { 135 ListValue redacted_args; 136 redacted_args.Append(Value::CreateStringValue("<redacted>")); 137 138 EXPECT_CALL(mock_router_, 139 RouteJsEvent("onUpdatedToken", 140 HasArgsAsList(redacted_args), NULL)); 141 EXPECT_CALL(mock_router_, 142 RouteJsEvent("onPassphraseAccepted", 143 HasArgsAsList(redacted_args), NULL)); 144 145 sync_manager_observer_.OnUpdatedToken("sensitive_token"); 146 sync_manager_observer_.OnPassphraseAccepted("sensitive_token"); 147 } 148 149 TEST_F(JsSyncManagerObserverTest, OnEncryptionComplete) { 150 ListValue expected_args; 151 ListValue* encrypted_type_values = new ListValue(); 152 syncable::ModelTypeSet encrypted_types; 153 154 expected_args.Append(encrypted_type_values); 155 for (int i = syncable::FIRST_REAL_MODEL_TYPE; 156 i < syncable::MODEL_TYPE_COUNT; ++i) { 157 syncable::ModelType type = syncable::ModelTypeFromInt(i); 158 encrypted_types.insert(type); 159 encrypted_type_values->Append(Value::CreateStringValue( 160 syncable::ModelTypeToString(type))); 161 } 162 163 EXPECT_CALL(mock_router_, 164 RouteJsEvent("onEncryptionComplete", 165 HasArgsAsList(expected_args), NULL)); 166 167 sync_manager_observer_.OnEncryptionComplete(encrypted_types); 168 } 169 170 TEST_F(JsSyncManagerObserverTest, OnMigrationNeededForTypes) { 171 ListValue expected_args; 172 ListValue* type_values = new ListValue(); 173 syncable::ModelTypeSet types; 174 175 expected_args.Append(type_values); 176 for (int i = syncable::FIRST_REAL_MODEL_TYPE; 177 i < syncable::MODEL_TYPE_COUNT; ++i) { 178 syncable::ModelType type = syncable::ModelTypeFromInt(i); 179 types.insert(type); 180 type_values->Append(Value::CreateStringValue( 181 syncable::ModelTypeToString(type))); 182 } 183 184 EXPECT_CALL(mock_router_, 185 RouteJsEvent("onMigrationNeededForTypes", 186 HasArgsAsList(expected_args), NULL)); 187 188 sync_manager_observer_.OnMigrationNeededForTypes(types); 189 } 190 191 namespace { 192 193 // Makes a node of the given model type. Returns the id of the 194 // newly-created node. 195 int64 MakeNode(sync_api::UserShare* share, syncable::ModelType model_type) { 196 sync_api::WriteTransaction trans(share); 197 sync_api::ReadNode root_node(&trans); 198 root_node.InitByRootLookup(); 199 sync_api::WriteNode node(&trans); 200 EXPECT_TRUE(node.InitUniqueByCreation( 201 model_type, root_node, 202 syncable::ModelTypeToString(model_type))); 203 node.SetIsFolder(false); 204 return node.GetId(); 205 } 206 207 } // namespace 208 209 TEST_F(JsSyncManagerObserverTest, OnChangesApplied) { 210 InSequence dummy; 211 212 TestUserShare test_user_share; 213 test_user_share.SetUp(); 214 215 // We don't test with passwords as that requires additional setup. 216 217 // Build a list of example ChangeRecords. 218 sync_api::SyncManager::ChangeRecord changes[syncable::MODEL_TYPE_COUNT]; 219 for (int i = syncable::AUTOFILL_PROFILE; 220 i < syncable::MODEL_TYPE_COUNT; ++i) { 221 changes[i].id = 222 MakeNode(test_user_share.user_share(), syncable::ModelTypeFromInt(i)); 223 switch (i % 3) { 224 case 0: 225 changes[i].action = 226 sync_api::SyncManager::ChangeRecord::ACTION_ADD; 227 break; 228 case 1: 229 changes[i].action = 230 sync_api::SyncManager::ChangeRecord::ACTION_UPDATE; 231 break; 232 default: 233 changes[i].action = 234 sync_api::SyncManager::ChangeRecord::ACTION_DELETE; 235 break; 236 } 237 { 238 sync_api::ReadTransaction trans(test_user_share.user_share()); 239 sync_api::ReadNode node(&trans); 240 EXPECT_TRUE(node.InitByIdLookup(changes[i].id)); 241 changes[i].specifics = node.GetEntry()->Get(syncable::SPECIFICS); 242 } 243 } 244 245 // For each i, we call OnChangesApplied() with the first arg equal 246 // to i cast to ModelType and the second argument with the changes 247 // starting from changes[i]. 248 249 // Set expectations for each data type. 250 for (int i = syncable::AUTOFILL_PROFILE; 251 i < syncable::MODEL_TYPE_COUNT; ++i) { 252 const std::string& model_type_str = 253 syncable::ModelTypeToString(syncable::ModelTypeFromInt(i)); 254 ListValue expected_args; 255 expected_args.Append(Value::CreateStringValue(model_type_str)); 256 ListValue* expected_changes = new ListValue(); 257 expected_args.Append(expected_changes); 258 for (int j = i; j < syncable::MODEL_TYPE_COUNT; ++j) { 259 sync_api::ReadTransaction trans(test_user_share.user_share()); 260 expected_changes->Append(changes[j].ToValue(&trans)); 261 } 262 EXPECT_CALL(mock_router_, 263 RouteJsEvent("onChangesApplied", 264 HasArgsAsList(expected_args), NULL)); 265 } 266 267 // Fire OnChangesApplied() for each data type. 268 for (int i = syncable::AUTOFILL_PROFILE; 269 i < syncable::MODEL_TYPE_COUNT; ++i) { 270 sync_api::ReadTransaction trans(test_user_share.user_share()); 271 sync_manager_observer_.OnChangesApplied(syncable::ModelTypeFromInt(i), 272 &trans, &changes[i], 273 syncable::MODEL_TYPE_COUNT - i); 274 } 275 276 test_user_share.TearDown(); 277 } 278 279 } // namespace 280 } // namespace browser_sync 281