two_client_typed_urls_sync_test.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
1// Copyright (c) 2012 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 "base/i18n/number_formatting.h"
6#include "base/memory/scoped_vector.h"
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/history/history_types.h"
9#include "chrome/browser/sessions/session_service.h"
10#include "chrome/browser/sync/test/integration/bookmarks_helper.h"
11#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
12#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
13#include "chrome/browser/sync/test/integration/sync_test.h"
14#include "chrome/browser/sync/test/integration/typed_urls_helper.h"
15
16using base::ASCIIToUTF16;
17using sync_integration_test_util::AwaitCommitActivityCompletion;
18using typed_urls_helper::AddUrlToHistory;
19using typed_urls_helper::AddUrlToHistoryWithTimestamp;
20using typed_urls_helper::AddUrlToHistoryWithTransition;
21using typed_urls_helper::AreVisitsEqual;
22using typed_urls_helper::AreVisitsUnique;
23using typed_urls_helper::AwaitCheckAllProfilesHaveSameURLsAsVerifier;
24using typed_urls_helper::CheckURLRowVectorsAreEqual;
25using typed_urls_helper::DeleteUrlFromHistory;
26using typed_urls_helper::GetTypedUrlsFromClient;
27using typed_urls_helper::GetUrlFromClient;
28using typed_urls_helper::GetVisitsFromClient;
29using typed_urls_helper::RemoveVisitsFromClient;
30
31class TwoClientTypedUrlsSyncTest : public SyncTest {
32 public:
33  TwoClientTypedUrlsSyncTest() : SyncTest(TWO_CLIENT) {}
34  virtual ~TwoClientTypedUrlsSyncTest() {}
35
36  ::testing::AssertionResult CheckClientsEqual() {
37    history::URLRows urls = GetTypedUrlsFromClient(0);
38    history::URLRows urls2 = GetTypedUrlsFromClient(1);
39    if (!CheckURLRowVectorsAreEqual(urls, urls2))
40      return ::testing::AssertionFailure() << "URLVectors are not equal";
41    // Now check the visits.
42    for (size_t i = 0; i < urls.size() && i < urls2.size(); i++) {
43      history::VisitVector visit1 = GetVisitsFromClient(0, urls[i].id());
44      history::VisitVector visit2 = GetVisitsFromClient(1, urls2[i].id());
45      if (!AreVisitsEqual(visit1, visit2))
46        return ::testing::AssertionFailure() << "Visits are not equal";
47    }
48    return ::testing::AssertionSuccess();
49  }
50
51  bool CheckNoDuplicateVisits() {
52    for (int i = 0; i < num_clients(); ++i) {
53      history::URLRows urls = GetTypedUrlsFromClient(i);
54      for (size_t j = 0; j < urls.size(); ++j) {
55        history::VisitVector visits = GetVisitsFromClient(i, urls[j].id());
56        if (!AreVisitsUnique(visits))
57          return false;
58      }
59    }
60    return true;
61  }
62
63  int GetVisitCountForFirstURL(int index) {
64    history::URLRows urls = GetTypedUrlsFromClient(index);
65    if (urls.size() == 0)
66      return 0;
67    else
68      return urls[0].visit_count();
69  }
70
71 private:
72  DISALLOW_COPY_AND_ASSIGN(TwoClientTypedUrlsSyncTest);
73};
74
75// TCM: 3728323
76IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, Add) {
77  const base::string16 kHistoryUrl(
78      ASCIIToUTF16("http://www.add-one-history.google.com/"));
79  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
80
81  // Populate one client with a URL, should sync to the other.
82  GURL new_url(kHistoryUrl);
83  AddUrlToHistory(0, new_url);
84  history::URLRows urls = GetTypedUrlsFromClient(0);
85  ASSERT_EQ(1U, urls.size());
86  ASSERT_EQ(new_url, urls[0].url());
87
88  // Both clients should have this URL.
89  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
90}
91
92IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddExpired) {
93  const base::string16 kHistoryUrl(
94      ASCIIToUTF16("http://www.add-one-history.google.com/"));
95  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
96
97  // Populate one client with a URL, should sync to the other.
98  GURL new_url(kHistoryUrl);
99  // Create a URL with a timestamp 1 year before today.
100  base::Time timestamp = base::Time::Now() - base::TimeDelta::FromDays(365);
101  AddUrlToHistoryWithTimestamp(0,
102                               new_url,
103                               content::PAGE_TRANSITION_TYPED,
104                               history::SOURCE_BROWSED,
105                               timestamp);
106  history::URLRows urls = GetTypedUrlsFromClient(0);
107  ASSERT_EQ(1U, urls.size());
108  ASSERT_EQ(new_url, urls[0].url());
109
110  // Let sync finish.
111  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
112
113  // Second client should still have no URLs since this one is expired.
114  urls = GetTypedUrlsFromClient(1);
115  ASSERT_EQ(0U, urls.size());
116}
117
118// Flake on mac: http://crbug/115526
119#if defined(OS_MACOSX)
120#define MAYBE_AddExpiredThenUpdate DISABLED_AddExpiredThenUpdate
121#else
122#define MAYBE_AddExpiredThenUpdate AddExpiredThenUpdate
123#endif
124IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, MAYBE_AddExpiredThenUpdate) {
125  const base::string16 kHistoryUrl(
126      ASCIIToUTF16("http://www.add-one-history.google.com/"));
127  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
128
129  // Populate one client with a URL, should sync to the other.
130  GURL new_url(kHistoryUrl);
131  // Create a URL with a timestamp 1 year before today.
132  base::Time timestamp = base::Time::Now() - base::TimeDelta::FromDays(365);
133  AddUrlToHistoryWithTimestamp(0,
134                               new_url,
135                               content::PAGE_TRANSITION_TYPED,
136                               history::SOURCE_BROWSED,
137                               timestamp);
138  std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
139  ASSERT_EQ(1U, urls.size());
140  ASSERT_EQ(new_url, urls[0].url());
141
142  // Let sync finish.
143  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
144
145  // Second client should still have no URLs since this one is expired.
146  urls = GetTypedUrlsFromClient(1);
147  ASSERT_EQ(0U, urls.size());
148
149  // Now drive an update on the first client.
150  AddUrlToHistory(0, new_url);
151
152  // Let sync finish again.
153  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
154
155  // Second client should have the URL now.
156  urls = GetTypedUrlsFromClient(1);
157  ASSERT_EQ(1U, urls.size());
158}
159
160// TCM: 3705291
161// flaky, see crbug.com/108511
162IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DISABLED_AddThenDelete) {
163  const base::string16 kHistoryUrl(
164      ASCIIToUTF16("http://www.add-one-history.google.com/"));
165  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
166
167  // Populate one client with a URL, should sync to the other.
168  GURL new_url(kHistoryUrl);
169  AddUrlToHistory(0, new_url);
170  history::URLRows urls = GetTypedUrlsFromClient(0);
171  ASSERT_EQ(1U, urls.size());
172  ASSERT_EQ(new_url, urls[0].url());
173
174  // Both clients should have this URL.
175  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
176
177  // Delete from first client, should delete from second.
178  DeleteUrlFromHistory(0, new_url);
179
180  // Neither client should have this URL.
181  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
182}
183
184// TCM: 3643277
185IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DisableEnableSync) {
186  const base::string16 kUrl1(ASCIIToUTF16("http://history1.google.com/"));
187  const base::string16 kUrl2(ASCIIToUTF16("http://history2.google.com/"));
188  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
189
190  // Disable typed url sync for one client, leave it active for the other.
191  GetClient(0)->DisableSyncForDatatype(syncer::TYPED_URLS);
192
193  // Add one URL to non-syncing client, add a different URL to the other,
194  // wait for sync cycle to complete. No data should be exchanged.
195  GURL url1(kUrl1);
196  GURL url2(kUrl2);
197  AddUrlToHistory(0, url1);
198  AddUrlToHistory(1, url2);
199  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((1))));
200
201  // Make sure that no data was exchanged.
202  history::URLRows post_sync_urls = GetTypedUrlsFromClient(0);
203  ASSERT_EQ(1U, post_sync_urls.size());
204  ASSERT_EQ(url1, post_sync_urls[0].url());
205  post_sync_urls = GetTypedUrlsFromClient(1);
206  ASSERT_EQ(1U, post_sync_urls.size());
207  ASSERT_EQ(url2, post_sync_urls[0].url());
208
209  // Enable typed url sync, make both URLs are synced to each client.
210  GetClient(0)->EnableSyncForDatatype(syncer::TYPED_URLS);
211
212  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
213}
214
215// flaky, see crbug.com/108511
216IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DISABLED_AddOneDeleteOther) {
217  const base::string16 kHistoryUrl(
218      ASCIIToUTF16("http://www.add-one-delete-history.google.com/"));
219  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
220
221  // Populate one client with a URL, should sync to the other.
222  GURL new_url(kHistoryUrl);
223  AddUrlToHistory(0, new_url);
224  history::URLRows urls = GetTypedUrlsFromClient(0);
225  ASSERT_EQ(1U, urls.size());
226  ASSERT_EQ(new_url, urls[0].url());
227
228  // Both clients should have this URL.
229  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
230
231  // Now, delete the URL from the second client.
232  DeleteUrlFromHistory(1, new_url);
233  urls = GetTypedUrlsFromClient(0);
234  ASSERT_EQ(1U, urls.size());
235
236  // Both clients should have this URL removed.
237  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
238}
239
240// flaky, see crbug.com/108511
241IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
242                       DISABLED_AddOneDeleteOtherAddAgain) {
243  const base::string16 kHistoryUrl(
244      ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
245  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
246
247  // Populate one client with a URL, should sync to the other.
248  GURL new_url(kHistoryUrl);
249  AddUrlToHistory(0, new_url);
250  history::URLRows urls = GetTypedUrlsFromClient(0);
251  ASSERT_EQ(1U, urls.size());
252  ASSERT_EQ(new_url, urls[0].url());
253
254  // Both clients should have this URL.
255  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
256
257  // Now, delete the URL from the second client.
258  DeleteUrlFromHistory(1, new_url);
259  urls = GetTypedUrlsFromClient(0);
260  ASSERT_EQ(1U, urls.size());
261
262  // Both clients should have this URL removed.
263  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
264
265  // Add it to the first client again, should succeed (tests that the deletion
266  // properly disassociates that URL).
267  AddUrlToHistory(0, new_url);
268
269  // Both clients should have this URL added again.
270  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
271}
272
273IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
274                       MergeTypedWithNonTypedDuringAssociation) {
275  ASSERT_TRUE(SetupClients());
276  GURL new_url("http://history.com");
277  base::Time timestamp = base::Time::Now();
278  // Put a non-typed URL in both clients with an identical timestamp.
279  // Then add a typed URL to the second client - this test makes sure that
280  // we properly merge both sets of visits together to end up with the same
281  // set of visits on both ends.
282  AddUrlToHistoryWithTimestamp(0, new_url, content::PAGE_TRANSITION_LINK,
283                               history::SOURCE_BROWSED, timestamp);
284  AddUrlToHistoryWithTimestamp(1, new_url, content::PAGE_TRANSITION_LINK,
285                               history::SOURCE_BROWSED, timestamp);
286  AddUrlToHistoryWithTimestamp(1, new_url, content::PAGE_TRANSITION_TYPED,
287                               history::SOURCE_BROWSED,
288                               timestamp + base::TimeDelta::FromSeconds(1));
289
290  // Now start up sync - URLs should get merged. Fully sync client 1 first,
291  // before syncing client 0, so we have both of client 1's URLs in the sync DB
292  // at the time that client 0 does model association.
293  ASSERT_TRUE(GetClient(1)->SetupSync()) << "SetupSync() failed";
294  AwaitCommitActivityCompletion(GetSyncService((1)));
295  ASSERT_TRUE(GetClient(0)->SetupSync()) << "SetupSync() failed";
296  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
297
298  ASSERT_TRUE(CheckClientsEqual());
299  // At this point, we should have no duplicates (total visit count should be
300  // 2). We only need to check client 0 since we already verified that both
301  // clients are identical above.
302  history::URLRows urls = GetTypedUrlsFromClient(0);
303  ASSERT_EQ(1U, urls.size());
304  ASSERT_EQ(new_url, urls[0].url());
305  ASSERT_TRUE(CheckNoDuplicateVisits());
306  ASSERT_EQ(2, GetVisitCountForFirstURL(0));
307}
308
309// Tests transitioning a URL from non-typed to typed when both clients
310// have already seen that URL (so a merge is required).
311IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
312                       MergeTypedWithNonTypedDuringChangeProcessing) {
313  ASSERT_TRUE(SetupClients());
314  GURL new_url("http://history.com");
315  base::Time timestamp = base::Time::Now();
316  // Setup both clients with the identical typed URL visit. This means we can't
317  // use the verifier in this test, because this will show up as two distinct
318  // visits in the verifier.
319  AddUrlToHistoryWithTimestamp(0, new_url, content::PAGE_TRANSITION_LINK,
320                               history::SOURCE_BROWSED, timestamp);
321  AddUrlToHistoryWithTimestamp(1, new_url, content::PAGE_TRANSITION_LINK,
322                               history::SOURCE_BROWSED, timestamp);
323
324  // Now start up sync. Neither URL should get synced as they do not look like
325  // typed URLs.
326  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
327  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
328  ASSERT_TRUE(CheckClientsEqual());
329  history::URLRows urls = GetTypedUrlsFromClient(0);
330  ASSERT_EQ(0U, urls.size());
331
332  // Now, add a typed visit to the first client.
333  AddUrlToHistoryWithTimestamp(0, new_url, content::PAGE_TRANSITION_TYPED,
334                               history::SOURCE_BROWSED,
335                               timestamp + base::TimeDelta::FromSeconds(1));
336
337  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
338  ASSERT_TRUE(CheckClientsEqual());
339  ASSERT_TRUE(CheckNoDuplicateVisits());
340  urls = GetTypedUrlsFromClient(0);
341  ASSERT_EQ(1U, urls.size());
342  ASSERT_EQ(2, GetVisitCountForFirstURL(0));
343  ASSERT_EQ(2, GetVisitCountForFirstURL(1));
344}
345
346// Tests transitioning a URL from non-typed to typed when one of the clients
347// has never seen that URL before (so no merge is necessary).
348IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, UpdateToNonTypedURL) {
349  const base::string16 kHistoryUrl(
350      ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
351  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
352
353  // Populate one client with a non-typed URL, should not be synced.
354  GURL new_url(kHistoryUrl);
355  AddUrlToHistoryWithTransition(0, new_url, content::PAGE_TRANSITION_LINK,
356                                history::SOURCE_BROWSED);
357  history::URLRows urls = GetTypedUrlsFromClient(0);
358  ASSERT_EQ(0U, urls.size());
359
360  // Both clients should have 0 typed URLs.
361  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
362  urls = GetTypedUrlsFromClient(0);
363  ASSERT_EQ(0U, urls.size());
364
365  // Now, add a typed visit to this URL.
366  AddUrlToHistory(0, new_url);
367
368  // Let sync finish.
369  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
370
371  // Both clients should have this URL as typed and have two visits synced up.
372  ASSERT_TRUE(CheckClientsEqual());
373  urls = GetTypedUrlsFromClient(0);
374  ASSERT_EQ(1U, urls.size());
375  ASSERT_EQ(new_url, urls[0].url());
376  ASSERT_EQ(2, GetVisitCountForFirstURL(0));
377}
378
379IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
380                       SkipImportedVisits) {
381
382  GURL imported_url("http://imported_url.com");
383  GURL browsed_url("http://browsed_url.com");
384  GURL browsed_and_imported_url("http://browsed_and_imported_url.com");
385  ASSERT_TRUE(SetupClients());
386
387  // Create 3 items in our first client - 1 imported, one browsed, one with
388  // both imported and browsed entries.
389  AddUrlToHistoryWithTransition(0, imported_url,
390                                content::PAGE_TRANSITION_TYPED,
391                                history::SOURCE_FIREFOX_IMPORTED);
392  AddUrlToHistoryWithTransition(0, browsed_url,
393                                content::PAGE_TRANSITION_TYPED,
394                                history::SOURCE_BROWSED);
395  AddUrlToHistoryWithTransition(0, browsed_and_imported_url,
396                                content::PAGE_TRANSITION_TYPED,
397                                history::SOURCE_FIREFOX_IMPORTED);
398
399  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
400  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
401  history::URLRows urls = GetTypedUrlsFromClient(1);
402  ASSERT_EQ(1U, urls.size());
403  ASSERT_EQ(browsed_url, urls[0].url());
404
405  // Now browse to 3rd URL - this should cause it to be synced, even though it
406  // was initially imported.
407  AddUrlToHistoryWithTransition(0, browsed_and_imported_url,
408                                content::PAGE_TRANSITION_TYPED,
409                                history::SOURCE_BROWSED);
410  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
411  urls = GetTypedUrlsFromClient(1);
412  ASSERT_EQ(2U, urls.size());
413
414  // Make sure the imported URL didn't make it over.
415  for (size_t i = 0; i < urls.size(); ++i) {
416    ASSERT_NE(imported_url, urls[i].url());
417  }
418}
419
420IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, BookmarksWithTypedVisit) {
421  GURL bookmark_url("http://www.bookmark.google.com/");
422  GURL bookmark_icon_url("http://www.bookmark.google.com/favicon.ico");
423  ASSERT_TRUE(SetupClients());
424  // Create a bookmark.
425  const BookmarkNode* node = bookmarks_helper::AddURL(
426      0, bookmarks_helper::IndexedURLTitle(0), bookmark_url);
427  bookmarks_helper::SetFavicon(0, node, bookmark_icon_url,
428      bookmarks_helper::CreateFavicon(SK_ColorWHITE),
429      bookmarks_helper::FROM_UI);
430  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
431
432  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
433  // A row in the DB for client 1 should have been created as a result of the
434  // sync.
435  history::URLRow row;
436  ASSERT_TRUE(GetUrlFromClient(1, bookmark_url, &row));
437
438  // Now, add a typed visit for client 0 to the bookmark URL and sync it over
439  // - this should not cause a crash.
440  AddUrlToHistory(0, bookmark_url);
441  ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
442
443  ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
444  history::URLRows urls = GetTypedUrlsFromClient(0);
445  ASSERT_EQ(1U, urls.size());
446  ASSERT_EQ(bookmark_url, urls[0].url());
447  ASSERT_EQ(1, GetVisitCountForFirstURL(0));
448}
449