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.
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/profile_sync_service_harness.h"
11 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
12 #include "chrome/browser/sync/test/integration/sync_test.h"
13 #include "chrome/browser/sync/test/integration/typed_urls_helper.h"
15 using typed_urls_helper::AddUrlToHistory;
16 using typed_urls_helper::AddUrlToHistoryWithTimestamp;
17 using typed_urls_helper::AddUrlToHistoryWithTransition;
18 using typed_urls_helper::AreVisitsEqual;
19 using typed_urls_helper::AreVisitsUnique;
20 using typed_urls_helper::AssertAllProfilesHaveSameURLsAsVerifier;
21 using typed_urls_helper::AssertURLRowVectorsAreEqual;
22 using typed_urls_helper::DeleteUrlFromHistory;
23 using typed_urls_helper::GetTypedUrlsFromClient;
24 using typed_urls_helper::GetUrlFromClient;
25 using typed_urls_helper::GetVisitsFromClient;
26 using typed_urls_helper::RemoveVisitsFromClient;
28 class TwoClientTypedUrlsSyncTest : public SyncTest {
30 TwoClientTypedUrlsSyncTest() : SyncTest(TWO_CLIENT) {}
31 virtual ~TwoClientTypedUrlsSyncTest() {}
33 bool CheckClientsEqual() {
34 history::URLRows urls = GetTypedUrlsFromClient(0);
35 history::URLRows urls2 = GetTypedUrlsFromClient(1);
36 AssertURLRowVectorsAreEqual(urls, urls2);
37 // Now check the visits.
38 for (size_t i = 0; i < urls.size() && i < urls2.size(); i++) {
39 history::VisitVector visit1 = GetVisitsFromClient(0, urls[i].id());
40 history::VisitVector visit2 = GetVisitsFromClient(1, urls2[i].id());
41 if (!AreVisitsEqual(visit1, visit2))
47 bool CheckNoDuplicateVisits() {
48 for (int i = 0; i < num_clients(); ++i) {
49 history::URLRows urls = GetTypedUrlsFromClient(i);
50 for (size_t j = 0; j < urls.size(); ++j) {
51 history::VisitVector visits = GetVisitsFromClient(i, urls[j].id());
52 if (!AreVisitsUnique(visits))
59 int GetVisitCountForFirstURL(int index) {
60 history::URLRows urls = GetTypedUrlsFromClient(index);
64 return urls[0].visit_count();
68 DISALLOW_COPY_AND_ASSIGN(TwoClientTypedUrlsSyncTest);
72 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, Add) {
73 const string16 kHistoryUrl(
74 ASCIIToUTF16("http://www.add-one-history.google.com/"));
75 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
77 // Populate one client with a URL, should sync to the other.
78 GURL new_url(kHistoryUrl);
79 AddUrlToHistory(0, new_url);
80 history::URLRows urls = GetTypedUrlsFromClient(0);
81 ASSERT_EQ(1U, urls.size());
82 ASSERT_EQ(new_url, urls[0].url());
85 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
87 // Both clients should have this URL.
88 AssertAllProfilesHaveSameURLsAsVerifier();
91 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddExpired) {
92 const string16 kHistoryUrl(
93 ASCIIToUTF16("http://www.add-one-history.google.com/"));
94 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
96 // Populate one client with a URL, should sync to the other.
97 GURL new_url(kHistoryUrl);
98 // Create a URL with a timestamp 1 year before today.
99 base::Time timestamp = base::Time::Now() - base::TimeDelta::FromDays(365);
100 AddUrlToHistoryWithTimestamp(0,
102 content::PAGE_TRANSITION_TYPED,
103 history::SOURCE_BROWSED,
105 history::URLRows urls = GetTypedUrlsFromClient(0);
106 ASSERT_EQ(1U, urls.size());
107 ASSERT_EQ(new_url, urls[0].url());
110 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
112 // Second client should still have no URLs since this one is expired.
113 urls = GetTypedUrlsFromClient(1);
114 ASSERT_EQ(0U, urls.size());
117 // Flake on mac: http://crbug/115526
118 #if defined(OS_MACOSX)
119 #define MAYBE_AddExpiredThenUpdate DISABLED_AddExpiredThenUpdate
121 #define MAYBE_AddExpiredThenUpdate AddExpiredThenUpdate
123 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, MAYBE_AddExpiredThenUpdate) {
124 const string16 kHistoryUrl(
125 ASCIIToUTF16("http://www.add-one-history.google.com/"));
126 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
128 // Populate one client with a URL, should sync to the other.
129 GURL new_url(kHistoryUrl);
130 // Create a URL with a timestamp 1 year before today.
131 base::Time timestamp = base::Time::Now() - base::TimeDelta::FromDays(365);
132 AddUrlToHistoryWithTimestamp(0,
134 content::PAGE_TRANSITION_TYPED,
135 history::SOURCE_BROWSED,
137 std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
138 ASSERT_EQ(1U, urls.size());
139 ASSERT_EQ(new_url, urls[0].url());
142 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
144 // Second client should still have no URLs since this one is expired.
145 urls = GetTypedUrlsFromClient(1);
146 ASSERT_EQ(0U, urls.size());
148 // Now drive an update on the first client.
149 AddUrlToHistory(0, new_url);
151 // Let sync finish again.
152 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
154 // Second client should have the URL now.
155 urls = GetTypedUrlsFromClient(1);
156 ASSERT_EQ(1U, urls.size());
160 // flaky, see crbug.com/108511
161 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DISABLED_AddThenDelete) {
162 const string16 kHistoryUrl(
163 ASCIIToUTF16("http://www.add-one-history.google.com/"));
164 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
166 // Populate one client with a URL, should sync to the other.
167 GURL new_url(kHistoryUrl);
168 AddUrlToHistory(0, new_url);
169 history::URLRows urls = GetTypedUrlsFromClient(0);
170 ASSERT_EQ(1U, urls.size());
171 ASSERT_EQ(new_url, urls[0].url());
174 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
176 // Both clients should have this URL.
177 AssertAllProfilesHaveSameURLsAsVerifier();
179 // Delete from first client, should delete from second.
180 DeleteUrlFromHistory(0, new_url);
183 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
185 // Neither client should have this URL.
186 AssertAllProfilesHaveSameURLsAsVerifier();
190 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DisableEnableSync) {
191 const string16 kUrl1(ASCIIToUTF16("http://history1.google.com/"));
192 const string16 kUrl2(ASCIIToUTF16("http://history2.google.com/"));
193 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
195 // Disable typed url sync for one client, leave it active for the other.
196 GetClient(0)->DisableSyncForDatatype(syncer::TYPED_URLS);
198 // Add one URL to non-syncing client, add a different URL to the other,
199 // wait for sync cycle to complete. No data should be exchanged.
202 AddUrlToHistory(0, url1);
203 AddUrlToHistory(1, url2);
204 ASSERT_TRUE(GetClient(1)->AwaitFullSyncCompletion("Added a typed url."));
206 // Make sure that no data was exchanged.
207 history::URLRows post_sync_urls = GetTypedUrlsFromClient(0);
208 ASSERT_EQ(1U, post_sync_urls.size());
209 ASSERT_EQ(url1, post_sync_urls[0].url());
210 post_sync_urls = GetTypedUrlsFromClient(1);
211 ASSERT_EQ(1U, post_sync_urls.size());
212 ASSERT_EQ(url2, post_sync_urls[0].url());
214 // Enable typed url sync, make both URLs are synced to each client.
215 GetClient(0)->EnableSyncForDatatype(syncer::TYPED_URLS);
216 ASSERT_TRUE(AwaitQuiescence());
218 AssertAllProfilesHaveSameURLsAsVerifier();
221 // flaky, see crbug.com/108511
222 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DISABLED_AddOneDeleteOther) {
223 const string16 kHistoryUrl(
224 ASCIIToUTF16("http://www.add-one-delete-history.google.com/"));
225 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
227 // Populate one client with a URL, should sync to the other.
228 GURL new_url(kHistoryUrl);
229 AddUrlToHistory(0, new_url);
230 history::URLRows urls = GetTypedUrlsFromClient(0);
231 ASSERT_EQ(1U, urls.size());
232 ASSERT_EQ(new_url, urls[0].url());
235 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
237 // Both clients should have this URL.
238 AssertAllProfilesHaveSameURLsAsVerifier();
240 // Now, delete the URL from the second client.
241 DeleteUrlFromHistory(1, new_url);
242 urls = GetTypedUrlsFromClient(0);
243 ASSERT_EQ(1U, urls.size());
246 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
248 // Both clients should have this URL removed.
249 AssertAllProfilesHaveSameURLsAsVerifier();
252 // flaky, see crbug.com/108511
253 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
254 DISABLED_AddOneDeleteOtherAddAgain) {
255 const string16 kHistoryUrl(
256 ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
257 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
259 // Populate one client with a URL, should sync to the other.
260 GURL new_url(kHistoryUrl);
261 AddUrlToHistory(0, new_url);
262 history::URLRows urls = GetTypedUrlsFromClient(0);
263 ASSERT_EQ(1U, urls.size());
264 ASSERT_EQ(new_url, urls[0].url());
267 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
269 // Both clients should have this URL.
270 AssertAllProfilesHaveSameURLsAsVerifier();
272 // Now, delete the URL from the second client.
273 DeleteUrlFromHistory(1, new_url);
274 urls = GetTypedUrlsFromClient(0);
275 ASSERT_EQ(1U, urls.size());
278 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
280 // Both clients should have this URL removed.
281 AssertAllProfilesHaveSameURLsAsVerifier();
283 // Add it to the first client again, should succeed (tests that the deletion
284 // properly disassociates that URL).
285 AddUrlToHistory(0, new_url);
288 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
290 // Both clients should have this URL added again.
291 AssertAllProfilesHaveSameURLsAsVerifier();
294 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
295 MergeTypedWithNonTypedDuringAssociation) {
296 ASSERT_TRUE(SetupClients());
297 GURL new_url("http://history.com");
298 base::Time timestamp = base::Time::Now();
299 // Put a non-typed URL in both clients with an identical timestamp.
300 // Then add a typed URL to the second client - this test makes sure that
301 // we properly merge both sets of visits together to end up with the same
302 // set of visits on both ends.
303 AddUrlToHistoryWithTimestamp(0, new_url, content::PAGE_TRANSITION_LINK,
304 history::SOURCE_BROWSED, timestamp);
305 AddUrlToHistoryWithTimestamp(1, new_url, content::PAGE_TRANSITION_LINK,
306 history::SOURCE_BROWSED, timestamp);
307 AddUrlToHistoryWithTimestamp(1, new_url, content::PAGE_TRANSITION_TYPED,
308 history::SOURCE_BROWSED,
309 timestamp + base::TimeDelta::FromSeconds(1));
311 // Now start up sync - URLs should get merged. Fully sync client 1 first,
312 // before syncing client 0, so we have both of client 1's URLs in the sync DB
313 // at the time that client 0 does model association.
314 ASSERT_TRUE(GetClient(1)->SetupSync()) << "SetupSync() failed";
315 GetClient(1)->AwaitFullSyncCompletion("Initial client sync");
316 ASSERT_TRUE(GetClient(0)->SetupSync()) << "SetupSync() failed";
317 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
319 ASSERT_TRUE(CheckClientsEqual());
320 // At this point, we should have no duplicates (total visit count should be
321 // 2). We only need to check client 0 since we already verified that both
322 // clients are identical above.
323 history::URLRows urls = GetTypedUrlsFromClient(0);
324 ASSERT_EQ(1U, urls.size());
325 ASSERT_EQ(new_url, urls[0].url());
326 ASSERT_TRUE(CheckNoDuplicateVisits());
327 ASSERT_EQ(2, GetVisitCountForFirstURL(0));
330 // Tests transitioning a URL from non-typed to typed when both clients
331 // have already seen that URL (so a merge is required).
332 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
333 MergeTypedWithNonTypedDuringChangeProcessing) {
334 ASSERT_TRUE(SetupClients());
335 GURL new_url("http://history.com");
336 base::Time timestamp = base::Time::Now();
337 // Setup both clients with the identical typed URL visit. This means we can't
338 // use the verifier in this test, because this will show up as two distinct
339 // visits in the verifier.
340 AddUrlToHistoryWithTimestamp(0, new_url, content::PAGE_TRANSITION_LINK,
341 history::SOURCE_BROWSED, timestamp);
342 AddUrlToHistoryWithTimestamp(1, new_url, content::PAGE_TRANSITION_LINK,
343 history::SOURCE_BROWSED, timestamp);
345 // Now start up sync. Neither URL should get synced as they do not look like
347 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
348 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
349 ASSERT_TRUE(CheckClientsEqual());
350 history::URLRows urls = GetTypedUrlsFromClient(0);
351 ASSERT_EQ(0U, urls.size());
353 // Now, add a typed visit to the first client.
354 AddUrlToHistoryWithTimestamp(0, new_url, content::PAGE_TRANSITION_TYPED,
355 history::SOURCE_BROWSED,
356 timestamp + base::TimeDelta::FromSeconds(1));
358 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
359 ASSERT_TRUE(CheckClientsEqual());
360 ASSERT_TRUE(CheckNoDuplicateVisits());
361 urls = GetTypedUrlsFromClient(0);
362 ASSERT_EQ(1U, urls.size());
363 ASSERT_EQ(2, GetVisitCountForFirstURL(0));
364 ASSERT_EQ(2, GetVisitCountForFirstURL(1));
367 // Tests transitioning a URL from non-typed to typed when one of the clients
368 // has never seen that URL before (so no merge is necessary).
369 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, UpdateToNonTypedURL) {
370 const string16 kHistoryUrl(
371 ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
372 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
374 // Populate one client with a non-typed URL, should not be synced.
375 GURL new_url(kHistoryUrl);
376 AddUrlToHistoryWithTransition(0, new_url, content::PAGE_TRANSITION_LINK,
377 history::SOURCE_BROWSED);
378 history::URLRows urls = GetTypedUrlsFromClient(0);
379 ASSERT_EQ(0U, urls.size());
382 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
384 // Both clients should have 0 typed URLs.
385 AssertAllProfilesHaveSameURLsAsVerifier();
386 urls = GetTypedUrlsFromClient(0);
387 ASSERT_EQ(0U, urls.size());
389 // Now, add a typed visit to this URL.
390 AddUrlToHistory(0, new_url);
393 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
395 // Both clients should have this URL as typed and have two visits synced up.
396 ASSERT_TRUE(CheckClientsEqual());
397 urls = GetTypedUrlsFromClient(0);
398 ASSERT_EQ(1U, urls.size());
399 ASSERT_EQ(new_url, urls[0].url());
400 ASSERT_EQ(2, GetVisitCountForFirstURL(0));
403 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
404 SkipImportedVisits) {
406 GURL imported_url("http://imported_url.com");
407 GURL browsed_url("http://browsed_url.com");
408 GURL browsed_and_imported_url("http://browsed_and_imported_url.com");
409 ASSERT_TRUE(SetupClients());
411 // Create 3 items in our first client - 1 imported, one browsed, one with
412 // both imported and browsed entries.
413 AddUrlToHistoryWithTransition(0, imported_url,
414 content::PAGE_TRANSITION_TYPED,
415 history::SOURCE_FIREFOX_IMPORTED);
416 AddUrlToHistoryWithTransition(0, browsed_url,
417 content::PAGE_TRANSITION_TYPED,
418 history::SOURCE_BROWSED);
419 AddUrlToHistoryWithTransition(0, browsed_and_imported_url,
420 content::PAGE_TRANSITION_TYPED,
421 history::SOURCE_FIREFOX_IMPORTED);
423 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
424 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
425 history::URLRows urls = GetTypedUrlsFromClient(1);
426 ASSERT_EQ(1U, urls.size());
427 ASSERT_EQ(browsed_url, urls[0].url());
429 // Now browse to 3rd URL - this should cause it to be synced, even though it
430 // was initially imported.
431 AddUrlToHistoryWithTransition(0, browsed_and_imported_url,
432 content::PAGE_TRANSITION_TYPED,
433 history::SOURCE_BROWSED);
434 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
435 urls = GetTypedUrlsFromClient(1);
436 ASSERT_EQ(2U, urls.size());
438 // Make sure the imported URL didn't make it over.
439 for (size_t i = 0; i < urls.size(); ++i) {
440 ASSERT_NE(imported_url, urls[i].url());
444 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, BookmarksWithTypedVisit) {
445 GURL bookmark_url("http://www.bookmark.google.com/");
446 GURL bookmark_icon_url("http://www.bookmark.google.com/favicon.ico");
447 ASSERT_TRUE(SetupClients());
448 // Create a bookmark.
449 const BookmarkNode* node = bookmarks_helper::AddURL(
450 0, bookmarks_helper::IndexedURLTitle(0), bookmark_url);
451 bookmarks_helper::SetFavicon(0, node, bookmark_icon_url,
452 bookmarks_helper::CreateFavicon(SK_ColorWHITE),
453 bookmarks_helper::FROM_UI);
454 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
456 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
457 // A row in the DB for client 1 should have been created as a result of the
460 ASSERT_TRUE(GetUrlFromClient(1, bookmark_url, &row));
462 // Now, add a typed visit for client 0 to the bookmark URL and sync it over
463 // - this should not cause a crash.
464 AddUrlToHistory(0, bookmark_url);
465 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
467 AssertAllProfilesHaveSameURLsAsVerifier();
468 history::URLRows urls = GetTypedUrlsFromClient(0);
469 ASSERT_EQ(1U, urls.size());
470 ASSERT_EQ(bookmark_url, urls[0].url());
471 ASSERT_EQ(1, GetVisitCountForFirstURL(0));