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
19namespace browser_sync {
20namespace {
21
22using ::testing::InSequence;
23using ::testing::StrictMock;
24
25class 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
33TEST_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
60TEST_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
80TEST_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
104TEST_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
116TEST_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
134TEST_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
149TEST_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
170TEST_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
191namespace {
192
193// Makes a node of the given model type.  Returns the id of the
194// newly-created node.
195int64 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
209TEST_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