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/sessions/session_service.h"
9 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
10 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
11 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
12 #include "chrome/browser/sync/test/integration/sync_test.h"
13 #include "chrome/browser/sync/test/integration/typed_urls_helper.h"
14 #include "components/history/core/browser/history_types.h"
16 using base::ASCIIToUTF16;
17 using sync_integration_test_util::AwaitCommitActivityCompletion;
18 using typed_urls_helper::AddUrlToHistory;
19 using typed_urls_helper::AddUrlToHistoryWithTimestamp;
20 using typed_urls_helper::AddUrlToHistoryWithTransition;
21 using typed_urls_helper::AreVisitsEqual;
22 using typed_urls_helper::AreVisitsUnique;
23 using typed_urls_helper::AwaitCheckAllProfilesHaveSameURLsAsVerifier;
24 using typed_urls_helper::CheckURLRowVectorsAreEqual;
25 using typed_urls_helper::DeleteUrlFromHistory;
26 using typed_urls_helper::GetTypedUrlsFromClient;
27 using typed_urls_helper::GetUrlFromClient;
28 using typed_urls_helper::GetVisitsFromClient;
29 using typed_urls_helper::RemoveVisitsFromClient;
31 class TwoClientTypedUrlsSyncTest : public SyncTest {
33 TwoClientTypedUrlsSyncTest() : SyncTest(TWO_CLIENT) {}
34 ~TwoClientTypedUrlsSyncTest() override {}
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";
48 return ::testing::AssertionSuccess();
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))
63 int GetVisitCountForFirstURL(int index) {
64 history::URLRows urls = GetTypedUrlsFromClient(index);
68 return urls[0].visit_count();
72 DISALLOW_COPY_AND_ASSIGN(TwoClientTypedUrlsSyncTest);
76 IN_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.";
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());
88 // Both clients should have this URL.
89 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
92 IN_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.";
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,
103 ui::PAGE_TRANSITION_TYPED,
104 history::SOURCE_BROWSED,
106 history::URLRows urls = GetTypedUrlsFromClient(0);
107 ASSERT_EQ(1U, urls.size());
108 ASSERT_EQ(new_url, urls[0].url());
111 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
113 // Second client should still have no URLs since this one is expired.
114 urls = GetTypedUrlsFromClient(1);
115 ASSERT_EQ(0U, urls.size());
118 // Flake on mac: http://crbug/115526
119 #if defined(OS_MACOSX)
120 #define MAYBE_AddExpiredThenUpdate DISABLED_AddExpiredThenUpdate
122 #define MAYBE_AddExpiredThenUpdate AddExpiredThenUpdate
124 IN_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.";
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,
135 ui::PAGE_TRANSITION_TYPED,
136 history::SOURCE_BROWSED,
138 std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
139 ASSERT_EQ(1U, urls.size());
140 ASSERT_EQ(new_url, urls[0].url());
143 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
145 // Second client should still have no URLs since this one is expired.
146 urls = GetTypedUrlsFromClient(1);
147 ASSERT_EQ(0U, urls.size());
149 // Now drive an update on the first client.
150 AddUrlToHistory(0, new_url);
152 // Let sync finish again.
153 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
155 // Second client should have the URL now.
156 urls = GetTypedUrlsFromClient(1);
157 ASSERT_EQ(1U, urls.size());
161 // flaky, see crbug.com/108511
162 IN_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.";
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());
174 // Both clients should have this URL.
175 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
177 // Delete from first client, should delete from second.
178 DeleteUrlFromHistory(0, new_url);
180 // Neither client should have this URL.
181 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
185 IN_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.";
190 // Disable typed url sync for one client, leave it active for the other.
191 GetClient(0)->DisableSyncForDatatype(syncer::TYPED_URLS);
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.
197 AddUrlToHistory(0, url1);
198 AddUrlToHistory(1, url2);
199 ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((1))));
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());
209 // Enable typed url sync, make both URLs are synced to each client.
210 GetClient(0)->EnableSyncForDatatype(syncer::TYPED_URLS);
212 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
215 // flaky, see crbug.com/108511
216 IN_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.";
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());
228 // Both clients should have this URL.
229 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
231 // Now, delete the URL from the second client.
232 DeleteUrlFromHistory(1, new_url);
233 urls = GetTypedUrlsFromClient(0);
234 ASSERT_EQ(1U, urls.size());
236 // Both clients should have this URL removed.
237 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
240 // flaky, see crbug.com/108511
241 IN_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.";
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());
254 // Both clients should have this URL.
255 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
257 // Now, delete the URL from the second client.
258 DeleteUrlFromHistory(1, new_url);
259 urls = GetTypedUrlsFromClient(0);
260 ASSERT_EQ(1U, urls.size());
262 // Both clients should have this URL removed.
263 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
265 // Add it to the first client again, should succeed (tests that the deletion
266 // properly disassociates that URL).
267 AddUrlToHistory(0, new_url);
269 // Both clients should have this URL added again.
270 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
273 IN_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, ui::PAGE_TRANSITION_LINK,
283 history::SOURCE_BROWSED, timestamp);
284 AddUrlToHistoryWithTimestamp(1, new_url, ui::PAGE_TRANSITION_LINK,
285 history::SOURCE_BROWSED, timestamp);
286 AddUrlToHistoryWithTimestamp(1, new_url, ui::PAGE_TRANSITION_TYPED,
287 history::SOURCE_BROWSED,
288 timestamp + base::TimeDelta::FromSeconds(1));
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)));
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));
309 // Tests transitioning a URL from non-typed to typed when both clients
310 // have already seen that URL (so a merge is required).
311 IN_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, ui::PAGE_TRANSITION_LINK,
320 history::SOURCE_BROWSED, timestamp);
321 AddUrlToHistoryWithTimestamp(1, new_url, ui::PAGE_TRANSITION_LINK,
322 history::SOURCE_BROWSED, timestamp);
324 // Now start up sync. Neither URL should get synced as they do not look like
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());
332 // Now, add a typed visit to the first client.
333 AddUrlToHistoryWithTimestamp(0, new_url, ui::PAGE_TRANSITION_TYPED,
334 history::SOURCE_BROWSED,
335 timestamp + base::TimeDelta::FromSeconds(1));
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));
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).
348 IN_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.";
353 // Populate one client with a non-typed URL, should not be synced.
354 GURL new_url(kHistoryUrl);
355 AddUrlToHistoryWithTransition(0, new_url, ui::PAGE_TRANSITION_LINK,
356 history::SOURCE_BROWSED);
357 history::URLRows urls = GetTypedUrlsFromClient(0);
358 ASSERT_EQ(0U, urls.size());
360 // Both clients should have 0 typed URLs.
361 ASSERT_TRUE(AwaitCheckAllProfilesHaveSameURLsAsVerifier());
362 urls = GetTypedUrlsFromClient(0);
363 ASSERT_EQ(0U, urls.size());
365 // Now, add a typed visit to this URL.
366 AddUrlToHistory(0, new_url);
369 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
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));
379 IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
380 SkipImportedVisits) {
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());
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 ui::PAGE_TRANSITION_TYPED,
391 history::SOURCE_FIREFOX_IMPORTED);
392 AddUrlToHistoryWithTransition(0, browsed_url,
393 ui::PAGE_TRANSITION_TYPED,
394 history::SOURCE_BROWSED);
395 AddUrlToHistoryWithTransition(0, browsed_and_imported_url,
396 ui::PAGE_TRANSITION_TYPED,
397 history::SOURCE_FIREFOX_IMPORTED);
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());
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 ui::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());
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());
420 IN_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.";
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
436 ASSERT_TRUE(GetUrlFromClient(1, bookmark_url, &row));
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)));
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));