summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-02 22:16:32 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-02 22:16:32 +0000
commit1695f10ff0d39d021782b3ab67013ec2c71c780c (patch)
tree2d59f7f859d59c1a81f8f5a2f313dad379cf2577 /chrome/browser
parent2bdb6e8a833382113f1d8c9bb6fb6cf45f7e8dd4 (diff)
downloadchromium_src-1695f10ff0d39d021782b3ab67013ec2c71c780c.zip
chromium_src-1695f10ff0d39d021782b3ab67013ec2c71c780c.tar.gz
chromium_src-1695f10ff0d39d021782b3ab67013ec2c71c780c.tar.bz2
Move sync test code out of chrome/test
BUG=90905 Review URL: http://codereview.chromium.org/7828055 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99464 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/sync/abstract_profile_sync_service_test.cc2
-rw-r--r--chrome/browser/sync/backend_migrator_unittest.cc2
-rw-r--r--chrome/browser/sync/engine/DEPS2
-rw-r--r--chrome/browser/sync/engine/apply_updates_command_unittest.cc4
-rw-r--r--chrome/browser/sync/engine/build_commit_command_unittest.cc2
-rw-r--r--chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc2
-rw-r--r--chrome/browser/sync/engine/clear_data_command_unittest.cc6
-rw-r--r--chrome/browser/sync/engine/download_updates_command_unittest.cc4
-rw-r--r--chrome/browser/sync/engine/process_commit_response_command_unittest.cc4
-rw-r--r--chrome/browser/sync/engine/sync_scheduler_unittest.cc4
-rw-r--r--chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc4
-rw-r--r--chrome/browser/sync/engine/syncer_proto_util_unittest.cc4
-rw-r--r--chrome/browser/sync/engine/syncer_unittest.cc8
-rw-r--r--chrome/browser/sync/engine/verify_updates_command_unittest.cc2
-rw-r--r--chrome/browser/sync/glue/sync_backend_registrar_unittest.cc2
-rw-r--r--chrome/browser/sync/internal_api/DEPS1
-rw-r--r--chrome/browser/sync/internal_api/syncapi_unittest.cc2
-rw-r--r--chrome/browser/sync/js/DEPS2
-rw-r--r--chrome/browser/sync/js/js_sync_manager_observer_unittest.cc2
-rw-r--r--chrome/browser/sync/profile_sync_service_autofill_unittest.cc2
-rw-r--r--chrome/browser/sync/profile_sync_service_bookmark_unittest.cc4
-rw-r--r--chrome/browser/sync/profile_sync_service_password_unittest.cc2
-rw-r--r--chrome/browser/sync/profile_sync_service_session_unittest.cc2
-rw-r--r--chrome/browser/sync/profile_sync_service_typed_url_unittest.cc2
-rw-r--r--chrome/browser/sync/sessions/DEPS2
-rw-r--r--chrome/browser/sync/sessions/ordered_commit_set_unittest.cc2
-rw-r--r--chrome/browser/sync/sessions/status_controller_unittest.cc2
-rw-r--r--chrome/browser/sync/sessions/sync_session_unittest.cc2
-rw-r--r--chrome/browser/sync/syncable/DEPS2
-rw-r--r--chrome/browser/sync/syncable/syncable_id_unittest.cc2
-rw-r--r--chrome/browser/sync/syncable/syncable_mock.h2
-rw-r--r--chrome/browser/sync/syncable/syncable_unittest.cc6
-rw-r--r--chrome/browser/sync/test/engine/mock_connection_manager.cc624
-rw-r--r--chrome/browser/sync/test/engine/mock_connection_manager.h364
-rw-r--r--chrome/browser/sync/test/engine/mock_gaia_authenticator.cc152
-rw-r--r--chrome/browser/sync/test/engine/mock_gaia_authenticator.h117
-rw-r--r--chrome/browser/sync/test/engine/mock_gaia_authenticator_unittest.cc158
-rw-r--r--chrome/browser/sync/test/engine/proto_extension_validator.h55
-rw-r--r--chrome/browser/sync/test/engine/syncer_command_test.h170
-rw-r--r--chrome/browser/sync/test/engine/test_directory_setter_upper.cc139
-rw-r--r--chrome/browser/sync/test/engine/test_directory_setter_upper.h144
-rw-r--r--chrome/browser/sync/test/engine/test_id_factory.h73
-rw-r--r--chrome/browser/sync/test/engine/test_syncable_utils.cc62
-rw-r--r--chrome/browser/sync/test/engine/test_syncable_utils.h41
-rw-r--r--chrome/browser/sync/test/engine/test_user_share.cc35
-rw-r--r--chrome/browser/sync/test/engine/test_user_share.h67
-rw-r--r--chrome/browser/sync/test/live_sync/PRESUBMIT.py13
-rw-r--r--chrome/browser/sync/test/live_sync/apps_helper.cc78
-rw-r--r--chrome/browser/sync/test/live_sync/apps_helper.h48
-rw-r--r--chrome/browser/sync/test/live_sync/autofill_helper.cc298
-rw-r--r--chrome/browser/sync/test/live_sync/autofill_helper.h97
-rw-r--r--chrome/browser/sync/test/live_sync/bookmarks_helper.cc593
-rw-r--r--chrome/browser/sync/test/live_sync/bookmarks_helper.h184
-rw-r--r--chrome/browser/sync/test/live_sync/extensions_helper.cc125
-rw-r--r--chrome/browser/sync/test/live_sync/extensions_helper.h73
-rw-r--r--chrome/browser/sync/test/live_sync/live_sync_test.cc632
-rw-r--r--chrome/browser/sync/test/live_sync/live_sync_test.h317
-rw-r--r--chrome/browser/sync/test/live_sync/many_client_bookmarks_sync_test.cc28
-rw-r--r--chrome/browser/sync/test/live_sync/many_client_passwords_sync_test.cc43
-rw-r--r--chrome/browser/sync/test/live_sync/many_client_preferences_sync_test.cc29
-rw-r--r--chrome/browser/sync/test/live_sync/migration_errors_test.cc453
-rw-r--r--chrome/browser/sync/test/live_sync/multiple_client_bookmarks_sync_test.cc31
-rw-r--r--chrome/browser/sync/test/live_sync/multiple_client_passwords_sync_test.cc40
-rw-r--r--chrome/browser/sync/test/live_sync/multiple_client_preferences_sync_test.cc36
-rw-r--r--chrome/browser/sync/test/live_sync/multiple_client_sessions_sync_test.cc86
-rw-r--r--chrome/browser/sync/test/live_sync/multiple_client_typed_urls_sync_test.cc72
-rw-r--r--chrome/browser/sync/test/live_sync/passwords_helper.cc205
-rw-r--r--chrome/browser/sync/test/live_sync/passwords_helper.h81
-rw-r--r--chrome/browser/sync/test/live_sync/performance/autofill_sync_perf_test.cc198
-rw-r--r--chrome/browser/sync/test/live_sync/performance/bookmarks_sync_perf_test.cc107
-rw-r--r--chrome/browser/sync/test/live_sync/performance/extensions_sync_perf_test.cc102
-rw-r--r--chrome/browser/sync/test/live_sync/performance/passwords_sync_perf_test.cc95
-rw-r--r--chrome/browser/sync/test/live_sync/performance/sessions_sync_perf_test.cc131
-rw-r--r--chrome/browser/sync/test/live_sync/performance/sync_timing_helper.cc46
-rw-r--r--chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h49
-rw-r--r--chrome/browser/sync/test/live_sync/performance/typed_urls_sync_perf_test.cc102
-rw-r--r--chrome/browser/sync/test/live_sync/preferences_helper.cc196
-rw-r--r--chrome/browser/sync/test/live_sync/preferences_helper.h100
-rw-r--r--chrome/browser/sync/test/live_sync/sessions_helper.cc293
-rw-r--r--chrome/browser/sync/test/live_sync/sessions_helper.h109
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_apps_sync_test.cc56
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_bookmarks_sync_test.cc150
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_extensions_sync_test.cc57
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_passwords_sync_test.cc46
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_preferences_sync_test.cc29
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_sessions_sync_test.cc48
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_themes_sync_test.cc99
-rw-r--r--chrome/browser/sync/test/live_sync/single_client_typed_urls_sync_test.cc46
-rw-r--r--chrome/browser/sync/test/live_sync/sync_datatype_helper.cc29
-rw-r--r--chrome/browser/sync/test/live_sync/sync_datatype_helper.h26
-rw-r--r--chrome/browser/sync/test/live_sync/sync_errors_test.cc48
-rw-r--r--chrome/browser/sync/test/live_sync/sync_extension_helper.cc361
-rw-r--r--chrome/browser/sync/test/live_sync/sync_extension_helper.h120
-rw-r--r--chrome/browser/sync/test/live_sync/sync_integration_test.py404
-rw-r--r--chrome/browser/sync/test/live_sync/themes_helper.cc75
-rw-r--r--chrome/browser/sync/test/live_sync/themes_helper.h55
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_apps_sync_test.cc280
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_autofill_sync_test.cc409
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_bookmarks_sync_test.cc1757
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_extensions_sync_test.cc283
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_passwords_sync_test.cc323
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_preferences_sync_test.cc669
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_sessions_sync_test.cc353
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_themes_sync_test.cc245
-rw-r--r--chrome/browser/sync/test/live_sync/two_client_typed_urls_sync_test.cc178
-rw-r--r--chrome/browser/sync/test/live_sync/typed_urls_helper.cc184
-rw-r--r--chrome/browser/sync/test/live_sync/typed_urls_helper.h51
-rw-r--r--chrome/browser/sync/test/null_directory_change_delegate.cc27
-rw-r--r--chrome/browser/sync/test/null_directory_change_delegate.h33
-rw-r--r--chrome/browser/sync/test/sessions/test_scoped_session_event_listener.h36
-rw-r--r--chrome/browser/sync/test/test_http_bridge_factory.cc42
-rw-r--r--chrome/browser/sync/test/test_http_bridge_factory.h54
-rw-r--r--chrome/browser/sync/test_profile_sync_service.cc2
-rw-r--r--chrome/browser/sync/test_profile_sync_service.h2
114 files changed, 13911 insertions, 48 deletions
diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.cc b/chrome/browser/sync/abstract_profile_sync_service_test.cc
index 640b273..01f379f 100644
--- a/chrome/browser/sync/abstract_profile_sync_service_test.cc
+++ b/chrome/browser/sync/abstract_profile_sync_service_test.cc
@@ -10,8 +10,8 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "chrome/browser/sync/util/cryptographer.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
using browser_sync::TestIdFactory;
using sync_api::UserShare;
diff --git a/chrome/browser/sync/backend_migrator_unittest.cc b/chrome/browser/sync/backend_migrator_unittest.cc
index 3d254b8..2c49b70 100644
--- a/chrome/browser/sync/backend_migrator_unittest.cc
+++ b/chrome/browser/sync/backend_migrator_unittest.cc
@@ -12,8 +12,8 @@
#include "chrome/browser/sync/protocol/sync.pb.h"
#include "chrome/browser/sync/sessions/session_state.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/test/engine/test_user_share.h"
#include "chrome/common/chrome_notification_types.h"
-#include "chrome/test/sync/engine/test_user_share.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/engine/DEPS b/chrome/browser/sync/engine/DEPS
index 0142088..59174bf 100644
--- a/chrome/browser/sync/engine/DEPS
+++ b/chrome/browser/sync/engine/DEPS
@@ -1,7 +1,6 @@
include_rules = [
"-chrome",
"+chrome/test/base",
- "+chrome/test/sync",
"+chrome/browser/sync/engine",
"+chrome/browser/sync/js",
@@ -9,6 +8,7 @@ include_rules = [
"+chrome/browser/sync/protocol",
"+chrome/browser/sync/sessions",
"+chrome/browser/sync/syncable",
+ "+chrome/browser/sync/test",
"+chrome/browser/sync/util",
# TODO(rlarocque) 19878: Move remaining syncapi-related headers to
diff --git a/chrome/browser/sync/engine/apply_updates_command_unittest.cc b/chrome/browser/sync/engine/apply_updates_command_unittest.cc
index aa32c71..6fcb2868 100644
--- a/chrome/browser/sync/engine/apply_updates_command_unittest.cc
+++ b/chrome/browser/sync/engine/apply_updates_command_unittest.cc
@@ -17,8 +17,8 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
-#include "chrome/test/sync/engine/syncer_command_test.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/engine/build_commit_command_unittest.cc b/chrome/browser/sync/engine/build_commit_command_unittest.cc
index 24b139b..d924517 100644
--- a/chrome/browser/sync/engine/build_commit_command_unittest.cc
+++ b/chrome/browser/sync/engine/build_commit_command_unittest.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "chrome/browser/sync/engine/build_commit_command.h"
-#include "chrome/test/sync/engine/syncer_command_test.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc b/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc
index 02ec54d..5ae0c78 100644
--- a/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc
+++ b/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc
@@ -8,7 +8,7 @@
#include "chrome/browser/sync/engine/syncer_end_command.h"
#include "chrome/browser/sync/sessions/sync_session.h"
-#include "chrome/test/sync/engine/syncer_command_test.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sync/engine/clear_data_command_unittest.cc b/chrome/browser/sync/engine/clear_data_command_unittest.cc
index 4ae4c8b..f4739a2 100644
--- a/chrome/browser/sync/engine/clear_data_command_unittest.cc
+++ b/chrome/browser/sync/engine/clear_data_command_unittest.cc
@@ -8,9 +8,9 @@
#include "chrome/browser/sync/protocol/preference_specifics.pb.h"
#include "chrome/browser/sync/protocol/sync.pb.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
-#include "chrome/test/sync/engine/proto_extension_validator.h"
-#include "chrome/test/sync/engine/syncer_command_test.h"
-#include "chrome/test/sync/sessions/test_scoped_session_event_listener.h"
+#include "chrome/browser/sync/test/engine/proto_extension_validator.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
+#include "chrome/browser/sync/test/sessions/test_scoped_session_event_listener.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/engine/download_updates_command_unittest.cc b/chrome/browser/sync/engine/download_updates_command_unittest.cc
index 4bc22f1..1e2aa2f 100644
--- a/chrome/browser/sync/engine/download_updates_command_unittest.cc
+++ b/chrome/browser/sync/engine/download_updates_command_unittest.cc
@@ -8,8 +8,8 @@
#include "chrome/browser/sync/protocol/preference_specifics.pb.h"
#include "chrome/browser/sync/protocol/sync.pb.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
-#include "chrome/test/sync/engine/proto_extension_validator.h"
-#include "chrome/test/sync/engine/syncer_command_test.h"
+#include "chrome/browser/sync/test/engine/proto_extension_validator.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/engine/process_commit_response_command_unittest.cc b/chrome/browser/sync/engine/process_commit_response_command_unittest.cc
index bfa97f2..a428622 100644
--- a/chrome/browser/sync/engine/process_commit_response_command_unittest.cc
+++ b/chrome/browser/sync/engine/process_commit_response_command_unittest.cc
@@ -14,8 +14,8 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
-#include "chrome/test/sync/engine/syncer_command_test.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/engine/sync_scheduler_unittest.cc b/chrome/browser/sync/engine/sync_scheduler_unittest.cc
index ad58f71..57e05c1 100644
--- a/chrome/browser/sync/engine/sync_scheduler_unittest.cc
+++ b/chrome/browser/sync/engine/sync_scheduler_unittest.cc
@@ -13,8 +13,8 @@
#include "chrome/browser/sync/engine/sync_scheduler.h"
#include "chrome/browser/sync/engine/syncer.h"
#include "chrome/browser/sync/sessions/test_util.h"
-#include "chrome/test/sync/engine/mock_connection_manager.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "chrome/browser/sync/test/engine/mock_connection_manager.h"
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc b/chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc
index 911c5db..2a581b2 100644
--- a/chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc
+++ b/chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc
@@ -8,8 +8,8 @@
#include "chrome/browser/sync/engine/sync_scheduler.h"
#include "chrome/browser/sync/sessions/sync_session_context.h"
#include "chrome/browser/sync/sessions/test_util.h"
-#include "chrome/test/sync/engine/mock_connection_manager.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "chrome/browser/sync/test/engine/mock_connection_manager.h"
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/engine/syncer_proto_util_unittest.cc b/chrome/browser/sync/engine/syncer_proto_util_unittest.cc
index 0a2bc9a..2b9fcbb 100644
--- a/chrome/browser/sync/engine/syncer_proto_util_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_proto_util_unittest.cc
@@ -12,8 +12,8 @@
#include "chrome/browser/sync/syncable/blob.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable.h"
-#include "chrome/test/sync/engine/mock_connection_manager.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "chrome/browser/sync/test/engine/mock_connection_manager.h"
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/engine/syncer_unittest.cc b/chrome/browser/sync/engine/syncer_unittest.cc
index 4e39352..093d473 100644
--- a/chrome/browser/sync/engine/syncer_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_unittest.cc
@@ -34,10 +34,10 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable.h"
-#include "chrome/test/sync/engine/mock_connection_manager.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
-#include "chrome/test/sync/engine/test_syncable_utils.h"
+#include "chrome/browser/sync/test/engine/mock_connection_manager.h"
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
+#include "chrome/browser/sync/test/engine/test_syncable_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::TimeDelta;
diff --git a/chrome/browser/sync/engine/verify_updates_command_unittest.cc b/chrome/browser/sync/engine/verify_updates_command_unittest.cc
index 03f6e49..a6fa195 100644
--- a/chrome/browser/sync/engine/verify_updates_command_unittest.cc
+++ b/chrome/browser/sync/engine/verify_updates_command_unittest.cc
@@ -10,7 +10,7 @@
#include "chrome/browser/sync/engine/mock_model_safe_workers.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
-#include "chrome/test/sync/engine/syncer_command_test.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
index 8d40955..806ff12 100644
--- a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
+++ b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
@@ -7,8 +7,8 @@
#include "chrome/browser/sync/glue/change_processor_mock.h"
#include "chrome/browser/sync/glue/ui_model_worker.h"
#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/test/engine/test_user_share.h"
#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/sync/engine/test_user_share.h"
#include "content/browser/browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/internal_api/DEPS b/chrome/browser/sync/internal_api/DEPS
index a393f32..f3e71ab 100644
--- a/chrome/browser/sync/internal_api/DEPS
+++ b/chrome/browser/sync/internal_api/DEPS
@@ -1,7 +1,6 @@
include_rules = [
"-chrome",
"+chrome/test/base",
- "+chrome/test/sync",
"+chrome/browser/sync",
"-chrome/browser/sync/api",
diff --git a/chrome/browser/sync/internal_api/syncapi_unittest.cc b/chrome/browser/sync/internal_api/syncapi_unittest.cc
index 4ee77b7..50e3a36 100644
--- a/chrome/browser/sync/internal_api/syncapi_unittest.cc
+++ b/chrome/browser/sync/internal_api/syncapi_unittest.cc
@@ -45,9 +45,9 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
+#include "chrome/browser/sync/test/engine/test_user_share.h"
#include "chrome/browser/sync/util/cryptographer.h"
#include "chrome/test/base/values_test_util.h"
-#include "chrome/test/sync/engine/test_user_share.h"
#include "content/browser/browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/js/DEPS b/chrome/browser/sync/js/DEPS
index 3b29013..3a59572 100644
--- a/chrome/browser/sync/js/DEPS
+++ b/chrome/browser/sync/js/DEPS
@@ -1,6 +1,5 @@
include_rules = [
"-chrome",
- "+chrome/test/sync",
"+chrome/browser/sync/js",
@@ -8,6 +7,7 @@ include_rules = [
"+chrome/browser/sync/sessions/session_state.h",
"+chrome/browser/sync/syncable/model_type.h",
"+chrome/browser/sync/syncable/transaction_observer.h",
+ "+chrome/browser/sync/test",
# TODO(akalin): this should be in base.
"+chrome/browser/sync/weak_handle.h",
diff --git a/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc b/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc
index 4ffd253..148091d 100644
--- a/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc
+++ b/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc
@@ -19,8 +19,8 @@
#include "chrome/browser/sync/js/js_test_util.h"
#include "chrome/browser/sync/sessions/session_state.h"
#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/test/engine/test_user_share.h"
#include "chrome/browser/sync/weak_handle.h"
-#include "chrome/test/sync/engine/test_user_share.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
index 7f46b91..2fc2b7f 100644
--- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
@@ -40,13 +40,13 @@
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "chrome/browser/webdata/autofill_change.h"
#include "chrome/browser/webdata/autofill_entry.h"
#include "chrome/browser/webdata/autofill_table.h"
#include "chrome/browser/webdata/web_database.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_source.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc b/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc
index 39e0306..a00414e 100644
--- a/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc
@@ -30,10 +30,10 @@
#include "chrome/browser/sync/internal_api/write_node.h"
#include "chrome/browser/sync/internal_api/write_transaction.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
+#include "chrome/browser/sync/test/engine/test_user_share.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
-#include "chrome/test/sync/engine/test_user_share.h"
#include "content/browser/browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/profile_sync_service_password_unittest.cc b/chrome/browser/sync/profile_sync_service_password_unittest.cc
index 175018a..8e45e78 100644
--- a/chrome/browser/sync/profile_sync_service_password_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc
@@ -30,11 +30,11 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/profile_mock.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_observer_mock.h"
#include "content/common/notification_source.h"
diff --git a/chrome/browser/sync/profile_sync_service_session_unittest.cc b/chrome/browser/sync/profile_sync_service_session_unittest.cc
index e47aa20..b1c7309 100644
--- a/chrome/browser/sync/profile_sync_service_session_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc
@@ -31,12 +31,12 @@
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/profile_mock.h"
#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
index 4bcd09a..103e7f0 100644
--- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
@@ -32,11 +32,11 @@
#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/test/base/profile_mock.h"
#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sync/sessions/DEPS b/chrome/browser/sync/sessions/DEPS
index 0d7660a..2a4b7d8 100644
--- a/chrome/browser/sync/sessions/DEPS
+++ b/chrome/browser/sync/sessions/DEPS
@@ -1,12 +1,12 @@
include_rules = [
"-chrome",
"+chrome/test/base",
- "+chrome/test/sync",
"+chrome/browser/sync/engine",
"+chrome/browser/sync/protocol",
"+chrome/browser/sync/sessions",
"+chrome/browser/sync/syncable",
+ "+chrome/browser/sync/test",
"+chrome/browser/sync/util",
"-chrome/browser/sync/internal_api",
diff --git a/chrome/browser/sync/sessions/ordered_commit_set_unittest.cc b/chrome/browser/sync/sessions/ordered_commit_set_unittest.cc
index 0c9db8e..cf1f4d9 100644
--- a/chrome/browser/sync/sessions/ordered_commit_set_unittest.cc
+++ b/chrome/browser/sync/sessions/ordered_commit_set_unittest.cc
@@ -4,7 +4,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "chrome/browser/sync/sessions/ordered_commit_set.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
using std::vector;
diff --git a/chrome/browser/sync/sessions/status_controller_unittest.cc b/chrome/browser/sync/sessions/status_controller_unittest.cc
index bd7296b..bebbd18 100644
--- a/chrome/browser/sync/sessions/status_controller_unittest.cc
+++ b/chrome/browser/sync/sessions/status_controller_unittest.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "chrome/browser/sync/sessions/sync_session.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/sessions/sync_session_unittest.cc b/chrome/browser/sync/sessions/sync_session_unittest.cc
index 4c58b45..34c91de 100644
--- a/chrome/browser/sync/sessions/sync_session_unittest.cc
+++ b/chrome/browser/sync/sessions/sync_session_unittest.cc
@@ -14,7 +14,7 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
#include "testing/gtest/include/gtest/gtest.h"
using syncable::WriteTransaction;
diff --git a/chrome/browser/sync/syncable/DEPS b/chrome/browser/sync/syncable/DEPS
index 032cd30..53bed5c 100644
--- a/chrome/browser/sync/syncable/DEPS
+++ b/chrome/browser/sync/syncable/DEPS
@@ -1,11 +1,11 @@
include_rules = [
"-chrome",
"+chrome/test/base",
- "+chrome/test/sync",
"+chrome/browser/sync/protocol",
"+chrome/browser/sync/sessions",
"+chrome/browser/sync/syncable",
+ "+chrome/browser/sync/test",
"+chrome/browser/sync/util",
# this file is wierd.
diff --git a/chrome/browser/sync/syncable/syncable_id_unittest.cc b/chrome/browser/sync/syncable/syncable_id_unittest.cc
index 56ab4a8..9703861 100644
--- a/chrome/browser/sync/syncable/syncable_id_unittest.cc
+++ b/chrome/browser/sync/syncable/syncable_id_unittest.cc
@@ -8,8 +8,8 @@
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "chrome/test/base/values_test_util.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
using std::vector;
diff --git a/chrome/browser/sync/syncable/syncable_mock.h b/chrome/browser/sync/syncable/syncable_mock.h
index 51692a7..656b3d5 100644
--- a/chrome/browser/sync/syncable/syncable_mock.h
+++ b/chrome/browser/sync/syncable/syncable_mock.h
@@ -9,7 +9,7 @@
#include <string>
#include "chrome/browser/sync/syncable/syncable.h"
-#include "chrome/test/sync/null_directory_change_delegate.h"
+#include "chrome/browser/sync/test/null_directory_change_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/syncable/syncable_unittest.cc b/chrome/browser/sync/syncable/syncable_unittest.cc
index e9d20e2..004e739 100644
--- a/chrome/browser/sync/syncable/syncable_unittest.cc
+++ b/chrome/browser/sync/syncable/syncable_unittest.cc
@@ -36,10 +36,10 @@
#include "chrome/browser/sync/syncable/directory_backing_store.h"
#include "chrome/browser/sync/syncable/directory_change_delegate.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
+#include "chrome/browser/sync/test/engine/test_syncable_utils.h"
+#include "chrome/browser/sync/test/null_directory_change_delegate.h"
#include "chrome/test/base/values_test_util.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
-#include "chrome/test/sync/engine/test_syncable_utils.h"
-#include "chrome/test/sync/null_directory_change_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/sqlite/sqlite3.h"
diff --git a/chrome/browser/sync/test/engine/mock_connection_manager.cc b/chrome/browser/sync/test/engine/mock_connection_manager.cc
new file mode 100644
index 0000000..22e11c0
--- /dev/null
+++ b/chrome/browser/sync/test/engine/mock_connection_manager.cc
@@ -0,0 +1,624 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Mock ServerConnectionManager class for use in client regression tests.
+
+#include "chrome/browser/sync/test/engine/mock_connection_manager.h"
+
+#include <map>
+
+#include "base/stringprintf.h"
+#include "base/tracked.h"
+#include "chrome/browser/sync/engine/syncer_proto_util.h"
+#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using browser_sync::HttpResponse;
+using browser_sync::ServerConnectionManager;
+using browser_sync::ServerConnectionEventListener;
+using browser_sync::ServerConnectionEvent;
+using browser_sync::SyncerProtoUtil;
+using browser_sync::TestIdFactory;
+using std::map;
+using std::string;
+using sync_pb::ClientToServerMessage;
+using sync_pb::ClientToServerResponse;
+using sync_pb::CommitMessage;
+using sync_pb::CommitResponse;
+using sync_pb::CommitResponse_EntryResponse;
+using sync_pb::GetUpdatesMessage;
+using sync_pb::SyncEntity;
+using syncable::DirectoryManager;
+using syncable::FIRST_REAL_MODEL_TYPE;
+using syncable::MODEL_TYPE_COUNT;
+using syncable::ModelType;
+using syncable::ScopedDirLookup;
+using syncable::WriteTransaction;
+
+MockConnectionManager::MockConnectionManager(DirectoryManager* dirmgr,
+ const string& name)
+ : ServerConnectionManager("unused", 0, false, "version"),
+ conflict_all_commits_(false),
+ conflict_n_commits_(0),
+ next_new_id_(10000),
+ store_birthday_("Store BDay!"),
+ store_birthday_sent_(false),
+ client_stuck_(false),
+ commit_time_rename_prepended_string_(""),
+ fail_next_postbuffer_(false),
+ directory_manager_(dirmgr),
+ directory_name_(name),
+ mid_commit_observer_(NULL),
+ throttling_(false),
+ fail_with_auth_invalid_(false),
+ fail_non_periodic_get_updates_(false),
+ client_command_(NULL),
+ next_position_in_parent_(2),
+ use_legacy_bookmarks_protocol_(false),
+ num_get_updates_requests_(0) {
+ server_reachable_ = true;
+ SetNewTimestamp(0);
+};
+
+MockConnectionManager::~MockConnectionManager() {
+ EXPECT_TRUE(update_queue_.empty()) << "Unfetched updates.";
+}
+
+void MockConnectionManager::SetCommitTimeRename(string prepend) {
+ commit_time_rename_prepended_string_ = prepend;
+}
+
+void MockConnectionManager::SetMidCommitCallback(Callback0::Type* callback) {
+ mid_commit_callback_.reset(callback);
+}
+
+void MockConnectionManager::SetMidCommitObserver(
+ MockConnectionManager::MidCommitObserver* observer) {
+ mid_commit_observer_ = observer;
+}
+
+bool MockConnectionManager::PostBufferToPath(PostBufferParams* params,
+ const string& path,
+ const string& auth_token,
+ browser_sync::ScopedServerStatusWatcher* watcher) {
+ ClientToServerMessage post;
+ CHECK(post.ParseFromString(params->buffer_in));
+ last_request_.CopyFrom(post);
+ client_stuck_ = post.sync_problem_detected();
+ ClientToServerResponse response;
+ response.Clear();
+
+ ScopedDirLookup directory(directory_manager_, directory_name_);
+ // For any non-AUTHENTICATE requests, a valid directory should be set up.
+ // If the Directory's locked when we do this, it's a problem as in normal
+ // use this function could take a while to return because it accesses the
+ // network. As we can't test this we do the next best thing and hang here
+ // when there's an issue.
+ if (post.message_contents() != ClientToServerMessage::AUTHENTICATE) {
+ CHECK(directory.good());
+ WriteTransaction wt(FROM_HERE, syncable::UNITTEST, directory);
+ }
+
+ if (fail_next_postbuffer_) {
+ fail_next_postbuffer_ = false;
+ return false;
+ }
+
+ if (!server_reachable_) {
+ params->response.server_status = HttpResponse::CONNECTION_UNAVAILABLE;
+ return false;
+ }
+
+ // Default to an ok connection.
+ params->response.server_status = HttpResponse::SERVER_CONNECTION_OK;
+ response.set_error_code(ClientToServerResponse::SUCCESS);
+ const string current_store_birthday = store_birthday();
+ response.set_store_birthday(current_store_birthday);
+ if (post.has_store_birthday() && post.store_birthday() !=
+ current_store_birthday) {
+ response.set_error_code(ClientToServerResponse::NOT_MY_BIRTHDAY);
+ response.set_error_message("Merry Unbirthday!");
+ response.SerializeToString(&params->buffer_out);
+ store_birthday_sent_ = true;
+ return true;
+ }
+ bool result = true;
+ EXPECT_TRUE(!store_birthday_sent_ || post.has_store_birthday() ||
+ post.message_contents() == ClientToServerMessage::AUTHENTICATE);
+ store_birthday_sent_ = true;
+
+ if (post.message_contents() == ClientToServerMessage::COMMIT) {
+ ProcessCommit(&post, &response);
+ } else if (post.message_contents() == ClientToServerMessage::GET_UPDATES) {
+ ProcessGetUpdates(&post, &response);
+ } else if (post.message_contents() == ClientToServerMessage::AUTHENTICATE) {
+ ProcessAuthenticate(&post, &response, auth_token);
+ } else if (post.message_contents() == ClientToServerMessage::CLEAR_DATA) {
+ ProcessClearData(&post, &response);
+ } else {
+ EXPECT_TRUE(false) << "Unknown/unsupported ClientToServerMessage";
+ return false;
+ }
+ if (client_command_.get()) {
+ response.mutable_client_command()->CopyFrom(*client_command_.get());
+ }
+
+ {
+ base::AutoLock lock(response_code_override_lock_);
+ if (throttling_) {
+ response.set_error_code(ClientToServerResponse::THROTTLED);
+ throttling_ = false;
+ }
+
+ if (fail_with_auth_invalid_)
+ response.set_error_code(ClientToServerResponse::AUTH_INVALID);
+ }
+
+ response.SerializeToString(&params->buffer_out);
+ if (post.message_contents() == ClientToServerMessage::COMMIT &&
+ mid_commit_callback_.get()) {
+ mid_commit_callback_->Run();
+ }
+ if (mid_commit_observer_) {
+ mid_commit_observer_->Observe();
+ }
+
+ return result;
+}
+
+bool MockConnectionManager::IsServerReachable() {
+ return true;
+}
+
+bool MockConnectionManager::IsUserAuthenticated() {
+ return true;
+}
+
+sync_pb::GetUpdatesResponse* MockConnectionManager::GetUpdateResponse() {
+ if (update_queue_.empty()) {
+ NextUpdateBatch();
+ }
+ return &update_queue_.back();
+}
+
+void MockConnectionManager::AddDefaultBookmarkData(sync_pb::SyncEntity* entity,
+ bool is_folder) {
+ if (use_legacy_bookmarks_protocol_) {
+ sync_pb::SyncEntity_BookmarkData* data = entity->mutable_bookmarkdata();
+ data->set_bookmark_folder(is_folder);
+
+ if (!is_folder) {
+ data->set_bookmark_url("http://google.com");
+ }
+ } else {
+ entity->set_folder(is_folder);
+ entity->mutable_specifics()->MutableExtension(sync_pb::bookmark);
+ if (!is_folder) {
+ entity->mutable_specifics()->MutableExtension(sync_pb::bookmark)->
+ set_url("http://google.com");
+ }
+ }
+}
+
+SyncEntity* MockConnectionManager::AddUpdateDirectory(int id,
+ int parent_id,
+ string name,
+ int64 version,
+ int64 sync_ts) {
+ return AddUpdateDirectory(TestIdFactory::FromNumber(id),
+ TestIdFactory::FromNumber(parent_id),
+ name,
+ version,
+ sync_ts);
+}
+
+sync_pb::ClientCommand* MockConnectionManager::GetNextClientCommand() {
+ if (!client_command_.get())
+ client_command_.reset(new sync_pb::ClientCommand());
+ return client_command_.get();
+}
+
+SyncEntity* MockConnectionManager::AddUpdateBookmark(int id, int parent_id,
+ string name, int64 version,
+ int64 sync_ts) {
+ return AddUpdateBookmark(TestIdFactory::FromNumber(id),
+ TestIdFactory::FromNumber(parent_id),
+ name,
+ version,
+ sync_ts);
+}
+
+SyncEntity* MockConnectionManager::AddUpdateFull(string id, string parent_id,
+ string name, int64 version,
+ int64 sync_ts, bool is_dir) {
+ SyncEntity* ent = GetUpdateResponse()->add_entries();
+ ent->set_id_string(id);
+ ent->set_parent_id_string(parent_id);
+ ent->set_non_unique_name(name);
+ ent->set_name(name);
+ ent->set_version(version);
+ ent->set_sync_timestamp(sync_ts);
+ ent->set_mtime(sync_ts);
+ ent->set_ctime(1);
+ ent->set_position_in_parent(GeneratePositionInParent());
+ AddDefaultBookmarkData(ent, is_dir);
+
+ return ent;
+}
+
+SyncEntity* MockConnectionManager::AddUpdateDirectory(string id,
+ string parent_id,
+ string name,
+ int64 version,
+ int64 sync_ts) {
+ return AddUpdateFull(id, parent_id, name, version, sync_ts, true);
+}
+
+SyncEntity* MockConnectionManager::AddUpdateBookmark(string id,
+ string parent_id,
+ string name, int64 version,
+ int64 sync_ts) {
+ return AddUpdateFull(id, parent_id, name, version, sync_ts, false);
+}
+
+SyncEntity* MockConnectionManager::AddUpdateFromLastCommit() {
+ EXPECT_EQ(1, last_sent_commit().entries_size());
+ EXPECT_EQ(1, last_commit_response().entryresponse_size());
+ EXPECT_EQ(CommitResponse::SUCCESS,
+ last_commit_response().entryresponse(0).response_type());
+
+ if (last_sent_commit().entries(0).deleted()) {
+ AddUpdateTombstone(syncable::Id::CreateFromServerId(
+ last_sent_commit().entries(0).id_string()));
+ } else {
+ SyncEntity* ent = GetUpdateResponse()->add_entries();
+ ent->CopyFrom(last_sent_commit().entries(0));
+ ent->clear_insert_after_item_id();
+ ent->clear_old_parent_id();
+ ent->set_position_in_parent(
+ last_commit_response().entryresponse(0).position_in_parent());
+ ent->set_version(
+ last_commit_response().entryresponse(0).version());
+ ent->set_id_string(
+ last_commit_response().entryresponse(0).id_string());
+ // Tests don't currently care about the following:
+ // originator_cache_guid, originator_client_item_id, parent_id_string,
+ // name, non_unique_name.
+ }
+ return GetMutableLastUpdate();
+}
+
+void MockConnectionManager::AddUpdateTombstone(const syncable::Id& id) {
+ // Tombstones have only the ID set and dummy values for the required fields.
+ SyncEntity* ent = GetUpdateResponse()->add_entries();
+ ent->set_id_string(id.GetServerId());
+ ent->set_version(0);
+ ent->set_name("");
+ ent->set_deleted(true);
+}
+
+void MockConnectionManager::SetLastUpdateDeleted() {
+ // Tombstones have only the ID set. Wipe anything else.
+ string id_string = GetMutableLastUpdate()->id_string();
+ GetUpdateResponse()->mutable_entries()->RemoveLast();
+ AddUpdateTombstone(syncable::Id::CreateFromServerId(id_string));
+}
+
+void MockConnectionManager::SetLastUpdateOriginatorFields(
+ const string& client_id,
+ const string& entry_id) {
+ GetMutableLastUpdate()->set_originator_cache_guid(client_id);
+ GetMutableLastUpdate()->set_originator_client_item_id(entry_id);
+}
+
+void MockConnectionManager::SetLastUpdateServerTag(const string& tag) {
+ GetMutableLastUpdate()->set_server_defined_unique_tag(tag);
+}
+
+void MockConnectionManager::SetLastUpdateClientTag(const string& tag) {
+ GetMutableLastUpdate()->set_client_defined_unique_tag(tag);
+}
+
+void MockConnectionManager::SetLastUpdatePosition(int64 server_position) {
+ GetMutableLastUpdate()->set_position_in_parent(server_position);
+}
+
+void MockConnectionManager::SetNewTimestamp(int ts) {
+ next_token_ = base::StringPrintf("mock connection ts = %d", ts);
+ ApplyToken();
+}
+
+void MockConnectionManager::ApplyToken() {
+ if (!update_queue_.empty()) {
+ GetUpdateResponse()->clear_new_progress_marker();
+ sync_pb::DataTypeProgressMarker* new_marker =
+ GetUpdateResponse()->add_new_progress_marker();
+ new_marker->set_data_type_id(-1); // Invalid -- clients shouldn't see.
+ new_marker->set_token(next_token_);
+ }
+}
+
+void MockConnectionManager::SetChangesRemaining(int64 timestamp) {
+ GetUpdateResponse()->set_changes_remaining(timestamp);
+}
+
+void MockConnectionManager::ProcessGetUpdates(ClientToServerMessage* csm,
+ ClientToServerResponse* response) {
+ CHECK(csm->has_get_updates());
+ ASSERT_EQ(csm->message_contents(), ClientToServerMessage::GET_UPDATES);
+ const GetUpdatesMessage& gu = csm->get_updates();
+ num_get_updates_requests_++;
+ EXPECT_FALSE(gu.has_from_timestamp());
+ EXPECT_FALSE(gu.has_requested_types());
+
+ if (fail_non_periodic_get_updates_) {
+ EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::PERIODIC,
+ gu.caller_info().source());
+ }
+
+ // Verify that the GetUpdates filter sent by the Syncer matches the test
+ // expectation.
+ for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
+ ModelType model_type = syncable::ModelTypeFromInt(i);
+ sync_pb::DataTypeProgressMarker const* progress_marker =
+ GetProgressMarkerForType(gu.from_progress_marker(), model_type);
+ EXPECT_EQ(expected_filter_[i], (progress_marker != NULL))
+ << "Syncer requested_types differs from test expectation.";
+ if (progress_marker) {
+ EXPECT_EQ((expected_payloads_.count(model_type) > 0 ?
+ expected_payloads_[model_type] :
+ std::string()),
+ progress_marker->notification_hint());
+ }
+ }
+
+ // Verify that the items we're about to send back to the client are of
+ // the types requested by the client. If this fails, it probably indicates
+ // a test bug.
+ EXPECT_TRUE(gu.fetch_folders());
+ EXPECT_FALSE(gu.has_requested_types());
+ if (update_queue_.empty()) {
+ GetUpdateResponse();
+ }
+ sync_pb::GetUpdatesResponse* updates = &update_queue_.front();
+ for (int i = 0; i < updates->entries_size(); ++i) {
+ if (!updates->entries(i).deleted()) {
+ ModelType entry_type = syncable::GetModelType(updates->entries(i));
+ EXPECT_TRUE(
+ IsModelTypePresentInSpecifics(gu.from_progress_marker(), entry_type))
+ << "Syncer did not request updates being provided by the test.";
+ }
+ }
+
+ response->mutable_get_updates()->CopyFrom(*updates);
+
+ // Set appropriate progress markers, overriding the value squirreled
+ // away by ApplyToken().
+ std::string token = response->get_updates().new_progress_marker(0).token();
+ response->mutable_get_updates()->clear_new_progress_marker();
+ for (int i = 0; i < gu.from_progress_marker_size(); ++i) {
+ if (gu.from_progress_marker(i).token() != token) {
+ sync_pb::DataTypeProgressMarker* new_marker =
+ response->mutable_get_updates()->add_new_progress_marker();
+ new_marker->set_data_type_id(gu.from_progress_marker(i).data_type_id());
+ new_marker->set_token(token);
+ }
+ }
+
+ update_queue_.pop_front();
+}
+
+void MockConnectionManager::SetClearUserDataResponseStatus(
+ sync_pb::ClientToServerResponse::ErrorType errortype ) {
+ // Note: this is not a thread-safe set, ok for now. NOT ok if tests
+ // run the syncer on the background thread while this method is called.
+ clear_user_data_response_errortype_ = errortype;
+}
+
+void MockConnectionManager::ProcessClearData(ClientToServerMessage* csm,
+ ClientToServerResponse* response) {
+ CHECK(csm->has_clear_user_data());
+ ASSERT_EQ(csm->message_contents(), ClientToServerMessage::CLEAR_DATA);
+ response->clear_user_data();
+ response->set_error_code(clear_user_data_response_errortype_);
+}
+
+void MockConnectionManager::ProcessAuthenticate(
+ ClientToServerMessage* csm,
+ ClientToServerResponse* response,
+ const std::string& auth_token) {
+ ASSERT_EQ(csm->message_contents(), ClientToServerMessage::AUTHENTICATE);
+ EXPECT_FALSE(auth_token.empty());
+
+ if (auth_token != valid_auth_token_) {
+ response->set_error_code(ClientToServerResponse::AUTH_INVALID);
+ return;
+ }
+
+ response->set_error_code(ClientToServerResponse::SUCCESS);
+ response->mutable_authenticate()->CopyFrom(auth_response_);
+ auth_response_.Clear();
+}
+
+void MockConnectionManager::SetAuthenticationResponseInfo(
+ const std::string& valid_auth_token,
+ const std::string& user_display_name,
+ const std::string& user_display_email,
+ const std::string& user_obfuscated_id) {
+ valid_auth_token_ = valid_auth_token;
+ sync_pb::UserIdentification* user = auth_response_.mutable_user();
+ user->set_display_name(user_display_name);
+ user->set_email(user_display_email);
+ user->set_obfuscated_id(user_obfuscated_id);
+}
+
+bool MockConnectionManager::ShouldConflictThisCommit() {
+ bool conflict = false;
+ if (conflict_all_commits_) {
+ conflict = true;
+ } else if (conflict_n_commits_ > 0) {
+ conflict = true;
+ --conflict_n_commits_;
+ }
+ return conflict;
+}
+
+void MockConnectionManager::ProcessCommit(ClientToServerMessage* csm,
+ ClientToServerResponse* response_buffer) {
+ CHECK(csm->has_commit());
+ ASSERT_EQ(csm->message_contents(), ClientToServerMessage::COMMIT);
+ map <string, string> changed_ids;
+ const CommitMessage& commit_message = csm->commit();
+ CommitResponse* commit_response = response_buffer->mutable_commit();
+ commit_messages_->push_back(new CommitMessage);
+ commit_messages_->back()->CopyFrom(commit_message);
+ map<string, CommitResponse_EntryResponse*> response_map;
+ for (int i = 0; i < commit_message.entries_size() ; i++) {
+ const sync_pb::SyncEntity& entry = commit_message.entries(i);
+ CHECK(entry.has_id_string());
+ string id = entry.id_string();
+ ASSERT_LT(entry.name().length(), 256ul) << " name probably too long. True "
+ "server name checking not implemented";
+ if (entry.version() == 0) {
+ // Relies on our new item string id format. (string representation of a
+ // negative number).
+ committed_ids_.push_back(syncable::Id::CreateFromClientString(id));
+ } else {
+ committed_ids_.push_back(syncable::Id::CreateFromServerId(id));
+ }
+ if (response_map.end() == response_map.find(id))
+ response_map[id] = commit_response->add_entryresponse();
+ CommitResponse_EntryResponse* er = response_map[id];
+ if (ShouldConflictThisCommit()) {
+ er->set_response_type(CommitResponse::CONFLICT);
+ continue;
+ }
+ er->set_response_type(CommitResponse::SUCCESS);
+ er->set_version(entry.version() + 1);
+ if (!commit_time_rename_prepended_string_.empty()) {
+ // Commit time rename sent down from the server.
+ er->set_name(commit_time_rename_prepended_string_ + entry.name());
+ }
+ string parent_id = entry.parent_id_string();
+ // Remap id's we've already assigned.
+ if (changed_ids.end() != changed_ids.find(parent_id)) {
+ parent_id = changed_ids[parent_id];
+ er->set_parent_id_string(parent_id);
+ }
+ if (entry.has_version() && 0 != entry.version()) {
+ er->set_id_string(id); // Allows verification.
+ } else {
+ string new_id = base::StringPrintf("mock_server:%d", next_new_id_++);
+ changed_ids[id] = new_id;
+ er->set_id_string(new_id);
+ }
+ }
+ commit_responses_->push_back(new CommitResponse(*commit_response));
+}
+
+SyncEntity* MockConnectionManager::AddUpdateDirectory(
+ syncable::Id id, syncable::Id parent_id, string name, int64 version,
+ int64 sync_ts) {
+ return AddUpdateDirectory(id.GetServerId(), parent_id.GetServerId(),
+ name, version, sync_ts);
+}
+
+SyncEntity* MockConnectionManager::AddUpdateBookmark(
+ syncable::Id id, syncable::Id parent_id, string name, int64 version,
+ int64 sync_ts) {
+ return AddUpdateBookmark(id.GetServerId(), parent_id.GetServerId(),
+ name, version, sync_ts);
+}
+
+SyncEntity* MockConnectionManager::GetMutableLastUpdate() {
+ sync_pb::GetUpdatesResponse* updates = GetUpdateResponse();
+ EXPECT_GT(updates->entries_size(), 0);
+ return updates->mutable_entries()->Mutable(updates->entries_size() - 1);
+}
+
+void MockConnectionManager::NextUpdateBatch() {
+ update_queue_.push_back(sync_pb::GetUpdatesResponse::default_instance());
+ SetChangesRemaining(0);
+ ApplyToken();
+}
+
+const CommitMessage& MockConnectionManager::last_sent_commit() const {
+ EXPECT_TRUE(!commit_messages_.empty());
+ return *commit_messages_->back();
+}
+
+const CommitResponse& MockConnectionManager::last_commit_response() const {
+ EXPECT_TRUE(!commit_responses_.empty());
+ return *commit_responses_->back();
+}
+
+void MockConnectionManager::ThrottleNextRequest(
+ ResponseCodeOverrideRequestor* visitor) {
+ base::AutoLock lock(response_code_override_lock_);
+ throttling_ = true;
+ if (visitor)
+ visitor->OnOverrideComplete();
+}
+
+void MockConnectionManager::FailWithAuthInvalid(
+ ResponseCodeOverrideRequestor* visitor) {
+ base::AutoLock lock(response_code_override_lock_);
+ fail_with_auth_invalid_ = true;
+ if (visitor)
+ visitor->OnOverrideComplete();
+}
+
+void MockConnectionManager::StopFailingWithAuthInvalid(
+ ResponseCodeOverrideRequestor* visitor) {
+ base::AutoLock lock(response_code_override_lock_);
+ fail_with_auth_invalid_ = false;
+ if (visitor)
+ visitor->OnOverrideComplete();
+}
+
+bool MockConnectionManager::IsModelTypePresentInSpecifics(
+ const google::protobuf::RepeatedPtrField<
+ sync_pb::DataTypeProgressMarker>& filter,
+ syncable::ModelType value) {
+ int data_type_id = syncable::GetExtensionFieldNumberFromModelType(value);
+ for (int i = 0; i < filter.size(); ++i) {
+ if (filter.Get(i).data_type_id() == data_type_id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+sync_pb::DataTypeProgressMarker const*
+ MockConnectionManager::GetProgressMarkerForType(
+ const google::protobuf::RepeatedPtrField<
+ sync_pb::DataTypeProgressMarker>& filter,
+ syncable::ModelType value) {
+ int data_type_id = syncable::GetExtensionFieldNumberFromModelType(value);
+ for (int i = 0; i < filter.size(); ++i) {
+ if (filter.Get(i).data_type_id() == data_type_id) {
+ return &(filter.Get(i));
+ }
+ }
+ return NULL;
+}
+
+void MockConnectionManager::SetServerReachable() {
+ server_status_ = HttpResponse::SERVER_CONNECTION_OK;
+ server_reachable_ = true;
+
+ FOR_EACH_OBSERVER(ServerConnectionEventListener, listeners_,
+ OnServerConnectionEvent(
+ ServerConnectionEvent(server_status_, server_reachable_)));
+}
+
+void MockConnectionManager::SetServerNotReachable() {
+ server_status_ = HttpResponse::CONNECTION_UNAVAILABLE;
+ server_reachable_ = false;
+
+ FOR_EACH_OBSERVER(ServerConnectionEventListener, listeners_,
+ OnServerConnectionEvent(
+ ServerConnectionEvent(server_status_, server_reachable_)));
+}
diff --git a/chrome/browser/sync/test/engine/mock_connection_manager.h b/chrome/browser/sync/test/engine/mock_connection_manager.h
new file mode 100644
index 0000000..fe98699
--- /dev/null
+++ b/chrome/browser/sync/test/engine/mock_connection_manager.h
@@ -0,0 +1,364 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Mock ServerConnectionManager class for use in client unit tests.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_MOCK_CONNECTION_MANAGER_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_MOCK_CONNECTION_MANAGER_H_
+#pragma once
+
+#include <bitset>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/sync/engine/net/server_connection_manager.h"
+#include "chrome/browser/sync/protocol/sync.pb.h"
+#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+
+namespace syncable {
+class DirectoryManager;
+class ScopedDirLookup;
+}
+namespace browser_sync {
+struct HttpResponse;
+}
+
+class MockConnectionManager : public browser_sync::ServerConnectionManager {
+ public:
+ class MidCommitObserver {
+ public:
+ virtual void Observe() = 0;
+
+ protected:
+ virtual ~MidCommitObserver() {}
+ };
+
+ MockConnectionManager(syncable::DirectoryManager* dirmgr,
+ const std::string& name);
+ virtual ~MockConnectionManager();
+
+ // Overridden ServerConnectionManager functions.
+ virtual bool PostBufferToPath(
+ PostBufferParams*,
+ const std::string& path,
+ const std::string& auth_token,
+ browser_sync::ScopedServerStatusWatcher* watcher) OVERRIDE;
+
+ virtual bool IsServerReachable() OVERRIDE;
+ virtual bool IsUserAuthenticated() OVERRIDE;
+
+ // Control of commit response.
+ void SetMidCommitCallback(Callback0::Type* callback);
+ void SetMidCommitObserver(MidCommitObserver* observer);
+
+ // Set this if you want commit to perform commit time rename. Will request
+ // that the client renames all commited entries, prepending this string.
+ void SetCommitTimeRename(std::string prepend);
+
+ // Generic versions of AddUpdate functions. Tests using these function should
+ // compile for both the int64 and string id based versions of the server.
+ // The SyncEntity returned is only valid until the Sync is completed
+ // (e.g. with SyncShare.) It allows to add further entity properties before
+ // sync, using SetLastXXX() methods and/or GetMutableLastUpdate().
+ sync_pb::SyncEntity* AddUpdateDirectory(syncable::Id id,
+ syncable::Id parent_id,
+ std::string name,
+ int64 version,
+ int64 sync_ts);
+ sync_pb::SyncEntity* AddUpdateBookmark(syncable::Id id,
+ syncable::Id parent_id,
+ std::string name,
+ int64 version,
+ int64 sync_ts);
+ // Versions of the AddUpdate functions that accept integer IDs.
+ sync_pb::SyncEntity* AddUpdateDirectory(int id,
+ int parent_id,
+ std::string name,
+ int64 version,
+ int64 sync_ts);
+ sync_pb::SyncEntity* AddUpdateBookmark(int id,
+ int parent_id,
+ std::string name,
+ int64 version,
+ int64 sync_ts);
+ // New protocol versions of the AddUpdate functions.
+ sync_pb::SyncEntity* AddUpdateDirectory(std::string id,
+ std::string parent_id,
+ std::string name,
+ int64 version,
+ int64 sync_ts);
+ sync_pb::SyncEntity* AddUpdateBookmark(std::string id,
+ std::string parent_id,
+ std::string name,
+ int64 version,
+ int64 sync_ts);
+
+ // Find the last commit sent by the client, and replay it for the next get
+ // updates command. This can be used to simulate the GetUpdates that happens
+ // immediately after a successful commit.
+ sync_pb::SyncEntity* AddUpdateFromLastCommit();
+
+ // Add a deleted item. Deletion records typically contain no
+ // additional information beyond the deletion, and no specifics.
+ // The server may send the originator fields.
+ void AddUpdateTombstone(const syncable::Id& id);
+
+ void SetLastUpdateDeleted();
+ void SetLastUpdateServerTag(const std::string& tag);
+ void SetLastUpdateClientTag(const std::string& tag);
+ void SetLastUpdateOriginatorFields(const std::string& client_id,
+ const std::string& entry_id);
+ void SetLastUpdatePosition(int64 position_in_parent);
+ void SetNewTimestamp(int ts);
+ void SetChangesRemaining(int64 count);
+
+ // Add a new batch of updates after the current one. Allows multiple
+ // GetUpdates responses to be buffered up, since the syncer may
+ // issue multiple requests during a sync cycle.
+ void NextUpdateBatch();
+
+ // For AUTHENTICATE responses.
+ void SetAuthenticationResponseInfo(const std::string& valid_auth_token,
+ const std::string& user_display_name,
+ const std::string& user_display_email,
+ const std::string& user_obfuscated_id);
+
+ void FailNextPostBufferToPathCall() { fail_next_postbuffer_ = true; }
+
+ void SetClearUserDataResponseStatus(
+ sync_pb::ClientToServerResponse::ErrorType errortype);
+
+ // A visitor class to allow a test to change some monitoring state atomically
+ // with the action of overriding the response codes sent back to the Syncer
+ // (for example, so you can say "ThrottleNextRequest, and assert no more
+ // requests are made once throttling is in effect" in one step.
+ class ResponseCodeOverrideRequestor {
+ public:
+ // Called with response_code_override_lock_ acquired.
+ virtual void OnOverrideComplete() = 0;
+
+ protected:
+ virtual ~ResponseCodeOverrideRequestor() {}
+ };
+ void ThrottleNextRequest(ResponseCodeOverrideRequestor* visitor);
+ void FailWithAuthInvalid(ResponseCodeOverrideRequestor* visitor);
+ void StopFailingWithAuthInvalid(ResponseCodeOverrideRequestor* visitor);
+ void FailNonPeriodicGetUpdates() { fail_non_periodic_get_updates_ = true; }
+
+ // Simple inspectors.
+ bool client_stuck() const { return client_stuck_; }
+
+ sync_pb::ClientCommand* GetNextClientCommand();
+
+ const std::vector<syncable::Id>& committed_ids() const {
+ return committed_ids_;
+ }
+ const std::vector<sync_pb::CommitMessage*>& commit_messages() const {
+ return commit_messages_.get();
+ }
+ const std::vector<sync_pb::CommitResponse*>& commit_responses() const {
+ return commit_responses_.get();
+ }
+ // Retrieve the last sent commit message.
+ const sync_pb::CommitMessage& last_sent_commit() const;
+
+ // Retrieve the last returned commit response.
+ const sync_pb::CommitResponse& last_commit_response() const;
+
+ // Retrieve the last request submitted to the server (regardless of type).
+ const sync_pb::ClientToServerMessage& last_request() const {
+ return last_request_;
+ }
+
+ void set_conflict_all_commits(bool value) {
+ conflict_all_commits_ = value;
+ }
+ void set_next_new_id(int value) {
+ next_new_id_ = value;
+ }
+ void set_conflict_n_commits(int value) {
+ conflict_n_commits_ = value;
+ }
+
+ void set_use_legacy_bookmarks_protocol(bool value) {
+ use_legacy_bookmarks_protocol_ = value;
+ }
+
+ void set_store_birthday(std::string new_birthday) {
+ // Multiple threads can set store_birthday_ in our tests, need to lock it to
+ // ensure atomic read/writes and avoid race conditions.
+ base::AutoLock lock(store_birthday_lock_);
+ store_birthday_ = new_birthday;
+ }
+
+ // Retrieve the number of GetUpdates requests that the mock server has
+ // seen since the last time this function was called. Can be used to
+ // verify that a GetUpdates actually did or did not happen after running
+ // the syncer.
+ int GetAndClearNumGetUpdatesRequests() {
+ int result = num_get_updates_requests_;
+ num_get_updates_requests_ = 0;
+ return result;
+ }
+
+ // Expect that GetUpdates will request exactly the types indicated in
+ // the bitset.
+ void ExpectGetUpdatesRequestTypes(
+ std::bitset<syncable::MODEL_TYPE_COUNT> expected_filter) {
+ expected_filter_ = expected_filter;
+ }
+
+ void ExpectGetUpdatesRequestPayloads(
+ const syncable::ModelTypePayloadMap& payloads) {
+ expected_payloads_ = payloads;
+ }
+
+ void SetServerReachable();
+
+ void SetServerNotReachable();
+
+ // Return by copy to be thread-safe.
+ const std::string store_birthday() {
+ base::AutoLock lock(store_birthday_lock_);
+ return store_birthday_;
+ }
+
+ // Locate the most recent update message for purpose of alteration.
+ sync_pb::SyncEntity* GetMutableLastUpdate();
+
+ private:
+ sync_pb::SyncEntity* AddUpdateFull(syncable::Id id, syncable::Id parentid,
+ std::string name, int64 version,
+ int64 sync_ts,
+ bool is_dir);
+ sync_pb::SyncEntity* AddUpdateFull(std::string id,
+ std::string parentid, std::string name,
+ int64 version, int64 sync_ts,
+ bool is_dir);
+ // Functions to handle the various types of server request.
+ void ProcessGetUpdates(sync_pb::ClientToServerMessage* csm,
+ sync_pb::ClientToServerResponse* response);
+ void ProcessAuthenticate(sync_pb::ClientToServerMessage* csm,
+ sync_pb::ClientToServerResponse* response,
+ const std::string& auth_token);
+ void ProcessCommit(sync_pb::ClientToServerMessage* csm,
+ sync_pb::ClientToServerResponse* response_buffer);
+ void ProcessClearData(sync_pb::ClientToServerMessage* csm,
+ sync_pb::ClientToServerResponse* response);
+ void AddDefaultBookmarkData(sync_pb::SyncEntity* entity, bool is_folder);
+
+ // Determine if one entry in a commit should be rejected with a conflict.
+ bool ShouldConflictThisCommit();
+
+ // Generate a numeric position_in_parent value. We use a global counter
+ // that only decreases; this simulates new objects always being added to the
+ // front of the ordering.
+ int64 GeneratePositionInParent() {
+ return next_position_in_parent_--;
+ }
+
+ // Get a mutable update response which will eventually be returned to the
+ // client.
+ sync_pb::GetUpdatesResponse* GetUpdateResponse();
+ void ApplyToken();
+
+ // Determine whether an progress marker array (like that sent in
+ // GetUpdates.from_progress_marker) indicates that a particular ModelType
+ // should be included.
+ bool IsModelTypePresentInSpecifics(
+ const google::protobuf::RepeatedPtrField<
+ sync_pb::DataTypeProgressMarker>& filter,
+ syncable::ModelType value);
+
+ sync_pb::DataTypeProgressMarker const* GetProgressMarkerForType(
+ const google::protobuf::RepeatedPtrField<
+ sync_pb::DataTypeProgressMarker>& filter,
+ syncable::ModelType value);
+
+ // All IDs that have been committed.
+ std::vector<syncable::Id> committed_ids_;
+
+ // Control of when/if we return conflicts.
+ bool conflict_all_commits_;
+ int conflict_n_commits_;
+
+ // Commit messages we've sent, and responses we've returned.
+ ScopedVector<sync_pb::CommitMessage> commit_messages_;
+ ScopedVector<sync_pb::CommitResponse> commit_responses_;
+
+ // The next id the mock will return to a commit.
+ int next_new_id_;
+
+ // The store birthday we send to the client.
+ std::string store_birthday_;
+ base::Lock store_birthday_lock_;
+ bool store_birthday_sent_;
+ bool client_stuck_;
+ std::string commit_time_rename_prepended_string_;
+
+ // Fail on the next call to PostBufferToPath().
+ bool fail_next_postbuffer_;
+
+ // Our directory.
+ syncable::DirectoryManager* directory_manager_;
+ std::string directory_name_;
+
+ // The updates we'll return to the next request.
+ std::list<sync_pb::GetUpdatesResponse> update_queue_;
+ scoped_ptr<Callback0::Type> mid_commit_callback_;
+ MidCommitObserver* mid_commit_observer_;
+
+ // The clear data response we'll return in the next response
+ sync_pb::ClientToServerResponse::ErrorType
+ clear_user_data_response_errortype_;
+
+ // The AUTHENTICATE response we'll return for auth requests.
+ sync_pb::AuthenticateResponse auth_response_;
+ // What we use to determine if we should return SUCCESS or BAD_AUTH_TOKEN.
+ std::string valid_auth_token_;
+
+ // Whether we are faking a server mandating clients to throttle requests.
+ // Protected by |response_code_override_lock_|.
+ bool throttling_;
+
+ // Whether we are failing all requests by returning
+ // ClientToServerResponse::AUTH_INVALID.
+ // Protected by |response_code_override_lock_|.
+ bool fail_with_auth_invalid_;
+
+ base::Lock response_code_override_lock_;
+
+ // True if we are only accepting GetUpdatesCallerInfo::PERIODIC requests.
+ bool fail_non_periodic_get_updates_;
+
+ scoped_ptr<sync_pb::ClientCommand> client_command_;
+
+ // The next value to use for the position_in_parent property.
+ int64 next_position_in_parent_;
+
+ // The default is to use the newer sync_pb::BookmarkSpecifics-style protocol.
+ // If this option is set to true, then the MockConnectionManager will
+ // use the older sync_pb::SyncEntity_BookmarkData-style protocol.
+ bool use_legacy_bookmarks_protocol_;
+
+ std::bitset<syncable::MODEL_TYPE_COUNT> expected_filter_;
+
+ syncable::ModelTypePayloadMap expected_payloads_;
+
+ int num_get_updates_requests_;
+
+ std::string next_token_;
+
+ sync_pb::ClientToServerMessage last_request_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockConnectionManager);
+};
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_MOCK_CONNECTION_MANAGER_H_
diff --git a/chrome/browser/sync/test/engine/mock_gaia_authenticator.cc b/chrome/browser/sync/test/engine/mock_gaia_authenticator.cc
new file mode 100644
index 0000000..acd817c
--- /dev/null
+++ b/chrome/browser/sync/test/engine/mock_gaia_authenticator.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Use MockGaiaAuthenticator to test your application by faking a login session.
+// This mock object should be initialized with the response you expect it to
+// return, and then can be used in exactly the same way as the real
+// GaiaAuthenticator.
+
+#include "chrome/browser/sync/test/engine/mock_gaia_authenticator.h"
+
+using std::string;
+
+namespace browser_sync {
+
+MockGaiaAuthenticator::MockGaiaAuthenticator(const char* user_agent,
+ const char* service_id,
+ const char* gaia_url) :
+ should_save_credentials_(false), current_user_("") {
+ // This discards user_agent, service_id, gaia_url since the mock object
+ // doesn't care about them.
+}
+
+MockGaiaAuthenticator::~MockGaiaAuthenticator() {}
+
+// Add a mock user to internal list of users.
+void MockGaiaAuthenticator::AddMockUser(MockUser mock_user) {
+ mock_credentials_[mock_user.email] = mock_user;
+}
+
+// A convenience method to add a mock user to internal list of users.
+void MockGaiaAuthenticator::AddMockUser(string email, string passwd,
+ string auth_token, string lsid,
+ string sid,
+ enum gaia::AuthenticationError
+ auth_error, string error_url,
+ string captcha_token,
+ string captcha_url) {
+ MockUser mock_user;
+ mock_user.email = email;
+ mock_user.passwd = passwd;
+ mock_user.auth_token = auth_token;
+ mock_user.lsid = lsid;
+ mock_user.sid = sid;
+ mock_user.auth_error = auth_error;
+ mock_user.error_url = error_url;
+ mock_user.captcha_token = captcha_token;
+ mock_user.captcha_url = captcha_url;
+ AddMockUser(mock_user);
+}
+
+// A convenience method to add a mock user to internal list of users.
+void MockGaiaAuthenticator::AddMockUser(string email, string passwd,
+ string auth_token, string lsid,
+ string sid,
+ enum gaia::AuthenticationError
+ auth_error) {
+ MockUser mock_user;
+ mock_user.email = email;
+ mock_user.passwd = passwd;
+ mock_user.auth_token = auth_token;
+ mock_user.lsid = lsid;
+ mock_user.sid = sid;
+ mock_user.auth_error = auth_error;
+ AddMockUser(mock_user);
+}
+
+void MockGaiaAuthenticator::RemoveMockUser(const char* email) {
+ mock_credentials_.erase(email);
+}
+
+void MockGaiaAuthenticator::RemoveAllMockUsers() {
+ mock_credentials_.clear();
+}
+
+bool MockGaiaAuthenticator::Authenticate() {
+ if (!should_save_credentials_) {
+ ResetCredentials();
+ return false;
+ }
+ return Authenticate(mock_credentials_[current_user_].email.c_str(),
+ mock_credentials_[current_user_].passwd.c_str(), true);
+}
+
+bool MockGaiaAuthenticator::Authenticate(const char* email,
+ const char* passwd,
+ bool should_save_credentials) {
+ // Simply assign value to field; the value is read by the accessors when
+ // reading.
+ should_save_credentials_ = should_save_credentials;
+
+ // Check if we already know about this mock user.
+ if (mock_credentials_.find(email) == mock_credentials_.end()) {
+ current_user_ = "";
+ return false;
+ }
+
+ // If found, keep the current logged-in user available for token requests.
+ current_user_ = email;
+
+ // Finding a user does not necessarily imply that the user was logged in OK.
+ // Therefore also check if the AuthenticationError is None.
+ return (mock_credentials_[current_user_].auth_error == gaia::None);
+}
+
+// Remove any stored knowledge about the currently logged-in user, but keep
+// details of mock users.
+void MockGaiaAuthenticator::ResetCredentials() {
+ current_user_ = "";
+}
+
+std::string MockGaiaAuthenticator::email() {
+ return (current_user_.empty() || !should_save_credentials_) ? "" :
+ mock_credentials_[current_user_].email;
+}
+
+std::string MockGaiaAuthenticator::auth_token() {
+ return (current_user_.empty()) ? "" :
+ mock_credentials_[current_user_].auth_token;
+}
+
+std::string MockGaiaAuthenticator::sid() {
+ return (current_user_.empty()) ? "" :
+ mock_credentials_[current_user_].sid;
+}
+
+std::string MockGaiaAuthenticator::lsid() {
+ return (current_user_.empty()) ? "" :
+ mock_credentials_[current_user_].lsid;
+}
+
+gaia::AuthenticationError MockGaiaAuthenticator::auth_error() {
+ return (current_user_.empty()) ? gaia::CredentialsNotSet :
+ mock_credentials_[current_user_].auth_error;
+}
+
+std::string MockGaiaAuthenticator::auth_error_url() {
+ return (current_user_.empty()) ? "" :
+ mock_credentials_[current_user_].error_url;
+}
+
+std::string MockGaiaAuthenticator::captcha_token() {
+ return (current_user_.empty()) ? "" :
+ mock_credentials_[current_user_].captcha_token;
+}
+
+std::string MockGaiaAuthenticator::captcha_url() {
+ return (current_user_.empty()) ? "" :
+ mock_credentials_[current_user_].captcha_url;
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/test/engine/mock_gaia_authenticator.h b/chrome/browser/sync/test/engine/mock_gaia_authenticator.h
new file mode 100644
index 0000000..e9f3251
--- /dev/null
+++ b/chrome/browser/sync/test/engine/mock_gaia_authenticator.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Use MockGaiaAuthenticator to test your application by faking a login session.
+// This mock object should be initialized with the response you expect it to
+// return for multiple users, and then can be used in exactly the same way
+// as the real GaiaAuthenticator.
+//
+// Sample usage:
+// MockGaiaAuthenticator mock_gaia_auth("User-Agent", SYNC_SERVICE_NAME,
+// "any random string");
+// mock_gaia_auth.AddMockUser("email", "password", "authtoken", "lsid", "sid",
+// AuthenticationError);
+// mock_gaia_auth.AddMockUser("email2", "password2", "authtoken2", "lsid2",
+// "sid2", AuthenticationError, error_url,
+// "captcha_token", "captcha_url");
+// if (gaia_auth.Authenticate("email", "passwd")) {
+// // Do something with: gaia_auth.auth_token(), or gaia_auth.sid(),
+// // or gaia_auth.lsid()
+// }
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_MOCK_GAIA_AUTHENTICATOR_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_MOCK_GAIA_AUTHENTICATOR_H_
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "base/port.h"
+
+#include "base/basictypes.h"
+#include "chrome/common/net/gaia/gaia_authenticator.h"
+
+namespace browser_sync {
+
+// A struct used internally for storing a user's credentials. You can either
+// create one yourself, or use the convenience methods to have the
+// MockGaiaAuthenticator create one for you.
+typedef struct {
+ std::string email;
+ std::string passwd;
+ std::string auth_token;
+ std::string sid;
+ std::string lsid;
+ gaia::AuthenticationError auth_error;
+ std::string captcha_token;
+ std::string captcha_url;
+ std::string error_url;
+} MockUser;
+
+// MockGaiaAuthenticator can be used to fake Gaia authentication without
+// actually making a network connection. For details about the methods shared
+// with GaiaAuthenticator, see GaiaAuthenticator in gaia_auth.h. Only methods
+// that are unique to MockGaiaAuthenticator are documented in this file.
+class MockGaiaAuthenticator {
+ public:
+ MockGaiaAuthenticator(const char* user_agent, const char* service_id,
+ const char* gaia_url);
+ ~MockGaiaAuthenticator();
+
+ // Add a mock user; takes a struct. You can populate any or all fields when
+ // adding a user. The email field is required, all others optional.
+ void AddMockUser(MockUser mock_user);
+
+ // A convenience method that makes it easy to create new mock users in a
+ // single method call. Includes all parameters.
+ void AddMockUser(std::string email, std::string passwd,
+ std::string auth_token,
+ std::string lsid, std::string sid,
+ gaia::AuthenticationError auth_error,
+ std::string error_url, std::string captcha_token,
+ std::string captcha_url);
+
+ // A convenience method that makes it easy to create new mock users in a
+ // single method call. Includes only the most common parameters. See overload
+ // if you want to pass all parameters.
+ void AddMockUser(std::string email, std::string passwd,
+ std::string auth_token,
+ std::string lsid, std::string sid,
+ enum gaia::AuthenticationError auth_error);
+
+ // Removes a mock user from the current list of added users.
+ void RemoveMockUser(const char* email);
+
+ // Removes all mock users from the current list of added users.
+ void RemoveAllMockUsers();
+
+ // See GaiaAuthenticator::Authenticate()
+ bool Authenticate();
+
+ // See GaiaAuthenticator::Authenticate(...)
+ bool Authenticate(const char* user_name, const char* password,
+ bool should_save_credentials = false);
+
+ // See GaiaAuthenticator::Authenticate(...)
+ void ResetCredentials();
+
+ // Accessors follow.
+ std::string email();
+ std::string auth_token();
+ std::string sid();
+ std::string lsid();
+ gaia::AuthenticationError auth_error();
+ std::string auth_error_url();
+ std::string captcha_token();
+ std::string captcha_url();
+
+ private:
+ bool should_save_credentials_;
+ std::map<std::string, MockUser> mock_credentials_;
+ std::string current_user_;
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_MOCK_GAIA_AUTHENTICATOR_H_
diff --git a/chrome/browser/sync/test/engine/mock_gaia_authenticator_unittest.cc b/chrome/browser/sync/test/engine/mock_gaia_authenticator_unittest.cc
new file mode 100644
index 0000000..a3571ba
--- /dev/null
+++ b/chrome/browser/sync/test/engine/mock_gaia_authenticator_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit tests for MockGaiaAuthenticator.
+
+#include "base/basictypes.h"
+#include "base/port.h"
+#include "chrome/browser/sync/protocol/service_constants.h"
+#include "chrome/browser/sync/test/engine/mock_gaia_authenticator.h"
+#include "chrome/common/net/gaia/gaia_authenticator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Test if authentication succeeds for a mock user added earlier.
+TEST(MockGaiaAuthenticatorTest, TestAuthenticationSuccess) {
+ browser_sync::MockGaiaAuthenticator
+ mock_gaia_auth("User-Agent", SYNC_SERVICE_NAME,
+ "some random url");
+
+ // Initialize a mock user, and add to mock authenticator.
+ browser_sync::MockUser mock_user;
+ mock_user.email = "test";
+ mock_user.passwd = "passwd";
+ mock_user.auth_token = "SomeAuthToken";
+ mock_user.lsid = "SomeLSID";
+ mock_user.sid = "SomeSID";
+ mock_user.auth_error = gaia::None;
+ mock_gaia_auth.AddMockUser(mock_user);
+
+ // Assert away ...
+ ASSERT_TRUE(mock_gaia_auth.Authenticate("test", "passwd"));
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "SomeAuthToken");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "SomeSID");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "SomeLSID");
+}
+
+// Test if authentication fails for a mock user that was never added.
+TEST(MockGaiaAuthenticatorTest, TestAuthenticationFailure) {
+ browser_sync::MockGaiaAuthenticator
+ mock_gaia_auth("User-Agent", SYNC_SERVICE_NAME,
+ "some random url");
+
+ // At this point, in real code, we would be adding mock users to our mock
+ // object. However, in this unittest, we exercise the path where this step is
+ // missing, and assert that the outcome is still consistent with that of the
+ // real GaiaAuthenticator.
+
+ // Assert away ...
+ ASSERT_FALSE(mock_gaia_auth.Authenticate("test", "passwd"));
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "");
+}
+
+// Test if authentication fails after a mock user is removed.
+TEST(MockGaiaAuthenticatorTest, TestRemoveMockUser) {
+ // Instantiate authenticator.
+ browser_sync::MockGaiaAuthenticator
+ mock_gaia_auth("User-Agent", SYNC_SERVICE_NAME,
+ "some random url");
+
+ // Add our mock user
+ mock_gaia_auth.AddMockUser("test", "passwd", "SomeAuthToken", "SomeLSID",
+ "SomeSID", gaia::None);
+
+ // Make sure authentication succeeds.
+ ASSERT_TRUE(mock_gaia_auth.Authenticate("test", "passwd"));
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "SomeAuthToken");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "SomeSID");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "SomeLSID");
+
+ // Remove the just-added user from our list.
+ mock_gaia_auth.RemoveMockUser("test");
+
+ // ... and authentication should fail.
+ ASSERT_FALSE(mock_gaia_auth.Authenticate("test", "passwd"));
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "");
+}
+
+// Test if authentication fails after all mock users are removed.
+TEST(MockGaiaAuthenticatorTest, TestRemoveAllMockUsers) {
+ // Instantiate authenticator.
+ browser_sync::MockGaiaAuthenticator
+ mock_gaia_auth("User-Agent", SYNC_SERVICE_NAME,
+ "some random url");
+
+ // Add our sample mock user.
+ mock_gaia_auth.AddMockUser("test", "passwd", "SomeAuthToken", "SomeLSID",
+ "SomeSID", gaia::None);
+
+ // Make sure authentication succeeds
+ ASSERT_TRUE(mock_gaia_auth.Authenticate("test", "passwd"));
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "SomeAuthToken");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "SomeSID");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "SomeLSID");
+
+ // Now remove all mock users.
+ mock_gaia_auth.RemoveAllMockUsers();
+
+ // And confirm that authentication fails.
+ ASSERT_FALSE(mock_gaia_auth.Authenticate("test", "passwd"));
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "");
+}
+
+// Test authentication with saved credentials.
+TEST(MockGaiaAuthenticatorTest, TestSavedCredentials) {
+ // Instantiate authenticator.
+ browser_sync::MockGaiaAuthenticator
+ mock_gaia_auth("User-Agent", SYNC_SERVICE_NAME,
+ "some random url");
+
+ // Add our sample mock user.
+ mock_gaia_auth.AddMockUser("test", "passwd", "SomeAuthToken", "SomeLSID",
+ "SomeSID", gaia::None);
+
+ // Ask to save credentials.
+ ASSERT_TRUE(mock_gaia_auth.Authenticate("test", "passwd", true));
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "SomeAuthToken");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "SomeSID");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "SomeLSID");
+
+ // Now make a call that uses saved credentials, and assert that we get the
+ // same tokens back.
+ ASSERT_TRUE(mock_gaia_auth.Authenticate());
+ ASSERT_STREQ(mock_gaia_auth.email().c_str(), "test");
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "SomeAuthToken");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "SomeSID");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "SomeLSID");
+
+ // Now clear the saved credentials by toggling the flag while authenticating.
+ ASSERT_TRUE(mock_gaia_auth.Authenticate("test", "passwd", false));
+
+ // Test if saved credentials have been cleared.
+ ASSERT_STREQ(mock_gaia_auth.email().c_str(), "");
+
+ // Assert that current authentication session still succeeds (we only asked
+ // not to save it for future requests.)
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "SomeAuthToken");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "SomeSID");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "SomeLSID");
+
+ // Now try to use saved credentials:
+ ASSERT_STREQ(mock_gaia_auth.email().c_str(), "");
+ ASSERT_FALSE(mock_gaia_auth.Authenticate());
+
+ // And assert that any future requests that rely on saved credentials fail.
+ ASSERT_STREQ(mock_gaia_auth.auth_token().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.sid().c_str(), "");
+ ASSERT_STREQ(mock_gaia_auth.lsid().c_str(), "");
+}
+
+} // namespace
diff --git a/chrome/browser/sync/test/engine/proto_extension_validator.h b/chrome/browser/sync/test/engine/proto_extension_validator.h
new file mode 100644
index 0000000..747bf3d
--- /dev/null
+++ b/chrome/browser/sync/test/engine/proto_extension_validator.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A test utility that makes it easy to assert the exact presence of
+// protobuf extensions in an extensible message. Example code:
+//
+// sync_pb::EntitySpecifics value; // Set up a test value.
+// value.MutableExtension(sync_pb::bookmark);
+//
+// ProtoExtensionValidator<sync_pb::EntitySpecifics> good_expectation(value);
+// good_expectation.ExpectHasExtension(sync_pb::bookmark);
+// good_expectation.ExpectHasNoOtherFieldsOrExtensions();
+//
+// ProtoExtensionValidator<sync_pb::EntitySpecifics> bad_expectation(value);
+// bad_expectation.ExpectHasExtension(sync_pb::preference); // Fails, no pref
+// bad_expectation.ExpectHasNoOtherFieldsOrExtensions(); // Fails, bookmark
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_PROTO_EXTENSION_VALIDATOR_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_PROTO_EXTENSION_VALIDATOR_H_
+#pragma once
+
+#include "base/string_util.h"
+#include "chrome/browser/sync/test/engine/syncer_command_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+
+template<typename MessageType>
+class ProtoExtensionValidator {
+ public:
+ explicit ProtoExtensionValidator(const MessageType& proto)
+ : proto_(proto) {
+ }
+ template<typename ExtensionTokenType>
+ void ExpectHasExtension(ExtensionTokenType token) {
+ EXPECT_TRUE(proto_.HasExtension(token))
+ << "Proto failed to contain expected extension";
+ proto_.ClearExtension(token);
+ EXPECT_FALSE(proto_.HasExtension(token));
+ }
+ void ExpectNoOtherFieldsOrExtensions() {
+ EXPECT_EQ(MessageType::default_instance().SerializeAsString(),
+ proto_.SerializeAsString())
+ << "Proto contained additional unexpected extensions.";
+ }
+
+ private:
+ MessageType proto_;
+ DISALLOW_COPY_AND_ASSIGN(ProtoExtensionValidator);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_PROTO_EXTENSION_VALIDATOR_H_
diff --git a/chrome/browser/sync/test/engine/syncer_command_test.h b/chrome/browser/sync/test/engine/syncer_command_test.h
new file mode 100644
index 0000000..e153be23
--- /dev/null
+++ b/chrome/browser/sync/test/engine/syncer_command_test.h
@@ -0,0 +1,170 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_SYNCER_COMMAND_TEST_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_SYNCER_COMMAND_TEST_H_
+#pragma once
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/engine/model_safe_worker.h"
+#include "chrome/browser/sync/sessions/sync_session.h"
+#include "chrome/browser/sync/sessions/sync_session_context.h"
+#include "chrome/browser/sync/test/engine/mock_connection_manager.h"
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+
+// A test fixture that simplifies writing unit tests for individual
+// SyncerCommands, providing convenient access to a test directory
+// and a syncer session.
+template<typename T>
+class SyncerCommandTestWithParam : public testing::TestWithParam<T>,
+ public sessions::SyncSession::Delegate,
+ public ModelSafeWorkerRegistrar {
+ public:
+ enum UseMockDirectory {
+ USE_MOCK_DIRECTORY
+ };
+
+ // SyncSession::Delegate implementation.
+ virtual void OnSilencedUntil(
+ const base::TimeTicks& silenced_until) OVERRIDE {
+ FAIL() << "Should not get silenced.";
+ }
+ virtual bool IsSyncingCurrentlySilenced() OVERRIDE {
+ ADD_FAILURE() << "No requests for silenced state should be made.";
+ return false;
+ }
+ virtual void OnReceivedLongPollIntervalUpdate(
+ const base::TimeDelta& new_interval) OVERRIDE {
+ FAIL() << "Should not get poll interval update.";
+ }
+ virtual void OnReceivedShortPollIntervalUpdate(
+ const base::TimeDelta& new_interval) OVERRIDE {
+ FAIL() << "Should not get poll interval update.";
+ }
+ virtual void OnReceivedSessionsCommitDelay(
+ const base::TimeDelta& new_delay) OVERRIDE {
+ FAIL() << "Should not get sessions commit delay.";
+ }
+ virtual void OnShouldStopSyncingPermanently() OVERRIDE {
+ FAIL() << "Shouldn't be called.";
+ }
+ virtual void OnSyncProtocolError(
+ const sessions::SyncSessionSnapshot& session) OVERRIDE {
+ FAIL() << "Shouldn't be called.";
+ }
+
+ // ModelSafeWorkerRegistrar implementation.
+ virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) OVERRIDE {
+ std::vector<scoped_refptr<ModelSafeWorker> >::iterator it;
+ for (it = workers_.begin(); it != workers_.end(); ++it)
+ out->push_back(*it);
+ }
+ virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) OVERRIDE {
+ ModelSafeRoutingInfo copy(routing_info_);
+ out->swap(copy);
+ }
+
+ protected:
+ SyncerCommandTestWithParam() : syncdb_(new TestDirectorySetterUpper()) {}
+
+ explicit SyncerCommandTestWithParam(UseMockDirectory use_mock)
+ : syncdb_(new MockDirectorySetterUpper()) {}
+
+ virtual ~SyncerCommandTestWithParam() {}
+ virtual void SetUp() {
+ syncdb_->SetUp();
+ ResetContext();
+ }
+ virtual void TearDown() {
+ syncdb_->TearDown();
+ }
+
+ TestDirectorySetterUpper* syncdb() { return syncdb_.get(); }
+ sessions::SyncSessionContext* context() const { return context_.get(); }
+ sessions::SyncSession::Delegate* delegate() { return this; }
+ ModelSafeWorkerRegistrar* registrar() { return this; }
+
+ // Lazily create a session requesting all datatypes with no payload.
+ sessions::SyncSession* session() {
+ syncable::ModelTypePayloadMap types =
+ syncable::ModelTypePayloadMapFromRoutingInfo(routing_info_,
+ std::string());
+ return session(sessions::SyncSourceInfo(types));
+ }
+
+ // Create a session with the provided source.
+ sessions::SyncSession* session(const sessions::SyncSourceInfo& source) {
+ if (!session_.get()) {
+ std::vector<ModelSafeWorker*> workers;
+ GetWorkers(&workers);
+ session_.reset(new sessions::SyncSession(context(), delegate(), source,
+ routing_info_, workers));
+ }
+ return session_.get();
+ }
+
+ void ClearSession() {
+ session_.reset();
+ }
+
+ void ResetContext() {
+ context_.reset(new sessions::SyncSessionContext(
+ mock_server_.get(), syncdb_->manager(), registrar(),
+ std::vector<SyncEngineEventListener*>()));
+ context_->set_account_name(syncdb_->name());
+ ClearSession();
+ }
+
+ // Install a MockServerConnection. Resets the context. By default,
+ // the context does not have a MockServerConnection attached.
+ void ConfigureMockServerConnection() {
+ mock_server_.reset(
+ new MockConnectionManager(syncdb_->manager(), syncdb_->name()));
+ ResetContext();
+ }
+
+ std::vector<scoped_refptr<ModelSafeWorker> >* workers() {
+ return &workers_;
+ }
+
+ const ModelSafeRoutingInfo& routing_info() { return routing_info_; }
+ ModelSafeRoutingInfo* mutable_routing_info() { return &routing_info_; }
+
+ MockConnectionManager* mock_server() {
+ return mock_server_.get();
+ }
+
+ private:
+ scoped_ptr<TestDirectorySetterUpper> syncdb_;
+ scoped_ptr<sessions::SyncSessionContext> context_;
+ scoped_ptr<MockConnectionManager> mock_server_;
+ scoped_ptr<sessions::SyncSession> session_;
+ std::vector<scoped_refptr<ModelSafeWorker> > workers_;
+ ModelSafeRoutingInfo routing_info_;
+ DISALLOW_COPY_AND_ASSIGN(SyncerCommandTestWithParam);
+};
+
+class SyncerCommandTest : public SyncerCommandTestWithParam<void*> {};
+
+class MockDirectorySyncerCommandTest
+ : public SyncerCommandTestWithParam<void*> {
+ public:
+ MockDirectorySyncerCommandTest()
+ : SyncerCommandTestWithParam<void*>(
+ SyncerCommandTestWithParam<void*>::USE_MOCK_DIRECTORY) {}
+ MockDirectorySetterUpper::MockDirectory* mock_directory() {
+ return static_cast<MockDirectorySetterUpper*>(syncdb())->directory();
+ }
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_SYNCER_COMMAND_TEST_H_
diff --git a/chrome/browser/sync/test/engine/test_directory_setter_upper.cc b/chrome/browser/sync/test/engine/test_directory_setter_upper.cc
new file mode 100644
index 0000000..44eeb19
--- /dev/null
+++ b/chrome/browser/sync/test/engine/test_directory_setter_upper.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
+
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "base/tracked.h"
+#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/syncable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using syncable::DirectoryManager;
+using syncable::ReadTransaction;
+using syncable::ScopedDirLookup;
+
+namespace browser_sync {
+
+TestDirectorySetterUpper::TestDirectorySetterUpper() : name_("Test") {}
+TestDirectorySetterUpper::TestDirectorySetterUpper(const std::string& name)
+ : name_(name) {}
+
+TestDirectorySetterUpper::~TestDirectorySetterUpper() {}
+
+void TestDirectorySetterUpper::Init() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ reset_directory_manager(new DirectoryManager(temp_dir_.path()));
+ // There shouldn't be any existing database in the newly-created
+ // temp dir.
+ ASSERT_FALSE(file_util::PathExists(manager_->GetSyncDataDatabasePath()));
+}
+
+void TestDirectorySetterUpper::reset_directory_manager(DirectoryManager* d) {
+ manager_.reset(d);
+}
+
+void TestDirectorySetterUpper::SetUp() {
+ Init();
+ ASSERT_TRUE(manager()->Open(name(), &delegate_));
+}
+
+void TestDirectorySetterUpper::TearDown() {
+ if (!manager())
+ return;
+
+ {
+ // A small scope so we don't have the dir open when we close it and reset
+ // the DirectoryManager below.
+ ScopedDirLookup dir(manager(), name());
+ CHECK(dir.good()) << "Bad directory during tear down check";
+ RunInvariantCheck(dir);
+ dir->SaveChanges();
+ RunInvariantCheck(dir);
+ dir->SaveChanges();
+ }
+
+ manager()->FinalSaveChangesForAll();
+ manager()->Close(name());
+ manager_.reset();
+ ASSERT_TRUE(temp_dir_.Delete());
+}
+
+void TestDirectorySetterUpper::RunInvariantCheck(const ScopedDirLookup& dir) {
+ {
+ // Check invariants for in-memory items.
+ ReadTransaction trans(FROM_HERE, dir);
+ dir->CheckTreeInvariants(&trans, false);
+ }
+ {
+ // Check invariants for all items.
+ ReadTransaction trans(FROM_HERE, dir);
+ dir->CheckTreeInvariants(&trans, true);
+ }
+}
+
+void ManuallyOpenedTestDirectorySetterUpper::SetUp() {
+ Init();
+}
+
+void ManuallyOpenedTestDirectorySetterUpper::Open() {
+ ASSERT_TRUE(manager()->Open(name(), &delegate_));
+ was_opened_ = true;
+}
+
+void ManuallyOpenedTestDirectorySetterUpper::TearDown() {
+ if (was_opened_) {
+ TestDirectorySetterUpper::TearDown();
+ }
+}
+
+TriggeredOpenTestDirectorySetterUpper::TriggeredOpenTestDirectorySetterUpper(
+ const std::string& name)
+ : TestDirectorySetterUpper(name) {
+}
+
+void TriggeredOpenTestDirectorySetterUpper::SetUp() {
+ Init();
+}
+
+void TriggeredOpenTestDirectorySetterUpper::TearDown() {
+ DirectoryManager::DirNames names;
+ manager()->GetOpenDirectories(&names);
+ if (!names.empty()) {
+ ASSERT_EQ(1U, names.size());
+ ASSERT_EQ(name(), names[0]);
+ TestDirectorySetterUpper::TearDown();
+ }
+}
+
+MockDirectorySetterUpper::MockDirectory::MockDirectory(
+ const std::string& name) {
+ InitKernel(name, &delegate_);
+}
+
+MockDirectorySetterUpper::MockDirectory::~MockDirectory() {}
+
+MockDirectorySetterUpper::Manager::Manager(
+ const FilePath& root_path, syncable::Directory* dir) :
+ syncable::DirectoryManager(root_path) {
+ managed_directory_ = dir;
+}
+
+MockDirectorySetterUpper::MockDirectorySetterUpper()
+ : directory_(new MockDirectory(name())) {
+}
+
+MockDirectorySetterUpper::~MockDirectorySetterUpper() {}
+
+void MockDirectorySetterUpper::SetUp() {
+ reset_directory_manager(new Manager(FilePath(), directory_.get()));
+}
+
+void MockDirectorySetterUpper::TearDown() {
+ // Nothing to do here.
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/test/engine/test_directory_setter_upper.h b/chrome/browser/sync/test/engine/test_directory_setter_upper.h
new file mode 100644
index 0000000..32af713
--- /dev/null
+++ b/chrome/browser/sync/test/engine/test_directory_setter_upper.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A handy class that takes care of setting up and destroying a
+// syncable::Directory instance for unit tests that require one.
+//
+// The expected usage is to make this a component of your test fixture:
+//
+// class AwesomenessTest : public testing::Test {
+// public:
+// virtual void SetUp() {
+// metadb_.SetUp();
+// }
+// virtual void TearDown() {
+// metadb_.TearDown();
+// }
+// protected:
+// TestDirectorySetterUpper metadb_;
+// };
+//
+// Then, in your tests, get at the directory like so:
+//
+// TEST_F(AwesomenessTest, IsMaximal) {
+// ScopedDirLookup dir(metadb_.manager(), metadb_.name());
+// ... now use |dir| to get at syncable::Entry objects ...
+// }
+//
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_DIRECTORY_SETTER_UPPER_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_DIRECTORY_SETTER_UPPER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/scoped_temp_dir.h"
+#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/syncable.h"
+#include "chrome/browser/sync/test/null_directory_change_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace syncable {
+class DirectoryManager;
+class ScopedDirLookup;
+} // namespace syncable
+
+namespace browser_sync {
+
+class TestDirectorySetterUpper {
+ public:
+ TestDirectorySetterUpper();
+ virtual ~TestDirectorySetterUpper();
+
+ // Create a DirectoryManager instance and use it to open the directory.
+ // Clears any existing database backing files that might exist on disk.
+ virtual void SetUp();
+
+ // Undo everything done by SetUp(): close the directory and delete the
+ // backing files. Before closing the directory, this will run the directory
+ // invariant checks and perform the SaveChanges action on the directory.
+ virtual void TearDown();
+
+ syncable::DirectoryManager* manager() const { return manager_.get(); }
+ const std::string& name() const { return name_; }
+
+ protected:
+ // Subclasses may want to use a different directory name.
+ explicit TestDirectorySetterUpper(const std::string& name);
+ virtual void Init();
+ void reset_directory_manager(syncable::DirectoryManager* d);
+
+ syncable::NullDirectoryChangeDelegate delegate_;
+
+ private:
+ void RunInvariantCheck(const syncable::ScopedDirLookup& dir);
+
+ scoped_ptr<syncable::DirectoryManager> manager_;
+ const std::string name_;
+ ScopedTempDir temp_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDirectorySetterUpper);
+};
+
+// A variant of the above where SetUp does not actually open the directory.
+// You must manually invoke Open(). This is useful if you are writing a test
+// that depends on the DirectoryManager::OPENED event.
+class ManuallyOpenedTestDirectorySetterUpper : public TestDirectorySetterUpper {
+ public:
+ ManuallyOpenedTestDirectorySetterUpper() : was_opened_(false) {}
+ virtual void SetUp();
+ virtual void TearDown();
+ void Open();
+ private:
+ bool was_opened_;
+};
+
+// Use this flavor if you have a test that will trigger the opening event to
+// happen automagically. It is careful on teardown to close only if needed.
+class TriggeredOpenTestDirectorySetterUpper : public TestDirectorySetterUpper {
+ public:
+ // A triggered open is typically in response to a successful auth event just
+ // as in "real life". In this case, the name that will be used should be
+ // deterministically known at construction, and is passed in |name|.
+ explicit TriggeredOpenTestDirectorySetterUpper(const std::string& name);
+ virtual void SetUp();
+ virtual void TearDown();
+};
+
+// Use this when you don't want to test the whole stack down to the Directory
+// level, as it installs a google mock Directory implementation.
+class MockDirectorySetterUpper : public TestDirectorySetterUpper {
+ public:
+ class Manager : public syncable::DirectoryManager {
+ public:
+ Manager(const FilePath& root_path, syncable::Directory* dir);
+ virtual ~Manager() { managed_directory_ = NULL; }
+ };
+
+ class MockDirectory : public syncable::Directory {
+ public:
+ explicit MockDirectory(const std::string& name);
+ virtual ~MockDirectory();
+ MOCK_METHOD1(PurgeEntriesWithTypeIn, void(const syncable::ModelTypeSet&));
+
+ private:
+ syncable::NullDirectoryChangeDelegate delegate_;
+ };
+
+ MockDirectorySetterUpper();
+ virtual ~MockDirectorySetterUpper();
+
+ virtual void SetUp();
+ virtual void TearDown();
+ MockDirectory* directory() { return directory_.get(); }
+
+ private:
+ scoped_ptr<MockDirectory> directory_;
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_DIRECTORY_SETTER_UPPER_H_
diff --git a/chrome/browser/sync/test/engine/test_id_factory.h b/chrome/browser/sync/test/engine/test_id_factory.h
new file mode 100644
index 0000000..ca98353
--- /dev/null
+++ b/chrome/browser/sync/test/engine/test_id_factory.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A tool making it easier to create IDs for unit testing.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_ID_FACTORY_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_ID_FACTORY_H_
+#pragma once
+
+#include <string>
+
+#include "base/string_number_conversions.h"
+#include "chrome/browser/sync/syncable/syncable_id.h"
+
+namespace browser_sync {
+
+class TestIdFactory {
+ public:
+ TestIdFactory() : next_value_(1337000) {}
+ ~TestIdFactory() {}
+
+ // Get the root ID.
+ static syncable::Id root() {
+ return syncable::Id();
+ }
+
+ // Make an ID from a number. If the number is zero, return the root ID.
+ // If the number is positive, create a server ID based on the value. If
+ // the number is negative, create a local ID based on the value. This
+ // is deterministic, and [FromNumber(X) == FromNumber(Y)] iff [X == Y].
+ static syncable::Id FromNumber(int64 value) {
+ if (value == 0)
+ return root();
+ else if (value < 0)
+ return syncable::Id::CreateFromClientString(base::Int64ToString(value));
+ else
+ return syncable::Id::CreateFromServerId(base::Int64ToString(value));
+ }
+
+ // Create a local ID from a name.
+ static syncable::Id MakeLocal(std::string name) {
+ return syncable::Id::CreateFromClientString(std::string("lient ") + name);
+ }
+
+ // Create a server ID from a string name.
+ static syncable::Id MakeServer(std::string name) {
+ return syncable::Id::CreateFromServerId(std::string("erver ") + name);
+ }
+
+ // Autogenerate a fresh local ID.
+ syncable::Id NewLocalId() {
+ return syncable::Id::CreateFromClientString(
+ std::string("_auto ") + base::IntToString(-next_value()));
+ }
+
+ // Autogenerate a fresh server ID.
+ syncable::Id NewServerId() {
+ return syncable::Id::CreateFromServerId(
+ std::string("_auto ") + base::IntToString(next_value()));
+ }
+
+ private:
+ int next_value() {
+ return next_value_++;
+ }
+ int next_value_;
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_ID_FACTORY_H_
+
diff --git a/chrome/browser/sync/test/engine/test_syncable_utils.cc b/chrome/browser/sync/test/engine/test_syncable_utils.cc
new file mode 100644
index 0000000..b76f6f7
--- /dev/null
+++ b/chrome/browser/sync/test/engine/test_syncable_utils.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Utilities to verify the state of items in unit tests.
+
+#include "chrome/browser/sync/test/engine/test_syncable_utils.h"
+
+#include "chrome/browser/sync/syncable/syncable.h"
+
+using std::string;
+
+namespace syncable {
+
+int CountEntriesWithName(BaseTransaction* rtrans,
+ const syncable::Id& parent_id,
+ const string& name) {
+ Directory::ChildHandles child_handles;
+ rtrans->directory()->GetChildHandlesById(rtrans, parent_id, &child_handles);
+ if (child_handles.size() <= 0) {
+ return 0;
+ }
+
+ int number_of_entries_with_name = 0;
+ for (Directory::ChildHandles::iterator i = child_handles.begin();
+ i != child_handles.end(); ++i) {
+ Entry e(rtrans, GET_BY_HANDLE, *i);
+ CHECK(e.good());
+ if (e.Get(NON_UNIQUE_NAME) == name) {
+ ++number_of_entries_with_name;
+ }
+ }
+ return number_of_entries_with_name;
+}
+
+Id GetFirstEntryWithName(BaseTransaction* rtrans,
+ const syncable::Id& parent_id,
+ const string& name) {
+ Directory::ChildHandles child_handles;
+ rtrans->directory()->GetChildHandlesById(rtrans, parent_id, &child_handles);
+
+ for (Directory::ChildHandles::iterator i = child_handles.begin();
+ i != child_handles.end(); ++i) {
+ Entry e(rtrans, GET_BY_HANDLE, *i);
+ CHECK(e.good());
+ if (e.Get(NON_UNIQUE_NAME) == name) {
+ return e.Get(ID);
+ }
+ }
+
+ CHECK(false);
+ return Id();
+}
+
+Id GetOnlyEntryWithName(BaseTransaction* rtrans,
+ const syncable::Id& parent_id,
+ const string& name) {
+ CHECK(1 == CountEntriesWithName(rtrans, parent_id, name));
+ return GetFirstEntryWithName(rtrans, parent_id, name);
+}
+
+} // namespace syncable
diff --git a/chrome/browser/sync/test/engine/test_syncable_utils.h b/chrome/browser/sync/test/engine/test_syncable_utils.h
new file mode 100644
index 0000000..2919e84
--- /dev/null
+++ b/chrome/browser/sync/test/engine/test_syncable_utils.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Utilities that are useful in verifying the state of items in a
+// syncable database.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_SYNCABLE_UTILS_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_SYNCABLE_UTILS_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/sync/syncable/syncable.h"
+
+namespace syncable {
+
+class BaseTransaction;
+class Id;
+
+// Count the number of entries with a given name inside of a parent.
+// Useful to check folder structure and for porting older tests that
+// rely on uniqueness inside of folders.
+int CountEntriesWithName(BaseTransaction* rtrans,
+ const syncable::Id& parent_id,
+ const std::string& name);
+
+// Get the first entry ID with name in a parent. The entry *must* exist.
+Id GetFirstEntryWithName(BaseTransaction* rtrans,
+ const syncable::Id& parent_id,
+ const std::string& name);
+
+// Assert that there's only one entry by this name in this parent.
+// Return the Id.
+Id GetOnlyEntryWithName(BaseTransaction* rtrans,
+ const syncable::Id& parent_id,
+ const std::string& name);
+
+} // namespace syncable
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_SYNCABLE_UTILS_H_
diff --git a/chrome/browser/sync/test/engine/test_user_share.cc b/chrome/browser/sync/test/engine/test_user_share.cc
new file mode 100644
index 0000000..6010d07
--- /dev/null
+++ b/chrome/browser/sync/test/engine/test_user_share.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/engine/test_user_share.h"
+
+#include "base/compiler_specific.h"
+
+namespace browser_sync {
+
+TestUserShare::TestUserShare() {}
+
+TestUserShare::~TestUserShare() {
+ CHECK(!user_share_.dir_manager.get());
+}
+
+void TestUserShare::SetUp() {
+ setter_upper_.SetUp();
+ // HACK: We have two scoped_ptrs to the same object. But we make
+ // sure to release one in TearDown.
+ user_share_.dir_manager.reset(setter_upper_.manager());
+ user_share_.name = setter_upper_.name();
+}
+
+void TestUserShare::TearDown() {
+ // Make sure we don't free the manager twice.
+ ignore_result(user_share_.dir_manager.release());
+ setter_upper_.TearDown();
+}
+
+sync_api::UserShare* TestUserShare::user_share() {
+ return &user_share_;
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/test/engine/test_user_share.h b/chrome/browser/sync/test/engine/test_user_share.h
new file mode 100644
index 0000000..cd996ee
--- /dev/null
+++ b/chrome/browser/sync/test/engine/test_user_share.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A handy class that takes care of setting up and destroying a
+// sync_api::UserShare instance for unit tests that require one.
+//
+// The expected usage is to make this a component of your test fixture:
+//
+// class AwesomenessTest : public testing::Test {
+// public:
+// virtual void SetUp() {
+// test_user_share_.SetUp();
+// }
+// virtual void TearDown() {
+// test_user_share_.TearDown();
+// }
+// protected:
+// TestUserShare test_user_share_;
+// };
+//
+// Then, in your tests:
+//
+// TEST_F(AwesomenessTest, IsMaximal) {
+// sync_api::ReadTransaction trans(test_user_share_.user_share());
+// ...
+// }
+//
+
+#ifndef CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_USER_SHARE_H_
+#define CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_USER_SHARE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/internal_api/user_share.h"
+#include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
+
+namespace browser_sync {
+
+class TestUserShare {
+ public:
+ TestUserShare();
+ ~TestUserShare();
+
+ // Sets up the UserShare instance. Clears any existing database
+ // backing files that might exist on disk.
+ void SetUp();
+
+ // Undo everything done by SetUp(): closes the UserShare and deletes
+ // the backing files. Before closing the directory, this will run
+ // the directory invariant checks and perform the SaveChanges action
+ // on the user share's directory.
+ void TearDown();
+
+ // Non-NULL iff called between a call to SetUp() and TearDown().
+ sync_api::UserShare* user_share();
+
+ private:
+ TestDirectorySetterUpper setter_upper_;
+ sync_api::UserShare user_share_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestUserShare);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_ENGINE_TEST_USER_SHARE_H_
diff --git a/chrome/browser/sync/test/live_sync/PRESUBMIT.py b/chrome/browser/sync/test/live_sync/PRESUBMIT.py
new file mode 100644
index 0000000..f49f215
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/PRESUBMIT.py
@@ -0,0 +1,13 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Chromium presubmit script for src/chrome/browser/sync/test/live_sync.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details on the presubmit API built into gcl.
+"""
+
+def GetPreferredTrySlaves():
+ return ['win', 'linux', 'mac', 'win_sync', 'linux_sync', 'mac_sync']
diff --git a/chrome/browser/sync/test/live_sync/apps_helper.cc b/chrome/browser/sync/test/live_sync/apps_helper.cc
new file mode 100644
index 0000000..eb68002
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/apps_helper.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/apps_helper.h"
+
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/browser/sync/test/live_sync/sync_extension_helper.h"
+#include "chrome/common/extensions/extension.h"
+
+using sync_datatype_helper::test;
+
+namespace {
+
+std::string CreateFakeAppName(int index) {
+ return "fakeapp" + base::IntToString(index);
+}
+
+} // namespace
+
+namespace apps_helper {
+
+bool HasSameAppsAsVerifier(int index) {
+ // TODO(akalin): We may want to filter out non-apps for some tests.
+ return SyncExtensionHelper::GetInstance()->ExtensionStatesMatch(
+ test()->GetProfile(index), test()->verifier());
+}
+
+bool AllProfilesHaveSameAppsAsVerifier() {
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (!HasSameAppsAsVerifier(i)) {
+ LOG(ERROR) << "Profile " << i << " doesn't have the same apps as the"
+ " verifier profile.";
+ return false;
+ }
+ }
+ return true;
+}
+
+void InstallApp(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->InstallExtension(
+ profile, CreateFakeAppName(index), Extension::TYPE_HOSTED_APP);
+}
+
+void UninstallApp(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->UninstallExtension(
+ profile, CreateFakeAppName(index));
+}
+
+void EnableApp(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->EnableExtension(
+ profile, CreateFakeAppName(index));
+}
+
+void DisableApp(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->DisableExtension(
+ profile, CreateFakeAppName(index));
+}
+
+void IncognitoEnableApp(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->IncognitoEnableExtension(
+ profile, CreateFakeAppName(index));
+}
+
+void IncognitoDisableApp(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->IncognitoDisableExtension(
+ profile, CreateFakeAppName(index));
+}
+
+void InstallAppsPendingForSync(Profile* profile) {
+ SyncExtensionHelper::GetInstance()->InstallExtensionsPendingForSync(
+ profile, Extension::TYPE_HOSTED_APP);
+}
+
+} // namespace apps_helper
diff --git a/chrome/browser/sync/test/live_sync/apps_helper.h b/chrome/browser/sync/test/live_sync/apps_helper.h
new file mode 100644
index 0000000..e1a1ca2
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/apps_helper.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_APPS_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_APPS_HELPER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+class Profile;
+
+namespace apps_helper {
+
+// Returns true iff the profile with index |index| has the same apps as the
+// verifier.
+bool HasSameAppsAsVerifier(int index) WARN_UNUSED_RESULT;
+
+// Returns true iff all existing profiles have the same apps as the verifier.
+bool AllProfilesHaveSameAppsAsVerifier() WARN_UNUSED_RESULT;
+
+// Installs the app for the given index to |profile|.
+void InstallApp(Profile* profile, int index);
+
+// Uninstalls the app for the given index from |profile|. Assumes that it was
+// previously installed.
+void UninstallApp(Profile* profile, int index);
+
+// Installs all pending synced apps for |profile|.
+void InstallAppsPendingForSync(Profile* profile);
+
+// Enables the app for the given index on |profile|.
+void EnableApp(Profile* profile, int index);
+
+// Disables the appfor the given index on |profile|.
+void DisableApp(Profile* profile, int index);
+
+// Enables the app for the given index in incognito mode on |profile|.
+void IncognitoEnableApp(Profile* profile, int index);
+
+// Disables the app for the given index in incognito mode on |profile|.
+void IncognitoDisableApp(Profile* profile, int index);
+
+} // namespace apps_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_APPS_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/autofill_helper.cc b/chrome/browser/sync/test/live_sync/autofill_helper.cc
new file mode 100644
index 0000000..a1a96ee
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/autofill_helper.cc
@@ -0,0 +1,298 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/autofill_helper.h"
+
+#include "chrome/browser/autofill/autofill_common_test.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/autofill/autofill_type.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/autofill/personal_data_manager_observer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_test_util.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/browser/webdata/autofill_entry.h"
+#include "chrome/browser/webdata/autofill_table.h"
+#include "chrome/browser/webdata/web_database.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/test/base/thread_observer_helper.h"
+#include "webkit/glue/form_field.h"
+
+using base::WaitableEvent;
+using sync_datatype_helper::test;
+using testing::_;
+
+namespace {
+
+class GetAllAutofillEntries
+ : public base::RefCountedThreadSafe<GetAllAutofillEntries> {
+ public:
+ explicit GetAllAutofillEntries(WebDataService* web_data_service)
+ : web_data_service_(web_data_service),
+ done_event_(false, false) {}
+
+ void Init() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ NewRunnableMethod(this, &GetAllAutofillEntries::Run));
+ done_event_.Wait();
+ }
+
+ const std::vector<AutofillEntry>& entries() const {
+ return entries_;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<GetAllAutofillEntries>;
+
+ void Run() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ web_data_service_->GetDatabase()->GetAutofillTable()->GetAllAutofillEntries(
+ &entries_);
+ done_event_.Signal();
+ }
+
+ WebDataService* web_data_service_;
+ base::WaitableEvent done_event_;
+ std::vector<AutofillEntry> entries_;
+};
+
+ACTION_P(SignalEvent, event) {
+ event->Signal();
+}
+
+class AutofillDBThreadObserverHelper : public DBThreadObserverHelper {
+ protected:
+ virtual void RegisterObservers() {
+ registrar_.Add(&observer_,
+ chrome::NOTIFICATION_AUTOFILL_ENTRIES_CHANGED,
+ NotificationService::AllSources());
+ registrar_.Add(&observer_,
+ chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED,
+ NotificationService::AllSources());
+ }
+};
+
+class MockPersonalDataManagerObserver : public PersonalDataManagerObserver {
+ public:
+ MOCK_METHOD0(OnPersonalDataChanged, void());
+};
+
+} // namespace
+
+namespace autofill_helper {
+
+AutofillProfile CreateAutofillProfile(ProfileType type) {
+ AutofillProfile profile;
+ switch (type) {
+ case PROFILE_MARION:
+ autofill_test::SetProfileInfoWithGuid(&profile,
+ "C837507A-6C3B-4872-AC14-5113F157D668",
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox",
+ "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910", "01987654321");
+ break;
+ case PROFILE_HOMER:
+ autofill_test::SetProfileInfoWithGuid(&profile,
+ "137DE1C3-6A30-4571-AC86-109B1ECFBE7F",
+ "Homer", "J.", "Simpson",
+ "homer@abc.com", "SNPP",
+ "1 Main St", "PO Box 1", "Springfield", "MA",
+ "94101", "US", "14155551212", "14155551313");
+ break;
+ case PROFILE_FRASIER:
+ autofill_test::SetProfileInfoWithGuid(&profile,
+ "9A5E6872-6198-4688-BF75-0016E781BB0A",
+ "Frasier", "Winslow", "Crane",
+ "", "randomness", "", "Apt. 4", "Seattle", "WA",
+ "99121", "US", "0000000000", "ABCDEFGHIJK");
+ break;
+ case PROFILE_NULL:
+ autofill_test::SetProfileInfoWithGuid(&profile,
+ "FE461507-7E13-4198-8E66-74C7DB6D8322",
+ "", "", "", "", "", "", "", "", "", "", "", "", "");
+ break;
+ }
+ return profile;
+}
+
+WebDataService* GetWebDataService(int index) {
+ return test()->GetProfile(index)->GetWebDataService(Profile::EXPLICIT_ACCESS);
+}
+
+PersonalDataManager* GetPersonalDataManager(int index) {
+ return test()->GetProfile(index)->GetPersonalDataManager();
+}
+
+void AddKeys(int profile, const std::set<AutofillKey>& keys) {
+ std::vector<webkit_glue::FormField> form_fields;
+ for (std::set<AutofillKey>::const_iterator i = keys.begin();
+ i != keys.end();
+ ++i) {
+ webkit_glue::FormField field;
+ field.name = i->name();
+ field.value = i->value();
+ form_fields.push_back(field);
+ }
+
+ WaitableEvent done_event(false, false);
+ scoped_refptr<AutofillDBThreadObserverHelper> observer_helper(
+ new AutofillDBThreadObserverHelper());
+ observer_helper->Init();
+
+ EXPECT_CALL(*observer_helper->observer(), Observe(_, _, _)).
+ WillOnce(SignalEvent(&done_event));
+ WebDataService* wds = GetWebDataService(profile);
+ wds->AddFormFields(form_fields);
+ done_event.Wait();
+}
+
+void RemoveKey(int profile, const AutofillKey& key) {
+ WaitableEvent done_event(false, false);
+ scoped_refptr<AutofillDBThreadObserverHelper> observer_helper(
+ new AutofillDBThreadObserverHelper());
+ observer_helper->Init();
+
+ EXPECT_CALL(*observer_helper->observer(), Observe(_, _, _)).
+ WillOnce(SignalEvent(&done_event));
+ WebDataService* wds = GetWebDataService(profile);
+ wds->RemoveFormValueForElementName(key.name(), key.value());
+ done_event.Wait();
+}
+
+std::set<AutofillEntry> GetAllKeys(int profile) {
+ WebDataService* wds = GetWebDataService(profile);
+ scoped_refptr<GetAllAutofillEntries> get_all_entries =
+ new GetAllAutofillEntries(wds);
+ get_all_entries->Init();
+ const std::vector<AutofillEntry>& all_entries = get_all_entries->entries();
+ std::set<AutofillEntry> all_keys;
+ for (std::vector<AutofillEntry>::const_iterator it = all_entries.begin();
+ it != all_entries.end(); ++it) {
+ all_keys.insert(*it);
+ }
+ return all_keys;
+}
+
+bool KeysMatch(int profile_a, int profile_b) {
+ return GetAllKeys(profile_a) == GetAllKeys(profile_b);
+}
+
+void SetProfiles(int profile, std::vector<AutofillProfile>* autofill_profiles) {
+ MockPersonalDataManagerObserver observer;
+ EXPECT_CALL(observer, OnPersonalDataChanged()).
+ WillOnce(QuitUIMessageLoop());
+ PersonalDataManager* pdm = GetPersonalDataManager(profile);
+ pdm->SetObserver(&observer);
+ pdm->SetProfiles(autofill_profiles);
+ MessageLoop::current()->Run();
+ pdm->RemoveObserver(&observer);
+}
+
+void AddProfile(int profile, const AutofillProfile& autofill_profile) {
+ const std::vector<AutofillProfile*>& all_profiles = GetAllProfiles(profile);
+ std::vector<AutofillProfile> autofill_profiles;
+ for (size_t i = 0; i < all_profiles.size(); ++i)
+ autofill_profiles.push_back(*all_profiles[i]);
+ autofill_profiles.push_back(autofill_profile);
+ autofill_helper::SetProfiles(profile, &autofill_profiles);
+}
+
+void RemoveProfile(int profile, const std::string& guid) {
+ const std::vector<AutofillProfile*>& all_profiles = GetAllProfiles(profile);
+ std::vector<AutofillProfile> autofill_profiles;
+ for (size_t i = 0; i < all_profiles.size(); ++i) {
+ if (all_profiles[i]->guid() != guid)
+ autofill_profiles.push_back(*all_profiles[i]);
+ }
+ autofill_helper::SetProfiles(profile, &autofill_profiles);
+}
+
+void UpdateProfile(int profile,
+ const std::string& guid,
+ const AutofillType& type,
+ const string16& value) {
+ const std::vector<AutofillProfile*>& all_profiles = GetAllProfiles(profile);
+ std::vector<AutofillProfile> profiles;
+ for (size_t i = 0; i < all_profiles.size(); ++i) {
+ profiles.push_back(*all_profiles[i]);
+ if (all_profiles[i]->guid() == guid)
+ profiles.back().SetInfo(type.field_type(), value);
+ }
+ autofill_helper::SetProfiles(profile, &profiles);
+}
+
+const std::vector<AutofillProfile*>& GetAllProfiles(
+ int profile) {
+ MockPersonalDataManagerObserver observer;
+ EXPECT_CALL(observer, OnPersonalDataChanged()).
+ WillOnce(QuitUIMessageLoop());
+ PersonalDataManager* pdm = GetPersonalDataManager(profile);
+ pdm->SetObserver(&observer);
+ pdm->Refresh();
+ MessageLoop::current()->Run();
+ pdm->RemoveObserver(&observer);
+ return pdm->web_profiles();
+}
+
+int GetProfileCount(int profile) {
+ return GetAllProfiles(profile).size();
+}
+
+int GetKeyCount(int profile) {
+ return GetAllKeys(profile).size();
+}
+
+bool ProfilesMatch(int profile_a, int profile_b) {
+ const std::vector<AutofillProfile*>& autofill_profiles_a =
+ GetAllProfiles(profile_a);
+ std::map<std::string, AutofillProfile> autofill_profiles_a_map;
+ for (size_t i = 0; i < autofill_profiles_a.size(); ++i) {
+ const AutofillProfile* p = autofill_profiles_a[i];
+ autofill_profiles_a_map[p->guid()] = *p;
+ }
+
+ const std::vector<AutofillProfile*>& autofill_profiles_b =
+ GetAllProfiles(profile_b);
+ for (size_t i = 0; i < autofill_profiles_b.size(); ++i) {
+ const AutofillProfile* p = autofill_profiles_b[i];
+ if (!autofill_profiles_a_map.count(p->guid())) {
+ LOG(ERROR) << "GUID " << p->guid() << " not found in profile "
+ << profile_b << ".";
+ return false;
+ }
+ AutofillProfile* expected_profile = &autofill_profiles_a_map[p->guid()];
+ expected_profile->set_guid(p->guid());
+ if (*expected_profile != *p) {
+ LOG(ERROR) << "Mismatch in profile with GUID " << p->guid() << ".";
+ return false;
+ }
+ autofill_profiles_a_map.erase(p->guid());
+ }
+
+ if (autofill_profiles_a_map.size()) {
+ LOG(ERROR) << "Entries present in Profile " << profile_a
+ << " but not in " << profile_b << ".";
+ return false;
+ }
+ return true;
+}
+
+bool AllProfilesMatch() {
+ for (int i = 1; i < test()->num_clients(); ++i) {
+ if (!ProfilesMatch(0, i)) {
+ LOG(ERROR) << "Profile " << i << "does not contain the same autofill "
+ "profiles as profile 0.";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace autofill_helper
diff --git a/chrome/browser/sync/test/live_sync/autofill_helper.h b/chrome/browser/sync/test/live_sync/autofill_helper.h
new file mode 100644
index 0000000..50732f5
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/autofill_helper.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_AUTOFILL_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_AUTOFILL_HELPER_H_
+#pragma once
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/string16.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+
+class AutofillEntry;
+class AutofillKey;
+class AutofillProfile;
+class AutofillType;
+class PersonalDataManager;
+class WebDataService;
+
+namespace autofill_helper {
+
+enum ProfileType {
+ PROFILE_MARION,
+ PROFILE_HOMER,
+ PROFILE_FRASIER,
+ PROFILE_NULL
+};
+
+// Used to access the web data service within a particular sync profile.
+WebDataService* GetWebDataService(int index) WARN_UNUSED_RESULT;
+
+// Used to access the personal data manager within a particular sync profile.
+PersonalDataManager* GetPersonalDataManager(int index) WARN_UNUSED_RESULT;
+
+// Adds the form fields in |keys| to the WebDataService of sync profile
+// |profile|.
+void AddKeys(int profile, const std::set<AutofillKey>& keys);
+
+// Removes the form field in |key| from the WebDataService of sync profile
+// |profile|.
+void RemoveKey(int profile, const AutofillKey& key);
+
+// Gets all the form fields in the WebDataService of sync profile |profile|.
+std::set<AutofillEntry> GetAllKeys(int profile) WARN_UNUSED_RESULT;
+
+// Compares the form fields in the WebDataServices of sync profiles
+// |profile_a| and |profile_b|. Returns true if they match.
+bool KeysMatch(int profile_a, int profile_b) WARN_UNUSED_RESULT;
+
+// Replaces the Autofill profiles in sync profile |profile| with
+// |autofill_profiles|.
+void SetProfiles(int profile, std::vector<AutofillProfile>* autofill_profiles);
+
+// Adds the autofill profile |autofill_profile| to sync profile |profile|.
+void AddProfile(int profile, const AutofillProfile& autofill_profile);
+
+// Removes the autofill profile with guid |guid| from sync profile
+// |profile|.
+void RemoveProfile(int profile, const std::string& guid);
+
+// Updates the autofill profile with guid |guid| in sync profile |profile|
+// to |type| and |value|.
+void UpdateProfile(int profile,
+ const std::string& guid,
+ const AutofillType& type,
+ const string16& value);
+
+// Gets all the Autofill profiles in the PersonalDataManager of sync profile
+// |profile|.
+const std::vector<AutofillProfile*>& GetAllProfiles(
+ int profile) WARN_UNUSED_RESULT;
+
+// Returns the number of autofill profiles contained by sync profile
+// |profile|.
+int GetProfileCount(int profile);
+
+// Returns the number of autofill keys contained by sync profile |profile|.
+int GetKeyCount(int profile);
+
+// Compares the Autofill profiles in the PersonalDataManagers of sync profiles
+// |profile_a| and |profile_b|. Returns true if they match.
+bool ProfilesMatch(int profile_a, int profile_b) WARN_UNUSED_RESULT;
+
+// Compares the autofill profiles for all sync profiles, and returns true if
+// they all match.
+bool AllProfilesMatch() WARN_UNUSED_RESULT;
+
+// Creates a test autofill profile based on the persona specified in |type|.
+AutofillProfile CreateAutofillProfile(ProfileType type);
+
+} // namespace autofill_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_AUTOFILL_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/bookmarks_helper.cc b/chrome/browser/sync/test/live_sync/bookmarks_helper.cc
new file mode 100644
index 0000000..18ed644
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/bookmarks_helper.cc
@@ -0,0 +1,593 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+
+#include "base/rand_util.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_model_observer.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/glue/bookmark_change_processor.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/models/tree_node_iterator.h"
+#include "ui/gfx/codec/png_codec.h"
+
+using sync_datatype_helper::test;
+
+namespace {
+
+// Helper class used to wait for changes to take effect on the favicon of a
+// particular bookmark node in a particular bookmark model.
+class FaviconChangeObserver : public BookmarkModelObserver {
+ public:
+ FaviconChangeObserver(BookmarkModel* model, const BookmarkNode* node)
+ : model_(model),
+ node_(node),
+ wait_for_load_(false) {
+ model->AddObserver(this);
+ }
+ virtual ~FaviconChangeObserver() {
+ model_->RemoveObserver(this);
+ }
+ void WaitForGetFavicon() {
+ wait_for_load_ = true;
+ ui_test_utils::RunMessageLoop();
+ ASSERT_TRUE(node_->is_favicon_loaded());
+ }
+ void WaitForSetFavicon() {
+ wait_for_load_ = false;
+ ui_test_utils::RunMessageLoop();
+ }
+ virtual void Loaded(BookmarkModel* model, bool ids_reassigned) OVERRIDE {}
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ const BookmarkNode* old_parent,
+ int old_index,
+ const BookmarkNode* new_parent,
+ int new_index) OVERRIDE {}
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int index) OVERRIDE {}
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int old_index,
+ const BookmarkNode* node) OVERRIDE {}
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ const BookmarkNode* node) OVERRIDE {
+ if (model == model_ && node == node_)
+ model->GetFavicon(node);
+ }
+ virtual void BookmarkNodeChildrenReordered(
+ BookmarkModel* model,
+ const BookmarkNode* node) OVERRIDE {}
+ virtual void BookmarkNodeFaviconChanged(
+ BookmarkModel* model,
+ const BookmarkNode* node) OVERRIDE {
+ if (model == model_ && node == node_) {
+ if (!wait_for_load_ || (wait_for_load_ && node->is_favicon_loaded()))
+ MessageLoopForUI::current()->Quit();
+ }
+ }
+
+ private:
+ BookmarkModel* model_;
+ const BookmarkNode* node_;
+ bool wait_for_load_;
+ DISALLOW_COPY_AND_ASSIGN(FaviconChangeObserver);
+};
+
+// A collection of URLs for which we have added favicons. Since loading a
+// favicon is an asynchronous operation and doesn't necessarily invoke a
+// callback, this collection is used to determine if we must wait for a URL's
+// favicon to load or not.
+std::set<GURL>* urls_with_favicons_ = NULL;
+
+// Returns the number of nodes of node type |node_type| in |model| whose
+// titles match the string |title|.
+int CountNodesWithTitlesMatching(BookmarkModel* model,
+ BookmarkNode::Type node_type,
+ const string16& title) {
+ ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
+ // Walk through the model tree looking for bookmark nodes of node type
+ // |node_type| whose titles match |title|.
+ int count = 0;
+ while (iterator.has_next()) {
+ const BookmarkNode* node = iterator.Next();
+ if ((node->type() == node_type) && (node->GetTitle() == title))
+ ++count;
+ }
+ return count;
+}
+
+// Checks if the favicon data in |bitmap_a| and |bitmap_b| are equivalent.
+// Returns true if they match.
+bool FaviconBitmapsMatch(const SkBitmap& bitmap_a, const SkBitmap& bitmap_b) {
+ if (bitmap_a.getSize() == 0U && bitmap_a.getSize() == 0U)
+ return true;
+ if ((bitmap_a.getSize() != bitmap_b.getSize()) ||
+ (bitmap_a.width() != bitmap_b.width()) ||
+ (bitmap_a.height() != bitmap_b.height())) {
+ LOG(ERROR) << "Favicon size mismatch: " << bitmap_a.getSize() << " ("
+ << bitmap_a.width() << "x" << bitmap_a.height() << ") vs. "
+ << bitmap_b.getSize() << " (" << bitmap_b.width() << "x"
+ << bitmap_b.height() << ")";
+ return false;
+ }
+ SkAutoLockPixels bitmap_lock_a(bitmap_a);
+ SkAutoLockPixels bitmap_lock_b(bitmap_b);
+ void* node_pixel_addr_a = bitmap_a.getPixels();
+ EXPECT_TRUE(node_pixel_addr_a);
+ void* node_pixel_addr_b = bitmap_b.getPixels();
+ EXPECT_TRUE(node_pixel_addr_b);
+ if (memcmp(node_pixel_addr_a, node_pixel_addr_b, bitmap_a.getSize()) != 0) {
+ LOG(ERROR) << "Favicon bitmap mismatch";
+ return false;
+ } else {
+ return true;
+ }
+}
+
+// Gets the favicon associated with |node| in |model|.
+const SkBitmap& GetFavicon(BookmarkModel* model, const BookmarkNode* node) {
+ // If a favicon wasn't explicitly set for a particular URL, simply return its
+ // blank favicon.
+ if (!urls_with_favicons_ ||
+ urls_with_favicons_->find(node->url()) == urls_with_favicons_->end()) {
+ return node->favicon();
+ }
+ // If a favicon was explicitly set, we may need to wait for it to be loaded
+ // via BookmarkModel::GetFavIcon(), which is an asynchronous operation.
+ if (!node->is_favicon_loaded()) {
+ FaviconChangeObserver observer(model, node);
+ model->GetFavicon(node);
+ observer.WaitForGetFavicon();
+ }
+ EXPECT_TRUE(node->is_favicon_loaded());
+ return node->favicon();
+}
+
+// Checks if the favicon in |node_a| from |model_a| matches that of |node_b|
+// from |model_b|. Returns true if they match.
+bool FaviconsMatch(BookmarkModel* model_a,
+ BookmarkModel* model_b,
+ const BookmarkNode* node_a,
+ const BookmarkNode* node_b) {
+ const SkBitmap& bitmap_a = GetFavicon(model_a, node_a);
+ const SkBitmap& bitmap_b = GetFavicon(model_b, node_b);
+ return FaviconBitmapsMatch(bitmap_a, bitmap_b);
+}
+
+// Does a deep comparison of BookmarkNode fields in |model_a| and |model_b|.
+// Returns true if they are all equal.
+bool NodesMatch(const BookmarkNode* node_a, const BookmarkNode* node_b) {
+ if (node_a == NULL || node_b == NULL)
+ return node_a == node_b;
+ if (node_a->is_folder() != node_b->is_folder()) {
+ LOG(ERROR) << "Cannot compare folder with bookmark";
+ return false;
+ }
+ if (node_a->GetTitle() != node_b->GetTitle()) {
+ LOG(ERROR) << "Title mismatch: " << node_a->GetTitle() << " vs. "
+ << node_b->GetTitle();
+ return false;
+ }
+ if (node_a->url() != node_b->url()) {
+ LOG(ERROR) << "URL mismatch: " << node_a->url() << " vs. "
+ << node_b->url();
+ return false;
+ }
+ if (node_a->parent()->GetIndexOf(node_a) !=
+ node_b->parent()->GetIndexOf(node_b)) {
+ LOG(ERROR) << "Index mismatch: "
+ << node_a->parent()->GetIndexOf(node_a) << " vs. "
+ << node_b->parent()->GetIndexOf(node_b);
+ return false;
+ }
+ return true;
+}
+
+// Checks if the hierarchies in |model_a| and |model_b| are equivalent in
+// terms of the data model and favicon. Returns true if they both match.
+// Note: Some peripheral fields like creation times are allowed to mismatch.
+bool BookmarkModelsMatch(BookmarkModel* model_a, BookmarkModel* model_b) {
+ bool ret_val = true;
+ ui::TreeNodeIterator<const BookmarkNode> iterator_a(model_a->root_node());
+ ui::TreeNodeIterator<const BookmarkNode> iterator_b(model_b->root_node());
+ while (iterator_a.has_next()) {
+ const BookmarkNode* node_a = iterator_a.Next();
+ if (!iterator_b.has_next()) {
+ LOG(ERROR) << "Models do not match.";
+ return false;
+ }
+ const BookmarkNode* node_b = iterator_b.Next();
+ ret_val = ret_val && NodesMatch(node_a, node_b);
+ if (node_a->is_folder() || node_b->is_folder())
+ continue;
+ ret_val = ret_val && FaviconsMatch(model_a, model_b, node_a, node_b);
+ }
+ ret_val = ret_val && (!iterator_b.has_next());
+ return ret_val;
+}
+
+// Finds the node in the verifier bookmark model that corresponds to
+// |foreign_node| in |foreign_model| and stores its address in |result|.
+void FindNodeInVerifier(BookmarkModel* foreign_model,
+ const BookmarkNode* foreign_node,
+ const BookmarkNode** result) {
+ // Climb the tree.
+ std::stack<int> path;
+ const BookmarkNode* walker = foreign_node;
+ while (walker != foreign_model->root_node()) {
+ path.push(walker->parent()->GetIndexOf(walker));
+ walker = walker->parent();
+ }
+
+ // Swing over to the other tree.
+ walker = bookmarks_helper::GetVerifierBookmarkModel()->root_node();
+
+ // Climb down.
+ while (!path.empty()) {
+ ASSERT_TRUE(walker->is_folder());
+ ASSERT_LT(path.top(), walker->child_count());
+ walker = walker->GetChild(path.top());
+ path.pop();
+ }
+
+ ASSERT_TRUE(NodesMatch(foreign_node, walker));
+ *result = walker;
+}
+
+} // namespace
+
+
+namespace bookmarks_helper {
+
+BookmarkModel* GetBookmarkModel(int index) {
+ return test()->GetProfile(index)->GetBookmarkModel();
+}
+
+const BookmarkNode* GetBookmarkBarNode(int index) {
+ return GetBookmarkModel(index)->bookmark_bar_node();
+}
+
+const BookmarkNode* GetOtherNode(int index) {
+ return GetBookmarkModel(index)->other_node();
+}
+
+BookmarkModel* GetVerifierBookmarkModel() {
+ return test()->verifier()->GetBookmarkModel();
+}
+
+const BookmarkNode* AddURL(int profile,
+ const std::wstring& title,
+ const GURL& url) {
+ return AddURL(profile, GetBookmarkBarNode(profile), 0, title, url);
+}
+
+const BookmarkNode* AddURL(int profile,
+ int index,
+ const std::wstring& title,
+ const GURL& url) {
+ return AddURL(profile, GetBookmarkBarNode(profile), index, title, url);
+}
+
+const BookmarkNode* AddURL(int profile,
+ const BookmarkNode* parent,
+ int index,
+ const std::wstring& title,
+ const GURL& url) {
+ if (GetBookmarkModel(profile)->GetNodeByID(parent->id()) != parent) {
+ LOG(ERROR) << "Node " << parent->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ return NULL;
+ }
+ const BookmarkNode* result = GetBookmarkModel(profile)->
+ AddURL(parent, index, WideToUTF16(title), url);
+ if (!result) {
+ LOG(ERROR) << "Could not add bookmark " << title << " to Profile "
+ << profile;
+ return NULL;
+ }
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_parent = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent);
+ const BookmarkNode* v_node = GetVerifierBookmarkModel()->
+ AddURL(v_parent, index, WideToUTF16(title), url);
+ if (!v_node) {
+ LOG(ERROR) << "Could not add bookmark " << title << " to the verifier";
+ return NULL;
+ }
+ EXPECT_TRUE(NodesMatch(v_node, result));
+ }
+ return result;
+}
+
+const BookmarkNode* AddFolder(int profile,
+ const std::wstring& title) {
+ return AddFolder(profile, GetBookmarkBarNode(profile), 0, title);
+}
+
+const BookmarkNode* AddFolder(int profile,
+ int index,
+ const std::wstring& title) {
+ return AddFolder(profile, GetBookmarkBarNode(profile), index, title);
+}
+
+const BookmarkNode* AddFolder(int profile,
+ const BookmarkNode* parent,
+ int index,
+ const std::wstring& title) {
+ if (GetBookmarkModel(profile)->GetNodeByID(parent->id()) != parent) {
+ LOG(ERROR) << "Node " << parent->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ return NULL;
+ }
+ const BookmarkNode* result =
+ GetBookmarkModel(profile)->AddFolder(parent, index, WideToUTF16(title));
+ EXPECT_TRUE(result);
+ if (!result) {
+ LOG(ERROR) << "Could not add folder " << title << " to Profile "
+ << profile;
+ return NULL;
+ }
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_parent = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent);
+ const BookmarkNode* v_node = GetVerifierBookmarkModel()->AddFolder(
+ v_parent, index, WideToUTF16(title));
+ if (!v_node) {
+ LOG(ERROR) << "Could not add folder " << title << " to the verifier";
+ return NULL;
+ }
+ EXPECT_TRUE(NodesMatch(v_node, result));
+ }
+ return result;
+}
+
+void SetTitle(int profile,
+ const BookmarkNode* node,
+ const std::wstring& new_title) {
+ ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(node->id()), node)
+ << "Node " << node->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_node = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node);
+ GetVerifierBookmarkModel()->SetTitle(v_node, WideToUTF16(new_title));
+ }
+ GetBookmarkModel(profile)->SetTitle(node, WideToUTF16(new_title));
+}
+
+void SetFavicon(int profile,
+ const BookmarkNode* node,
+ const std::vector<unsigned char>& icon_bytes_vector) {
+ ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(node->id()), node)
+ << "Node " << node->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ ASSERT_EQ(BookmarkNode::URL, node->type())
+ << "Node " << node->GetTitle() << " must be a url.";
+ if (urls_with_favicons_ == NULL)
+ urls_with_favicons_ = new std::set<GURL>();
+ urls_with_favicons_->insert(node->url());
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_node = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node);
+ FaviconChangeObserver v_observer(GetVerifierBookmarkModel(), v_node);
+ browser_sync::BookmarkChangeProcessor::ApplyBookmarkFavicon(
+ v_node, test()->verifier(), icon_bytes_vector);
+ v_observer.WaitForSetFavicon();
+ }
+ FaviconChangeObserver observer(GetBookmarkModel(profile), node);
+ browser_sync::BookmarkChangeProcessor::ApplyBookmarkFavicon(
+ node, test()->GetProfile(profile), icon_bytes_vector);
+ observer.WaitForSetFavicon();
+}
+
+const BookmarkNode* SetURL(int profile,
+ const BookmarkNode* node,
+ const GURL& new_url) {
+ if (GetBookmarkModel(profile)->GetNodeByID(node->id()) != node) {
+ LOG(ERROR) << "Node " << node->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ return NULL;
+ }
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_node = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node);
+ bookmark_utils::ApplyEditsWithNoFolderChange(
+ GetVerifierBookmarkModel(),
+ v_node->parent(),
+ BookmarkEditor::EditDetails(v_node),
+ v_node->GetTitle(),
+ new_url);
+ }
+ return bookmark_utils::ApplyEditsWithNoFolderChange(
+ GetBookmarkModel(profile),
+ node->parent(),
+ BookmarkEditor::EditDetails(node),
+ node->GetTitle(),
+ new_url);
+}
+
+void Move(int profile,
+ const BookmarkNode* node,
+ const BookmarkNode* new_parent,
+ int index) {
+ ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(node->id()), node)
+ << "Node " << node->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_new_parent = NULL;
+ const BookmarkNode* v_node = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), new_parent, &v_new_parent);
+ FindNodeInVerifier(GetBookmarkModel(profile), node, &v_node);
+ GetVerifierBookmarkModel()->Move(v_node, v_new_parent, index);
+ }
+ GetBookmarkModel(profile)->Move(node, new_parent, index);
+}
+
+void Remove(int profile,
+ const BookmarkNode* parent,
+ int index) {
+ ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(parent->id()), parent)
+ << "Node " << parent->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_parent = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent);
+ ASSERT_TRUE(NodesMatch(parent->GetChild(index), v_parent->GetChild(index)));
+ GetVerifierBookmarkModel()->Remove(v_parent, index);
+ }
+ GetBookmarkModel(profile)->Remove(parent, index);
+}
+
+void SortChildren(int profile, const BookmarkNode* parent) {
+ ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(parent->id()), parent)
+ << "Node " << parent->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ if (test()->use_verifier()) {
+ const BookmarkNode* v_parent = NULL;
+ FindNodeInVerifier(GetBookmarkModel(profile), parent, &v_parent);
+ GetVerifierBookmarkModel()->SortChildren(v_parent);
+ }
+ GetBookmarkModel(profile)->SortChildren(parent);
+}
+
+void ReverseChildOrder(int profile, const BookmarkNode* parent) {
+ ASSERT_EQ(GetBookmarkModel(profile)->GetNodeByID(parent->id()), parent)
+ << "Node " << parent->GetTitle() << " does not belong to "
+ << "Profile " << profile;
+ int child_count = parent->child_count();
+ if (child_count <= 0)
+ return;
+ for (int index = 0; index < child_count; ++index) {
+ Move(profile, parent->GetChild(index), parent, child_count - index);
+ }
+}
+
+bool ModelMatchesVerifier(int profile) {
+ if (!test()->use_verifier()) {
+ LOG(ERROR) << "Illegal to call ModelMatchesVerifier() after "
+ << "DisableVerifier(). Use ModelsMatch() instead.";
+ return false;
+ }
+ return BookmarkModelsMatch(GetVerifierBookmarkModel(),
+ GetBookmarkModel(profile));
+}
+
+bool AllModelsMatchVerifier() {
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (!ModelMatchesVerifier(i)) {
+ LOG(ERROR) << "Model " << i << " does not match the verifier.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ModelsMatch(int profile_a, int profile_b) {
+ return BookmarkModelsMatch(GetBookmarkModel(profile_a),
+ GetBookmarkModel(profile_b));
+}
+
+bool AllModelsMatch() {
+ for (int i = 1; i < test()->num_clients(); ++i) {
+ if (!ModelsMatch(0, i)) {
+ LOG(ERROR) << "Model " << i << " does not match Model 0.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ContainsDuplicateBookmarks(int profile) {
+ ui::TreeNodeIterator<const BookmarkNode> iterator(
+ GetBookmarkModel(profile)->root_node());
+ while (iterator.has_next()) {
+ const BookmarkNode* node = iterator.Next();
+ if (node->is_folder())
+ continue;
+ std::vector<const BookmarkNode*> nodes;
+ GetBookmarkModel(profile)->GetNodesByURL(node->url(), &nodes);
+ EXPECT_TRUE(nodes.size() >= 1);
+ for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin();
+ it != nodes.end(); ++it) {
+ if (node->id() != (*it)->id() &&
+ node->parent() == (*it)->parent() &&
+ node->GetTitle() == (*it)->GetTitle()){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+const BookmarkNode* GetUniqueNodeByURL(int profile, const GURL& url) {
+ std::vector<const BookmarkNode*> nodes;
+ GetBookmarkModel(profile)->GetNodesByURL(url, &nodes);
+ EXPECT_EQ(1U, nodes.size());
+ if (nodes.empty())
+ return NULL;
+ return nodes[0];
+}
+
+int CountBookmarksWithTitlesMatching(int profile, const std::wstring& title) {
+ return CountNodesWithTitlesMatching(GetBookmarkModel(profile),
+ BookmarkNode::URL,
+ WideToUTF16(title));
+}
+
+int CountFoldersWithTitlesMatching(int profile, const std::wstring& title) {
+ return CountNodesWithTitlesMatching(GetBookmarkModel(profile),
+ BookmarkNode::FOLDER,
+ WideToUTF16(title));
+}
+
+std::vector<unsigned char> CreateFavicon(int seed) {
+ const int w = 16;
+ const int h = 16;
+ SkBitmap bmp;
+ bmp.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bmp.allocPixels();
+ uint32_t* src_data = bmp.getAddr32(0, 0);
+ for (int i = 0; i < w * h; ++i) {
+ src_data[i] = SkPreMultiplyARGB((seed + i) % 255,
+ (seed + i) % 250,
+ (seed + i) % 245,
+ (seed + i) % 240);
+ }
+ std::vector<unsigned char> favicon;
+ gfx::PNGCodec::EncodeBGRASkBitmap(bmp, false, &favicon);
+ return favicon;
+}
+
+std::string IndexedURL(int i) {
+ return StringPrintf("http://www.host.ext:1234/path/filename/%d", i);
+}
+
+std::wstring IndexedURLTitle(int i) {
+ return StringPrintf(L"URL Title %d", i);
+}
+
+std::wstring IndexedFolderName(int i) {
+ return StringPrintf(L"Folder Name %d", i);
+}
+
+std::wstring IndexedSubfolderName(int i) {
+ return StringPrintf(L"Subfolder Name %d", i);
+}
+
+std::wstring IndexedSubsubfolderName(int i) {
+ return StringPrintf(L"Subsubfolder Name %d", i);
+}
+
+} // namespace bookmarks_helper
diff --git a/chrome/browser/sync/test/live_sync/bookmarks_helper.h b/chrome/browser/sync/test/live_sync/bookmarks_helper.h
new file mode 100644
index 0000000..8de894d
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/bookmarks_helper.h
@@ -0,0 +1,184 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_BOOKMARKS_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_BOOKMARKS_HELPER_H_
+#pragma once
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+class GURL;
+class Profile;
+
+namespace bookmarks_helper {
+
+// Used to access the bookmark model within a particular sync profile.
+BookmarkModel* GetBookmarkModel(int index) WARN_UNUSED_RESULT;
+
+// Used to access the bookmark bar within a particular sync profile.
+const BookmarkNode* GetBookmarkBarNode(int index) WARN_UNUSED_RESULT;
+
+// Used to access the "other bookmarks" node within a particular sync profile.
+const BookmarkNode* GetOtherNode(int index) WARN_UNUSED_RESULT;
+
+// Used to access the bookmarks within the verifier sync profile.
+BookmarkModel* GetVerifierBookmarkModel() WARN_UNUSED_RESULT;
+
+// Adds a URL with address |url| and title |title| to the bookmark bar of
+// profile |profile|. Returns a pointer to the node that was added.
+const BookmarkNode* AddURL(
+ int profile,
+ const std::wstring& title,
+ const GURL& url) WARN_UNUSED_RESULT;
+
+// Adds a URL with address |url| and title |title| to the bookmark bar of
+// profile |profile| at position |index|. Returns a pointer to the node that
+// was added.
+const BookmarkNode* AddURL(
+ int profile,
+ int index,
+ const std::wstring& title,
+ const GURL& url) WARN_UNUSED_RESULT;
+
+// Adds a URL with address |url| and title |title| under the node |parent| of
+// profile |profile| at position |index|. Returns a pointer to the node that
+// was added.
+const BookmarkNode* AddURL(
+ int profile,
+ const BookmarkNode* parent,
+ int index,
+ const std::wstring& title,
+ const GURL& url) WARN_UNUSED_RESULT;
+
+// Adds a folder named |title| to the bookmark bar of profile |profile|.
+// Returns a pointer to the folder that was added.
+const BookmarkNode* AddFolder(
+ int profile,
+ const std::wstring& title) WARN_UNUSED_RESULT;
+
+// Adds a folder named |title| to the bookmark bar of profile |profile| at
+// position |index|. Returns a pointer to the folder that was added.
+const BookmarkNode* AddFolder(
+ int profile,
+ int index,
+ const std::wstring& title) WARN_UNUSED_RESULT;
+
+// Adds a folder named |title| to the node |parent| in the bookmark model of
+// profile |profile| at position |index|. Returns a pointer to the node that
+// was added.
+const BookmarkNode* AddFolder(
+ int profile,
+ const BookmarkNode* parent,
+ int index,
+ const std::wstring& title) WARN_UNUSED_RESULT;
+
+// Changes the title of the node |node| in the bookmark model of profile
+// |profile| to |new_title|.
+void SetTitle(int profile,
+ const BookmarkNode* node,
+ const std::wstring& new_title);
+
+// Sets the favicon of the node |node| (of type BookmarkNode::URL) in the
+// bookmark model of profile |profile| using the data in |icon_bytes_vector|.
+void SetFavicon(
+ int profile,
+ const BookmarkNode* node,
+ const std::vector<unsigned char>& icon_bytes_vector);
+
+// Changes the url of the node |node| in the bookmark model of profile
+// |profile| to |new_url|. Returns a pointer to the node with the changed url.
+const BookmarkNode* SetURL(
+ int profile,
+ const BookmarkNode* node,
+ const GURL& new_url) WARN_UNUSED_RESULT;
+
+// Moves the node |node| in the bookmark model of profile |profile| so it ends
+// up under the node |new_parent| at position |index|.
+void Move(
+ int profile,
+ const BookmarkNode* node,
+ const BookmarkNode* new_parent,
+ int index);
+
+// Removes the node in the bookmark model of profile |profile| under the node
+// |parent| at position |index|.
+void Remove(int profile, const BookmarkNode* parent, int index);
+
+// Sorts the children of the node |parent| in the bookmark model of profile
+// |profile|.
+void SortChildren(int profile, const BookmarkNode* parent);
+
+// Reverses the order of the children of the node |parent| in the bookmark
+// model of profile |profile|.
+void ReverseChildOrder(int profile, const BookmarkNode* parent);
+
+// Checks if the bookmark model of profile |profile| matches the verifier
+// bookmark model. Returns true if they match.
+bool ModelMatchesVerifier(int profile) WARN_UNUSED_RESULT;
+
+// Checks if the bookmark models of all sync profiles match the verifier
+// bookmark model. Returns true if they match.
+bool AllModelsMatchVerifier() WARN_UNUSED_RESULT;
+
+// Checks if the bookmark models of |profile_a| and |profile_b| match each
+// other. Returns true if they match.
+bool ModelsMatch(int profile_a, int profile_b) WARN_UNUSED_RESULT;
+
+// Checks if the bookmark models of all sync profiles match each other. Does
+// not compare them with the verifier bookmark model. Returns true if they
+// match.
+bool AllModelsMatch() WARN_UNUSED_RESULT;
+
+// Checks if the bookmark model of profile |profile| contains any instances of
+// two bookmarks with the same URL under the same parent folder. Returns true
+// if even one instance is found.
+bool ContainsDuplicateBookmarks(int profile);
+
+// Gets the node in the bookmark model of profile |profile| that has the url
+// |url|. Note: Only one instance of |url| is assumed to be present.
+const BookmarkNode* GetUniqueNodeByURL(
+ int profile,
+ const GURL& url) WARN_UNUSED_RESULT;
+
+// Returns the number of bookmarks in bookmark model of profile |profile|
+// whose titles match the string |title|.
+int CountBookmarksWithTitlesMatching(
+ int profile,
+ const std::wstring& title) WARN_UNUSED_RESULT;
+
+// Returns the number of bookmark folders in the bookmark model of profile
+// |profile| whose titles contain the query string |title|.
+int CountFoldersWithTitlesMatching(
+ int profile,
+ const std::wstring& title) WARN_UNUSED_RESULT;
+
+// Creates a unique favicon using |seed|.
+std::vector<unsigned char> CreateFavicon(int seed);
+
+// Returns a URL identifiable by |i|.
+std::string IndexedURL(int i);
+
+// Returns a URL title identifiable by |i|.
+std::wstring IndexedURLTitle(int i);
+
+// Returns a folder name identifiable by |i|.
+std::wstring IndexedFolderName(int i);
+
+// Returns a subfolder name identifiable by |i|.
+std::wstring IndexedSubfolderName(int i);
+
+// Returns a subsubfolder name identifiable by |i|.
+std::wstring IndexedSubsubfolderName(int i);
+
+} // namespace bookmarks_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_BOOKMARKS_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/extensions_helper.cc b/chrome/browser/sync/test/live_sync/extensions_helper.cc
new file mode 100644
index 0000000..6bd9a20
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/extensions_helper.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/extensions_helper.h"
+
+#include <cstring>
+
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/browser/sync/test/live_sync/sync_extension_helper.h"
+#include "chrome/common/extensions/extension.h"
+
+using sync_datatype_helper::test;
+
+namespace extensions_helper {
+
+const char extension_name_prefix[] = "fakeextension";
+
+bool HasSameExtensionsAsVerifier(int index) {
+ return SyncExtensionHelper::GetInstance()->ExtensionStatesMatch(
+ test()->GetProfile(index), test()->verifier());
+}
+
+bool AllProfilesHaveSameExtensionsAsVerifier() {
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (!HasSameExtensionsAsVerifier(i)) {
+ LOG(ERROR) << "Profile " << i << " doesn't have the same extensions as"
+ " the verifier profile.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AllProfilesHaveSameExtensions() {
+ for (int i = 1; i < test()->num_clients(); ++i) {
+ if (!SyncExtensionHelper::GetInstance()->ExtensionStatesMatch(
+ test()->GetProfile(0), test()->GetProfile(i))) {
+ LOG(ERROR) << "Profile " << i << " doesnt have the same extensions as"
+ " profile 0.";
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void InstallExtension(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->InstallExtension(
+ profile, CreateFakeExtensionName(index), Extension::TYPE_EXTENSION);
+}
+
+void UninstallExtension(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->UninstallExtension(
+ profile, CreateFakeExtensionName(index));
+}
+
+std::vector<int> GetInstalledExtensions(Profile* profile) {
+ std::vector<int> indices;
+ std::vector<std::string> names =
+ SyncExtensionHelper::GetInstance()->GetInstalledExtensionNames(profile);
+ for (std::vector<std::string>::const_iterator it = names.begin();
+ it != names.end(); ++it) {
+ int index;
+ if (ExtensionNameToIndex(*it, &index)) {
+ indices.push_back(index);
+ }
+ }
+ return indices;
+}
+
+void EnableExtension(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->EnableExtension(
+ profile, CreateFakeExtensionName(index));
+}
+
+void DisableExtension(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->DisableExtension(
+ profile, CreateFakeExtensionName(index));
+}
+
+bool IsExtensionEnabled(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->IsExtensionEnabled(
+ profile, CreateFakeExtensionName(index));
+}
+
+void IncognitoEnableExtension(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->IncognitoEnableExtension(
+ profile, CreateFakeExtensionName(index));
+}
+
+void IncognitoDisableExtension(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->IncognitoDisableExtension(
+ profile, CreateFakeExtensionName(index));
+}
+
+bool IsIncognitoEnabled(Profile* profile, int index) {
+ return SyncExtensionHelper::GetInstance()->IsIncognitoEnabled(
+ profile, CreateFakeExtensionName(index));
+}
+
+void InstallExtensionsPendingForSync(Profile* profile) {
+ SyncExtensionHelper::GetInstance()->InstallExtensionsPendingForSync(
+ profile, Extension::TYPE_EXTENSION);
+}
+
+std::string CreateFakeExtensionName(int index) {
+ return extension_name_prefix + base::IntToString(index);
+}
+
+bool ExtensionNameToIndex(const std::string& name, int* index) {
+ if (!StartsWithASCII(name, extension_name_prefix, true) ||
+ !base::StringToInt(name.substr(strlen(extension_name_prefix)), index)) {
+ LOG(WARNING) << "Unable to convert extension name \"" << name
+ << "\" to index";
+ return false;
+ }
+ return true;
+}
+
+} // namespace extensions_helper
diff --git a/chrome/browser/sync/test/live_sync/extensions_helper.h b/chrome/browser/sync/test/live_sync/extensions_helper.h
new file mode 100644
index 0000000..7103bca
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/extensions_helper.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_EXTENSIONS_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_EXTENSIONS_HELPER_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+class Profile;
+
+namespace extensions_helper {
+
+// Returns true iff the profile with index |index| has the same extensions
+// as the verifier.
+bool HasSameExtensionsAsVerifier(int index) WARN_UNUSED_RESULT;
+
+// Returns true iff all existing profiles have the same extensions
+// as the verifier.
+bool AllProfilesHaveSameExtensionsAsVerifier() WARN_UNUSED_RESULT;
+
+// Returns true iff all existing profiles have the same extensions.
+bool AllProfilesHaveSameExtensions() WARN_UNUSED_RESULT;
+
+// Installs the extension for the given index to |profile|.
+void InstallExtension(Profile* profile, int index);
+
+// Uninstalls the extension for the given index from |profile|. Assumes that
+// it was previously installed.
+void UninstallExtension(Profile* profile, int index);
+
+// Returns a vector containing the indices of all currently installed
+// test extensions on |profile|.
+std::vector<int> GetInstalledExtensions(Profile* profile);
+
+// Installs all pending synced extensions for |profile|.
+void InstallExtensionsPendingForSync(Profile* profile);
+
+// Enables the extension for the given index on |profile|.
+void EnableExtension(Profile* profile, int index);
+
+// Disables the extension for the given index on |profile|.
+void DisableExtension(Profile* profile, int index);
+
+// Returns true if the extension with index |index| is enabled on |profile|.
+bool IsExtensionEnabled(Profile* profile, int index);
+
+// Enables the extension for the given index in incognito mode on |profile|.
+void IncognitoEnableExtension(Profile* profile, int index);
+
+// Disables the extension for the given index in incognito mode on |profile|.
+void IncognitoDisableExtension(Profile* profile, int index);
+
+// Returns true if the extension with index |index| is enabled in incognito
+// mode on |profile|.
+bool IsIncognitoEnabled(Profile* profile, int index);
+
+// Returns a unique extension name based in the integer |index|.
+std::string CreateFakeExtensionName(int index);
+
+// Converts a fake extension name back into the index used to generate it.
+// Returns true if successful, false on failure.
+bool ExtensionNameToIndex(const std::string& name, int* index);
+
+} // namespace extensions_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_EXTENSIONS_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/live_sync_test.cc b/chrome/browser/sync/test/live_sync/live_sync_test.cc
new file mode 100644
index 0000000..d47f34e
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/live_sync_test.cc
@@ -0,0 +1,632 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/stringprintf.h"
+#include "base/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/password_manager/encryptor.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/sync/notifier/p2p_notifier.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/common/url_fetcher.h"
+#include "content/test/test_url_fetcher_factory.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+#include "net/base/network_change_notifier.h"
+#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_config_service_fixed.h"
+#include "net/proxy/proxy_service.h"
+#include "net/test/test_server.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+
+namespace switches {
+const char kPasswordFileForTest[] = "password-file-for-test";
+const char kSyncUserForTest[] = "sync-user-for-test";
+const char kSyncPasswordForTest[] = "sync-password-for-test";
+const char kSyncServerCommandLine[] = "sync-server-command-line";
+}
+
+namespace {
+// The URLs for different calls in the Google Accounts programmatic login API.
+const char kClientLoginUrl[] = "https://www.google.com/accounts/ClientLogin";
+const char kGetUserInfoUrl[] = "https://www.google.com/accounts/GetUserInfo";
+const char kIssueAuthTokenUrl[] =
+ "https://www.google.com/accounts/IssueAuthToken";
+const char kSearchDomainCheckUrl[] =
+ "https://www.google.com/searchdomaincheck?format=domain&type=chrome";
+}
+
+// Helper class that checks whether a sync test server is running or not.
+class SyncServerStatusChecker : public URLFetcher::Delegate {
+ public:
+ SyncServerStatusChecker() : running_(false) {}
+
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ running_ = (status.status() == net::URLRequestStatus::SUCCESS &&
+ response_code == 200 && data.find("ok") == 0);
+ MessageLoop::current()->Quit();
+ }
+
+ bool running() const { return running_; }
+
+ private:
+ bool running_;
+};
+
+class SetProxyConfigTask : public Task {
+ public:
+ SetProxyConfigTask(base::WaitableEvent* done,
+ net::URLRequestContextGetter* url_request_context_getter,
+ const net::ProxyConfig& proxy_config)
+ : done_(done),
+ url_request_context_getter_(url_request_context_getter),
+ proxy_config_(proxy_config) {
+ }
+
+ void Run() {
+ net::ProxyService* proxy_service =
+ url_request_context_getter_->GetURLRequestContext()->proxy_service();
+ proxy_service->ResetConfigService(
+ new net::ProxyConfigServiceFixed(proxy_config_));
+ done_->Signal();
+ }
+
+ private:
+ base::WaitableEvent* done_;
+ net::URLRequestContextGetter* url_request_context_getter_;
+ net::ProxyConfig proxy_config_;
+};
+
+LiveSyncTest::LiveSyncTest(TestType test_type)
+ : sync_server_(net::TestServer::TYPE_SYNC, FilePath()),
+ test_type_(test_type),
+ server_type_(SERVER_TYPE_UNDECIDED),
+ num_clients_(-1),
+ use_verifier_(true),
+ test_server_handle_(base::kNullProcessHandle) {
+ InProcessBrowserTest::set_show_window(true);
+ sync_datatype_helper::AssociateWithTest(this);
+ switch (test_type_) {
+ case SINGLE_CLIENT: {
+ num_clients_ = 1;
+ break;
+ }
+ case TWO_CLIENT: {
+ num_clients_ = 2;
+ break;
+ }
+ case MULTIPLE_CLIENT: {
+ num_clients_ = 3;
+ break;
+ }
+ case MANY_CLIENT: {
+ num_clients_ = 10;
+ break;
+ }
+ }
+}
+
+LiveSyncTest::~LiveSyncTest() {}
+
+void LiveSyncTest::SetUp() {
+ CommandLine* cl = CommandLine::ForCurrentProcess();
+ if (cl->HasSwitch(switches::kPasswordFileForTest)) {
+ ReadPasswordFile();
+ } else if (cl->HasSwitch(switches::kSyncUserForTest) &&
+ cl->HasSwitch(switches::kSyncPasswordForTest)) {
+ username_ = cl->GetSwitchValueASCII(switches::kSyncUserForTest);
+ password_ = cl->GetSwitchValueASCII(switches::kSyncPasswordForTest);
+ } else {
+ SetupMockGaiaResponses();
+ }
+
+ if (!cl->HasSwitch(switches::kSyncServiceURL) &&
+ !cl->HasSwitch(switches::kSyncServerCommandLine)) {
+ // If neither a sync server URL nor a sync server command line is
+ // provided, start up a local python sync test server and point Chrome
+ // to its URL. This is the most common configuration, and the only
+ // one that makes sense for most developers.
+ server_type_ = LOCAL_PYTHON_SERVER;
+ } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
+ cl->HasSwitch(switches::kSyncServerCommandLine)) {
+ // If a sync server URL and a sync server command line are provided,
+ // start up a local sync server by running the command line. Chrome
+ // will connect to the server at the URL that was provided.
+ server_type_ = LOCAL_LIVE_SERVER;
+ } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
+ !cl->HasSwitch(switches::kSyncServerCommandLine)) {
+ // If a sync server URL is provided, but not a server command line,
+ // it is assumed that the server is already running. Chrome will
+ // automatically connect to it at the URL provided. There is nothing
+ // to do here.
+ server_type_ = EXTERNAL_LIVE_SERVER;
+ } else {
+ // If a sync server command line is provided, but not a server URL,
+ // we flag an error.
+ LOG(FATAL) << "Can't figure out how to run a server.";
+ }
+
+ if (username_.empty() || password_.empty())
+ LOG(FATAL) << "Cannot run sync tests without GAIA credentials.";
+
+ // Mock the Mac Keychain service. The real Keychain can block on user input.
+#if defined(OS_MACOSX)
+ Encryptor::UseMockKeychain(true);
+#endif
+
+ // Yield control back to the InProcessBrowserTest framework.
+ InProcessBrowserTest::SetUp();
+}
+
+void LiveSyncTest::TearDown() {
+ // Allow the InProcessBrowserTest framework to perform its tear down.
+ InProcessBrowserTest::TearDown();
+
+ // Stop the local python test server. This is a no-op if one wasn't started.
+ TearDownLocalPythonTestServer();
+
+ // Stop the local sync test server. This is a no-op if one wasn't started.
+ TearDownLocalTestServer();
+}
+
+void LiveSyncTest::SetUpCommandLine(CommandLine* cl) {
+ AddTestSwitches(cl);
+ AddOptionalTypesToCommandLine(cl);
+}
+
+void LiveSyncTest::AddTestSwitches(CommandLine* cl) {
+ // TODO(rsimha): Until we implement a fake Tango server against which tests
+ // can run, we need to set the --sync-notification-method to "p2p".
+ if (!cl->HasSwitch(switches::kSyncNotificationMethod))
+ cl->AppendSwitchASCII(switches::kSyncNotificationMethod, "p2p");
+
+ // Disable non-essential access of external network resources.
+ if (!cl->HasSwitch(switches::kDisableBackgroundNetworking))
+ cl->AppendSwitch(switches::kDisableBackgroundNetworking);
+}
+
+void LiveSyncTest::AddOptionalTypesToCommandLine(CommandLine* cl) {
+ // TODO(sync): Remove this once sessions sync is enabled by default.
+ if (!cl->HasSwitch(switches::kEnableSyncSessions))
+ cl->AppendSwitch(switches::kEnableSyncSessions);
+}
+
+// static
+Profile* LiveSyncTest::MakeProfile(const FilePath::StringType name) {
+ FilePath path;
+ PathService::Get(chrome::DIR_USER_DATA, &path);
+ path = path.Append(name);
+
+ if (!file_util::PathExists(path))
+ CHECK(file_util::CreateDirectory(path));
+
+ return Profile::CreateProfile(path);
+}
+
+Profile* LiveSyncTest::GetProfile(int index) {
+ if (profiles_.empty())
+ LOG(FATAL) << "SetupClients() has not yet been called.";
+ if (index < 0 || index >= static_cast<int>(profiles_.size()))
+ LOG(FATAL) << "GetProfile(): Index is out of bounds.";
+ return profiles_[index];
+}
+
+Browser* LiveSyncTest::GetBrowser(int index) {
+ if (browsers_.empty())
+ LOG(FATAL) << "SetupClients() has not yet been called.";
+ if (index < 0 || index >= static_cast<int>(browsers_.size()))
+ LOG(FATAL) << "GetBrowser(): Index is out of bounds.";
+ return browsers_[index];
+}
+
+ProfileSyncServiceHarness* LiveSyncTest::GetClient(int index) {
+ if (clients_.empty())
+ LOG(FATAL) << "SetupClients() has not yet been called.";
+ if (index < 0 || index >= static_cast<int>(clients_.size()))
+ LOG(FATAL) << "GetClient(): Index is out of bounds.";
+ return clients_[index];
+}
+
+Profile* LiveSyncTest::verifier() {
+ if (verifier_.get() == NULL)
+ LOG(FATAL) << "SetupClients() has not yet been called.";
+ return verifier_.get();
+}
+
+void LiveSyncTest::DisableVerifier() {
+ use_verifier_ = false;
+}
+
+bool LiveSyncTest::SetupClients() {
+ if (num_clients_ <= 0)
+ LOG(FATAL) << "num_clients_ incorrectly initialized.";
+ if (!profiles_.empty() || !browsers_.empty() || !clients_.empty())
+ LOG(FATAL) << "SetupClients() has already been called.";
+
+ // Start up a sync test server if one is needed.
+ SetUpTestServerIfRequired();
+
+ // Create the required number of sync profiles, browsers and clients.
+ for (int i = 0; i < num_clients_; ++i) {
+ profiles_.push_back(MakeProfile(
+ base::StringPrintf(FILE_PATH_LITERAL("Profile%d"), i)));
+ EXPECT_FALSE(GetProfile(i) == NULL) << "GetProfile(" << i << ") failed.";
+
+ browsers_.push_back(Browser::Create(GetProfile(i)));
+ EXPECT_FALSE(GetBrowser(i) == NULL) << "GetBrowser(" << i << ") failed.";
+
+ clients_.push_back(
+ new ProfileSyncServiceHarness(GetProfile(i), username_, password_));
+ EXPECT_FALSE(GetClient(i) == NULL) << "GetClient(" << i << ") failed.";
+
+ ui_test_utils::WaitForBookmarkModelToLoad(
+ GetProfile(i)->GetBookmarkModel());
+ }
+
+ // Create the verifier profile.
+ verifier_.reset(MakeProfile(FILE_PATH_LITERAL("Verifier")));
+ ui_test_utils::WaitForBookmarkModelToLoad(verifier()->GetBookmarkModel());
+ return (verifier_.get() != NULL);
+}
+
+bool LiveSyncTest::SetupSync() {
+ // Create sync profiles and clients if they haven't already been created.
+ if (profiles_.empty()) {
+ if (!SetupClients())
+ LOG(FATAL) << "SetupClients() failed.";
+ }
+
+ // Sync each of the profiles.
+ for (int i = 0; i < num_clients_; ++i) {
+ if (!GetClient(i)->SetupSync())
+ LOG(FATAL) << "SetupSync() failed.";
+ }
+
+ return true;
+}
+
+void LiveSyncTest::CleanUpOnMainThread() {
+ // Close all browser windows.
+ BrowserList::CloseAllBrowsers();
+ ui_test_utils::RunAllPendingInMessageLoop();
+
+ // All browsers should be closed at this point, or else we could see memory
+ // corruption in QuitBrowser().
+ CHECK_EQ(0U, BrowserList::size());
+
+ profiles_.reset();
+ clients_.reset();
+ verifier_.reset(NULL);
+}
+
+void LiveSyncTest::SetUpInProcessBrowserTestFixture() {
+ // We don't take a reference to |resolver|, but mock_host_resolver_override_
+ // does, so effectively assumes ownership.
+ net::RuleBasedHostResolverProc* resolver =
+ new net::RuleBasedHostResolverProc(host_resolver());
+ resolver->AllowDirectLookup("*.google.com");
+ // On Linux, we use Chromium's NSS implementation which uses the following
+ // hosts for certificate verification. Without these overrides, running the
+ // integration tests on Linux causes error as we make external DNS lookups.
+ resolver->AllowDirectLookup("*.thawte.com");
+ resolver->AllowDirectLookup("*.geotrust.com");
+ resolver->AllowDirectLookup("*.gstatic.com");
+ mock_host_resolver_override_.reset(
+ new net::ScopedDefaultHostResolverProc(resolver));
+}
+
+void LiveSyncTest::TearDownInProcessBrowserTestFixture() {
+ mock_host_resolver_override_.reset();
+}
+
+void LiveSyncTest::ReadPasswordFile() {
+ CommandLine* cl = CommandLine::ForCurrentProcess();
+ password_file_ = cl->GetSwitchValuePath(switches::kPasswordFileForTest);
+ if (password_file_.empty())
+ LOG(FATAL) << "Can't run live server test without specifying --"
+ << switches::kPasswordFileForTest << "=<filename>";
+ std::string file_contents;
+ file_util::ReadFileToString(password_file_, &file_contents);
+ ASSERT_NE(file_contents, "") << "Password file \""
+ << password_file_.value() << "\" does not exist.";
+ std::vector<std::string> tokens;
+ std::string delimiters = "\r\n";
+ Tokenize(file_contents, delimiters, &tokens);
+ ASSERT_EQ(2U, tokens.size()) << "Password file \""
+ << password_file_.value()
+ << "\" must contain exactly two lines of text.";
+ username_ = tokens[0];
+ password_ = tokens[1];
+}
+
+void LiveSyncTest::SetupMockGaiaResponses() {
+ username_ = "user@gmail.com";
+ password_ = "password";
+ factory_.reset(new URLFetcherFactory());
+ fake_factory_.reset(new FakeURLFetcherFactory(factory_.get()));
+ fake_factory_->SetFakeResponse(kClientLoginUrl, "SID=sid\nLSID=lsid", true);
+ fake_factory_->SetFakeResponse(kGetUserInfoUrl, "email=user@gmail.com", true);
+ fake_factory_->SetFakeResponse(kIssueAuthTokenUrl, "auth", true);
+ fake_factory_->SetFakeResponse(kSearchDomainCheckUrl, ".google.com", true);
+}
+
+// Start up a local sync server based on the value of server_type_, which
+// was determined from the command line parameters.
+void LiveSyncTest::SetUpTestServerIfRequired() {
+ if (server_type_ == LOCAL_PYTHON_SERVER) {
+ if (!SetUpLocalPythonTestServer())
+ LOG(FATAL) << "Failed to set up local python sync and XMPP servers";
+ } else if (server_type_ == LOCAL_LIVE_SERVER) {
+ // Using mock gaia credentials requires the use of a mock XMPP server.
+ if (username_ == "user@gmail.com" && !SetUpLocalPythonTestServer())
+ LOG(FATAL) << "Failed to set up local python XMPP server";
+ if (!SetUpLocalTestServer())
+ LOG(FATAL) << "Failed to set up local test server";
+ } else if (server_type_ == EXTERNAL_LIVE_SERVER) {
+ // Nothing to do; we'll just talk to the URL we were given.
+ } else {
+ LOG(FATAL) << "Don't know which server environment to run test in.";
+ }
+}
+
+bool LiveSyncTest::SetUpLocalPythonTestServer() {
+ EXPECT_TRUE(sync_server_.Start())
+ << "Could not launch local python test server.";
+
+ CommandLine* cl = CommandLine::ForCurrentProcess();
+ if (server_type_ == LOCAL_PYTHON_SERVER) {
+ std::string sync_service_url = sync_server_.GetURL("chromiumsync").spec();
+ cl->AppendSwitchASCII(switches::kSyncServiceURL, sync_service_url);
+ VLOG(1) << "Started local python sync server at " << sync_service_url;
+ }
+
+ int xmpp_port = 0;
+ if (!sync_server_.server_data().GetInteger("xmpp_port", &xmpp_port)) {
+ LOG(ERROR) << "Could not find valid xmpp_port value";
+ return false;
+ }
+ if ((xmpp_port <= 0) || (xmpp_port > kuint16max)) {
+ LOG(ERROR) << "Invalid xmpp port: " << xmpp_port;
+ return false;
+ }
+
+ net::HostPortPair xmpp_host_port_pair(sync_server_.host_port_pair());
+ xmpp_host_port_pair.set_port(xmpp_port);
+ xmpp_port_.reset(new net::ScopedPortException(xmpp_port));
+
+ if (!cl->HasSwitch(switches::kSyncNotificationHost)) {
+ cl->AppendSwitchASCII(switches::kSyncNotificationHost,
+ xmpp_host_port_pair.ToString());
+ // The local XMPP server only supports insecure connections.
+ cl->AppendSwitch(switches::kSyncAllowInsecureXmppConnection);
+ }
+ VLOG(1) << "Started local python XMPP server at "
+ << xmpp_host_port_pair.ToString();
+
+ return true;
+}
+
+bool LiveSyncTest::SetUpLocalTestServer() {
+ CommandLine* cl = CommandLine::ForCurrentProcess();
+ CommandLine::StringType server_cmdline_string = cl->GetSwitchValueNative(
+ switches::kSyncServerCommandLine);
+ CommandLine::StringVector server_cmdline_vector;
+ CommandLine::StringType delimiters(FILE_PATH_LITERAL(" "));
+ Tokenize(server_cmdline_string, delimiters, &server_cmdline_vector);
+ CommandLine server_cmdline(server_cmdline_vector);
+ base::LaunchOptions options;
+#if defined(OS_WIN)
+ options.start_hidden = true;
+#endif
+ if (!base::LaunchProcess(server_cmdline, options, &test_server_handle_))
+ LOG(ERROR) << "Could not launch local test server.";
+
+ const int kMaxWaitTime = TestTimeouts::action_max_timeout_ms();
+ const int kNumIntervals = 15;
+ if (WaitForTestServerToStart(kMaxWaitTime, kNumIntervals)) {
+ VLOG(1) << "Started local test server at "
+ << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << ".";
+ return true;
+ } else {
+ LOG(ERROR) << "Could not start local test server at "
+ << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << ".";
+ return false;
+ }
+}
+
+bool LiveSyncTest::TearDownLocalPythonTestServer() {
+ if (!sync_server_.Stop()) {
+ LOG(ERROR) << "Could not stop local python test server.";
+ return false;
+ }
+ xmpp_port_.reset();
+ return true;
+}
+
+bool LiveSyncTest::TearDownLocalTestServer() {
+ if (test_server_handle_ != base::kNullProcessHandle) {
+ EXPECT_TRUE(base::KillProcess(test_server_handle_, 0, false))
+ << "Could not stop local test server.";
+ base::CloseProcessHandle(test_server_handle_);
+ test_server_handle_ = base::kNullProcessHandle;
+ }
+ return true;
+}
+
+bool LiveSyncTest::WaitForTestServerToStart(int time_ms, int intervals) {
+ for (int i = 0; i < intervals; ++i) {
+ if (IsTestServerRunning())
+ return true;
+ base::PlatformThread::Sleep(time_ms / intervals);
+ }
+ return false;
+}
+
+bool LiveSyncTest::IsTestServerRunning() {
+ CommandLine* cl = CommandLine::ForCurrentProcess();
+ std::string sync_url = cl->GetSwitchValueASCII(switches::kSyncServiceURL);
+ GURL sync_url_status(sync_url.append("/healthz"));
+ SyncServerStatusChecker delegate;
+ URLFetcher fetcher(sync_url_status, URLFetcher::GET, &delegate);
+ fetcher.set_request_context(Profile::Deprecated::GetDefaultRequestContext());
+ fetcher.Start();
+ ui_test_utils::RunMessageLoop();
+ return delegate.running();
+}
+
+void LiveSyncTest::EnableNetwork(Profile* profile) {
+ SetProxyConfig(profile->GetRequestContext(),
+ net::ProxyConfig::CreateDirect());
+ // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed.
+ net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+}
+
+void LiveSyncTest::DisableNetwork(Profile* profile) {
+ // Set the current proxy configuration to a nonexistent proxy to effectively
+ // disable networking.
+ net::ProxyConfig config;
+ config.proxy_rules().ParseFromString("http=127.0.0.1:0");
+ SetProxyConfig(profile->GetRequestContext(), config);
+ // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed.
+ net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+}
+
+bool LiveSyncTest::EnableEncryption(int index, syncable::ModelType type) {
+ return GetClient(index)->EnableEncryptionForType(type);
+}
+
+bool LiveSyncTest::IsEncrypted(int index, syncable::ModelType type) {
+ return GetClient(index)->IsTypeEncrypted(type);
+}
+
+bool LiveSyncTest::AwaitQuiescence() {
+ return ProfileSyncServiceHarness::AwaitQuiescence(clients());
+}
+
+bool LiveSyncTest::ServerSupportsNotificationControl() const {
+ EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_);
+
+ // Supported only if we're using the python testserver.
+ return server_type_ == LOCAL_PYTHON_SERVER;
+}
+
+void LiveSyncTest::DisableNotifications() {
+ ASSERT_TRUE(ServerSupportsNotificationControl());
+ std::string path = "chromiumsync/disablenotifications";
+ ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
+ ASSERT_EQ("Notifications disabled",
+ UTF16ToASCII(browser()->GetSelectedTabContents()->GetTitle()));
+}
+
+void LiveSyncTest::EnableNotifications() {
+ ASSERT_TRUE(ServerSupportsNotificationControl());
+ std::string path = "chromiumsync/enablenotifications";
+ ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
+ ASSERT_EQ("Notifications enabled",
+ UTF16ToASCII(browser()->GetSelectedTabContents()->GetTitle()));
+}
+
+void LiveSyncTest::TriggerNotification(
+ const syncable::ModelTypeSet& changed_types) {
+ ASSERT_TRUE(ServerSupportsNotificationControl());
+ const std::string& data =
+ sync_notifier::P2PNotificationData("from_server",
+ sync_notifier::NOTIFY_ALL,
+ changed_types).ToString();
+ const std::string& path =
+ std::string("chromiumsync/sendnotification?channel=") +
+ sync_notifier::kSyncP2PNotificationChannel + "&data=" + data;
+ ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
+ ASSERT_EQ("Notification sent",
+ UTF16ToASCII(browser()->GetSelectedTabContents()->GetTitle()));
+}
+
+bool LiveSyncTest::ServerSupportsErrorTriggering() const {
+ EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_);
+
+ // Supported only if we're using the python testserver.
+ return server_type_ == LOCAL_PYTHON_SERVER;
+}
+
+void LiveSyncTest::TriggerMigrationDoneError(
+ const syncable::ModelTypeSet& model_types) {
+ ASSERT_TRUE(ServerSupportsErrorTriggering());
+ std::string path = "chromiumsync/migrate";
+ char joiner = '?';
+ for (syncable::ModelTypeSet::const_iterator it = model_types.begin();
+ it != model_types.end(); ++it) {
+ path.append(base::StringPrintf("%ctype=%d", joiner,
+ syncable::GetExtensionFieldNumberFromModelType(*it)));
+ joiner = '&';
+ }
+ ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
+ ASSERT_EQ("Migration: 200",
+ UTF16ToASCII(browser()->GetSelectedTabContents()->GetTitle()));
+}
+
+void LiveSyncTest::TriggerBirthdayError() {
+ ASSERT_TRUE(ServerSupportsErrorTriggering());
+ std::string path = "chromiumsync/birthdayerror";
+ ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
+ ASSERT_EQ("Birthday error",
+ UTF16ToASCII(browser()->GetSelectedTabContents()->GetTitle()));
+}
+
+void LiveSyncTest::TriggerTransientError() {
+ ASSERT_TRUE(ServerSupportsErrorTriggering());
+ std::string path = "chromiumsync/transienterror";
+ ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
+ ASSERT_EQ("Transient error",
+ UTF16ToASCII(browser()->GetSelectedTabContents()->GetTitle()));
+}
+
+void LiveSyncTest::TriggerSetSyncTabs() {
+ ASSERT_TRUE(ServerSupportsErrorTriggering());
+ std::string path = "chromiumsync/synctabs";
+ ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
+ ASSERT_EQ("Sync Tabs",
+ UTF16ToASCII(browser()->GetSelectedTabContents()->GetTitle()));
+}
+
+void LiveSyncTest::SetProxyConfig(net::URLRequestContextGetter* context_getter,
+ const net::ProxyConfig& proxy_config) {
+ base::WaitableEvent done(false, false);
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ new SetProxyConfigTask(&done,
+ context_getter,
+ proxy_config));
+ done.Wait();
+}
diff --git a/chrome/browser/sync/test/live_sync/live_sync_test.h b/chrome/browser/sync/test/live_sync/live_sync_test.h
new file mode 100644
index 0000000..2769510c
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/live_sync_test.h
@@ -0,0 +1,317 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_LIVE_SYNC_TEST_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_LIVE_SYNC_TEST_H_
+#pragma once
+
+#include "chrome/test/base/in_process_browser_test.h"
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/process_util.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/test/test_server.h"
+
+class CommandLine;
+class Profile;
+class ProfileSyncServiceHarness;
+class FakeURLFetcherFactory;
+class URLFetcherFactory;
+
+namespace net {
+class ProxyConfig;
+class ScopedDefaultHostResolverProc;
+class URLRequestContextGetter;
+}
+
+// This is the base class for integration tests for all sync data types. Derived
+// classes must be defined for each sync data type. Individual tests are defined
+// using the IN_PROC_BROWSER_TEST_F macro.
+class LiveSyncTest : public InProcessBrowserTest {
+ public:
+ // The different types of live sync tests that can be implemented.
+ enum TestType {
+ // Tests where only one client profile is synced with the server. Typically
+ // sanity level tests.
+ SINGLE_CLIENT,
+
+ // Tests where two client profiles are synced with the server. Typically
+ // functionality level tests.
+ TWO_CLIENT,
+
+ // Tests where three or more client profiles are synced with the server.
+ // Typically, these tests create client side races and verify that sync
+ // works.
+ MULTIPLE_CLIENT,
+
+ // Tests where several client profiles are synced with the server. Only used
+ // by stress tests.
+ MANY_CLIENT
+ };
+
+ // The type of server we're running against.
+ enum ServerType {
+ SERVER_TYPE_UNDECIDED,
+ LOCAL_PYTHON_SERVER, // The mock python server that runs locally and is
+ // part of the Chromium checkout.
+ LOCAL_LIVE_SERVER, // Some other server (maybe the real binary used by
+ // Google's sync service) that can be started on
+ // a per-test basis by running a command
+ EXTERNAL_LIVE_SERVER, // A remote server that the test code has no control
+ // over whatsoever; cross your fingers that the
+ // account state is initially clean.
+ };
+
+ // A LiveSyncTest must be associated with a particular test type.
+ explicit LiveSyncTest(TestType test_type);
+
+ virtual ~LiveSyncTest();
+
+ // Validates command line parameters and creates a local python test server if
+ // specified.
+ virtual void SetUp() OVERRIDE;
+
+ // Brings down local python test server if one was created.
+ virtual void TearDown() OVERRIDE;
+
+ // Sets up command line flags required for sync tests.
+ virtual void SetUpCommandLine(CommandLine* cl) OVERRIDE;
+
+ // Used to get the number of sync clients used by a test.
+ int num_clients() WARN_UNUSED_RESULT { return num_clients_; }
+
+ // Returns a pointer to a particular sync profile. Callee owns the object
+ // and manages its lifetime.
+ Profile* GetProfile(int index) WARN_UNUSED_RESULT;
+
+ // Returns a pointer to a particular browser. Callee owns the object
+ // and manages its lifetime.
+ Browser* GetBrowser(int index) WARN_UNUSED_RESULT;
+
+ // Returns a pointer to a particular sync client. Callee owns the object
+ // and manages its lifetime.
+ ProfileSyncServiceHarness* GetClient(int index) WARN_UNUSED_RESULT;
+
+ // Returns a reference to the collection of sync clients. Callee owns the
+ // object and manages its lifetime.
+ std::vector<ProfileSyncServiceHarness*>& clients() WARN_UNUSED_RESULT {
+ return clients_.get();
+ }
+
+ // Returns a pointer to the sync profile that is used to verify changes to
+ // individual sync profiles. Callee owns the object and manages its lifetime.
+ Profile* verifier() WARN_UNUSED_RESULT;
+
+ // Used to determine whether the verifier profile should be updated or not.
+ bool use_verifier() WARN_UNUSED_RESULT { return use_verifier_; }
+
+ // After calling this method, changes made to a profile will no longer be
+ // reflected in the verifier profile. Note: Not all datatypes use this.
+ // TODO(rsimha): Hook up all datatypes to this mechanism.
+ void DisableVerifier();
+
+ // Initializes sync clients and profiles but does not sync any of them.
+ virtual bool SetupClients() WARN_UNUSED_RESULT;
+
+ // Initializes sync clients and profiles if required and syncs each of them.
+ virtual bool SetupSync() WARN_UNUSED_RESULT;
+
+ // Enable outgoing network connections for the given profile.
+ virtual void EnableNetwork(Profile* profile);
+
+ // Disable outgoing network connections for the given profile.
+ virtual void DisableNetwork(Profile* profile);
+
+ // Encrypts the datatype |type| for profile |index|.
+ bool EnableEncryption(int index, syncable::ModelType type);
+
+ // Checks if the datatype |type| is encrypted for profile |index|.
+ bool IsEncrypted(int index, syncable::ModelType type);
+
+ // Blocks until all sync clients have completed their mutual sync cycles.
+ // Returns true if a quiescent state was successfully reached.
+ bool AwaitQuiescence();
+
+ // Returns true if the server being used supports controlling
+ // notifications.
+ bool ServerSupportsNotificationControl() const;
+
+ // Disable notifications on the server. This operation is available
+ // only if ServerSupportsNotificationControl() returned true.
+ void DisableNotifications();
+
+ // Enable notifications on the server. This operation is available
+ // only if ServerSupportsNotificationControl() returned true.
+ void EnableNotifications();
+
+ // Trigger a notification to be sent to all clients. This operation
+ // is available only if ServerSupportsNotificationControl() returned
+ // true.
+ void TriggerNotification(const syncable::ModelTypeSet& changed_types);
+
+ // Returns true if the server being used supports injecting errors.
+ bool ServerSupportsErrorTriggering() const;
+
+ // Triggers a migration for one or more datatypes, and waits
+ // for the server to complete it. This operation is available
+ // only if ServerSupportsErrorTriggering() returned true.
+ void TriggerMigrationDoneError(const syncable::ModelTypeSet& model_types);
+
+ // Triggers the server to set its birthday to a random value thereby
+ // the server would return a birthday error on next sync.
+ void TriggerBirthdayError();
+
+ // Triggers a transient error on the server. Note the server will stay in
+ // this state until shut down.
+ void TriggerTransientError();
+
+ // Triggers setting the sync_tabs field of the nigori node.
+ void TriggerSetSyncTabs();
+
+ protected:
+ // Add custom switches needed for running the test.
+ virtual void AddTestSwitches(CommandLine* cl);
+
+ // Append the command line switches to enable experimental types that aren't
+ // on by default yet.
+ virtual void AddOptionalTypesToCommandLine(CommandLine* cl);
+
+ // InProcessBrowserTest override. Destroys all the sync clients and sync
+ // profiles created by a test.
+ virtual void CleanUpOnMainThread() OVERRIDE;
+
+ // InProcessBrowserTest override. Changes behavior of the default host
+ // resolver to avoid DNS lookup errors.
+ virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
+
+ // InProcessBrowserTest override. Resets the host resolver its default
+ // behavior.
+ virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
+
+ // GAIA account used by the test case.
+ std::string username_;
+
+ // GAIA password used by the test case.
+ std::string password_;
+
+ // Locally available plain text file in which GAIA credentials are stored.
+ FilePath password_file_;
+
+ private:
+ // Helper to ProfileManager::CreateProfile that handles path creation.
+ static Profile* MakeProfile(const FilePath::StringType name);
+
+ // Helper method used to read GAIA credentials from a local password file
+ // specified via the "--password-file-for-test" command line switch.
+ // Note: The password file must be a plain text file with exactly two lines --
+ // the username on the first line and the password on the second line.
+ void ReadPasswordFile();
+
+ // Helper method that starts up a sync test server if required.
+ void SetUpTestServerIfRequired();
+
+ // Helper method used to start up a local python test server. Note: We set up
+ // an XMPP-only python server if |server_type_| is LOCAL_LIVE_SERVER and mock
+ // gaia credentials are in use. Returns true if successful.
+ bool SetUpLocalPythonTestServer();
+
+ // Helper method used to start up a local sync test server. Returns true if
+ // successful.
+ bool SetUpLocalTestServer();
+
+ // Helper method used to destroy the local python sync test server if one was
+ // created. Returns true if successful.
+ bool TearDownLocalPythonTestServer();
+
+ // Helper method used to destroy the local sync test server if one was
+ // created. Returns true if successful.
+ bool TearDownLocalTestServer();
+
+ // Helper method that waits for up to |time_ms| milliseconds for the test
+ // server to start. Splits the time into |intervals| intervals, and polls the
+ // server after each interval to see if it has started. Returns true if
+ // successful.
+ bool WaitForTestServerToStart(int time_ms, int intervals);
+
+ // Helper method used to check if the test server is up and running.
+ bool IsTestServerRunning();
+
+ // Used to disable and enable network connectivity by providing and
+ // clearing an invalid proxy configuration.
+ void SetProxyConfig(net::URLRequestContextGetter* context,
+ const net::ProxyConfig& proxy_config);
+
+ // Helper method used to set up fake responses for kClientLoginUrl,
+ // kIssueAuthTokenUrl, kGetUserInfoUrl and kSearchDomainCheckUrl in order to
+ // mock out calls to GAIA servers.
+ void SetupMockGaiaResponses();
+
+ // Test server of type sync, started on demand.
+ net::TestServer sync_server_;
+
+ // Helper class to whitelist the notification port.
+ scoped_ptr<net::ScopedPortException> xmpp_port_;
+
+ // Used to differentiate between single-client, two-client, multi-client and
+ // many-client tests.
+ TestType test_type_;
+
+ // Tells us what kind of server we're using (some tests run only on certain
+ // server types).
+ ServerType server_type_;
+
+ // Number of sync clients that will be created by a test.
+ int num_clients_;
+
+ // Collection of sync profiles used by a test. A sync profile maintains sync
+ // data contained within its own subdirectory under the chrome user data
+ // directory.
+ ScopedVector<Profile> profiles_;
+
+ // Collection of pointers to the browser objects used by a test. One browser
+ // instance is created for each sync profile. Browser object lifetime is
+ // managed by BrowserList, so we don't use a ScopedVector here.
+ std::vector<Browser*> browsers_;
+
+ // Collection of sync clients used by a test. A sync client is associated with
+ // a sync profile, and implements methods that sync the contents of the
+ // profile with the server.
+ ScopedVector<ProfileSyncServiceHarness> clients_;
+
+ // Sync profile against which changes to individual profiles are verified. We
+ // don't need a corresponding verifier sync client because the contents of the
+ // verifier profile are strictly local, and are not meant to be synced.
+ scoped_ptr<Profile> verifier_;
+
+ // Indicates whether changes to a profile should also change the verifier
+ // profile or not.
+ bool use_verifier_;
+
+ // Sync integration tests need to make live DNS requests for access to
+ // GAIA and sync server URLs under google.com. We use a scoped version
+ // to override the default resolver while the test is active.
+ scoped_ptr<net::ScopedDefaultHostResolverProc> mock_host_resolver_override_;
+
+ // Used to start and stop the local test server.
+ base::ProcessHandle test_server_handle_;
+
+ // Fake URLFetcher factory used to mock out GAIA signin.
+ scoped_ptr<FakeURLFetcherFactory> fake_factory_;
+
+ // The URLFetcherFactory instance used to instantiate |fake_factory_|.
+ scoped_ptr<URLFetcherFactory> factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(LiveSyncTest);
+};
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(LiveSyncTest);
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_LIVE_SYNC_TEST_H_
diff --git a/chrome/browser/sync/test/live_sync/many_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/live_sync/many_client_bookmarks_sync_test.cc
new file mode 100644
index 0000000..9b87fae
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/many_client_bookmarks_sync_test.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using bookmarks_helper::AddURL;
+using bookmarks_helper::AllModelsMatch;
+
+class ManyClientBookmarksSyncTest : public LiveSyncTest {
+ public:
+ ManyClientBookmarksSyncTest() : LiveSyncTest(MANY_CLIENT) {}
+ virtual ~ManyClientBookmarksSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ManyClientBookmarksSyncTest);
+};
+
+// TODO(rsimha): Enable once http://crbug.com/69604 is fixed.
+IN_PROC_BROWSER_TEST_F(ManyClientBookmarksSyncTest, DISABLED_Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AddURL(0, L"Google URL", GURL("http://www.google.com/")) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitGroupSyncCycleCompletion(clients()));
+ ASSERT_TRUE(AllModelsMatch());
+}
diff --git a/chrome/browser/sync/test/live_sync/many_client_passwords_sync_test.cc b/chrome/browser/sync/test/live_sync/many_client_passwords_sync_test.cc
new file mode 100644
index 0000000..e701cc9
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/many_client_passwords_sync_test.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/password_manager/password_form_data.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/passwords_helper.h"
+
+using passwords_helper::AddLogin;
+using passwords_helper::AllProfilesContainSamePasswordFormsAsVerifier;
+using passwords_helper::CreateTestPasswordForm;
+using passwords_helper::GetPasswordCount;
+using passwords_helper::GetPasswordStore;
+using passwords_helper::GetVerifierPasswordCount;
+using passwords_helper::GetVerifierPasswordStore;
+
+using webkit_glue::PasswordForm;
+
+class ManyClientPasswordsSyncTest : public LiveSyncTest {
+ public:
+ ManyClientPasswordsSyncTest() : LiveSyncTest(MANY_CLIENT) {}
+ virtual ~ManyClientPasswordsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ManyClientPasswordsSyncTest);
+};
+
+// TODO(rsimha): Enable once http://crbug.com/69604 is fixed.
+IN_PROC_BROWSER_TEST_F(ManyClientPasswordsSyncTest, DISABLED_Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ PasswordForm form = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form);
+ ASSERT_EQ(1, GetVerifierPasswordCount());
+ AddLogin(GetPasswordStore(0), form);
+ ASSERT_EQ(1, GetPasswordCount(0));
+ ASSERT_TRUE(GetClient(0)->AwaitGroupSyncCycleCompletion(clients()));
+
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+ ASSERT_EQ(1, GetPasswordCount(0));
+}
diff --git a/chrome/browser/sync/test/live_sync/many_client_preferences_sync_test.cc b/chrome/browser/sync/test/live_sync/many_client_preferences_sync_test.cc
new file mode 100644
index 0000000..a66b979
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/many_client_preferences_sync_test.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/preferences_helper.h"
+#include "chrome/common/pref_names.h"
+
+using preferences_helper::BooleanPrefMatches;
+using preferences_helper::ChangeBooleanPref;
+
+class ManyClientPreferencesSyncTest : public LiveSyncTest {
+ public:
+ ManyClientPreferencesSyncTest() : LiveSyncTest(MANY_CLIENT) {}
+ virtual ~ManyClientPreferencesSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ManyClientPreferencesSyncTest);
+};
+
+// TODO(rsimha): Enable once http://crbug.com/69604 is fixed.
+IN_PROC_BROWSER_TEST_F(ManyClientPreferencesSyncTest, DISABLED_Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kHomePageIsNewTabPage));
+ ChangeBooleanPref(0, prefs::kHomePageIsNewTabPage);
+ ASSERT_TRUE(GetClient(0)->AwaitGroupSyncCycleCompletion(clients()));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kHomePageIsNewTabPage));
+}
diff --git a/chrome/browser/sync/test/live_sync/migration_errors_test.cc b/chrome/browser/sync/test/live_sync/migration_errors_test.cc
new file mode 100644
index 0000000..febefc8
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/migration_errors_test.cc
@@ -0,0 +1,453 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(akalin): Rename this file to migration_test.cc.
+
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/preferences_helper.h"
+#include "chrome/browser/translate/translate_prefs.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/ui_test_utils.h"
+
+using bookmarks_helper::AddURL;
+using bookmarks_helper::IndexedURL;
+using bookmarks_helper::IndexedURLTitle;
+
+using preferences_helper::BooleanPrefMatches;
+using preferences_helper::ChangeBooleanPref;
+
+namespace {
+
+// Utility functions to make a model type set out of a small number of
+// model types.
+
+syncable::ModelTypeSet MakeSet(syncable::ModelType type) {
+ syncable::ModelTypeSet model_types;
+ model_types.insert(type);
+ return model_types;
+}
+
+syncable::ModelTypeSet MakeSet(syncable::ModelType type1,
+ syncable::ModelType type2) {
+ syncable::ModelTypeSet model_types;
+ model_types.insert(type1);
+ model_types.insert(type2);
+ return model_types;
+}
+
+// An ordered list of model types sets to migrate. Used by
+// RunMigrationTest().
+typedef std::deque<syncable::ModelTypeSet> MigrationList;
+
+// Utility functions to make a MigrationList out of a small number of
+// model types / model type sets.
+
+MigrationList MakeList(const syncable::ModelTypeSet& model_types) {
+ return MigrationList(1, model_types);
+}
+
+MigrationList MakeList(const syncable::ModelTypeSet& model_types1,
+ const syncable::ModelTypeSet& model_types2) {
+ MigrationList migration_list;
+ migration_list.push_back(model_types1);
+ migration_list.push_back(model_types2);
+ return migration_list;
+}
+
+MigrationList MakeList(syncable::ModelType type) {
+ return MakeList(MakeSet(type));
+}
+
+MigrationList MakeList(syncable::ModelType type1,
+ syncable::ModelType type2) {
+ return MakeList(MakeSet(type1), MakeSet(type2));
+}
+
+class MigrationTest : public LiveSyncTest {
+ public:
+ explicit MigrationTest(TestType test_type) : LiveSyncTest(test_type) {}
+ virtual ~MigrationTest() {}
+
+ // TODO(akalin): Add more MODIFY_(data type) trigger methods, as
+ // well as a poll-based trigger method.
+ enum TriggerMethod { MODIFY_PREF, MODIFY_BOOKMARK, TRIGGER_NOTIFICATION };
+
+ syncable::ModelTypeSet GetPreferredDataTypes() {
+ syncable::ModelTypeSet preferred_data_types;
+ GetClient(0)->service()->GetPreferredDataTypes(&preferred_data_types);
+ // Make sure all clients have the same preferred data types.
+ for (int i = 1; i < num_clients(); ++i) {
+ syncable::ModelTypeSet other_preferred_data_types;
+ GetClient(i)->service()->GetPreferredDataTypes(
+ &other_preferred_data_types);
+ EXPECT_EQ(preferred_data_types, other_preferred_data_types);
+ }
+ return preferred_data_types;
+ }
+
+ // Returns a MigrationList with every enabled data type in its own
+ // set.
+ MigrationList GetPreferredDataTypesList() {
+ MigrationList migration_list;
+ const syncable::ModelTypeSet& preferred_data_types =
+ GetPreferredDataTypes();
+ for (syncable::ModelTypeSet::const_iterator it =
+ preferred_data_types.begin();
+ it != preferred_data_types.end(); ++it) {
+ migration_list.push_back(MakeSet(*it));
+ }
+ return migration_list;
+ }
+
+ // Trigger a migration for the given types with the given method.
+ void TriggerMigration(const syncable::ModelTypeSet& model_types,
+ TriggerMethod trigger_method) {
+ switch (trigger_method) {
+ case MODIFY_PREF:
+ // Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
+ // notification to happen (since model association on a
+ // boolean pref clobbers the local value), so it doesn't work
+ // for anything but single-client tests.
+ ASSERT_EQ(1, num_clients());
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+ ChangeBooleanPref(0, prefs::kShowHomeButton);
+ break;
+ case MODIFY_BOOKMARK:
+ ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
+ break;
+ case TRIGGER_NOTIFICATION:
+ TriggerNotification(model_types);
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+
+ // Block until all clients have completed migration for the given
+ // types.
+ void AwaitMigration(const syncable::ModelTypeSet& migrate_types) {
+ for (int i = 0; i < num_clients(); ++i) {
+ ASSERT_TRUE(GetClient(i)->AwaitMigration(migrate_types));
+ }
+ }
+
+ bool ShouldRunMigrationTest() const {
+ if (!ServerSupportsNotificationControl() ||
+ !ServerSupportsErrorTriggering()) {
+ LOG(WARNING) << "Test skipped in this server environment.";
+ return false;
+ }
+ return true;
+ }
+
+ // Makes sure migration works with the given migration list and
+ // trigger method.
+ void RunMigrationTest(const MigrationList& migration_list,
+ TriggerMethod trigger_method) {
+ ASSERT_TRUE(ShouldRunMigrationTest());
+
+ // If we have only one client, turn off notifications to avoid the
+ // possibility of spurious sync cycles.
+ bool do_test_without_notifications =
+ (trigger_method != TRIGGER_NOTIFICATION && num_clients() == 1);
+
+ if (do_test_without_notifications) {
+ DisableNotifications();
+ }
+
+ // Phase 1: Trigger the migrations on the server.
+ for (MigrationList::const_iterator it = migration_list.begin();
+ it != migration_list.end(); ++it) {
+ TriggerMigrationDoneError(*it);
+ }
+
+ // Phase 2: Trigger each migration individually and wait for it to
+ // complete. (Multiple migrations may be handled by each
+ // migration cycle, but there's no guarantee of that, so we have
+ // to trigger each migration individually.)
+ for (MigrationList::const_iterator it = migration_list.begin();
+ it != migration_list.end(); ++it) {
+ TriggerMigration(*it, trigger_method);
+ AwaitMigration(*it);
+ }
+
+ // Phase 3: Wait for all clients to catch up.
+ AwaitQuiescence();
+
+ // Re-enable notifications if we disabled it.
+ if (do_test_without_notifications) {
+ EnableNotifications();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MigrationTest);
+};
+
+class MigrationSingleClientTest : public MigrationTest {
+ public:
+ MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT) {}
+ virtual ~MigrationSingleClientTest() {}
+
+ void RunSingleClientMigrationTest(const MigrationList& migration_list,
+ TriggerMethod trigger_method) {
+ if (!ShouldRunMigrationTest()) {
+ return;
+ }
+ ASSERT_TRUE(SetupSync());
+ RunMigrationTest(migration_list, trigger_method);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest);
+};
+
+// The simplest possible migration tests -- a single data type.
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
+ RunSingleClientMigrationTest(MakeList(syncable::PREFERENCES), MODIFY_PREF);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
+ RunSingleClientMigrationTest(MakeList(syncable::PREFERENCES),
+ MODIFY_BOOKMARK);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
+ PrefsOnlyTriggerNotification) {
+ RunSingleClientMigrationTest(MakeList(syncable::PREFERENCES),
+ TRIGGER_NOTIFICATION);
+}
+
+// Nigori is handled specially, so we test that separately.
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
+ RunSingleClientMigrationTest(MakeList(syncable::PREFERENCES),
+ TRIGGER_NOTIFICATION);
+}
+
+// A little more complicated -- two data types.
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
+ BookmarksPrefsIndividually) {
+ RunSingleClientMigrationTest(
+ MakeList(syncable::BOOKMARKS, syncable::PREFERENCES),
+ MODIFY_PREF);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
+ RunSingleClientMigrationTest(
+ MakeList(MakeSet(syncable::BOOKMARKS, syncable::PREFERENCES)),
+ MODIFY_BOOKMARK);
+}
+
+// Two data types with one being nigori.
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriIndividiaully) {
+ RunSingleClientMigrationTest(
+ MakeList(syncable::PREFERENCES, syncable::NIGORI),
+ TRIGGER_NOTIFICATION);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
+ RunSingleClientMigrationTest(
+ MakeList(MakeSet(syncable::PREFERENCES, syncable::NIGORI)),
+ MODIFY_PREF);
+}
+
+// The whole shebang -- all data types.
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesIndividually) {
+ ASSERT_TRUE(SetupClients());
+ RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
+ AllTypesIndividuallyTriggerNotification) {
+ ASSERT_TRUE(SetupClients());
+ RunSingleClientMigrationTest(GetPreferredDataTypesList(),
+ TRIGGER_NOTIFICATION);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
+ ASSERT_TRUE(SetupClients());
+ RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
+ MODIFY_PREF);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
+ AllTypesAtOnceTriggerNotification) {
+ ASSERT_TRUE(SetupClients());
+ RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
+ TRIGGER_NOTIFICATION);
+}
+
+// All data types plus nigori.
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
+ AllTypesWithNigoriIndividually) {
+ ASSERT_TRUE(SetupClients());
+ MigrationList migration_list = GetPreferredDataTypesList();
+ migration_list.push_front(MakeSet(syncable::NIGORI));
+ RunSingleClientMigrationTest(migration_list, MODIFY_BOOKMARK);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesWithNigoriAtOnce) {
+ ASSERT_TRUE(SetupClients());
+ syncable::ModelTypeSet all_types = GetPreferredDataTypes();
+ all_types.insert(syncable::NIGORI);
+ RunSingleClientMigrationTest(MakeList(all_types), MODIFY_PREF);
+}
+
+class MigrationTwoClientTest : public MigrationTest {
+ public:
+ MigrationTwoClientTest() : MigrationTest(TWO_CLIENT) {}
+ virtual ~MigrationTwoClientTest() {}
+
+ // Helper function that verifies that preferences sync still works.
+ void VerifyPrefSync() {
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+ ChangeBooleanPref(0, prefs::kShowHomeButton);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+ }
+
+ void RunTwoClientMigrationTest(const MigrationList& migration_list,
+ TriggerMethod trigger_method) {
+ if (!ShouldRunMigrationTest()) {
+ return;
+ }
+ ASSERT_TRUE(SetupSync());
+
+ // Make sure pref sync works before running the migration test.
+ VerifyPrefSync();
+
+ RunMigrationTest(migration_list, trigger_method);
+
+ // Make sure pref sync still works after running the migration
+ // test.
+ VerifyPrefSync();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest);
+};
+
+// Easiest possible test of migration errors: triggers a server
+// migration on one datatype, then modifies some other datatype.
+IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
+ MigratePrefsThenModifyBookmark) {
+ RunTwoClientMigrationTest(MakeList(syncable::PREFERENCES),
+ MODIFY_BOOKMARK);
+}
+
+// Triggers a server migration on two datatypes, then makes a local
+// modification to one of them.
+IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
+ MigratePrefsAndBookmarksThenModifyBookmark) {
+ RunTwoClientMigrationTest(
+ MakeList(syncable::PREFERENCES, syncable::BOOKMARKS),
+ MODIFY_BOOKMARK);
+}
+
+// Migrate every datatype in sequence; the catch being that the server
+// will only tell the client about the migrations one at a time.
+IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigrationHellWithoutNigori) {
+ ASSERT_TRUE(SetupClients());
+ MigrationList migration_list = GetPreferredDataTypesList();
+ // Let the first nudge be a datatype that's neither prefs nor
+ // bookmarks.
+ migration_list.push_front(MakeSet(syncable::THEMES));
+ RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigrationHellWithNigori) {
+ ASSERT_TRUE(SetupClients());
+ MigrationList migration_list = GetPreferredDataTypesList();
+ // Let the first nudge be a datatype that's neither prefs nor
+ // bookmarks.
+ migration_list.push_front(MakeSet(syncable::THEMES));
+ // Pop off one so that we don't migrate all data types; the syncer
+ // freaks out if we do that (see http://crbug.com/94882).
+ ASSERT_GE(migration_list.size(), 2u);
+ ASSERT_NE(migration_list.back(), MakeSet(syncable::NIGORI));
+ migration_list.back() = MakeSet(syncable::NIGORI);
+ RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
+}
+
+class MigrationReconfigureTest : public MigrationTwoClientTest {
+ public:
+ MigrationReconfigureTest() {}
+
+ virtual void SetUpCommandLine(CommandLine* cl) OVERRIDE {
+ AddTestSwitches(cl);
+ // Do not add optional datatypes.
+ }
+
+ virtual ~MigrationReconfigureTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest);
+};
+
+IN_PROC_BROWSER_TEST_F(MigrationReconfigureTest, SetSyncTabs) {
+ if (!ServerSupportsErrorTriggering()) {
+ LOG(WARNING) << "Test skipped in this server environment.";
+ return;
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_FALSE(GetClient(0)->IsTypeRegistered(syncable::SESSIONS));
+ ASSERT_FALSE(GetClient(0)->IsTypePreferred(syncable::SESSIONS));
+
+ // Phase 1: Before migrating anything, create & sync a preference.
+ VerifyPrefSync();
+
+ // Phase 2: Trigger setting the sync_tabs field.
+ TriggerSetSyncTabs();
+
+ // Phase 3: Modify a bookmark and wait for it to sync.
+ ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Phase 4: Verify that preferences can still be synchronized.
+ VerifyPrefSync();
+
+ // Phase 5: Verify that sessions are registered and enabled.
+ ASSERT_TRUE(GetClient(0)->IsTypeRegistered(syncable::SESSIONS));
+ ASSERT_TRUE(GetClient(0)->IsTypePreferred(syncable::SESSIONS));
+}
+
+IN_PROC_BROWSER_TEST_F(MigrationReconfigureTest, SetSyncTabsAndMigrate) {
+ if (!ServerSupportsErrorTriggering()) {
+ LOG(WARNING) << "Test skipped in this server environment.";
+ return;
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_FALSE(GetClient(0)->IsTypeRegistered(syncable::SESSIONS));
+ ASSERT_FALSE(GetClient(0)->IsTypePreferred(syncable::SESSIONS));
+
+ // Phase 1: Before migrating anything, create & sync a preference.
+ VerifyPrefSync();
+
+ // Phase 2: Trigger setting the sync_tabs field.
+ TriggerSetSyncTabs();
+
+ // Phase 3: Trigger a preference migration on the server.
+ RunMigrationTest(MakeList(syncable::PREFERENCES), MODIFY_BOOKMARK);
+
+ // Phase 5: Verify that preferences can still be synchronized.
+ VerifyPrefSync();
+
+ // Phase 6: Verify that sessions are registered and enabled.
+ ASSERT_TRUE(GetClient(0)->IsTypeRegistered(syncable::SESSIONS));
+ ASSERT_TRUE(GetClient(0)->IsTypePreferred(syncable::SESSIONS));
+}
+
+} // namespace
diff --git a/chrome/browser/sync/test/live_sync/multiple_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/live_sync/multiple_client_bookmarks_sync_test.cc
new file mode 100644
index 0000000..cf70e6a
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/multiple_client_bookmarks_sync_test.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using bookmarks_helper::AddURL;
+using bookmarks_helper::AllModelsMatch;
+
+class MultipleClientBookmarksSyncTest : public LiveSyncTest {
+ public:
+ MultipleClientBookmarksSyncTest() : LiveSyncTest(MULTIPLE_CLIENT) {}
+ virtual ~MultipleClientBookmarksSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultipleClientBookmarksSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(MultipleClientBookmarksSyncTest, Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+ for (int i = 0; i < num_clients(); ++i) {
+ ASSERT_TRUE(AddURL(i, base::StringPrintf(L"Google URL %d", i),
+ GURL(StringPrintf("http://www.google.com/%d", i))) != NULL);
+ }
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+}
diff --git a/chrome/browser/sync/test/live_sync/multiple_client_passwords_sync_test.cc b/chrome/browser/sync/test/live_sync/multiple_client_passwords_sync_test.cc
new file mode 100644
index 0000000..d722cc2
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/multiple_client_passwords_sync_test.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/password_manager/password_form_data.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/passwords_helper.h"
+
+using passwords_helper::AddLogin;
+using passwords_helper::AllProfilesContainSamePasswordForms;
+using passwords_helper::CreateTestPasswordForm;
+using passwords_helper::GetPasswordCount;
+using passwords_helper::GetPasswordStore;
+
+using webkit_glue::PasswordForm;
+
+class MultipleClientPasswordsSyncTest : public LiveSyncTest {
+ public:
+ MultipleClientPasswordsSyncTest() : LiveSyncTest(MULTIPLE_CLIENT) {}
+ virtual ~MultipleClientPasswordsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultipleClientPasswordsSyncTest);
+};
+
+// TODO(rsimha): Enable after crbug.com/77993 is fixed.
+IN_PROC_BROWSER_TEST_F(MultipleClientPasswordsSyncTest, DISABLED_Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ for (int i = 0; i < num_clients(); ++i) {
+ PasswordForm form = CreateTestPasswordForm(i);
+ AddLogin(GetPasswordStore(i), form);
+ }
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(num_clients(), GetPasswordCount(0));
+ ASSERT_TRUE(AllProfilesContainSamePasswordForms());
+}
diff --git a/chrome/browser/sync/test/live_sync/multiple_client_preferences_sync_test.cc b/chrome/browser/sync/test/live_sync/multiple_client_preferences_sync_test.cc
new file mode 100644
index 0000000..b2b32c6
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/multiple_client_preferences_sync_test.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/preferences_helper.h"
+#include "chrome/common/pref_names.h"
+
+using preferences_helper::ChangeListPref;
+using preferences_helper::ListPrefMatches;
+
+class MultipleClientPreferencesSyncTest : public LiveSyncTest {
+ public:
+ MultipleClientPreferencesSyncTest() : LiveSyncTest(MULTIPLE_CLIENT) {}
+ virtual ~MultipleClientPreferencesSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultipleClientPreferencesSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(MultipleClientPreferencesSyncTest, Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < num_clients(); ++i) {
+ ListValue urls;
+ urls.Append(Value::CreateStringValue(
+ base::StringPrintf("http://www.google.com/%d", i)));
+ ChangeListPref(i, prefs::kURLsToRestoreOnStartup, urls);
+ }
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(ListPrefMatches(prefs::kURLsToRestoreOnStartup));
+}
diff --git a/chrome/browser/sync/test/live_sync/multiple_client_sessions_sync_test.cc b/chrome/browser/sync/test/live_sync/multiple_client_sessions_sync_test.cc
new file mode 100644
index 0000000..e7236fa
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/multiple_client_sessions_sync_test.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_vector.h"
+#include "base/stringprintf.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sessions_helper.h"
+
+using sessions_helper::CheckForeignSessionsAgainst;
+using sessions_helper::CheckInitialState;
+using sessions_helper::OpenTabAndGetLocalWindows;
+
+class MultipleClientSessionsSyncTest : public LiveSyncTest {
+ public:
+ MultipleClientSessionsSyncTest() : LiveSyncTest(MULTIPLE_CLIENT) {}
+ virtual ~MultipleClientSessionsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultipleClientSessionsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(MultipleClientSessionsSyncTest, AllChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ScopedVector<SessionWindowVector> client_windows;
+
+ for (int i = 0; i < num_clients(); ++i) {
+ ASSERT_TRUE(CheckInitialState(i));
+ }
+
+ // Open tabs on all clients and retain window information.
+ for (int i = 0; i < num_clients(); ++i) {
+ SessionWindowVector* windows = new SessionWindowVector();
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(
+ i, GURL(StringPrintf("about:bubba%i", i)), *windows));
+ client_windows.push_back(windows);
+ }
+
+ // Wait for sync.
+ ASSERT_TRUE(AwaitQuiescence());
+
+ // Get foreign session data from all clients and check it against all
+ // client_windows.
+ for (int i = 0; i < num_clients(); ++i) {
+ ASSERT_TRUE(CheckForeignSessionsAgainst(i, client_windows.get()));
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(MultipleClientSessionsSyncTest,
+ EncryptedAndChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ScopedVector<SessionWindowVector> client_windows;
+
+ for (int i = 0; i < num_clients(); ++i) {
+ ASSERT_TRUE(CheckInitialState(i));
+ }
+
+ // Enable encryption on client 0, should propagate to all other clients.
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+
+ // Wait for sync.
+ // TODO(zea): Fix sync completion detection so we don't need this. For now,
+ // the profile sync service harness detects completion before all encryption
+ // changes are propagated.
+ ASSERT_TRUE(GetClient(0)->AwaitGroupSyncCycleCompletion(clients()));
+
+ // Open tabs on all clients and retain window information.
+ for (int i = 0; i < num_clients(); ++i) {
+ SessionWindowVector* windows = new SessionWindowVector();
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(
+ i, GURL(StringPrintf("about:bubba%i", i)), *windows));
+ client_windows.push_back(windows);
+ }
+
+ // Wait for sync.
+ ASSERT_TRUE(AwaitQuiescence());
+
+ // Get foreign session data from all clients and check it against all
+ // client_windows.
+ for (int i = 0; i < num_clients(); ++i) {
+ ASSERT_TRUE(IsEncrypted(i, syncable::SESSIONS));
+ ASSERT_TRUE(CheckForeignSessionsAgainst(i, client_windows.get()));
+ }
+}
diff --git a/chrome/browser/sync/test/live_sync/multiple_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/live_sync/multiple_client_typed_urls_sync_test.cc
new file mode 100644
index 0000000..01b0f7c
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/multiple_client_typed_urls_sync_test.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/number_formatting.h"
+#include "base/memory/scoped_vector.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/typed_urls_helper.h"
+
+using typed_urls_helper::AddUrlToHistory;
+using typed_urls_helper::AssertAllProfilesHaveSameURLsAsVerifier;
+using typed_urls_helper::GetTypedUrlsFromClient;
+
+class MultipleClientTypedUrlsSyncTest : public LiveSyncTest {
+ public:
+ MultipleClientTypedUrlsSyncTest() : LiveSyncTest(MULTIPLE_CLIENT) {}
+ virtual ~MultipleClientTypedUrlsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultipleClientTypedUrlsSyncTest);
+};
+
+// TCM: 3728323
+IN_PROC_BROWSER_TEST_F(MultipleClientTypedUrlsSyncTest, AddToOne) {
+ const string16 kHistoryUrl(
+ ASCIIToUTF16("http://www.add-one-history.google.com/"));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Populate one client with a URL, should sync to all others.
+ GURL new_url(kHistoryUrl);
+ AddUrlToHistory(0, new_url);
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+ ASSERT_EQ(new_url, urls[0].url());
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitGroupSyncCycleCompletion(clients()));
+
+ // All clients should have this URL.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
+
+IN_PROC_BROWSER_TEST_F(MultipleClientTypedUrlsSyncTest, AddToAll) {
+ const string16 kHistoryUrl(
+ ASCIIToUTF16("http://www.add-all-history.google.com/"));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Populate clients with the same URL.
+ for (int i = 0; i < num_clients(); ++i) {
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(i);
+ ASSERT_EQ(0U, urls.size());
+
+ string16 unique_url = kHistoryUrl + base::FormatNumber(i);
+ GURL new_url(unique_url);
+ AddUrlToHistory(i, new_url);
+
+ urls = GetTypedUrlsFromClient(i);
+ ASSERT_EQ(1U, urls.size());
+ ASSERT_EQ(new_url, urls[0].url());
+ }
+
+ // Wait for sync.
+ ASSERT_TRUE(AwaitQuiescence());
+
+ // Verify that all clients have all urls.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
+
diff --git a/chrome/browser/sync/test/live_sync/passwords_helper.cc b/chrome/browser/sync/test/live_sync/passwords_helper.cc
new file mode 100644
index 0000000..b888e25
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/passwords_helper.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/passwords_helper.h"
+
+#include "base/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/password_manager/password_form_data.h"
+#include "chrome/browser/password_manager/password_store_consumer.h"
+#include "chrome/browser/password_manager/password_store.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/browser/browser_thread.h"
+
+using webkit_glue::PasswordForm;
+using sync_datatype_helper::test;
+
+const std::string kFakeSignonRealm = "http://fake-signon-realm.google.com/";
+const char* kIndexedFakeOrigin = "http://fake-signon-realm.google.com/%d";
+
+namespace {
+
+// We use a WaitableEvent to wait when logins are added, removed, or updated
+// instead of running the UI message loop because of a restriction that
+// prevents a DB thread from initiating a quit of the UI message loop.
+void PasswordStoreCallback(base::WaitableEvent* wait_event) {
+ // Wake up passwords_helper::AddLogin.
+ wait_event->Signal();
+}
+
+class PasswordStoreConsumerHelper : public PasswordStoreConsumer {
+ public:
+ explicit PasswordStoreConsumerHelper(std::vector<PasswordForm>* result)
+ : PasswordStoreConsumer(),
+ result_(result) {}
+
+ virtual void OnPasswordStoreRequestDone(
+ CancelableRequestProvider::Handle handle,
+ const std::vector<PasswordForm*>& result) {
+ result_->clear();
+ for (std::vector<PasswordForm*>::const_iterator it = result.begin();
+ it != result.end(); ++it) {
+ // Make a copy of the form since it gets deallocated after the caller of
+ // this method returns.
+ result_->push_back(**it);
+ }
+
+ // Quit the message loop to wake up passwords_helper::GetLogins.
+ MessageLoopForUI::current()->Quit();
+ }
+
+ private:
+ std::vector<PasswordForm>* result_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordStoreConsumerHelper);
+};
+
+} // namespace
+
+namespace passwords_helper {
+
+void AddLogin(PasswordStore* store, const PasswordForm& form) {
+ ASSERT_TRUE(store);
+ base::WaitableEvent wait_event(true, false);
+ store->AddLogin(form);
+ store->ScheduleTask(NewRunnableFunction(&PasswordStoreCallback, &wait_event));
+ wait_event.Wait();
+}
+
+void UpdateLogin(PasswordStore* store, const PasswordForm& form) {
+ ASSERT_TRUE(store);
+ base::WaitableEvent wait_event(true, false);
+ store->UpdateLogin(form);
+ store->ScheduleTask(NewRunnableFunction(&PasswordStoreCallback, &wait_event));
+ wait_event.Wait();
+}
+
+void GetLogins(PasswordStore* store, std::vector<PasswordForm>& matches) {
+ ASSERT_TRUE(store);
+ PasswordForm matcher_form;
+ matcher_form.signon_realm = kFakeSignonRealm;
+ PasswordStoreConsumerHelper consumer(&matches);
+ store->GetLogins(matcher_form, &consumer);
+ ui_test_utils::RunMessageLoop();
+}
+
+void RemoveLogin(PasswordStore* store, const PasswordForm& form) {
+ ASSERT_TRUE(store);
+ base::WaitableEvent wait_event(true, false);
+ store->RemoveLogin(form);
+ store->ScheduleTask(NewRunnableFunction(&PasswordStoreCallback, &wait_event));
+ wait_event.Wait();
+}
+
+void RemoveLogins(PasswordStore* store) {
+ std::vector<PasswordForm> forms;
+ GetLogins(store, forms);
+ for (std::vector<PasswordForm>::iterator it = forms.begin();
+ it != forms.end(); ++it) {
+ RemoveLogin(store, *it);
+ }
+}
+
+void SetPassphrase(int index, const std::string& passphrase, bool is_creation) {
+ test()->GetProfile(index)->GetProfileSyncService("")->SetPassphrase(
+ passphrase, true, is_creation);
+}
+
+PasswordStore* GetPasswordStore(int index) {
+ return test()->GetProfile(index)->GetPasswordStore(Profile::IMPLICIT_ACCESS);
+}
+
+PasswordStore* GetVerifierPasswordStore() {
+ return test()->verifier()->GetPasswordStore(Profile::IMPLICIT_ACCESS);
+}
+
+bool ProfileContainsSamePasswordFormsAsVerifier(int index) {
+ std::vector<PasswordForm> verifier_forms;
+ std::vector<PasswordForm> forms;
+ GetLogins(GetVerifierPasswordStore(), verifier_forms);
+ GetLogins(GetPasswordStore(index), forms);
+ bool result = ContainsSamePasswordForms(verifier_forms, forms);
+ if (!result) {
+ LOG(ERROR) << "Password forms in Verifier Profile:";
+ for (std::vector<PasswordForm>::iterator it = verifier_forms.begin();
+ it != verifier_forms.end(); ++it) {
+ LOG(ERROR) << *it << std::endl;
+ }
+ LOG(ERROR) << "Password forms in Profile" << index << ":";
+ for (std::vector<PasswordForm>::iterator it = forms.begin();
+ it != forms.end(); ++it) {
+ LOG(ERROR) << *it << std::endl;
+ }
+ }
+ return result;
+}
+
+bool ProfilesContainSamePasswordForms(int index_a, int index_b) {
+ std::vector<PasswordForm> forms_a;
+ std::vector<PasswordForm> forms_b;
+ GetLogins(GetPasswordStore(index_a), forms_a);
+ GetLogins(GetPasswordStore(index_b), forms_b);
+ bool result = ContainsSamePasswordForms(forms_a, forms_b);
+ if (!result) {
+ LOG(ERROR) << "Password forms in Profile" << index_a << ":";
+ for (std::vector<PasswordForm>::iterator it = forms_a.begin();
+ it != forms_a.end(); ++it) {
+ LOG(ERROR) << *it << std::endl;
+ }
+ LOG(ERROR) << "Password forms in Profile" << index_b << ":";
+ for (std::vector<PasswordForm>::iterator it = forms_b.begin();
+ it != forms_b.end(); ++it) {
+ LOG(ERROR) << *it << std::endl;
+ }
+ }
+ return result;
+}
+
+bool AllProfilesContainSamePasswordFormsAsVerifier() {
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (!ProfileContainsSamePasswordFormsAsVerifier(i)) {
+ LOG(ERROR) << "Profile " << i << " does not contain the same password "
+ " forms as the verifier.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AllProfilesContainSamePasswordForms() {
+ for (int i = 1; i < test()->num_clients(); ++i) {
+ if (!ProfilesContainSamePasswordForms(0, i)) {
+ LOG(ERROR) << "Profile " << i << " does not contain the same password "
+ " forms as Profile 0.";
+ return false;
+ }
+ }
+ return true;
+}
+
+int GetPasswordCount(int index) {
+ std::vector<PasswordForm> forms;
+ GetLogins(GetPasswordStore(index), forms);
+ return forms.size();
+}
+
+int GetVerifierPasswordCount() {
+ std::vector<PasswordForm> verifier_forms;
+ GetLogins(GetVerifierPasswordStore(), verifier_forms);
+ return verifier_forms.size();
+}
+
+PasswordForm CreateTestPasswordForm(int index) {
+ PasswordForm form;
+ form.signon_realm = kFakeSignonRealm;
+ form.origin = GURL(base::StringPrintf(kIndexedFakeOrigin, index));
+ form.username_value = ASCIIToUTF16(base::StringPrintf("username%d", index));
+ form.password_value = ASCIIToUTF16(base::StringPrintf("password%d", index));
+ return form;
+}
+
+} // namespace passwords_helper
diff --git a/chrome/browser/sync/test/live_sync/passwords_helper.h b/chrome/browser/sync/test/live_sync/passwords_helper.h
new file mode 100644
index 0000000..296d2b9
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/passwords_helper.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PASSWORDS_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PASSWORDS_HELPER_H_
+#pragma once
+
+#include <vector>
+
+#include "base/time.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "webkit/glue/password_form.h"
+
+class PasswordStore;
+
+namespace passwords_helper {
+
+// Adds the login held in |form| to the password store |store|. Even though
+// logins are normally added asynchronously, this method will block until the
+// login is added.
+void AddLogin(PasswordStore* store, const webkit_glue::PasswordForm& form);
+
+// Update the data held in password store |store| with a modified |form|.
+// This method blocks until the operation is complete.
+void UpdateLogin(PasswordStore* store, const webkit_glue::PasswordForm& form);
+
+// Searches |store| for all logins matching a fake signon realm used only by
+// LivePasswordsSyncTest and adds the results to |matches|. Note that the
+// caller is responsible for deleting the forms added to |matches|.
+void GetLogins(PasswordStore* store,
+ std::vector<webkit_glue::PasswordForm>& matches);
+
+// Removes the login held in |form| from the password store |store|. This
+// method blocks until the operation is complete.
+void RemoveLogin(PasswordStore* store, const webkit_glue::PasswordForm& form);
+
+// Removes all password forms from the password store |store|.
+void RemoveLogins(PasswordStore* store);
+
+// Sets the cryptographer's passphrase for the profile at index |index| to
+// |passphrase|. |is_creation| is true if a new passphrase is being set up
+// and false otherwise.
+void SetPassphrase(int index, const std::string& passphrase, bool is_creation);
+
+// Gets the password store of the profile with index |index|.
+PasswordStore* GetPasswordStore(int index);
+
+// Gets the password store of the verifier profile.
+PasswordStore* GetVerifierPasswordStore();
+
+// Returns true iff the profile with index |index| contains the same password
+// forms as the verifier profile.
+bool ProfileContainsSamePasswordFormsAsVerifier(int index);
+
+// Returns true iff the profile with index |index_a| contains the same
+// password forms as the profile with index |index_b|.
+bool ProfilesContainSamePasswordForms(int index_a, int index_b);
+
+// Returns true iff all profiles contain the same password forms as the
+// verifier profile.
+bool AllProfilesContainSamePasswordFormsAsVerifier();
+
+// Returns true iff all profiles contain the same password forms.
+bool AllProfilesContainSamePasswordForms();
+
+// Returns the number of forms in the password store of the profile with index
+// |index|.
+int GetPasswordCount(int index);
+
+// Returns the number of forms in the password store of the verifier profile.
+int GetVerifierPasswordCount();
+
+// Creates a test password form with a well known fake signon realm used only
+// by LivePasswordsSyncTest based on |index|.
+webkit_glue::PasswordForm CreateTestPasswordForm(int index);
+
+} // namespace passwords_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PASSWORDS_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/performance/autofill_sync_perf_test.cc b/chrome/browser/sync/test/live_sync/performance/autofill_sync_perf_test.cc
new file mode 100644
index 0000000..364019d
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/autofill_sync_perf_test.cc
@@ -0,0 +1,198 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/autofill_common_test.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/autofill_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h"
+#include "chrome/browser/webdata/autofill_entry.h"
+
+using autofill_helper::AllProfilesMatch;
+using autofill_helper::GetAllKeys;
+using autofill_helper::GetAllProfiles;
+using autofill_helper::GetKeyCount;
+using autofill_helper::GetProfileCount;
+using autofill_helper::RemoveKey;
+using autofill_helper::SetProfiles;
+
+static const int kNumKeys = 150;
+static const int kNumProfiles = 150;
+
+class AutofillSyncPerfTest : public LiveSyncTest {
+ public:
+ AutofillSyncPerfTest()
+ : LiveSyncTest(TWO_CLIENT),
+ guid_number_(0),
+ name_number_(0),
+ value_number_(0) {}
+
+ // Adds |num_profiles| new autofill profiles to the sync profile |profile|.
+ void AddProfiles(int profile, int num_profiles);
+
+ // Updates all autofill profiles for the sync profile |profile|.
+ void UpdateProfiles(int profile);
+
+ // Removes all autofill profiles from |profile|.
+ void RemoveProfiles(int profile);
+
+ // Adds |num_keys| new autofill keys to the sync profile |profile|.
+ void AddKeys(int profile, int num_keys);
+
+ // Removes all autofill keys from |profile|.
+ void RemoveKeys(int profile);
+
+ private:
+ // Returns a new unique autofill profile.
+ const AutofillProfile NextAutofillProfile();
+
+ // Returns a new unique autofill key.
+ const AutofillKey NextAutofillKey();
+
+ // Returns an unused unique guid.
+ const std::string NextGUID();
+
+ // Returns a unique guid based on the input integer |n|.
+ const std::string IntToGUID(int n);
+
+ // Returns a new unused unique name.
+ const std::string NextName();
+
+ // Returns a unique name based on the input integer |n|.
+ const std::string IntToName(int n);
+
+ // Returns a new unused unique value for autofill entries.
+ const std::string NextValue();
+
+ // Returnes a unique value based on the input integer |n|.
+ const std::string IntToValue(int n);
+
+ int guid_number_;
+ int name_number_;
+ int value_number_;
+ DISALLOW_COPY_AND_ASSIGN(AutofillSyncPerfTest);
+};
+
+void AutofillSyncPerfTest::AddProfiles(int profile, int num_profiles) {
+ const std::vector<AutofillProfile*>& all_profiles =
+ GetAllProfiles(profile);
+ std::vector<AutofillProfile> autofill_profiles;
+ for (size_t i = 0; i < all_profiles.size(); ++i) {
+ autofill_profiles.push_back(*all_profiles[i]);
+ }
+ for (int i = 0; i < num_profiles; ++i) {
+ autofill_profiles.push_back(NextAutofillProfile());
+ }
+ SetProfiles(profile, &autofill_profiles);
+}
+
+void AutofillSyncPerfTest::UpdateProfiles(int profile) {
+ const std::vector<AutofillProfile*>& all_profiles =
+ GetAllProfiles(profile);
+ std::vector<AutofillProfile> autofill_profiles;
+ for (size_t i = 0; i < all_profiles.size(); ++i) {
+ autofill_profiles.push_back(*all_profiles[i]);
+ autofill_profiles.back().SetInfo(AutofillFieldType(NAME_FIRST),
+ UTF8ToUTF16(NextName()));
+ }
+ SetProfiles(profile, &autofill_profiles);
+}
+
+void AutofillSyncPerfTest::RemoveProfiles(int profile) {
+ std::vector<AutofillProfile> empty;
+ SetProfiles(profile, &empty);
+}
+
+void AutofillSyncPerfTest::AddKeys(int profile, int num_keys) {
+ std::set<AutofillKey> keys;
+ for (int i = 0; i < num_keys; ++i) {
+ keys.insert(NextAutofillKey());
+ }
+ autofill_helper::AddKeys(profile, keys);
+}
+
+void AutofillSyncPerfTest::RemoveKeys(int profile) {
+ std::set<AutofillEntry> keys = GetAllKeys(profile);
+ for (std::set<AutofillEntry>::const_iterator it = keys.begin();
+ it != keys.end(); ++it) {
+ RemoveKey(profile, it->key());
+ }
+}
+
+const AutofillProfile AutofillSyncPerfTest::NextAutofillProfile() {
+ AutofillProfile profile;
+ autofill_test::SetProfileInfoWithGuid(&profile, NextGUID().c_str(),
+ NextName().c_str(), "", "", "", "", "",
+ "", "", "", "", "", "", "");
+ return profile;
+}
+
+const AutofillKey AutofillSyncPerfTest::NextAutofillKey() {
+ return AutofillKey(NextName().c_str(), NextName().c_str());
+}
+
+const std::string AutofillSyncPerfTest::NextGUID() {
+ return IntToGUID(guid_number_++);
+}
+
+const std::string AutofillSyncPerfTest::IntToGUID(int n) {
+ return StringPrintf("00000000-0000-0000-0000-%012X", n);
+}
+
+const std::string AutofillSyncPerfTest::NextName() {
+ return IntToName(name_number_++);
+}
+
+const std::string AutofillSyncPerfTest::IntToName(int n) {
+ return StringPrintf("Name%d", n);
+}
+
+const std::string AutofillSyncPerfTest::NextValue() {
+ return IntToValue(value_number_++);
+}
+
+const std::string AutofillSyncPerfTest::IntToValue(int n) {
+ return StringPrintf("Value%d", n);
+}
+
+IN_PROC_BROWSER_TEST_F(AutofillSyncPerfTest, AutofillProfiles_P0) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // TCM ID - 7557873.
+ AddProfiles(0, kNumProfiles);
+ base::TimeDelta dt =
+ SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumProfiles, GetProfileCount(1));
+ SyncTimingHelper::PrintResult("autofill", "add_autofill_profiles", dt);
+
+ // TCM ID - 7549835.
+ UpdateProfiles(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumProfiles, GetProfileCount(1));
+ SyncTimingHelper::PrintResult("autofill", "update_autofill_profiles", dt);
+
+ // TCM ID - 7553678.
+ RemoveProfiles(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(0, GetProfileCount(1));
+ SyncTimingHelper::PrintResult("autofill", "delete_autofill_profiles", dt);
+}
+
+IN_PROC_BROWSER_TEST_F(AutofillSyncPerfTest, Autofill_P0) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddKeys(0, kNumKeys);
+ base::TimeDelta dt =
+ SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumKeys, GetKeyCount(1));
+ SyncTimingHelper::PrintResult("autofill", "add_autofill_keys", dt);
+
+ RemoveKeys(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(0, GetKeyCount(1));
+ SyncTimingHelper::PrintResult("autofill", "delete_autofill_keys", dt);
+}
diff --git a/chrome/browser/sync/test/live_sync/performance/bookmarks_sync_perf_test.cc b/chrome/browser/sync/test/live_sync/performance/bookmarks_sync_perf_test.cc
new file mode 100644
index 0000000..72103fa
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/bookmarks_sync_perf_test.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h"
+
+using bookmarks_helper::AddURL;
+using bookmarks_helper::AllModelsMatch;
+using bookmarks_helper::GetBookmarkBarNode;
+using bookmarks_helper::IndexedURL;
+using bookmarks_helper::IndexedURLTitle;
+using bookmarks_helper::Remove;
+using bookmarks_helper::SetURL;
+
+static const int kNumBookmarks = 150;
+
+class BookmarksSyncPerfTest : public LiveSyncTest {
+ public:
+ BookmarksSyncPerfTest()
+ : LiveSyncTest(TWO_CLIENT),
+ url_number_(0),
+ url_title_number_(0) {}
+
+ // Adds |num_urls| new unique bookmarks to the bookmark bar for |profile|.
+ void AddURLs(int profile, int num_urls);
+
+ // Updates the URL for all bookmarks in the bookmark bar for |profile|.
+ void UpdateURLs(int profile);
+
+ // Removes all bookmarks in the bookmark bar for |profile|.
+ void RemoveURLs(int profile);
+
+ // Returns the number of bookmarks stored in the bookmark bar for |profile|.
+ int GetURLCount(int profile);
+
+ private:
+ // Returns a new unique bookmark URL.
+ std::string NextIndexedURL();
+
+ // Returns a new unique bookmark title.
+ std::wstring NextIndexedURLTitle();
+
+ int url_number_;
+ int url_title_number_;
+ DISALLOW_COPY_AND_ASSIGN(BookmarksSyncPerfTest);
+};
+
+void BookmarksSyncPerfTest::AddURLs(int profile, int num_urls) {
+ for (int i = 0; i < num_urls; ++i) {
+ ASSERT_TRUE(AddURL(
+ profile, 0, NextIndexedURLTitle(), GURL(NextIndexedURL())) != NULL);
+ }
+}
+
+void BookmarksSyncPerfTest::UpdateURLs(int profile) {
+ for (int i = 0;
+ i < GetBookmarkBarNode(profile)->child_count();
+ ++i) {
+ ASSERT_TRUE(SetURL(profile,
+ GetBookmarkBarNode(profile)->GetChild(i),
+ GURL(NextIndexedURL())));
+ }
+}
+
+void BookmarksSyncPerfTest::RemoveURLs(int profile) {
+ while (!GetBookmarkBarNode(profile)->empty()) {
+ Remove(profile, GetBookmarkBarNode(profile), 0);
+ }
+}
+
+int BookmarksSyncPerfTest::GetURLCount(int profile) {
+ return GetBookmarkBarNode(profile)->child_count();
+}
+
+std::string BookmarksSyncPerfTest::NextIndexedURL() {
+ return IndexedURL(url_number_++);
+}
+
+std::wstring BookmarksSyncPerfTest::NextIndexedURLTitle() {
+ return IndexedURLTitle(url_title_number_++);
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarksSyncPerfTest, P0) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // TCM ID - 7556828.
+ AddURLs(0, kNumBookmarks);
+ base::TimeDelta dt =
+ SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumBookmarks, GetURLCount(1));
+ SyncTimingHelper::PrintResult("bookmarks", "add_bookmarks", dt);
+
+ // TCM ID - 7564762.
+ UpdateURLs(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumBookmarks, GetURLCount(1));
+ SyncTimingHelper::PrintResult("bookmarks", "update_bookmarks", dt);
+
+ // TCM ID - 7566626.
+ RemoveURLs(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(0, GetURLCount(1));
+ SyncTimingHelper::PrintResult("bookmarks", "delete_bookmarks", dt);
+}
diff --git a/chrome/browser/sync/test/live_sync/performance/extensions_sync_perf_test.cc b/chrome/browser/sync/test/live_sync/performance/extensions_sync_perf_test.cc
new file mode 100644
index 0000000..dc88286
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/extensions_sync_perf_test.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/extensions_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h"
+
+using extensions_helper::AllProfilesHaveSameExtensions;
+using extensions_helper::AllProfilesHaveSameExtensionsAsVerifier;
+using extensions_helper::DisableExtension;
+using extensions_helper::EnableExtension;
+using extensions_helper::GetInstalledExtensions;
+using extensions_helper::InstallExtension;
+using extensions_helper::InstallExtensionsPendingForSync;
+using extensions_helper::IsExtensionEnabled;
+using extensions_helper::UninstallExtension;
+
+// TODO(braffert): Replicate these tests for apps.
+
+static const int kNumExtensions = 150;
+
+class ExtensionsSyncPerfTest : public LiveSyncTest {
+ public:
+ ExtensionsSyncPerfTest()
+ : LiveSyncTest(TWO_CLIENT),
+ extension_number_(0) {}
+
+ // Adds |num_extensions| new unique extensions to |profile|.
+ void AddExtensions(int profile, int num_extensions);
+
+ // Updates the enabled/disabled state for all extensions in |profile|.
+ void UpdateExtensions(int profile);
+
+ // Uninstalls all currently installed extensions from |profile|.
+ void RemoveExtensions(int profile);
+
+ // Returns the number of currently installed extensions for |profile|.
+ int GetExtensionCount(int profile);
+
+ private:
+ int extension_number_;
+ DISALLOW_COPY_AND_ASSIGN(ExtensionsSyncPerfTest);
+};
+
+void ExtensionsSyncPerfTest::AddExtensions(int profile, int num_extensions) {
+ for (int i = 0; i < num_extensions; ++i) {
+ InstallExtension(GetProfile(profile), extension_number_++);
+ }
+}
+
+void ExtensionsSyncPerfTest::UpdateExtensions(int profile) {
+ std::vector<int> extensions = GetInstalledExtensions(GetProfile(profile));
+ for (std::vector<int>::iterator it = extensions.begin();
+ it != extensions.end(); ++it) {
+ if (IsExtensionEnabled(GetProfile(profile), *it)) {
+ DisableExtension(GetProfile(profile), *it);
+ } else {
+ EnableExtension(GetProfile(profile), *it);
+ }
+ }
+}
+
+int ExtensionsSyncPerfTest::GetExtensionCount(int profile) {
+ return GetInstalledExtensions(GetProfile(profile)).size();
+}
+
+void ExtensionsSyncPerfTest::RemoveExtensions(int profile) {
+ std::vector<int> extensions = GetInstalledExtensions(GetProfile(profile));
+ for (std::vector<int>::iterator it = extensions.begin();
+ it != extensions.end(); ++it) {
+ UninstallExtension(GetProfile(profile), *it);
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionsSyncPerfTest, P0) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ int num_default_extensions = GetExtensionCount(0);
+ int expected_extension_count = num_default_extensions + kNumExtensions;
+
+ // TCM ID - 7563874.
+ AddExtensions(0, kNumExtensions);
+ base::TimeDelta dt =
+ SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ InstallExtensionsPendingForSync(GetProfile(1));
+ ASSERT_EQ(expected_extension_count, GetExtensionCount(1));
+ SyncTimingHelper::PrintResult("extensions", "add_extensions", dt);
+
+ // TCM ID - 7655397.
+ UpdateExtensions(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(expected_extension_count, GetExtensionCount(1));
+ SyncTimingHelper::PrintResult("extensions", "update_extensions", dt);
+
+ // TCM ID - 7567721.
+ RemoveExtensions(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(num_default_extensions, GetExtensionCount(1));
+ SyncTimingHelper::PrintResult("extensions", "delete_extensions", dt);
+}
diff --git a/chrome/browser/sync/test/live_sync/performance/passwords_sync_perf_test.cc b/chrome/browser/sync/test/live_sync/performance/passwords_sync_perf_test.cc
new file mode 100644
index 0000000..f5dda87
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/passwords_sync_perf_test.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/password_manager/password_store.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/passwords_helper.h"
+#include "chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h"
+
+using passwords_helper::AddLogin;
+using passwords_helper::CreateTestPasswordForm;
+using passwords_helper::GetLogins;
+using passwords_helper::GetPasswordCount;
+using passwords_helper::GetPasswordStore;
+using passwords_helper::UpdateLogin;
+
+static const int kNumPasswords = 150;
+
+class PasswordsSyncPerfTest : public LiveSyncTest {
+ public:
+ PasswordsSyncPerfTest() : LiveSyncTest(TWO_CLIENT), password_number_(0) {}
+
+ // Adds |num_logins| new unique passwords to |profile|.
+ void AddLogins(int profile, int num_logins);
+
+ // Updates the password for all logins for |profile|.
+ void UpdateLogins(int profile);
+
+ // Removes all logins for |profile|.
+ void RemoveLogins(int profile);
+
+ private:
+ // Returns a new unique login.
+ webkit_glue::PasswordForm NextLogin();
+
+ // Returns a new unique password value.
+ std::string NextPassword();
+
+ int password_number_;
+ DISALLOW_COPY_AND_ASSIGN(PasswordsSyncPerfTest);
+};
+
+void PasswordsSyncPerfTest::AddLogins(int profile, int num_logins) {
+ for (int i = 0; i < num_logins; ++i) {
+ AddLogin(GetPasswordStore(profile), NextLogin());
+ }
+}
+
+void PasswordsSyncPerfTest::UpdateLogins(int profile) {
+ std::vector<webkit_glue::PasswordForm> logins;
+ GetLogins(GetPasswordStore(profile), logins);
+ for (std::vector<webkit_glue::PasswordForm>::iterator it = logins.begin();
+ it != logins.end(); ++it) {
+ (*it).password_value = ASCIIToUTF16(NextPassword());
+ UpdateLogin(GetPasswordStore(profile), (*it));
+ }
+}
+
+void PasswordsSyncPerfTest::RemoveLogins(int profile) {
+ passwords_helper::RemoveLogins(GetPasswordStore(profile));
+}
+
+webkit_glue::PasswordForm PasswordsSyncPerfTest::NextLogin() {
+ return CreateTestPasswordForm(password_number_++);
+}
+
+std::string PasswordsSyncPerfTest::NextPassword() {
+ return base::StringPrintf("password%d", password_number_++);
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordsSyncPerfTest, P0) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // TCM ID - 7367749.
+ AddLogins(0, kNumPasswords);
+ base::TimeDelta dt =
+ SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumPasswords, GetPasswordCount(1));
+ SyncTimingHelper::PrintResult("passwords", "add_passwords", dt);
+
+ // TCM ID - 7365093.
+ UpdateLogins(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumPasswords, GetPasswordCount(1));
+ SyncTimingHelper::PrintResult("passwords", "update_passwords", dt);
+
+ // TCM ID - 7557852
+ RemoveLogins(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(0, GetPasswordCount(1));
+ SyncTimingHelper::PrintResult("passwords", "delete_passwords", dt);
+}
diff --git a/chrome/browser/sync/test/live_sync/performance/sessions_sync_perf_test.cc b/chrome/browser/sync/test/live_sync/performance/sessions_sync_perf_test.cc
new file mode 100644
index 0000000..90f1a3f
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/sessions_sync_perf_test.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sessions_helper.h"
+
+using sessions_helper::GetLocalSession;
+using sessions_helper::GetSessionData;
+using sessions_helper::OpenMultipleTabs;
+using sessions_helper::WaitForTabsToLoad;
+
+static const int kNumTabs = 150;
+
+class SessionsSyncPerfTest: public LiveSyncTest {
+ public:
+ SessionsSyncPerfTest() : LiveSyncTest(TWO_CLIENT), url_number_(0) {}
+
+ // Opens |num_tabs| new tabs on |profile|.
+ void AddTabs(int profile, int num_tabs);
+
+ // Update all tabs in |profile| by visiting a new URL.
+ void UpdateTabs(int profile);
+
+ // Close all tabs in |profile|.
+ void RemoveTabs(int profile);
+
+ // Returns the number of open tabs in all sessions (local + foreign) for
+ // |profile|. Returns -1 on failure.
+ int GetTabCount(int profile);
+
+ private:
+ // Returns a new unique URL.
+ GURL NextURL();
+
+ // Returns a unique URL according to the integer |n|.
+ GURL IntToURL(int n);
+
+ int url_number_;
+ DISALLOW_COPY_AND_ASSIGN(SessionsSyncPerfTest);
+};
+
+void SessionsSyncPerfTest::AddTabs(int profile, int num_tabs) {
+ std::vector<GURL> urls;
+ for (int i = 0; i < num_tabs; ++i) {
+ urls.push_back(NextURL());
+ }
+ OpenMultipleTabs(profile, urls);
+}
+
+void SessionsSyncPerfTest::UpdateTabs(int profile) {
+ Browser* browser = GetBrowser(profile);
+ GURL url;
+ std::vector<GURL> urls;
+ for (int i = 0; i < browser->tab_count(); ++i) {
+ browser->SelectNumberedTab(i);
+ url = NextURL();
+ browser->OpenURL(
+ OpenURLParams(url, GURL("www.google.com"), CURRENT_TAB, 0));
+ urls.push_back(url);
+ }
+ WaitForTabsToLoad(profile, urls);
+}
+
+void SessionsSyncPerfTest::RemoveTabs(int profile) {
+ GetBrowser(profile)->CloseAllTabs();
+}
+
+int SessionsSyncPerfTest::GetTabCount(int profile) {
+ int tab_count = 0;
+ const SyncedSession* local_session;
+ SyncedSessionVector sessions;
+
+ if (!GetLocalSession(profile, &local_session)) {
+ VLOG(1) << "GetLocalSession returned false";
+ return -1;
+ }
+
+ if (!GetSessionData(profile, &sessions)) {
+ // Foreign session data may be empty. In this case we only count tabs in
+ // the local session.
+ VLOG(1) << "GetSessionData returned false";
+ }
+
+ sessions.push_back(local_session);
+ for (SyncedSessionVector::const_iterator it = sessions.begin();
+ it != sessions.end(); ++it) {
+ for (SessionWindowVector::const_iterator win_it = (*it)->windows.begin();
+ win_it != (*it)->windows.end();
+ ++win_it) {
+ tab_count += (*win_it)->tabs.size();
+ }
+ }
+ return tab_count;
+}
+
+GURL SessionsSyncPerfTest::NextURL() {
+ return IntToURL(url_number_++);
+}
+
+GURL SessionsSyncPerfTest::IntToURL(int n) {
+ return GURL(StringPrintf("http://history%d.google.com/", n));
+}
+
+IN_PROC_BROWSER_TEST_F(SessionsSyncPerfTest, P0) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddTabs(0, kNumTabs);
+ base::TimeDelta dt =
+ SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumTabs, GetTabCount(0));
+ ASSERT_EQ(kNumTabs, GetTabCount(1));
+ SyncTimingHelper::PrintResult("tabs", "add_tabs", dt);
+
+ UpdateTabs(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumTabs, GetTabCount(0));
+ ASSERT_EQ(kNumTabs, GetTabCount(1));
+ SyncTimingHelper::PrintResult("tabs", "update_tabs", dt);
+
+ RemoveTabs(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ // New tab page remains open on profile 0 after closing all tabs.
+ ASSERT_EQ(1, GetTabCount(0));
+ ASSERT_EQ(0, GetTabCount(1));
+ SyncTimingHelper::PrintResult("tabs", "delete_tabs", dt);
+}
diff --git a/chrome/browser/sync/test/live_sync/performance/sync_timing_helper.cc b/chrome/browser/sync/test/live_sync/performance/sync_timing_helper.cc
new file mode 100644
index 0000000..8d985f6
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/sync_timing_helper.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h"
+
+#include "base/string_number_conversions.h"
+#include "base/time.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+SyncTimingHelper::SyncTimingHelper() {}
+
+SyncTimingHelper::~SyncTimingHelper() {}
+
+// static
+base::TimeDelta SyncTimingHelper::TimeSyncCycle(
+ ProfileSyncServiceHarness* client) {
+ base::Time start = base::Time::Now();
+ EXPECT_TRUE(client->AwaitSyncCycleCompletion("Timing sync cycle."));
+ return base::Time::Now() - start;
+}
+
+// static
+base::TimeDelta SyncTimingHelper::TimeMutualSyncCycle(
+ ProfileSyncServiceHarness* client, ProfileSyncServiceHarness* partner) {
+ base::Time start = base::Time::Now();
+ EXPECT_TRUE(client->AwaitMutualSyncCycleCompletion(partner));
+ return base::Time::Now() - start;
+}
+
+// static
+base::TimeDelta SyncTimingHelper::TimeUntilQuiescence(
+ std::vector<ProfileSyncServiceHarness*>& clients) {
+ base::Time start = base::Time::Now();
+ EXPECT_TRUE(ProfileSyncServiceHarness::AwaitQuiescence(clients));
+ return base::Time::Now() - start;
+}
+
+// static
+void SyncTimingHelper::PrintResult(const std::string& measurement,
+ const std::string& trace,
+ const base::TimeDelta& dt) {
+ printf("*RESULT %s: %s= %s ms\n", measurement.c_str(), trace.c_str(),
+ base::IntToString(dt.InMillisecondsF()).c_str());
+}
diff --git a/chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h b/chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h
new file mode 100644
index 0000000..8d87aeb
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PERFORMANCE_SYNC_TIMING_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PERFORMANCE_SYNC_TIMING_HELPER_H_
+#pragma once
+
+#include "base/basictypes.h"
+
+#include <string>
+#include <vector>
+
+namespace base {
+class TimeDelta;
+}
+
+class ProfileSyncServiceHarness;
+
+class SyncTimingHelper {
+ public:
+ SyncTimingHelper();
+ ~SyncTimingHelper();
+
+ // Returns the time taken for |client| to complete a single sync cycle.
+ static base::TimeDelta TimeSyncCycle(ProfileSyncServiceHarness* client);
+
+ // Returns the time taken for both |client| and |partner| to complete a sync
+ // cycle.
+ static base::TimeDelta TimeMutualSyncCycle(
+ ProfileSyncServiceHarness* client, ProfileSyncServiceHarness* partner);
+
+ // Returns the time taken for all clients in |clients| to complete their
+ // respective sync cycles.
+ static base::TimeDelta TimeUntilQuiescence(
+ std::vector<ProfileSyncServiceHarness*>& clients);
+
+ // Print a timing measurement in a format appropriate for the chromium perf
+ // dashboard. Simplified version of methods defined in
+ // chrome/test/ui/ui_perf_test.{h,cc}.
+ static void PrintResult(const std::string& measurement,
+ const std::string& trace,
+ const base::TimeDelta& dt);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyncTimingHelper);
+};
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PERFORMANCE_SYNC_TIMING_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/performance/typed_urls_sync_perf_test.cc b/chrome/browser/sync/test/live_sync/performance/typed_urls_sync_perf_test.cc
new file mode 100644
index 0000000..1aa8535
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/performance/typed_urls_sync_perf_test.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/stringprintf.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/performance/sync_timing_helper.h"
+#include "chrome/browser/sync/test/live_sync/typed_urls_helper.h"
+
+using typed_urls_helper::AddUrlToHistory;
+using typed_urls_helper::AssertAllProfilesHaveSameURLsAsVerifier;
+using typed_urls_helper::DeleteUrlFromHistory;
+using typed_urls_helper::GetTypedUrlsFromClient;
+
+static const int kNumUrls = 150;
+
+class TypedUrlsSyncPerfTest : public LiveSyncTest {
+ public:
+ TypedUrlsSyncPerfTest()
+ : LiveSyncTest(TWO_CLIENT),
+ url_number_(0) {}
+
+ // Adds |num_urls| new unique typed urls to |profile|.
+ void AddURLs(int profile, int num_urls);
+
+ // Update all typed urls in |profile| by visiting them once again.
+ void UpdateURLs(int profile);
+
+ // Removes all typed urls for |profile|.
+ void RemoveURLs(int profile);
+
+ // Returns the number of typed urls stored in |profile|.
+ int GetURLCount(int profile);
+
+ private:
+ // Returns a new unique typed URL.
+ GURL NextURL();
+
+ // Returns a unique URL according to the integer |n|.
+ GURL IntToURL(int n);
+
+ int url_number_;
+ DISALLOW_COPY_AND_ASSIGN(TypedUrlsSyncPerfTest);
+};
+
+void TypedUrlsSyncPerfTest::AddURLs(int profile, int num_urls) {
+ for (int i = 0; i < num_urls; ++i) {
+ AddUrlToHistory(profile, NextURL());
+ }
+}
+
+void TypedUrlsSyncPerfTest::UpdateURLs(int profile) {
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(profile);
+ for (std::vector<history::URLRow>::const_iterator it = urls.begin();
+ it != urls.end(); ++it) {
+ AddUrlToHistory(profile, it->url());
+ }
+}
+
+void TypedUrlsSyncPerfTest::RemoveURLs(int profile) {
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(profile);
+ for (std::vector<history::URLRow>::const_iterator it = urls.begin();
+ it != urls.end(); ++it) {
+ DeleteUrlFromHistory(profile, it->url());
+ }
+}
+
+int TypedUrlsSyncPerfTest::GetURLCount(int profile) {
+ return GetTypedUrlsFromClient(profile).size();
+}
+
+GURL TypedUrlsSyncPerfTest::NextURL() {
+ return IntToURL(url_number_++);
+}
+
+GURL TypedUrlsSyncPerfTest::IntToURL(int n) {
+ return GURL(StringPrintf("http://history%d.google.com/", n));
+}
+
+IN_PROC_BROWSER_TEST_F(TypedUrlsSyncPerfTest, P0) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // TCM ID - 7985716.
+ AddURLs(0, kNumUrls);
+ base::TimeDelta dt =
+ SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumUrls, GetURLCount(1));
+ SyncTimingHelper::PrintResult("typed_urls", "add_typed_urls", dt);
+
+ // TCM ID - 7981755.
+ UpdateURLs(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(kNumUrls, GetURLCount(1));
+ SyncTimingHelper::PrintResult("typed_urls", "update_typed_urls", dt);
+
+ // TCM ID - 7651271.
+ RemoveURLs(0);
+ dt = SyncTimingHelper::TimeMutualSyncCycle(GetClient(0), GetClient(1));
+ ASSERT_EQ(0, GetURLCount(1));
+ SyncTimingHelper::PrintResult("typed_urls", "delete_typed_urls", dt);
+}
diff --git a/chrome/browser/sync/test/live_sync/preferences_helper.cc b/chrome/browser/sync/test/live_sync/preferences_helper.cc
new file mode 100644
index 0000000..89d59d5
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/preferences_helper.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/preferences_helper.h"
+
+#include "base/values.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+
+using sync_datatype_helper::test;
+
+namespace preferences_helper {
+
+PrefService* GetPrefs(int index) {
+ return test()->GetProfile(index)->GetPrefs();
+}
+
+PrefService* GetVerifierPrefs() {
+ return test()->verifier()->GetPrefs();
+}
+
+void ChangeBooleanPref(int index, const char* pref_name) {
+ bool new_value = !GetPrefs(index)->GetBoolean(pref_name);
+ GetPrefs(index)->SetBoolean(pref_name, new_value);
+ if (test()->use_verifier())
+ GetVerifierPrefs()->SetBoolean(pref_name, new_value);
+}
+
+void ChangeIntegerPref(int index, const char* pref_name, int new_value) {
+ GetPrefs(index)->SetInteger(pref_name, new_value);
+ if (test()->use_verifier())
+ GetVerifierPrefs()->SetInteger(pref_name, new_value);
+}
+
+void ChangeDoublePref(int index, const char* pref_name, double new_value) {
+ GetPrefs(index)->SetDouble(pref_name, new_value);
+ if (test()->use_verifier())
+ GetVerifierPrefs()->SetDouble(pref_name, new_value);
+}
+
+void ChangeStringPref(int index,
+ const char* pref_name,
+ const std::string& new_value) {
+ GetPrefs(index)->SetString(pref_name, new_value);
+ if (test()->use_verifier())
+ GetVerifierPrefs()->SetString(pref_name, new_value);
+}
+
+void AppendStringPref(int index,
+ const char* pref_name,
+ const std::string& append_value) {
+ ChangeStringPref(index,
+ pref_name,
+ GetPrefs(index)->GetString(pref_name) + append_value);
+}
+
+void ChangeFilePathPref(int index,
+ const char* pref_name,
+ const FilePath& new_value) {
+ GetPrefs(index)->SetFilePath(pref_name, new_value);
+ if (test()->use_verifier())
+ GetVerifierPrefs()->SetFilePath(pref_name, new_value);
+}
+
+void ChangeListPref(int index,
+ const char* pref_name,
+ const ListValue& new_value) {
+ {
+ ListPrefUpdate update(GetPrefs(index), pref_name);
+ ListValue* list = update.Get();
+ for (ListValue::const_iterator it = new_value.begin();
+ it != new_value.end();
+ ++it) {
+ list->Append((*it)->DeepCopy());
+ }
+ }
+
+ if (test()->use_verifier()) {
+ ListPrefUpdate update_verifier(GetVerifierPrefs(), pref_name);
+ ListValue* list_verifier = update_verifier.Get();
+ for (ListValue::const_iterator it = new_value.begin();
+ it != new_value.end();
+ ++it) {
+ list_verifier->Append((*it)->DeepCopy());
+ }
+ }
+}
+
+bool BooleanPrefMatches(const char* pref_name) {
+ bool reference_value;
+ if (test()->use_verifier()) {
+ reference_value = GetVerifierPrefs()->GetBoolean(pref_name);
+ } else {
+ reference_value = GetPrefs(0)->GetBoolean(pref_name);
+ }
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (reference_value != GetPrefs(i)->GetBoolean(pref_name)) {
+ LOG(ERROR) << "Boolean preference " << pref_name << " mismatched in"
+ << " profile " << i << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool IntegerPrefMatches(const char* pref_name) {
+ int reference_value;
+ if (test()->use_verifier()) {
+ reference_value = GetVerifierPrefs()->GetInteger(pref_name);
+ } else {
+ reference_value = GetPrefs(0)->GetInteger(pref_name);
+ }
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (reference_value != GetPrefs(i)->GetInteger(pref_name)) {
+ LOG(ERROR) << "Integer preference " << pref_name << " mismatched in"
+ << " profile " << i << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DoublePrefMatches(const char* pref_name) {
+ double reference_value;
+ if (test()->use_verifier()) {
+ reference_value = GetVerifierPrefs()->GetDouble(pref_name);
+ } else {
+ reference_value = GetPrefs(0)->GetDouble(pref_name);
+ }
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (reference_value != GetPrefs(i)->GetDouble(pref_name)) {
+ LOG(ERROR) << "Double preference " << pref_name << " mismatched in"
+ << " profile " << i << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool StringPrefMatches(const char* pref_name) {
+ std::string reference_value;
+ if (test()->use_verifier()) {
+ reference_value = GetVerifierPrefs()->GetString(pref_name);
+ } else {
+ reference_value = GetPrefs(0)->GetString(pref_name);
+ }
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (reference_value != GetPrefs(i)->GetString(pref_name)) {
+ LOG(ERROR) << "String preference " << pref_name << " mismatched in"
+ << " profile " << i << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FilePathPrefMatches(const char* pref_name) {
+ FilePath reference_value;
+ if (test()->use_verifier()) {
+ reference_value = GetVerifierPrefs()->GetFilePath(pref_name);
+ } else {
+ reference_value = GetPrefs(0)->GetFilePath(pref_name);
+ }
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (reference_value != GetPrefs(i)->GetFilePath(pref_name)) {
+ LOG(ERROR) << "FilePath preference " << pref_name << " mismatched in"
+ << " profile " << i << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ListPrefMatches(const char* pref_name) {
+ const ListValue* reference_value;
+ if (test()->use_verifier()) {
+ reference_value = GetVerifierPrefs()->GetList(pref_name);
+ } else {
+ reference_value = GetPrefs(0)->GetList(pref_name);
+ }
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ if (!reference_value->Equals(GetPrefs(i)->GetList(pref_name))) {
+ LOG(ERROR) << "List preference " << pref_name << " mismatched in"
+ << " profile " << i << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace preferences_helper
diff --git a/chrome/browser/sync/test/live_sync/preferences_helper.h b/chrome/browser/sync/test/live_sync/preferences_helper.h
new file mode 100644
index 0000000..6fa4ce0
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/preferences_helper.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PREFERENCES_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PREFERENCES_HELPER_H_
+#pragma once
+
+#include "base/file_path.h"
+#include "base/values.h"
+
+#include <string>
+
+class PrefService;
+
+namespace preferences_helper {
+
+// Used to access the preferences within a particular sync profile.
+PrefService* GetPrefs(int index);
+
+// Used to access the preferences within the verifier sync profile.
+PrefService* GetVerifierPrefs();
+
+// Inverts the value of the boolean preference with name |pref_name| in the
+// profile with index |index|. Also inverts its value in |verifier| if
+// DisableVerifier() hasn't been called.
+void ChangeBooleanPref(int index, const char* pref_name);
+
+// Changes the value of the integer preference with name |pref_name| in the
+// profile with index |index| to |new_value|. Also changes its value in
+// |verifier| if DisableVerifier() hasn't been called.
+void ChangeIntegerPref(int index, const char* pref_name, int new_value);
+
+// Changes the value of the double preference with name |pref_name| in the
+// profile with index |index| to |new_value|. Also changes its value in
+// |verifier| if DisableVerifier() hasn't been called.
+void ChangeDoublePref(int index, const char* pref_name, double new_value);
+
+// Changes the value of the string preference with name |pref_name| in the
+// profile with index |index| to |new_value|. Also changes its value in
+// |verifier| if DisableVerifier() hasn't been called.
+void ChangeStringPref(int index,
+ const char* pref_name,
+ const std::string& new_value);
+
+// Modifies the value of the string preference with name |pref_name| in the
+// profile with index |index| by appending |append_value| to its current
+// value. Also changes its value in |verifier| if DisableVerifier() hasn't
+// been called.
+void AppendStringPref(int index,
+ const char* pref_name,
+ const std::string& append_value);
+
+// Changes the value of the file path preference with name |pref_name| in the
+// profile with index |index| to |new_value|. Also changes its value in
+// |verifier| if DisableVerifier() hasn't been called.
+void ChangeFilePathPref(int index,
+ const char* pref_name,
+ const FilePath& new_value);
+
+// Changes the value of the list preference with name |pref_name| in the
+// profile with index |index| to |new_value|. Also changes its value in
+// |verifier| if DisableVerifier() hasn't been called.
+void ChangeListPref(int index,
+ const char* pref_name,
+ const ListValue& new_value);
+
+// Used to verify that the boolean preference with name |pref_name| has the
+// same value across all profiles. Also checks |verifier| if DisableVerifier()
+// hasn't been called.
+bool BooleanPrefMatches(const char* pref_name) WARN_UNUSED_RESULT;
+
+// Used to verify that the integer preference with name |pref_name| has the
+// same value across all profiles. Also checks |verifier| if DisableVerifier()
+// hasn't been called.
+bool IntegerPrefMatches(const char* pref_name) WARN_UNUSED_RESULT;
+
+// Used to verify that the double preference with name |pref_name| has the
+// same value across all profiles. Also checks |verifier| if DisableVerifier()
+// hasn't been called.
+bool DoublePrefMatches(const char* pref_name) WARN_UNUSED_RESULT;
+
+// Used to verify that the string preference with name |pref_name| has the
+// same value across all profiles. Also checks |verifier| if DisableVerifier()
+// hasn't been called.
+bool StringPrefMatches(const char* pref_name) WARN_UNUSED_RESULT;
+
+// Used to verify that the file path preference with name |pref_name| has the
+// same value across all profiles. Also checks |verifier| if DisableVerifier()
+// hasn't been called.
+bool FilePathPrefMatches(const char* pref_name) WARN_UNUSED_RESULT;
+
+// Used to verify that the list preference with name |pref_name| has the
+// same value across all profiles. Also checks |verifier| if DisableVerifier()
+// hasn't been called.
+bool ListPrefMatches(const char* pref_name) WARN_UNUSED_RESULT;
+
+} // namespace preferences_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_PREFERENCES_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/sessions_helper.cc b/chrome/browser/sync/test/live_sync/sessions_helper.cc
new file mode 100644
index 0000000..af18366
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sessions_helper.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/sessions_helper.h"
+
+#include "base/stl_util.h"
+#include "base/test/test_timeouts.h"
+#include "base/time.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/glue/session_model_associator.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "googleurl/src/gurl.h"
+
+using sync_datatype_helper::test;
+
+namespace sessions_helper {
+
+bool GetLocalSession(int index, const SyncedSession** session) {
+ return test()->GetProfile(index)->GetProfileSyncService()->
+ GetSessionModelAssociator()->GetLocalSession(session);
+}
+
+bool ModelAssociatorHasTabWithUrl(int index, const GURL& url) {
+ ui_test_utils::RunAllPendingInMessageLoop();
+ const SyncedSession* local_session;
+ if (!GetLocalSession(index, &local_session)) {
+ return false;
+ }
+
+ if (local_session->windows.size() == 0) {
+ VLOG(1) << "Empty windows vector";
+ return false;
+ }
+
+ int nav_index;
+ TabNavigation nav;
+ for (SessionWindowVector::const_iterator it =
+ local_session->windows.begin(); it != local_session->windows.end();
+ ++it) {
+ if ((*it)->tabs.size() == 0) {
+ VLOG(1) << "Empty tabs vector";
+ continue;
+ }
+ for (std::vector<SessionTab*>::const_iterator tab_it =
+ (*it)->tabs.begin(); tab_it != (*it)->tabs.end(); ++tab_it) {
+ if ((*tab_it)->navigations.size() == 0) {
+ VLOG(1) << "Empty navigations vector";
+ continue;
+ }
+ nav_index = (*tab_it)->current_navigation_index;
+ nav = (*tab_it)->navigations[nav_index];
+ if (nav.virtual_url() == url) {
+ VLOG(1) << "Found tab with url " << url.spec();
+ if (nav.title().empty()) {
+ VLOG(1) << "No title!";
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+ VLOG(1) << "Could not find tab with url " << url.spec();
+ return false;
+}
+
+bool OpenTab(int index, const GURL& url) {
+ VLOG(1) << "Opening tab: " << url.spec() << " using profile " << index << ".";
+ test()->GetBrowser(index)->ShowSingletonTab(url);
+ return WaitForTabsToLoad(index, std::vector<GURL>(1, url));
+}
+
+bool OpenMultipleTabs(int index, const std::vector<GURL>& urls) {
+ Browser* browser = test()->GetBrowser(index);
+ for (std::vector<GURL>::const_iterator it = urls.begin();
+ it != urls.end(); ++it) {
+ VLOG(1) << "Opening tab: " << it->spec() << " using profile " << index
+ << ".";
+ browser->ShowSingletonTab(*it);
+ }
+ return WaitForTabsToLoad(index, urls);
+}
+
+bool WaitForTabsToLoad(int index, const std::vector<GURL>& urls) {
+ VLOG(1) << "Waiting for session to propagate to associator.";
+ static const int timeout_milli = TestTimeouts::action_max_timeout_ms();
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ base::TimeTicks end_time = start_time +
+ base::TimeDelta::FromMilliseconds(timeout_milli);
+ bool found;
+ for (std::vector<GURL>::const_iterator it = urls.begin();
+ it != urls.end(); ++it) {
+ found = false;
+ while (!found) {
+ found = ModelAssociatorHasTabWithUrl(index, *it);
+ if (base::TimeTicks::Now() >= end_time) {
+ LOG(ERROR) << "Failed to find all tabs after " << timeout_milli/1000.0
+ << " seconds.";
+ return false;
+ }
+ if (!found) {
+ test()->GetProfile(index)->GetProfileSyncService()->
+ GetSessionModelAssociator()->
+ BlockUntilLocalChangeForTest(timeout_milli);
+ ui_test_utils::RunMessageLoop();
+ }
+ }
+ }
+ return true;
+}
+
+bool GetLocalWindows(int index, SessionWindowVector& local_windows) {
+ // The local session provided by GetLocalSession is owned, and has lifetime
+ // controlled, by the model associator, so we must make our own copy.
+ const SyncedSession* local_session;
+ if (!GetLocalSession(index, &local_session)) {
+ return false;
+ }
+ for (size_t w = 0; w < local_session->windows.size(); ++w) {
+ const SessionWindow& window = *local_session->windows.at(w);
+ SessionWindow* new_window = new SessionWindow();
+ for (size_t t = 0; t < window.tabs.size(); ++t) {
+ const SessionTab& tab = *window.tabs.at(t);
+ SessionTab* new_tab = new SessionTab();
+ new_tab->navigations.resize(tab.navigations.size());
+ std::copy(tab.navigations.begin(), tab.navigations.end(),
+ new_tab->navigations.begin());
+ new_window->tabs.push_back(new_tab);
+ }
+ local_windows.push_back(new_window);
+ }
+
+ // Sort the windows so their order is deterministic.
+ SortSessionWindows(local_windows);
+
+ return true;
+}
+
+bool OpenTabAndGetLocalWindows(int index,
+ const GURL& url,
+ SessionWindowVector& local_windows) {
+ if (!OpenTab(index, url)) {
+ return false;
+ }
+ return GetLocalWindows(index, local_windows);
+}
+
+bool CheckInitialState(int index) {
+ if (0 != GetNumWindows(index))
+ return false;
+ if (0 != GetNumForeignSessions(index))
+ return false;
+ return true;
+}
+
+int GetNumWindows(int index) {
+ const SyncedSession* local_session;
+ if (!GetLocalSession(index, &local_session)) {
+ return 0;
+ }
+ return local_session->windows.size();
+}
+
+int GetNumForeignSessions(int index) {
+ SyncedSessionVector sessions;
+ if (!test()->GetProfile(index)->GetProfileSyncService()->
+ GetSessionModelAssociator()->GetAllForeignSessions(&sessions))
+ return 0;
+ return sessions.size();
+}
+
+bool GetSessionData(int index, SyncedSessionVector* sessions) {
+ if (!test()->GetProfile(index)->GetProfileSyncService()->
+ GetSessionModelAssociator()->GetAllForeignSessions(sessions))
+ return false;
+ SortSyncedSessions(sessions);
+ return true;
+}
+
+bool CompareSessionWindows(SessionWindow* lhs, SessionWindow* rhs) {
+ if (!lhs ||
+ !rhs ||
+ lhs->tabs.size() < 1 ||
+ rhs->tabs.size() < 1 ||
+ lhs->tabs[0]->navigations.size() < 1 ||
+ rhs->tabs[0]->navigations.size() < 1) {
+ // Catchall for uncomparable data.
+ return false;
+ }
+
+ return lhs->tabs[0]->navigations[0].virtual_url() <
+ rhs->tabs[0]->navigations[0].virtual_url();
+}
+
+void SortSessionWindows(SessionWindowVector& windows) {
+ std::sort(windows.begin(), windows.end(),
+ CompareSessionWindows);
+}
+
+bool CompareSyncedSessions(const SyncedSession* lhs, const SyncedSession* rhs) {
+ if (!lhs ||
+ !rhs ||
+ lhs->windows.size() < 1 ||
+ rhs->windows.size() < 1) {
+ // Catchall for uncomparable data.
+ return false;
+ }
+
+ return CompareSessionWindows(lhs->windows[0], rhs->windows[0]);
+}
+
+void SortSyncedSessions(SyncedSessionVector* sessions) {
+ std::sort(sessions->begin(), sessions->end(),
+ CompareSyncedSessions);
+}
+
+bool NavigationEquals(const TabNavigation& expected,
+ const TabNavigation& actual) {
+ if (expected.virtual_url() != actual.virtual_url()) {
+ LOG(ERROR) << "Expected url " << expected.virtual_url()
+ << ", actual " << actual.virtual_url();
+ return false;
+ }
+ if (expected.referrer() != actual.referrer()) {
+ LOG(ERROR) << "Expected referrer " << expected.referrer()
+ << ", actual " << actual.referrer();
+ return false;
+ }
+ if (expected.title() != actual.title()) {
+ LOG(ERROR) << "Expected title " << expected.title()
+ << ", actual " << actual.title();
+ return false;
+ }
+ if (expected.transition() != actual.transition()) {
+ LOG(ERROR) << "Expected transition " << expected.transition()
+ << ", actual " << actual.transition();
+ return false;
+ }
+ return true;
+}
+
+bool WindowsMatch(const SessionWindowVector& win1,
+ const SessionWindowVector& win2) {
+ SessionTab* client0_tab;
+ SessionTab* client1_tab;
+ if (win1.size() != win2.size())
+ return false;
+ for (size_t i = 0; i < win1.size(); ++i) {
+ if (win1[i]->tabs.size() != win2[i]->tabs.size())
+ return false;
+ for (size_t j = 0; j < win1[i]->tabs.size(); ++j) {
+ client0_tab = win1[i]->tabs[j];
+ client1_tab = win2[i]->tabs[j];
+ for (size_t k = 0; k < client0_tab->navigations.size(); ++k) {
+ if (!NavigationEquals(client0_tab->navigations[k],
+ client1_tab->navigations[k])) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CheckForeignSessionsAgainst(
+ int index,
+ const std::vector<SessionWindowVector*>& windows) {
+ SyncedSessionVector sessions;
+ if (!GetSessionData(index, &sessions))
+ return false;
+ if ((size_t)(test()->num_clients()-1) != sessions.size())
+ return false;
+
+ int window_index = 0;
+ for (size_t j = 0; j < sessions.size(); ++j, ++window_index) {
+ if (window_index == index)
+ window_index++; // Skip self.
+ if (!WindowsMatch(sessions[j]->windows, *windows[window_index]))
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace sessions_helper
diff --git a/chrome/browser/sync/test/live_sync/sessions_helper.h b/chrome/browser/sync/test/live_sync/sessions_helper.h
new file mode 100644
index 0000000..f508b4b
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sessions_helper.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SESSIONS_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SESSIONS_HELPER_H_
+#pragma once
+
+#include <algorithm>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/sessions/session_types.h"
+#include "chrome/browser/sync/engine/nigori_util.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+class GURL;
+
+typedef std::vector<const SyncedSession*> SyncedSessionVector;
+typedef std::vector<SessionWindow*> SessionWindowVector;
+
+namespace sessions_helper {
+
+// Copies the local session windows of profile |index| to |local_windows|.
+// Returns true if successful.
+bool GetLocalWindows(int index, SessionWindowVector& local_windows);
+
+// Creates and verifies the creation of a new window for profile |index| with
+// one tab displaying |url|. Copies the SessionWindow associated with the new
+// window to |local_windows|. Returns true if successful.
+bool OpenTabAndGetLocalWindows(int index,
+ const GURL& url,
+ SessionWindowVector& local_windows);
+
+// Checks that window count and foreign session count are 0.
+bool CheckInitialState(int index);
+
+// Returns number of open windows for a profile.
+int GetNumWindows(int index);
+
+// Returns number of foreign sessions for a profile.
+int GetNumForeignSessions(int index);
+
+// Fills the sessions vector with the model associator's foreign session data.
+// Caller owns |sessions|, but not SyncedSessions objects within.
+bool GetSessionData(int index, SyncedSessionVector* sessions);
+
+// Compare session windows based on their first tab's url.
+// Returns true if the virtual url of the lhs is < the rhs.
+bool CompareSessionWindows(SessionWindow* lhs, SessionWindow* rhs);
+
+// Sort session windows using our custom comparator (first tab url
+// comparison).
+void SortSessionWindows(SessionWindowVector& windows);
+
+// Compares a foreign session based on the first session window.
+// Returns true based on the comparison of the session windows.
+bool CompareSyncedSessions(const SyncedSession* lhs, const SyncedSession* rhs);
+
+// Sort a SyncedSession vector using our custom SyncedSession comparator.
+void SortSyncedSessions(SyncedSessionVector* sessions);
+
+// Compares two tab navigations base on the parameters we sync.
+// (Namely, we don't sync state or type mask)
+bool NavigationEquals(const TabNavigation& expected,
+ const TabNavigation& actual);
+
+// Verifies that two SessionWindows match.
+// Returns:
+// - true if all the following match:
+// 1. number of SessionWindows,
+// 2. number of tabs per SessionWindow,
+// 3. number of tab navigations per tab,
+// 4. actual tab navigations contents
+// - false otherwise.
+bool WindowsMatch(const SessionWindowVector& win1,
+ const SessionWindowVector& win2);
+
+// Retrieves the foreign sessions for a particular profile and compares them
+// with a reference SessionWindow list.
+// Returns true if the session windows of the foreign session matches the
+// reference.
+bool CheckForeignSessionsAgainst(
+ int index,
+ const std::vector<SessionWindowVector*>& windows);
+
+// Open a single tab and block until the session model associator is aware
+// of it. Returns true upon success, false otherwise.
+bool OpenTab(int index, const GURL& url);
+
+// Open multiple tabs and block until the session model associator is aware
+// of all of them. Returns true on success, false on failure.
+bool OpenMultipleTabs(int index, const std::vector<GURL>& urls);
+
+// Wait for a session change to propagate to the model associator. Will not
+// return until each url in |urls| has been found.
+bool WaitForTabsToLoad(int index, const std::vector<GURL>& urls);
+
+// Check if the session model associator's knows that the current open tab
+// has this url.
+bool ModelAssociatorHasTabWithUrl(int index, const GURL& url);
+
+// Stores a pointer to the local session for a given profile in |session|.
+// Returns true on success, false on failure.
+bool GetLocalSession(int index, const SyncedSession** session);
+
+} // namespace sessions_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SESSIONS_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/single_client_apps_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_apps_sync_test.cc
new file mode 100644
index 0000000..25b024a
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_apps_sync_test.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/apps_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using apps_helper::AllProfilesHaveSameAppsAsVerifier;
+using apps_helper::InstallApp;
+
+class SingleClientAppsSyncTest : public LiveSyncTest {
+ public:
+ SingleClientAppsSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+
+ virtual ~SingleClientAppsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientAppsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithNoApps) {
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithSomeApps) {
+ ASSERT_TRUE(SetupClients());
+
+ const int kNumApps = 5;
+ for (int i = 0; i < kNumApps; ++i) {
+ InstallApp(GetProfile(0), i);
+ InstallApp(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, InstallSomeApps) {
+ ASSERT_TRUE(SetupSync());
+
+ const int kNumApps = 5;
+ for (int i = 0; i < kNumApps; ++i) {
+ InstallApp(GetProfile(0), i);
+ InstallApp(verifier(), i);
+ }
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for app changes."));
+
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
diff --git a/chrome/browser/sync/test/live_sync/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_bookmarks_sync_test.cc
new file mode 100644
index 0000000..c4bb875
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_bookmarks_sync_test.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using bookmarks_helper::AddFolder;
+using bookmarks_helper::AddURL;
+using bookmarks_helper::GetBookmarkBarNode;
+using bookmarks_helper::GetOtherNode;
+using bookmarks_helper::ModelMatchesVerifier;
+using bookmarks_helper::Move;
+using bookmarks_helper::Remove;
+using bookmarks_helper::SetTitle;
+
+class SingleClientBookmarksSyncTest : public LiveSyncTest {
+ public:
+ SingleClientBookmarksSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+ virtual ~SingleClientBookmarksSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientBookmarksSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, OfflineToOnline) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ DisableNetwork(GetProfile(0));
+ const BookmarkNode* node = AddFolder(0, L"title");
+ SetTitle(0, node, L"new_title");
+ ASSERT_FALSE(GetClient(0)->AwaitSyncCycleCompletion("Offline state change."));
+ ASSERT_EQ(ProfileSyncService::Status::OFFLINE_UNSYNCED,
+ GetClient(0)->GetStatus().summary);
+
+ EnableNetwork(GetProfile(0));
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Commit changes."));
+ ASSERT_EQ(ProfileSyncService::Status::READY,
+ GetClient(0)->GetStatus().summary);
+ ASSERT_TRUE(ModelMatchesVerifier(0));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, Sanity) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ // Starting state:
+ // other_node
+ // -> top
+ // -> tier1_a
+ // -> http://mail.google.com "tier1_a_url0"
+ // -> http://www.pandora.com "tier1_a_url1"
+ // -> http://www.facebook.com "tier1_a_url2"
+ // -> tier1_b
+ // -> http://www.nhl.com "tier1_b_url0"
+ const BookmarkNode* top = AddFolder(0, GetOtherNode(0), 0, L"top");
+ const BookmarkNode* tier1_a = AddFolder(0, top, 0, L"tier1_a");
+ const BookmarkNode* tier1_b = AddFolder(0, top, 1, L"tier1_b");
+ const BookmarkNode* tier1_a_url0 = AddURL(
+ 0, tier1_a, 0, L"tier1_a_url0", GURL("http://mail.google.com"));
+ const BookmarkNode* tier1_a_url1 = AddURL(
+ 0, tier1_a, 1, L"tier1_a_url1", GURL("http://www.pandora.com"));
+ const BookmarkNode* tier1_a_url2 = AddURL(
+ 0, tier1_a, 2, L"tier1_a_url2", GURL("http://www.facebook.com"));
+ const BookmarkNode* tier1_b_url0 = AddURL(
+ 0, tier1_b, 0, L"tier1_b_url0", GURL("http://www.nhl.com"));
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for initial sync completed."));
+ ASSERT_TRUE(ModelMatchesVerifier(0));
+
+ // Ultimately we want to end up with the following model; but this test is
+ // more about the journey than the destination.
+ //
+ // bookmark_bar
+ // -> CNN (www.cnn.com)
+ // -> tier1_a
+ // -> tier1_a_url2 (www.facebook.com)
+ // -> tier1_a_url1 (www.pandora.com)
+ // -> Porsche (www.porsche.com)
+ // -> Bank of America (www.bankofamerica.com)
+ // -> Seattle Bubble
+ // other_node
+ // -> top
+ // -> tier1_b
+ // -> Wired News (www.wired.com)
+ // -> tier2_b
+ // -> tier1_b_url0
+ // -> tier3_b
+ // -> Toronto Maple Leafs (mapleleafs.nhl.com)
+ // -> Wynn (www.wynnlasvegas.com)
+ // -> tier1_a_url0
+ const BookmarkNode* bar = GetBookmarkBarNode(0);
+ const BookmarkNode* cnn = AddURL(0, bar, 0, L"CNN",
+ GURL("http://www.cnn.com"));
+ ASSERT_TRUE(cnn != NULL);
+ Move(0, tier1_a, bar, 1);
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Bookmark moved."));
+ ASSERT_TRUE(ModelMatchesVerifier(0));
+
+ const BookmarkNode* porsche = AddURL(0, bar, 2, L"Porsche",
+ GURL("http://www.porsche.com"));
+ // Rearrange stuff in tier1_a.
+ ASSERT_EQ(tier1_a, tier1_a_url2->parent());
+ ASSERT_EQ(tier1_a, tier1_a_url1->parent());
+ Move(0, tier1_a_url2, tier1_a, 0);
+ Move(0, tier1_a_url1, tier1_a, 2);
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Rearrange stuff in tier1_a"));
+ ASSERT_TRUE(ModelMatchesVerifier(0));
+
+ ASSERT_EQ(1, tier1_a_url0->parent()->GetIndexOf(tier1_a_url0));
+ Move(0, tier1_a_url0, bar, bar->child_count());
+ const BookmarkNode* boa = AddURL(0, bar, bar->child_count(),
+ L"Bank of America", GURL("https://www.bankofamerica.com"));
+ ASSERT_TRUE(boa != NULL);
+ Move(0, tier1_a_url0, top, top->child_count());
+ const BookmarkNode* bubble = AddURL(
+ 0, bar, bar->child_count(), L"Seattle Bubble",
+ GURL("http://seattlebubble.com"));
+ ASSERT_TRUE(bubble != NULL);
+ const BookmarkNode* wired = AddURL(0, bar, 2, L"Wired News",
+ GURL("http://www.wired.com"));
+ const BookmarkNode* tier2_b = AddFolder(
+ 0, tier1_b, 0, L"tier2_b");
+ Move(0, tier1_b_url0, tier2_b, 0);
+ Move(0, porsche, bar, 0);
+ SetTitle(0, wired, L"News Wired");
+ SetTitle(0, porsche, L"ICanHazPorsche?");
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Change title."));
+ ASSERT_TRUE(ModelMatchesVerifier(0));
+
+ ASSERT_EQ(tier1_a_url0->id(), top->GetChild(top->child_count() - 1)->id());
+ Remove(0, top, top->child_count() - 1);
+ Move(0, wired, tier1_b, 0);
+ Move(0, porsche, bar, 3);
+ const BookmarkNode* tier3_b = AddFolder(0, tier2_b, 1, L"tier3_b");
+ const BookmarkNode* leafs = AddURL(
+ 0, tier1_a, 0, L"Toronto Maple Leafs", GURL("http://mapleleafs.nhl.com"));
+ const BookmarkNode* wynn = AddURL(0, bar, 1, L"Wynn",
+ GURL("http://www.wynnlasvegas.com"));
+
+ Move(0, wynn, tier3_b, 0);
+ Move(0, leafs, tier3_b, 0);
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Move after addition of bookmarks."));
+ ASSERT_TRUE(ModelMatchesVerifier(0));
+}
diff --git a/chrome/browser/sync/test/live_sync/single_client_extensions_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_extensions_sync_test.cc
new file mode 100644
index 0000000..7155826
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_extensions_sync_test.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/extensions_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using extensions_helper::AllProfilesHaveSameExtensionsAsVerifier;
+using extensions_helper::InstallExtension;
+
+class SingleClientExtensionsSyncTest : public LiveSyncTest {
+ public:
+ SingleClientExtensionsSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+
+ virtual ~SingleClientExtensionsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientExtensionsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, StartWithNoExtensions) {
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest,
+ StartWithSomeExtensions) {
+ ASSERT_TRUE(SetupClients());
+
+ const int kNumExtensions = 5;
+ for (int i = 0; i < kNumExtensions; ++i) {
+ InstallExtension(GetProfile(0), i);
+ InstallExtension(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, InstallSomeExtensions) {
+ ASSERT_TRUE(SetupSync());
+
+ const int kNumExtensions = 5;
+ for (int i = 0; i < kNumExtensions; ++i) {
+ InstallExtension(GetProfile(0), i);
+ InstallExtension(verifier(), i);
+ }
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for extension changes."));
+
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
diff --git a/chrome/browser/sync/test/live_sync/single_client_passwords_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_passwords_sync_test.cc
new file mode 100644
index 0000000..3d81f50
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_passwords_sync_test.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/password_form_data.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/passwords_helper.h"
+
+using passwords_helper::AddLogin;
+using passwords_helper::CreateTestPasswordForm;
+using passwords_helper::GetPasswordCount;
+using passwords_helper::GetPasswordStore;
+using passwords_helper::GetVerifierPasswordCount;
+using passwords_helper::GetVerifierPasswordStore;
+using passwords_helper::ProfileContainsSamePasswordFormsAsVerifier;
+
+using webkit_glue::PasswordForm;
+
+class SingleClientPasswordsSyncTest : public LiveSyncTest {
+ public:
+ SingleClientPasswordsSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+ virtual ~SingleClientPasswordsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientPasswordsSyncTest);
+};
+
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(SingleClientPasswordsSyncTest, DISABLED_Sanity) {
+#else
+IN_PROC_BROWSER_TEST_F(SingleClientPasswordsSyncTest, Sanity) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ PasswordForm form = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form);
+ ASSERT_EQ(1, GetVerifierPasswordCount());
+ AddLogin(GetPasswordStore(0), form);
+ ASSERT_EQ(1, GetPasswordCount(0));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Added a login."));
+ ASSERT_TRUE(ProfileContainsSamePasswordFormsAsVerifier(0));
+ ASSERT_EQ(1, GetPasswordCount(0));
+}
diff --git a/chrome/browser/sync/test/live_sync/single_client_preferences_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_preferences_sync_test.cc
new file mode 100644
index 0000000..cb96004
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_preferences_sync_test.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/pref_names.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/preferences_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using preferences_helper::BooleanPrefMatches;
+using preferences_helper::ChangeBooleanPref;
+
+class SingleClientPreferencesSyncTest : public LiveSyncTest {
+ public:
+ SingleClientPreferencesSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+ virtual ~SingleClientPreferencesSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientPreferencesSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientPreferencesSyncTest, Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kHomePageIsNewTabPage));
+ ChangeBooleanPref(0, prefs::kHomePageIsNewTabPage);
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for prefs change."));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kHomePageIsNewTabPage));
+}
diff --git a/chrome/browser/sync/test/live_sync/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_sessions_sync_test.cc
new file mode 100644
index 0000000..382c983
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_sessions_sync_test.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sessions_helper.h"
+
+using sessions_helper::CheckInitialState;
+using sessions_helper::GetLocalWindows;
+using sessions_helper::GetSessionData;
+using sessions_helper::OpenTabAndGetLocalWindows;
+using sessions_helper::WindowsMatch;
+
+class SingleClientSessionsSyncTest : public LiveSyncTest {
+ public:
+ SingleClientSessionsSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+ virtual ~SingleClientSessionsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientSessionsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+
+ ScopedVector<SessionWindow> old_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(0,
+ GURL("about:bubba"),
+ old_windows.get()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for session change."));
+
+ // Get foreign session data from client 0.
+ SyncedSessionVector sessions;
+ ASSERT_FALSE(GetSessionData(0, &sessions));
+ ASSERT_EQ(0U, sessions.size());
+
+ // Verify client didn't change.
+ ScopedVector<SessionWindow> new_windows;
+ ASSERT_TRUE(GetLocalWindows(0, new_windows.get()));
+ ASSERT_TRUE(WindowsMatch(old_windows.get(), new_windows.get()));
+}
diff --git a/chrome/browser/sync/test/live_sync/single_client_themes_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_themes_sync_test.cc
new file mode 100644
index 0000000..ce5f431
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_themes_sync_test.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/themes_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using themes_helper::GetCustomTheme;
+using themes_helper::GetThemeID;
+using themes_helper::UseCustomTheme;
+using themes_helper::UseDefaultTheme;
+using themes_helper::UseNativeTheme;
+using themes_helper::UsingCustomTheme;
+using themes_helper::UsingDefaultTheme;
+using themes_helper::UsingNativeTheme;
+
+class SingleClientThemesSyncTest : public LiveSyncTest {
+ public:
+ SingleClientThemesSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+ virtual ~SingleClientThemesSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientThemesSyncTest);
+};
+
+// TODO(akalin): Add tests for model association (i.e., tests that
+// start with SetupClients(), change the theme state, then call
+// SetupSync()).
+
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, CustomTheme) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(verifier()));
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(verifier(), 0);
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for custom themes change."));
+
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+}
+
+// TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, FAILS_NativeTheme) {
+#else
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, NativeTheme) {
+#endif // OS_CHROMEOS
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(verifier(), 0);
+ ASSERT_FALSE(UsingNativeTheme(GetProfile(0)));
+ ASSERT_FALSE(UsingNativeTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for custom themes change."));
+
+ UseNativeTheme(GetProfile(0));
+ UseNativeTheme(verifier());
+ ASSERT_TRUE(UsingNativeTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingNativeTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for native themes change."));
+
+ ASSERT_TRUE(UsingNativeTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingNativeTheme(verifier()));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, DefaultTheme) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(verifier(), 0);
+ ASSERT_FALSE(UsingDefaultTheme(GetProfile(0)));
+ ASSERT_FALSE(UsingDefaultTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for custom themes change."));
+
+ UseDefaultTheme(GetProfile(0));
+ UseDefaultTheme(verifier());
+ ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingDefaultTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for native themes change."));
+
+ ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingDefaultTheme(verifier()));
+}
diff --git a/chrome/browser/sync/test/live_sync/single_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/live_sync/single_client_typed_urls_sync_test.cc
new file mode 100644
index 0000000..064d02d
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/single_client_typed_urls_sync_test.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/typed_urls_helper.h"
+
+using typed_urls_helper::AddUrlToHistory;
+using typed_urls_helper::AssertAllProfilesHaveSameURLsAsVerifier;
+using typed_urls_helper::GetTypedUrlsFromClient;
+
+const std::string kSanityHistoryUrl = "http://www.sanity-history.google.com";
+
+class SingleClientTypedUrlsSyncTest : public LiveSyncTest {
+ public:
+ SingleClientTypedUrlsSyncTest() : LiveSyncTest(SINGLE_CLIENT) {}
+ virtual ~SingleClientTypedUrlsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SingleClientTypedUrlsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientTypedUrlsSyncTest, Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(0U, urls.size());
+
+ GURL new_url(kSanityHistoryUrl);
+ AddUrlToHistory(0, new_url);
+
+ urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+ ASSERT_EQ(new_url, urls[0].url());
+ AssertAllProfilesHaveSameURLsAsVerifier();
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion(
+ "Waiting for typed url change."));
+
+ // Verify client did not change.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
+
diff --git a/chrome/browser/sync/test/live_sync/sync_datatype_helper.cc b/chrome/browser/sync/test/live_sync/sync_datatype_helper.cc
new file mode 100644
index 0000000..3e50ced
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sync_datatype_helper.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+namespace {
+
+// The LiveSyncTest instance associated with sync_datatype_helper.
+static LiveSyncTest* test = NULL;
+
+} // namespace
+
+namespace sync_datatype_helper {
+
+void AssociateWithTest(LiveSyncTest* test) {
+ ASSERT_TRUE(test != NULL) << "Cannot associate with null test.";
+ ASSERT_TRUE(::test == NULL) << "Already associated with a test.";
+ ::test = test;
+}
+
+LiveSyncTest* test() {
+ EXPECT_TRUE(::test != NULL) << "Must call AssociateWithTest first.";
+ return ::test;
+}
+
+} // namespace sync_datatype_helper
diff --git a/chrome/browser/sync/test/live_sync/sync_datatype_helper.h b/chrome/browser/sync/test/live_sync/sync_datatype_helper.h
new file mode 100644
index 0000000..8a2593c
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sync_datatype_helper.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SYNC_DATATYPE_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SYNC_DATATYPE_HELPER_H_
+#pragma once
+
+#include "base/basictypes.h"
+
+class LiveSyncTest;
+
+namespace sync_datatype_helper {
+
+// Associates an instance of LiveSyncTest with sync_datatype_helper. Must be
+// called before any of the methods in the per-datatype helper namespaces can be
+// used.
+void AssociateWithTest(LiveSyncTest* test);
+
+// Returns a pointer to the instance of LiveSyncTest associated with the
+// per-datatype helpers after making sure it is valid.
+LiveSyncTest* test();
+
+} // namespace sync_datatype_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SYNC_DATATYPE_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/sync_errors_test.cc b/chrome/browser/sync/test/live_sync/sync_errors_test.cc
new file mode 100644
index 0000000..835ece7
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sync_errors_test.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using bookmarks_helper::AddFolder;
+using bookmarks_helper::SetTitle;
+
+class SyncErrorTest : public LiveSyncTest{
+ public:
+ SyncErrorTest() : LiveSyncTest(SINGLE_CLIENT) {}
+ virtual ~SyncErrorTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyncErrorTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SyncErrorTest, BirthdayErrorTest) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ const BookmarkNode* node1 = AddFolder(0, 0, L"title1");
+ SetTitle(0, node1, L"new_title1");
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Offline state change."));
+ TriggerBirthdayError();
+
+ // Now make one more change so we will do another sync.
+ const BookmarkNode* node2 = AddFolder(0, 0, L"title2");
+ SetTitle(0, node2, L"new_title2");
+ ASSERT_TRUE(GetClient(0)->AwaitSyncDisabled("Birthday error."));
+}
+
+IN_PROC_BROWSER_TEST_F(SyncErrorTest, TransientErrorTest) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ const BookmarkNode* node1 = AddFolder(0, 0, L"title1");
+ SetTitle(0, node1, L"new_title1");
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Offline state change."));
+ TriggerTransientError();
+
+ // Now make one more change so we will do another sync.
+ const BookmarkNode* node2 = AddFolder(0, 0, L"title2");
+ SetTitle(0, node2, L"new_title2");
+ ASSERT_TRUE(
+ GetClient(0)->AwaitExponentialBackoffVerification());
+}
diff --git a/chrome/browser/sync/test/live_sync/sync_extension_helper.cc b/chrome/browser/sync/test/live_sync/sync_extension_helper.cc
new file mode 100644
index 0000000..e5de2e4
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sync_extension_helper.cc
@@ -0,0 +1,361 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/sync_extension_helper.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/pending_extension_info.h"
+#include "chrome/browser/extensions/pending_extension_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+SyncExtensionHelper::ExtensionState::ExtensionState()
+ : enabled_state(ENABLED), incognito_enabled(false) {}
+
+SyncExtensionHelper::ExtensionState::~ExtensionState() {}
+
+bool SyncExtensionHelper::ExtensionState::Equals(
+ const SyncExtensionHelper::ExtensionState &other) const {
+ return ((enabled_state == other.enabled_state) &&
+ (incognito_enabled == other.incognito_enabled));
+}
+
+// static
+SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
+ SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get();
+ instance->SetupIfNecessary(sync_datatype_helper::test());
+ return instance;
+}
+
+SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
+
+SyncExtensionHelper::~SyncExtensionHelper() {}
+
+// static
+std::string SyncExtensionHelper::NameToId(const std::string& name) {
+ std::string id;
+ EXPECT_TRUE(Extension::GenerateId(name, &id));
+ return id;
+}
+
+void SyncExtensionHelper::SetupIfNecessary(LiveSyncTest* test) {
+ if (setup_completed_)
+ return;
+
+ for (int i = 0; i < test->num_clients(); ++i) {
+ SetupProfile(test->GetProfile(i));
+ }
+ SetupProfile(test->verifier());
+
+ setup_completed_ = true;
+}
+
+void SyncExtensionHelper::InstallExtension(
+ Profile* profile, const std::string& name, Extension::Type type) {
+ scoped_refptr<Extension> extension = GetExtension(profile, name, type);
+ ASSERT_TRUE(extension.get()) << "Could not get extension " << name
+ << " (profile = " << profile << ")";
+ profile->GetExtensionService()->OnExtensionInstalled(
+ extension, extension->UpdatesFromGallery(), 0);
+}
+
+void SyncExtensionHelper::UninstallExtension(
+ Profile* profile, const std::string& name) {
+ ExtensionService::UninstallExtensionHelper(profile->GetExtensionService(),
+ NameToId(name));
+}
+
+std::vector<std::string> SyncExtensionHelper::GetInstalledExtensionNames(
+ Profile* profile) const {
+ std::vector<std::string> names;
+ ExtensionService* extension_service = profile->GetExtensionService();
+
+ const ExtensionList* extensions = extension_service->extensions();
+ for (ExtensionList::const_iterator it = extensions->begin();
+ it != extensions->end(); ++it) {
+ names.push_back((*it)->name());
+ }
+
+ const ExtensionList* disabled_extensions =
+ extension_service->disabled_extensions();
+ for (ExtensionList::const_iterator it = disabled_extensions->begin();
+ it != disabled_extensions->end(); ++it) {
+ names.push_back((*it)->name());
+ }
+
+ const ExtensionList* terminated_extensions =
+ extension_service->terminated_extensions();
+ for (ExtensionList::const_iterator it = terminated_extensions->begin();
+ it != terminated_extensions->end(); ++it) {
+ names.push_back((*it)->name());
+ }
+
+ return names;
+}
+
+void SyncExtensionHelper::EnableExtension(Profile* profile,
+ const std::string& name) {
+ profile->GetExtensionService()->EnableExtension(NameToId(name));
+}
+
+void SyncExtensionHelper::DisableExtension(Profile* profile,
+ const std::string& name) {
+ profile->GetExtensionService()->DisableExtension(NameToId(name));
+}
+
+bool SyncExtensionHelper::IsExtensionEnabled(
+ Profile* profile, const std::string& name) const {
+ return profile->GetExtensionService()->IsExtensionEnabled(NameToId(name));
+}
+
+void SyncExtensionHelper::IncognitoEnableExtension(
+ Profile* profile, const std::string& name) {
+ profile->GetExtensionService()->SetIsIncognitoEnabled(NameToId(name), true);
+}
+
+void SyncExtensionHelper::IncognitoDisableExtension(
+ Profile* profile, const std::string& name) {
+ profile->GetExtensionService()->SetIsIncognitoEnabled(NameToId(name), false);
+}
+
+bool SyncExtensionHelper::IsIncognitoEnabled(
+ Profile* profile, const std::string& name) const {
+ return profile->GetExtensionService()->IsIncognitoEnabled(NameToId(name));
+}
+
+
+bool SyncExtensionHelper::IsExtensionPendingInstallForSync(
+ Profile* profile, const std::string& id) const {
+ const PendingExtensionManager* pending_extension_manager =
+ profile->GetExtensionService()->pending_extension_manager();
+ PendingExtensionInfo info;
+ if (!pending_extension_manager->GetById(id, &info)) {
+ return false;
+ }
+ return info.is_from_sync();
+}
+
+void SyncExtensionHelper::InstallExtensionsPendingForSync(
+ Profile* profile, Extension::Type type) {
+ // TODO(akalin): Mock out the servers that the extensions auto-update
+ // mechanism talk to so as to more closely match what actually happens.
+ // Background networking will need to be re-enabled for extensions tests.
+
+ // We make a copy here since InstallExtension() removes the
+ // extension from the extensions service's copy.
+ const PendingExtensionManager* pending_extension_manager =
+ profile->GetExtensionService()->pending_extension_manager();
+ PendingExtensionManager::PendingExtensionMap pending_extensions(
+ pending_extension_manager->begin(),
+ pending_extension_manager->end());
+ for (PendingExtensionManager::const_iterator it = pending_extensions.begin();
+ it != pending_extensions.end(); ++it) {
+ if (!it->second.is_from_sync()) {
+ continue;
+ }
+ const std::string& id = it->first;
+ StringMap::const_iterator it2 = id_to_name_.find(id);
+ if (it2 == id_to_name_.end()) {
+ ADD_FAILURE() << "Could not get name for id " << id
+ << " (profile = " << profile->GetDebugName() << ")";
+ continue;
+ }
+ InstallExtension(profile, it2->second, type);
+ }
+}
+
+SyncExtensionHelper::ExtensionStateMap
+ SyncExtensionHelper::GetExtensionStates(Profile* profile) {
+ const std::string& profile_debug_name = profile->GetDebugName();
+
+ ExtensionStateMap extension_state_map;
+
+ ExtensionService* extension_service = profile->GetExtensionService();
+
+ const ExtensionList* extensions = extension_service->extensions();
+ for (ExtensionList::const_iterator it = extensions->begin();
+ it != extensions->end(); ++it) {
+ const std::string& id = (*it)->id();
+ extension_state_map[id].enabled_state = ExtensionState::ENABLED;
+ extension_state_map[id].incognito_enabled =
+ extension_service->IsIncognitoEnabled(id);
+ VLOG(2) << "Extension " << (*it)->id() << " in profile "
+ << profile_debug_name << " is enabled";
+ }
+
+ const ExtensionList* disabled_extensions =
+ extension_service->disabled_extensions();
+ for (ExtensionList::const_iterator it = disabled_extensions->begin();
+ it != disabled_extensions->end(); ++it) {
+ const std::string& id = (*it)->id();
+ extension_state_map[id].enabled_state = ExtensionState::DISABLED;
+ extension_state_map[id].incognito_enabled =
+ extension_service->IsIncognitoEnabled(id);
+ VLOG(2) << "Extension " << (*it)->id() << " in profile "
+ << profile_debug_name << " is disabled";
+ }
+
+ const PendingExtensionManager* pending_extension_manager =
+ extension_service->pending_extension_manager();
+ PendingExtensionManager::const_iterator it;
+ for (it = pending_extension_manager->begin();
+ it != pending_extension_manager->end(); ++it) {
+ const std::string& id = it->first;
+ extension_state_map[id].enabled_state = ExtensionState::PENDING;
+ extension_state_map[id].incognito_enabled =
+ extension_service->IsIncognitoEnabled(id);
+ VLOG(2) << "Extension " << it->first << " in profile "
+ << profile_debug_name << " is pending";
+ }
+
+ return extension_state_map;
+}
+
+bool SyncExtensionHelper::ExtensionStatesMatch(
+ Profile* profile1, Profile* profile2) {
+ const ExtensionStateMap& state_map1 = GetExtensionStates(profile1);
+ const ExtensionStateMap& state_map2 = GetExtensionStates(profile2);
+ if (state_map1.size() != state_map2.size()) {
+ VLOG(1) << "Number of extensions for profile " << profile1->GetDebugName()
+ << " does not match profile " << profile2->GetDebugName();
+ return false;
+ }
+
+ ExtensionStateMap::const_iterator it1 = state_map1.begin();
+ ExtensionStateMap::const_iterator it2 = state_map2.begin();
+ while (it1 != state_map1.end()) {
+ if (it1->first != it2->first) {
+ VLOG(1) << "Extensions for profile " << profile1->GetDebugName()
+ << " do not match profile " << profile2->GetDebugName();
+ return false;
+ } else if (!it1->second.Equals(it2->second)) {
+ VLOG(1) << "Extension states for profile " << profile1->GetDebugName()
+ << " do not match profile " << profile2->GetDebugName();
+ return false;
+ }
+ ++it1;
+ ++it2;
+ }
+ return true;
+}
+
+void SyncExtensionHelper::SetupProfile(Profile* profile) {
+ profile->InitExtensions(true);
+ profile_extensions_.insert(make_pair(profile, ExtensionNameMap()));
+}
+
+namespace {
+
+std::string NameToPublicKey(const std::string& name) {
+ std::string public_key;
+ std::string pem;
+ EXPECT_TRUE(Extension::ProducePEM(name, &pem) &&
+ Extension::FormatPEMForFileOutput(pem, &public_key,
+ true /* is_public */));
+ return public_key;
+}
+
+// TODO(akalin): Somehow unify this with MakeExtension() in
+// extension_util_unittest.cc.
+scoped_refptr<Extension> CreateExtension(
+ const FilePath& base_dir, const std::string& name,
+ Extension::Type type) {
+ DictionaryValue source;
+ source.SetString(extension_manifest_keys::kName, name);
+ const std::string& public_key = NameToPublicKey(name);
+ source.SetString(extension_manifest_keys::kPublicKey, public_key);
+ source.SetString(extension_manifest_keys::kVersion, "0.0.0.0");
+ switch (type) {
+ case Extension::TYPE_EXTENSION:
+ // Do nothing.
+ break;
+ case Extension::TYPE_THEME:
+ source.Set(extension_manifest_keys::kTheme, new DictionaryValue());
+ break;
+ case Extension::TYPE_HOSTED_APP:
+ case Extension::TYPE_PACKAGED_APP:
+ source.Set(extension_manifest_keys::kApp, new DictionaryValue());
+ source.SetString(extension_manifest_keys::kLaunchWebURL,
+ "http://www.example.com");
+ break;
+ default:
+ ADD_FAILURE();
+ return NULL;
+ }
+ const FilePath sub_dir = FilePath().AppendASCII(name);
+ FilePath extension_dir;
+ if (!file_util::PathExists(base_dir) &&
+ !file_util::CreateDirectory(base_dir) &&
+ !file_util::CreateTemporaryDirInDir(
+ base_dir, sub_dir.value(), &extension_dir)) {
+ ADD_FAILURE();
+ return NULL;
+ }
+ std::string error;
+ scoped_refptr<Extension> extension =
+ Extension::Create(extension_dir, Extension::INTERNAL,
+ source, Extension::STRICT_ERROR_CHECKS, &error);
+ if (!error.empty()) {
+ ADD_FAILURE() << error;
+ return NULL;
+ }
+ if (!extension.get()) {
+ ADD_FAILURE();
+ return NULL;
+ }
+ if (extension->name() != name) {
+ EXPECT_EQ(name, extension->name());
+ return NULL;
+ }
+ if (extension->GetType() != type) {
+ EXPECT_EQ(type, extension->GetType());
+ return NULL;
+ }
+ return extension;
+}
+
+} // namespace
+
+scoped_refptr<Extension> SyncExtensionHelper::GetExtension(
+ Profile* profile, const std::string& name,
+ Extension::Type type) {
+ if (name.empty()) {
+ ADD_FAILURE();
+ return NULL;
+ }
+ ProfileExtensionNameMap::iterator it = profile_extensions_.find(profile);
+ if (it == profile_extensions_.end()) {
+ ADD_FAILURE();
+ return NULL;
+ }
+ ExtensionNameMap::const_iterator it2 = it->second.find(name);
+ if (it2 != it->second.end()) {
+ return it2->second;
+ }
+
+ scoped_refptr<Extension> extension =
+ CreateExtension(profile->GetExtensionService()->install_directory(),
+ name, type);
+ if (!extension.get()) {
+ ADD_FAILURE();
+ return NULL;
+ }
+ const std::string& expected_id = NameToId(name);
+ if (extension->id() != expected_id) {
+ EXPECT_EQ(expected_id, extension->id());
+ return NULL;
+ }
+ VLOG(2) << "created extension with name = "
+ << name << ", id = " << expected_id;
+ (it->second)[name] = extension;
+ id_to_name_[expected_id] = name;
+ return extension;
+}
diff --git a/chrome/browser/sync/test/live_sync/sync_extension_helper.h b/chrome/browser/sync/test/live_sync/sync_extension_helper.h
new file mode 100644
index 0000000..4aa3f1d
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sync_extension_helper.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SYNC_EXTENSION_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SYNC_EXTENSION_HELPER_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "chrome/common/extensions/extension.h"
+
+class Extension;
+class LiveSyncTest;
+class Profile;
+
+class SyncExtensionHelper {
+ public:
+ // Singleton implementation.
+ static SyncExtensionHelper* GetInstance();
+
+ // Returns a generated extension ID for the given name.
+ static std::string NameToId(const std::string& name);
+
+ // Initializes the profiles in |test| and registers them with
+ // internal data structures.
+ void SetupIfNecessary(LiveSyncTest* test);
+
+ // Installs the extension with the given name to |profile|.
+ void InstallExtension(
+ Profile* profile, const std::string& name, Extension::Type type);
+
+ // Uninstalls the extension with the given name from |profile|.
+ void UninstallExtension(Profile* profile, const std::string& name);
+
+ // Returns a vector containing the names of all currently installed extensions
+ // on |profile|.
+ std::vector<std::string> GetInstalledExtensionNames(Profile* profile) const;
+
+ // Enables the extension with the given name on |profile|.
+ void EnableExtension(Profile* profile, const std::string& name);
+
+ // Disables the extension with the given name on |profile|.
+ void DisableExtension(Profile* profile, const std::string& name);
+
+ // Returns true if the extension with the given name is enabled on |profile|.
+ bool IsExtensionEnabled(Profile* profile, const std::string& name) const;
+
+ // Enables the extension with the given name to run in incognito mode
+ void IncognitoEnableExtension(Profile* profile, const std::string& name);
+
+ // Disables the extension with the given name from running in incognito mode
+ void IncognitoDisableExtension(Profile* profile, const std::string& name);
+
+ // Returns true iff the extension is enabled in incognito mode on |profile|.
+ bool IsIncognitoEnabled(Profile* profile, const std::string& name) const;
+
+ // Returns true iff the extension with the given id is pending
+ // install in |profile|.
+ bool IsExtensionPendingInstallForSync(
+ Profile* profile, const std::string& id) const;
+
+ // Installs all extensions pending sync in |profile| of the given
+ // type.
+ void InstallExtensionsPendingForSync(Profile* profile, Extension::Type type);
+
+ // Returns true iff |profile1| and |profile2| have the same extensions and
+ // they are all in the same state.
+ static bool ExtensionStatesMatch(Profile* profile1, Profile* profile2);
+
+ private:
+ struct ExtensionState {
+ enum EnabledState { DISABLED, PENDING, ENABLED };
+
+ ExtensionState();
+ ~ExtensionState();
+ bool Equals(const ExtensionState &other) const;
+
+ EnabledState enabled_state;
+ bool incognito_enabled;
+ };
+
+ typedef std::map<std::string, ExtensionState> ExtensionStateMap;
+ typedef std::map<std::string, scoped_refptr<Extension> > ExtensionNameMap;
+ typedef std::map<Profile*, ExtensionNameMap> ProfileExtensionNameMap;
+ typedef std::map<std::string, std::string> StringMap;
+
+ friend struct DefaultSingletonTraits<SyncExtensionHelper>;
+
+ SyncExtensionHelper();
+ ~SyncExtensionHelper();
+
+ // Returns a map from |profile|'s installed extensions to their state.
+ static ExtensionStateMap GetExtensionStates(Profile* profile);
+
+ // Initializes extensions for |profile| and creates an entry in
+ // |profile_extensions_| for it.
+ void SetupProfile(Profile* profile);
+
+ // Returns an extension for the given name in |profile|. type and
+ // index. Two extensions with the name but different profiles will
+ // have the same id.
+ scoped_refptr<Extension> GetExtension(
+ Profile* profile, const std::string& name,
+ Extension::Type type) WARN_UNUSED_RESULT;
+
+ ProfileExtensionNameMap profile_extensions_;
+ StringMap id_to_name_;
+ bool setup_completed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncExtensionHelper);
+};
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_SYNC_EXTENSION_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/sync_integration_test.py b/chrome/browser/sync/test/live_sync/sync_integration_test.py
new file mode 100644
index 0000000..17ed876
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/sync_integration_test.py
@@ -0,0 +1,404 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""A tool to run a chrome sync integration test, used by the buildbot slaves.
+
+ When this is run, the current directory (cwd) should be the outer build
+ directory (e.g., chrome-release/build/).
+
+ For a list of command-line options, call this script with '--help'.
+"""
+
+__author__ = 'tejasshah@chromium.org'
+
+
+import logging
+import optparse
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import time
+import urllib2
+
+
+USAGE = '%s [options] [test args]' % os.path.basename(sys.argv[0])
+HTTP_SERVER_URL = None
+HTTP_SERVER_PORT = None
+
+
+class PathNotFound(Exception): pass
+
+
+def ListSyncTests(test_exe_path):
+ """Returns list of the enabled live sync tests.
+
+ Args:
+ test_exe_path: Absolute path to test exe file.
+
+ Returns:
+ List of the tests that should be run.
+ """
+ command = [test_exe_path]
+ command.append('--gtest_list_tests')
+ proc = subprocess.Popen(
+ command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1)
+ proc.wait()
+ sys.stdout.flush()
+ test_list = []
+ for line in proc.stdout.readlines():
+ if not line.strip():
+ continue
+ elif line.count("."): # A new test collection
+ test_collection = line.split(".")[0].strip();
+ else: # An individual test to run
+ test_name = line.strip()
+ test_list.append("%s.%s" % (test_collection, test_name))
+ logging.info('List of tests to run: %s' % (test_list))
+ return test_list
+
+
+def GetUrl(command, port):
+ """Prepares the URL with appropriate command to send it to http server.
+
+ Args:
+ command: Command for HTTP server
+ port: Port number as a parameter to the command
+
+ Returns:
+ Formulated URL with the command and parameter.
+ """
+ command_url = (
+ '%s:%s/%s?port=%s'
+ % (HTTP_SERVER_URL, HTTP_SERVER_PORT, command, port))
+ return command_url
+
+
+def StartSyncServer(port=None):
+ """Starts a chrome sync server.
+
+ Args:
+ port: Port number on which sync server to start
+ (Default value none, in this case it uses random port).
+
+ Returns:
+ If success then port number on which sync server started, else None.
+ """
+ sync_port = None
+ if port:
+ start_sync_server_url = GetUrl('startsyncserver', port)
+ else:
+ start_sync_server_url = (
+ '%s:%s/%s' % (HTTP_SERVER_URL, HTTP_SERVER_PORT, 'startsyncserver'))
+ req = urllib2.Request(start_sync_server_url)
+ try:
+ response = urllib2.urlopen(req)
+ except urllib2.HTTPError, e:
+ logging.error(
+ 'Could not start sync server, something went wrong.'
+ 'Request URL: %s , Error Code: %s'% (start_sync_server_url, e.code))
+ return sync_port
+ except urllib2.URLError, e:
+ logging.error(
+ 'Failed to reach HTTP server.Request URL: %s , Error: %s'
+ % (start_sync_server_url, e.reason))
+ return sync_port
+ else:
+ # Let's read response and parse the sync server port number.
+ output = response.readlines()
+ # Regex to ensure that sync server started and extract the port number.
+ regex = re.compile(
+ ".*not.*running.*on.*port\s*:\s*(\d+).*started.*new.*sync.*server",
+ re.IGNORECASE|re.MULTILINE|re.DOTALL)
+ r = regex.search(output[0])
+ if r:
+ sync_port = r.groups()[0]
+ if sync_port:
+ logging.info(
+ 'Started Sync Server Successfully on Port:%s. Request URL: %s , '
+ 'Response: %s' % (sync_port, start_sync_server_url, output))
+ response.fp._sock.recv = None
+ response.close()
+ return sync_port
+
+
+def CheckIfSyncServerRunning(port):
+ """Check the healthz status of a chrome sync server.
+
+ Args:
+ port: Port number on which sync server is running
+
+ Returns:
+ True: If sync server running.
+ False: Otherwise.
+ """
+ sync_server_healthz_url = ('%s:%s/healthz' % (HTTP_SERVER_URL, port))
+ req = urllib2.Request(sync_server_healthz_url)
+ try:
+ response = urllib2.urlopen(req)
+ except urllib2.HTTPError, e:
+ logging.error(
+ 'It seems like Sync Server is not running, healthz check failed.'
+ 'Request URL: %s , Error Code: %s'% (sync_server_healthz_url, e.code))
+ return False
+ except urllib2.URLError, e:
+ logging.error(
+ 'Failed to reach Sync server, healthz check failed.'
+ 'Request URL: %s , Error: %s'% (sync_server_healthz_url, e.reason))
+ return False
+ else:
+ logging.info(
+ 'Sync Server healthz check Passed.Request URL: %s , Response: %s'
+ % (sync_server_healthz_url, response.readlines()))
+ response.fp._sock.recv = None
+ response.close()
+ return True
+
+
+def StopSyncServer(port):
+ """Stops a chrome sync server.
+
+ Args:
+ port: Port number on which sync server to Stop
+
+ Returns:
+ Success/Failure as a bool value.
+ """
+ stop_sync_server_url = GetUrl('stopsyncserver', port)
+ req = urllib2.Request(stop_sync_server_url)
+ logging.info("Stopping: %s" % stop_sync_server_url)
+ try:
+ response = urllib2.urlopen(req)
+ except urllib2.HTTPError, e:
+ logging.error(
+ 'Could not stop sync server, something went wrong.'
+ 'Request URL: %s , Error Code: %s'% (stop_sync_server_url, e.code))
+ return False
+ except urllib2.URLError, e:
+ logging.error(
+ 'Failed to reach HTTP server.Request URL: %s , Error: %s'
+ % (stop_sync_server_url, e.reason))
+ return False
+ else:
+ logging.info(
+ 'Stopped Sync Server Successfully.Request URL: %s , Response: %s'
+ % (stop_sync_server_url, response.readlines()))
+ response.fp._sock.recv = None
+ response.close()
+ return True
+
+
+def ShowGtestLikeFailure(test_exe_path, test_name):
+ """Show a gtest-like error when the test can't be run for some reason.
+
+ The scripts responsible for detecting test failures watch for this pattern.
+
+ Args:
+ test_exe_path: Absolute path to the test exe file.
+ test_name: The name of the test that wasn't run.
+ """
+ print '[ RUN ] %s.%s' % (os.path.basename(test_exe_path), test_name)
+ print '[ FAILED ] %s.%s' % (os.path.basename(test_exe_path), test_name)
+
+
+def RunCommand(command):
+ """Runs the command list, printing its output and returning its exit status.
+
+ Prints the given command (which should be a list of one or more strings),
+ then runs it and prints its stdout and stderr together to stdout,
+ line-buffered, converting line endings to CRLF (see note below). Waits for
+ the command to terminate and returns its status.
+
+ Args:
+ command: Command to run.
+
+ Returns:
+ Process exit code.
+ """
+ print '\n' + subprocess.list2cmdline(command) + '\n',
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, bufsize=1)
+ last_flush_time = time.time()
+ while proc.poll() == None:
+ # Note that Windows Python converts \n to \r\n automatically whenever it
+ # encounters it written to a text file (including stdout). The only way
+ # around it is to write to a binary file, which isn't feasible for stdout.
+ # So we're stuck with \r\n here even though we explicitly write \n. (We
+ # could write \r instead, which doesn't get converted to \r\n, but that's
+ # probably more troublesome for people trying to read the files.)
+ line = proc.stdout.readline()
+ if line:
+ # The comma at the end tells python to not add \n, which is \r\n on
+ # Windows.
+ print line.rstrip() + '\n',
+ # Python on windows writes the buffer only when it reaches 4k. This is
+ # not fast enough. Flush each 10 seconds instead.
+ if time.time() - last_flush_time >= 10:
+ sys.stdout.flush()
+ last_flush_time = time.time()
+ sys.stdout.flush()
+ # Write the remaining buffer.
+ for line in proc.stdout.readlines():
+ print line.rstrip() + '\n',
+ sys.stdout.flush()
+ return proc.returncode
+
+
+def RunOneTest(test_exe_path, test_name, username, password):
+ """Run one live sync test after setting up a fresh server environment.
+
+ Args:
+ test_exe_path: Absolute path to test exe file.
+ test_name: the name of the one test to run.
+ username: test account username.
+ password: test account password.
+
+ Returns:
+ Zero for suceess.
+ Non-zero for failure.
+ """
+ def LogTestNotRun(message):
+ ShowGtestLikeFailure(test_exe_path, test_name)
+ logging.info('\n\n%s did not run because %s' % (test_name, message))
+
+ try:
+ logging.info('\n\n*************************************')
+ logging.info('%s Start' % (test_name))
+ sync_port = StartSyncServer()
+ if not sync_port:
+ LogTestNotRun('starting the sync server failed.')
+ return 1
+ if not CheckIfSyncServerRunning(sync_port):
+ LogTestNotRun('sync server running check failed.')
+ return 1
+ logging.info('Verified that sync server is running on port: %s', sync_port)
+ user_dir = GenericSetup(test_name)
+ logging.info('Created user data dir %s' % (user_dir))
+ command = [
+ test_exe_path,
+ '--gtest_filter='+ test_name,
+ '--user-data-dir=' + user_dir,
+ '--sync-user-for-test=' + username,
+ '--sync-password-for-test=' + password,
+ '--sync-url=' + HTTP_SERVER_URL + ':' + sync_port]
+ logging.info(
+ '%s will run with command: %s'
+ % (test_name, subprocess.list2cmdline(command)))
+ result = RunCommand(command)
+ StopSyncServer(sync_port)
+ return result
+ finally:
+ logging.info('%s End' % (test_name))
+
+
+def GenericSetup(test_name):
+ """Generic setup for running one test.
+
+ Args:
+ test_name: The name of a test that is about to be run.
+
+ Returns:
+ user_dir: Absolute path to user data dir created for the test.
+ """
+ user_dir = tempfile.mkdtemp(prefix=test_name + '.')
+ return user_dir
+
+
+def main_win(options, args):
+ """Main Function for win32 platform which drives the test here.
+
+ Using the target build configuration, run the executable given in the
+ first non-option argument, passing any following arguments to that
+ executable.
+
+ Args:
+ options: Option parameters.
+ args: Test arguments.
+
+ Returns:
+ Result: Zero for success/ Non-zero for failure.
+ """
+ final_result = 0
+ test_exe = 'sync_integration_tests.exe'
+ build_dir = os.path.abspath(options.build_dir)
+ test_exe_path = os.path.join(build_dir, options.target, test_exe)
+ if not os.path.exists(test_exe_path):
+ raise PathNotFound('Unable to find %s' % test_exe_path)
+ test_list = ListSyncTests(test_exe_path)
+ for test_name in test_list:
+ result = RunOneTest(
+ test_exe_path, test_name, options.sync_test_username,
+ options.sync_test_password)
+ # If any single test fails then final result should be failure
+ if result != 0:
+ final_result = result
+ return final_result
+
+if '__main__' == __name__:
+ # Initialize logging.
+ log_level = logging.INFO
+ logging.basicConfig(
+ level=log_level, format='%(asctime)s %(filename)s:%(lineno)-3d'
+ ' %(levelname)s %(message)s', datefmt='%y%m%d %H:%M:%S')
+
+ option_parser = optparse.OptionParser(usage=USAGE)
+
+ # Since the trailing program to run may have has command-line args of its
+ # own, we need to stop parsing when we reach the first positional argument.
+ option_parser.disable_interspersed_args()
+
+ option_parser.add_option(
+ '', '--target', default='Release', help='Build target (Debug or Release)'
+ ' Default value Release.')
+ option_parser.add_option(
+ '', '--build-dir', help='Path to main build directory'
+ '(the parent of the Release or Debug directory).')
+ option_parser.add_option(
+ '', '--http-server-url', help='Path to http server that can be used to'
+ ' start/stop sync server e.g. http://http_server_url_without_port')
+ option_parser.add_option(
+ '', '--http-server-port', help='Port on which http server is running'
+ ' e.g. 1010')
+ option_parser.add_option(
+ '', '--sync-test-username', help='Test username e.g. foo@gmail.com')
+ option_parser.add_option(
+ '', '--sync-test-password', help='Password for the test account')
+ options, args = option_parser.parse_args()
+ if not options.sync_test_password:
+ # Check along side this script under src/ first, and then check in
+ # the user profile dir.
+ password_file_path = os.path.join(
+ os.path.dirname(__file__),'sync_password')
+ if (not os.path.exists(password_file_path)):
+ password_file_path = os.path.join(
+ os.environ['USERPROFILE'], 'sync_password')
+
+ if os.path.exists(password_file_path):
+ fs = open(password_file_path, 'r')
+ lines = fs.readlines()
+ if len(lines)==1:
+ options.sync_test_password = lines[0].strip()
+ else:
+ sys.stderr.write('sync_password file is not in required format.\n')
+ sys.exit(1)
+ else:
+ sys.stderr.write(
+ 'Missing required parameter- sync_test_password, please fix it.\n')
+ sys.exit(1)
+ if (not options.build_dir or not options.http_server_url or
+ not options.http_server_port or not options.sync_test_username):
+ sys.stderr.write('Missing required parameter, please fix it.\n')
+ option_parser.print_help()
+ sys.exit(1)
+ if sys.platform == 'win32':
+ HTTP_SERVER_URL = options.http_server_url
+ HTTP_SERVER_PORT = options.http_server_port
+ sys.exit(main_win(options, args))
+ else:
+ sys.stderr.write('Unknown sys.platform value %s\n' % repr(sys.platform))
+ sys.exit(1)
diff --git a/chrome/browser/sync/test/live_sync/themes_helper.cc b/chrome/browser/sync/test/live_sync/themes_helper.cc
new file mode 100644
index 0000000..8956340
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/themes_helper.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/themes_helper.h"
+
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "chrome/browser/sync/test/live_sync/sync_extension_helper.h"
+
+using sync_datatype_helper::test;
+
+namespace {
+
+// Make a name to pass to an extension helper.
+std::string MakeName(int index) {
+ return "faketheme" + base::IntToString(index);
+}
+
+ThemeService* GetThemeService(Profile* profile) {
+ return ThemeServiceFactory::GetForProfile(profile);
+}
+
+} // namespace
+
+namespace themes_helper {
+
+std::string GetCustomTheme(int index) {
+ return SyncExtensionHelper::GetInstance()->NameToId(MakeName(index));
+}
+
+std::string GetThemeID(Profile* profile) {
+ return GetThemeService(profile)->GetThemeID();
+}
+
+bool UsingCustomTheme(Profile* profile) {
+ return GetThemeID(profile) != ThemeService::kDefaultThemeID;
+}
+
+bool UsingDefaultTheme(Profile* profile) {
+ return GetThemeService(profile)->UsingDefaultTheme();
+}
+
+bool UsingNativeTheme(Profile* profile) {
+ return GetThemeService(profile)->UsingNativeTheme();
+}
+
+bool ThemeIsPendingInstall(Profile* profile, const std::string& id) {
+ return SyncExtensionHelper::GetInstance()->
+ IsExtensionPendingInstallForSync(profile, id);
+}
+
+bool HasOrWillHaveCustomTheme(Profile* profile, const std::string& id) {
+ return (GetThemeID(profile) == id) || ThemeIsPendingInstall(profile, id);
+}
+
+void UseCustomTheme(Profile* profile, int index) {
+ SyncExtensionHelper::GetInstance()->InstallExtension(
+ profile, MakeName(index), Extension::TYPE_THEME);
+}
+
+void UseDefaultTheme(Profile* profile) {
+ GetThemeService(profile)->UseDefaultTheme();
+}
+
+void UseNativeTheme(Profile* profile) {
+ // TODO(akalin): Fix this inconsistent naming in the theme service.
+ GetThemeService(profile)->SetNativeTheme();
+}
+
+} // namespace themes_helper
diff --git a/chrome/browser/sync/test/live_sync/themes_helper.h b/chrome/browser/sync/test/live_sync/themes_helper.h
new file mode 100644
index 0000000..6e28d1a
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/themes_helper.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_THEMES_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_THEMES_HELPER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+class Profile;
+
+namespace themes_helper {
+
+// Gets the unique ID of the custom theme with the given index.
+std::string GetCustomTheme(int index) WARN_UNUSED_RESULT;
+
+// Gets the ID of |profile|'s theme.
+std::string GetThemeID(Profile* profile) WARN_UNUSED_RESULT;
+
+// Returns true iff |profile| is using a custom theme.
+bool UsingCustomTheme(Profile* profile) WARN_UNUSED_RESULT;
+
+// Returns true iff |profile| is using the default theme.
+bool UsingDefaultTheme(Profile* profile) WARN_UNUSED_RESULT;
+
+// Returns true iff |profile| is using the native theme.
+bool UsingNativeTheme(Profile* profile) WARN_UNUSED_RESULT;
+
+// Returns true iff a theme with the given ID is pending install in
+// |profile|.
+bool ThemeIsPendingInstall(
+ Profile* profile, const std::string& id) WARN_UNUSED_RESULT;
+
+// Returns true iff |profile|'s current theme is the given
+// custom theme or if the given theme is pending install.
+bool HasOrWillHaveCustomTheme(
+ Profile* profile, const std::string& id) WARN_UNUSED_RESULT;
+
+// Sets |profile| to use the custom theme with the given index.
+void UseCustomTheme(Profile* profile, int index);
+
+// Sets |profile| to use the default theme.
+void UseDefaultTheme(Profile* profile);
+
+// Sets |profile| to use the native theme.
+void UseNativeTheme(Profile* profile);
+
+} // namespace themes_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_THEMES_HELPER_H_
diff --git a/chrome/browser/sync/test/live_sync/two_client_apps_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_apps_sync_test.cc
new file mode 100644
index 0000000..d6c48ea
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_apps_sync_test.cc
@@ -0,0 +1,280 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/apps_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using apps_helper::AllProfilesHaveSameAppsAsVerifier;
+using apps_helper::DisableApp;
+using apps_helper::EnableApp;
+using apps_helper::HasSameAppsAsVerifier;
+using apps_helper::IncognitoDisableApp;
+using apps_helper::IncognitoEnableApp;
+using apps_helper::InstallApp;
+using apps_helper::InstallAppsPendingForSync;
+using apps_helper::UninstallApp;
+
+class TwoClientAppsSyncTest : public LiveSyncTest {
+ public:
+ TwoClientAppsSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+
+ virtual ~TwoClientAppsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientAppsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, StartWithNoApps) {
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, StartWithSameApps) {
+ ASSERT_TRUE(SetupClients());
+
+ const int kNumApps = 5;
+ for (int i = 0; i < kNumApps; ++i) {
+ InstallApp(GetProfile(0), i);
+ InstallApp(GetProfile(1), i);
+ InstallApp(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, StartWithDifferentApps) {
+ ASSERT_TRUE(SetupClients());
+
+ int i = 0;
+
+ const int kNumCommonApps = 5;
+ for (int j = 0; j < kNumCommonApps; ++i, ++j) {
+ InstallApp(GetProfile(0), i);
+ InstallApp(GetProfile(1), i);
+ InstallApp(verifier(), i);
+ }
+
+ const int kNumProfile0Apps = 10;
+ for (int j = 0; j < kNumProfile0Apps; ++i, ++j) {
+ InstallApp(GetProfile(0), i);
+ InstallApp(verifier(), i);
+ }
+
+ const int kNumProfile1Apps = 10;
+ for (int j = 0; j < kNumProfile1Apps; ++i, ++j) {
+ InstallApp(GetProfile(1), i);
+ InstallApp(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallAppsPendingForSync(GetProfile(0));
+ InstallAppsPendingForSync(GetProfile(1));
+
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, InstallDifferentApps) {
+ ASSERT_TRUE(SetupClients());
+
+ int i = 0;
+
+ const int kNumCommonApps = 5;
+ for (int j = 0; j < kNumCommonApps; ++i, ++j) {
+ InstallApp(GetProfile(0), i);
+ InstallApp(GetProfile(1), i);
+ InstallApp(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ const int kNumProfile0Apps = 10;
+ for (int j = 0; j < kNumProfile0Apps; ++i, ++j) {
+ InstallApp(GetProfile(0), i);
+ InstallApp(verifier(), i);
+ }
+
+ const int kNumProfile1Apps = 10;
+ for (int j = 0; j < kNumProfile1Apps; ++i, ++j) {
+ InstallApp(GetProfile(1), i);
+ InstallApp(verifier(), i);
+ }
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallAppsPendingForSync(GetProfile(0));
+ InstallAppsPendingForSync(GetProfile(1));
+
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TCM ID - 3711279.
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, Add) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ InstallApp(GetProfile(0), 0);
+ InstallApp(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallAppsPendingForSync(GetProfile(0));
+ InstallAppsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TCM ID - 3706267.
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, Uninstall) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ InstallApp(GetProfile(0), 0);
+ InstallApp(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallAppsPendingForSync(GetProfile(0));
+ InstallAppsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ UninstallApp(GetProfile(0), 0);
+ UninstallApp(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TCM ID - 3699295.
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, Merge) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ InstallApp(GetProfile(0), 0);
+ InstallApp(GetProfile(1), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ UninstallApp(GetProfile(0), 0);
+ InstallApp(GetProfile(0), 1);
+ InstallApp(verifier(), 1);
+
+ InstallApp(GetProfile(0), 2);
+ InstallApp(GetProfile(1), 2);
+ InstallApp(verifier(), 2);
+
+ InstallApp(GetProfile(1), 3);
+ InstallApp(verifier(), 3);
+
+ ASSERT_TRUE(AwaitQuiescence());
+ InstallAppsPendingForSync(GetProfile(0));
+ InstallAppsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TCM ID - 7723126.
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, UpdateEnableDisableApp) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ InstallApp(GetProfile(0), 0);
+ InstallApp(GetProfile(1), 0);
+ InstallApp(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ DisableApp(GetProfile(0), 0);
+ DisableApp(verifier(), 0);
+ ASSERT_TRUE(HasSameAppsAsVerifier(0));
+ ASSERT_FALSE(HasSameAppsAsVerifier(1));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ EnableApp(GetProfile(1), 0);
+ EnableApp(verifier(), 0);
+ ASSERT_TRUE(HasSameAppsAsVerifier(1));
+ ASSERT_FALSE(HasSameAppsAsVerifier(0));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TCM ID - 7706637.
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, UpdateIncognitoEnableDisable) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ InstallApp(GetProfile(0), 0);
+ InstallApp(GetProfile(1), 0);
+ InstallApp(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ IncognitoEnableApp(GetProfile(0), 0);
+ IncognitoEnableApp(verifier(), 0);
+ ASSERT_TRUE(HasSameAppsAsVerifier(0));
+ ASSERT_FALSE(HasSameAppsAsVerifier(1));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ IncognitoDisableApp(GetProfile(1), 0);
+ IncognitoDisableApp(verifier(), 0);
+ ASSERT_TRUE(HasSameAppsAsVerifier(1));
+ ASSERT_FALSE(HasSameAppsAsVerifier(0));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TCM ID - 3718276.
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, DisableApps) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForDatatype(syncable::APPS));
+ InstallApp(GetProfile(0), 0);
+ InstallApp(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(HasSameAppsAsVerifier(0));
+ ASSERT_FALSE(HasSameAppsAsVerifier(1));
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForDatatype(syncable::APPS));
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallAppsPendingForSync(GetProfile(0));
+ InstallAppsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TCM ID - 3720303.
+IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, DisableSync) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForAllDatatypes());
+ InstallApp(GetProfile(0), 0);
+ InstallApp(verifier(), 0);
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Installed an app."));
+ ASSERT_TRUE(HasSameAppsAsVerifier(0));
+ ASSERT_FALSE(HasSameAppsAsVerifier(1));
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForAllDatatypes());
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallAppsPendingForSync(GetProfile(0));
+ InstallAppsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameAppsAsVerifier());
+}
+
+// TODO(akalin): Add tests exercising:
+// - Offline installation/uninstallation behavior
+// - App-specific properties
diff --git a/chrome/browser/sync/test/live_sync/two_client_autofill_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_autofill_sync_test.cc
new file mode 100644
index 0000000..1978a61
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_autofill_sync_test.cc
@@ -0,0 +1,409 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/autofill_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/webdata/autofill_entry.h"
+
+using autofill_helper::AddKeys;
+using autofill_helper::AddProfile;
+using autofill_helper::CreateAutofillProfile;
+using autofill_helper::GetAllKeys;
+using autofill_helper::GetAllProfiles;
+using autofill_helper::KeysMatch;
+using autofill_helper::ProfilesMatch;
+using autofill_helper::PROFILE_FRASIER;
+using autofill_helper::PROFILE_HOMER;
+using autofill_helper::PROFILE_MARION;
+using autofill_helper::PROFILE_NULL;
+using autofill_helper::RemoveKey;
+using autofill_helper::RemoveProfile;
+using autofill_helper::UpdateProfile;
+
+// Autofill entry length is limited to 1024. See http://crbug.com/49332.
+const size_t kMaxDataLength = 1024;
+
+class TwoClientAutofillSyncTest : public LiveSyncTest {
+ public:
+ TwoClientAutofillSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+ virtual ~TwoClientAutofillSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientAutofillSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, WebDataServiceSanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Client0 adds a key.
+ std::set<AutofillKey> keys;
+ keys.insert(AutofillKey("name0", "value0"));
+ AddKeys(0, keys);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(KeysMatch(0, 1));
+ ASSERT_EQ(1U, GetAllKeys(0).size());
+
+ // Client1 adds a key.
+ keys.clear();
+ keys.insert(AutofillKey("name1", "value1-0"));
+ AddKeys(1, keys);
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(KeysMatch(0, 1));
+ ASSERT_EQ(2U, GetAllKeys(0).size());
+
+ // Client0 adds a key with the same name.
+ keys.clear();
+ keys.insert(AutofillKey("name1", "value1-1"));
+ AddKeys(0, keys);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(KeysMatch(0, 1));
+ ASSERT_EQ(3U, GetAllKeys(0).size());
+
+ // Client1 removes a key.
+ RemoveKey(1, AutofillKey("name1", "value1-0"));
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(KeysMatch(0, 1));
+ ASSERT_EQ(2U, GetAllKeys(0).size());
+
+ // Client0 removes the rest.
+ RemoveKey(0, AutofillKey("name0", "value0"));
+ RemoveKey(0, AutofillKey("name1", "value1-1"));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(KeysMatch(0, 1));
+ ASSERT_EQ(0U, GetAllKeys(0).size());
+}
+
+// TCM ID - 3678296.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, AddUnicodeProfile) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ std::set<AutofillKey> keys;
+ keys.insert(AutofillKey(WideToUTF16(L"Sigur R\u00F3s"),
+ WideToUTF16(L"\u00C1g\u00E6tis byrjun")));
+ AddKeys(0, keys);
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(KeysMatch(0, 1));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest,
+ AddDuplicateNamesToSameProfile) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ std::set<AutofillKey> keys;
+ keys.insert(AutofillKey("name0", "value0-0"));
+ keys.insert(AutofillKey("name0", "value0-1"));
+ keys.insert(AutofillKey("name1", "value1"));
+ AddKeys(0, keys);
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(KeysMatch(0, 1));
+ ASSERT_EQ(2U, GetAllKeys(0).size());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest,
+ AddDuplicateNamesToDifferentProfiles) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ std::set<AutofillKey> keys0;
+ keys0.insert(AutofillKey("name0", "value0-0"));
+ keys0.insert(AutofillKey("name1", "value1"));
+ AddKeys(0, keys0);
+
+ std::set<AutofillKey> keys1;
+ keys1.insert(AutofillKey("name0", "value0-1"));
+ keys1.insert(AutofillKey("name2", "value2"));
+ keys1.insert(AutofillKey("name3", "value3"));
+ AddKeys(1, keys1);
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(KeysMatch(0, 1));
+ ASSERT_EQ(5U, GetAllKeys(0).size());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest,
+ PersonalDataManagerSanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Client0 adds a profile.
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ // Client1 adds a profile.
+ AddProfile(1, CreateAutofillProfile(PROFILE_MARION));
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(2U, GetAllProfiles(0).size());
+
+ // Client0 adds the same profile.
+ AddProfile(0, CreateAutofillProfile(PROFILE_MARION));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(2U, GetAllProfiles(0).size());
+
+ // Client1 removes a profile.
+ RemoveProfile(1, GetAllProfiles(1)[0]->guid());
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ // Client0 updates a profile.
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(NAME_FIRST),
+ ASCIIToUTF16("Bart"));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ // Client1 removes remaining profile.
+ RemoveProfile(1, GetAllProfiles(1)[0]->guid());
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(0U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 7261786.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, AddDuplicateProfiles) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3636294.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, SameProfileWithConflict) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ AutofillProfile profile0 = CreateAutofillProfile(PROFILE_HOMER);
+ AutofillProfile profile1 = CreateAutofillProfile(PROFILE_HOMER);
+ profile1.SetInfo(PHONE_FAX_WHOLE_NUMBER, ASCIIToUTF16("1234567890"));
+
+ AddProfile(0, profile0);
+ AddProfile(1, profile1);
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3626291.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, AddEmptyProfile) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_NULL));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(0U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3616283.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, AddProfile) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3632260.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, AddMultipleProfiles) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ AddProfile(0, CreateAutofillProfile(PROFILE_MARION));
+ AddProfile(0, CreateAutofillProfile(PROFILE_FRASIER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(3U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3602257.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, DeleteProfile) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ RemoveProfile(1, GetAllProfiles(1)[0]->guid());
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(0U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3627300.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, MergeProfiles) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ AddProfile(1, CreateAutofillProfile(PROFILE_MARION));
+ AddProfile(1, CreateAutofillProfile(PROFILE_FRASIER));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(3U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3665264.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, UpdateFields) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(NAME_FIRST),
+ ASCIIToUTF16("Lisa"));
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(EMAIL_ADDRESS),
+ ASCIIToUTF16("grrrl@TV.com"));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3628299.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, ConflictingFields) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(NAME_FIRST),
+ ASCIIToUTF16("Lisa"));
+ UpdateProfile(1,
+ GetAllProfiles(1)[0]->guid(),
+ AutofillType(NAME_FIRST),
+ ASCIIToUTF16("Bart"));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3663293.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, DisableAutofill) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(syncable::AUTOFILL));
+ AddProfile(0, CreateAutofillProfile(PROFILE_FRASIER));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_FALSE(ProfilesMatch(0, 1));
+ ASSERT_EQ(2U, GetAllProfiles(0).size());
+ ASSERT_EQ(1U, GetAllProfiles(1).size());
+
+ ASSERT_TRUE(GetClient(0)->EnableSyncForDatatype(syncable::AUTOFILL));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(2U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3661291.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, DisableSync) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForAllDatatypes());
+ AddProfile(0, CreateAutofillProfile(PROFILE_FRASIER));
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Added a profile."));
+ ASSERT_FALSE(ProfilesMatch(0, 1));
+ ASSERT_EQ(2U, GetAllProfiles(0).size());
+ ASSERT_EQ(1U, GetAllProfiles(1).size());
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForAllDatatypes());
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(2U, GetAllProfiles(0).size());
+}
+
+// TCM ID - 3608295.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, MaxLength) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ string16 max_length_string(kMaxDataLength, '.');
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(NAME_FIRST),
+ max_length_string);
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(NAME_LAST),
+ max_length_string);
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(EMAIL_ADDRESS),
+ max_length_string);
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(ADDRESS_HOME_LINE1),
+ max_length_string);
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+}
+
+// TODO(braffert): Remove FAILS annotation when crbug.com/85769 is resolved.
+// TCM ID - 7735472.
+IN_PROC_BROWSER_TEST_F(TwoClientAutofillSyncTest, FAILS_ExceedsMaxLength) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ AddProfile(0, CreateAutofillProfile(PROFILE_HOMER));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfilesMatch(0, 1));
+ ASSERT_EQ(1U, GetAllProfiles(0).size());
+
+ string16 exceeds_max_length_string(kMaxDataLength + 1, '.');
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(NAME_FIRST),
+ exceeds_max_length_string);
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(NAME_LAST),
+ exceeds_max_length_string);
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(EMAIL_ADDRESS),
+ exceeds_max_length_string);
+ UpdateProfile(0,
+ GetAllProfiles(0)[0]->guid(),
+ AutofillType(ADDRESS_HOME_LINE1),
+ exceeds_max_length_string);
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_FALSE(ProfilesMatch(0, 1));
+}
diff --git a/chrome/browser/sync/test/live_sync/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_bookmarks_sync_test.cc
new file mode 100644
index 0000000..869be0e
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_bookmarks_sync_test.cc
@@ -0,0 +1,1757 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/rand_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/bookmarks_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using bookmarks_helper::AddFolder;
+using bookmarks_helper::AddURL;
+using bookmarks_helper::AllModelsMatch;
+using bookmarks_helper::AllModelsMatchVerifier;
+using bookmarks_helper::ContainsDuplicateBookmarks;
+using bookmarks_helper::CountBookmarksWithTitlesMatching;
+using bookmarks_helper::CreateFavicon;
+using bookmarks_helper::GetBookmarkBarNode;
+using bookmarks_helper::GetOtherNode;
+using bookmarks_helper::GetUniqueNodeByURL;
+using bookmarks_helper::IndexedFolderName;
+using bookmarks_helper::IndexedSubfolderName;
+using bookmarks_helper::IndexedSubsubfolderName;
+using bookmarks_helper::IndexedURL;
+using bookmarks_helper::IndexedURLTitle;
+using bookmarks_helper::Move;
+using bookmarks_helper::Remove;
+using bookmarks_helper::ReverseChildOrder;
+using bookmarks_helper::SetFavicon;
+using bookmarks_helper::SetTitle;
+using bookmarks_helper::SetURL;
+using bookmarks_helper::SortChildren;
+
+const std::string kGenericURL = "http://www.host.ext:1234/path/filename";
+const std::wstring kGenericURLTitle = L"URL Title";
+const std::wstring kGenericFolderName = L"Folder Name";
+const std::wstring kGenericSubfolderName = L"Subfolder Name";
+const std::wstring kGenericSubsubfolderName = L"Subsubfolder Name";
+
+class TwoClientBookmarksSyncTest : public LiveSyncTest {
+ public:
+ TwoClientBookmarksSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+ virtual ~TwoClientBookmarksSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientBookmarksSyncTest);
+};
+
+const std::vector<unsigned char> GenericFavicon() {
+ return CreateFavicon(254);
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, Sanity) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL google_url("http://www.google.com");
+ ASSERT_TRUE(AddURL(0, L"Google", google_url) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AddURL(1, L"Yahoo", GURL("http://www.yahoo.com")) != NULL);
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* new_folder = AddFolder(0, 2, L"New Folder");
+ Move(0, GetUniqueNodeByURL(0, google_url), new_folder, 0);
+ SetTitle(0, GetBookmarkBarNode(0)->GetChild(0), L"Yahoo!!");
+ ASSERT_TRUE(AddURL(0, GetBookmarkBarNode(0), 1, L"CNN",
+ GURL("http://www.cnn.com")) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(1, L"Facebook", GURL("http://www.facebook.com")) != NULL);
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ SortChildren(1, GetBookmarkBarNode(1));
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ DisableVerifier();
+ SetTitle(0, GetUniqueNodeByURL(0, google_url), L"Google++");
+ SetTitle(1, GetUniqueNodeByURL(1, google_url), L"Google--");
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SimultaneousURLChanges) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL initial_url("http://www.google.com");
+ GURL second_url("http://www.google.com/abc");
+ GURL third_url("http://www.google.com/def");
+ std::wstring title = L"Google";
+
+ ASSERT_TRUE(AddURL(0, title, initial_url) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ DisableVerifier();
+ ASSERT_TRUE(SetURL(
+ 0, GetUniqueNodeByURL(0, initial_url), second_url) != NULL);
+ ASSERT_TRUE(SetURL(
+ 1, GetUniqueNodeByURL(1, initial_url), third_url) != NULL);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+
+ SetTitle(0, GetBookmarkBarNode(0)->GetChild(0), L"Google1");
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatch());
+}
+
+// Test Scribe ID - 370558.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_AddFirstFolder) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddFolder(0, kGenericFolderName) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370559.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_AddFirstBMWithoutFavicon) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370489.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_AddFirstBMWithFavicon) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* bookmark = AddURL(0, kGenericURLTitle, GURL(kGenericURL));
+ ASSERT_TRUE(bookmark != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ SetFavicon(0, bookmark, GenericFavicon());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370560.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_AddNonHTTPBMs) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(
+ 0, L"FTP URL", GURL("ftp://user:password@host:1234/path")) != NULL);
+ ASSERT_TRUE(AddURL(0, L"File URL", GURL("file://host/path")) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370561.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_AddFirstBMUnderFolder) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ ASSERT_TRUE(AddURL(
+ 0, folder, 0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370562.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_AddSeveralBMsUnderBMBarAndOtherBM) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 20; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ ASSERT_TRUE(AddURL(
+ 0, GetOtherNode(0), i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370563.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_AddSeveralBMsAndFolders) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 15; ++i) {
+ if (base::RandDouble() > 0.6) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedFolderName(i);
+ const BookmarkNode* folder = AddFolder(0, i, title);
+ ASSERT_TRUE(folder != NULL);
+ if (base::RandDouble() > 0.4) {
+ for (int i = 0; i < 20; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(
+ AddURL(0, folder, i, title, url) != NULL);
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 10; i++) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, GetOtherNode(0), i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370641.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DuplicateBMWithDifferentURLSameName) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL url0 = GURL(IndexedURL(0));
+ GURL url1 = GURL(IndexedURL(1));
+ ASSERT_TRUE(AddURL(0, kGenericURLTitle, url0) != NULL);
+ ASSERT_TRUE(AddURL(0, kGenericURLTitle, url1) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 370639 - Add bookmarks with different name and same URL.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DuplicateBookmarksWithSameURL) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring title0 = IndexedURLTitle(0);
+ std::wstring title1 = IndexedURLTitle(1);
+ ASSERT_TRUE(AddURL(0, title0, GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(AddURL(0, title1, GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371817.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMName) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring title = IndexedURLTitle(1);
+ const BookmarkNode* bookmark = AddURL(0, title, GURL(kGenericURL));
+ ASSERT_TRUE(bookmark != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring new_title = IndexedURLTitle(2);
+ SetTitle(0, bookmark, new_title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371822.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMURL) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL url = GURL(IndexedURL(1));
+ const BookmarkNode* bookmark = AddURL(0, kGenericURLTitle, url);
+ ASSERT_TRUE(bookmark != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL new_url = GURL(IndexedURL(2));
+ ASSERT_TRUE(SetURL(0, bookmark, new_url) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+
+// Test Scribe ID - 371818 - Renaming the same bookmark name twice.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_TwiceRenamingBookmarkName) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring title = IndexedURLTitle(1);
+ const BookmarkNode* bookmark = AddURL(0, title, GURL(kGenericURL));
+ ASSERT_TRUE(bookmark != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring new_title = IndexedURLTitle(2);
+ SetTitle(0, bookmark, new_title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ SetTitle(0, bookmark, title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371823 - Renaming the same bookmark URL twice.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_TwiceRenamingBookmarkURL) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL url = GURL(IndexedURL(1));
+ const BookmarkNode* bookmark = AddURL(0, kGenericURLTitle, url);
+ ASSERT_TRUE(bookmark != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL new_url = GURL(IndexedURL(2));
+ ASSERT_TRUE(SetURL(0, bookmark, new_url) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(SetURL(0, bookmark, url) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371824.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMFolder) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring title = IndexedFolderName(1);
+ const BookmarkNode* folder = AddFolder(0, title);
+ ASSERT_TRUE(AddURL(
+ 0, folder, 0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring new_title = IndexedFolderName(2);
+ SetTitle(0, folder, new_title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371825.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameEmptyBMFolder) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring title = IndexedFolderName(1);
+ const BookmarkNode* folder = AddFolder(0, title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring new_title = IndexedFolderName(2);
+ SetTitle(0, folder, new_title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371826.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_RenameBMFolderWithLongHierarchy) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring title = IndexedFolderName(1);
+ const BookmarkNode* folder = AddFolder(0, title);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 120; ++i) {
+ if (base::RandDouble() > 0.15) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedSubfolderName(i);
+ ASSERT_TRUE(AddFolder(0, folder, i, title) != NULL);
+ }
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring new_title = IndexedFolderName(2);
+ SetTitle(0, folder, new_title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371827.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_RenameBMFolderThatHasParentAndChildren) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 1; i < 15; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ }
+ std::wstring title = IndexedSubfolderName(1);
+ const BookmarkNode* subfolder = AddFolder(0, folder, 0, title);
+ for (int i = 0; i < 120; ++i) {
+ if (base::RandDouble() > 0.15) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, subfolder, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedSubsubfolderName(i);
+ ASSERT_TRUE(AddFolder(0, subfolder, i, title) != NULL);
+ }
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ std::wstring new_title = IndexedSubfolderName(2);
+ SetTitle(0, subfolder, new_title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371828.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMNameAndURL) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL url = GURL(IndexedURL(1));
+ std::wstring title = IndexedURLTitle(1);
+ const BookmarkNode* bookmark = AddURL(0, title, url);
+ ASSERT_TRUE(bookmark != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL new_url = GURL(IndexedURL(2));
+ std::wstring new_title = IndexedURLTitle(2);
+ bookmark = SetURL(0, bookmark, new_url);
+ ASSERT_TRUE(bookmark != NULL);
+ SetTitle(0, bookmark, new_title);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371832.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DeleteBMEmptyAccountAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(
+ 0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, GetBookmarkBarNode(0), 0);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371833.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelBMNonEmptyAccountAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 20; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, GetBookmarkBarNode(0), 0);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371835.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelFirstBMUnderBMFoldNonEmptyFoldAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, folder, 0);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371836.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelLastBMUnderBMFoldNonEmptyFoldAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, folder, folder->child_count() - 1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371856.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelMiddleBMUnderBMFoldNonEmptyFoldAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, folder, 4);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371857.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelBMsUnderBMFoldEmptyFolderAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ int child_count = folder->child_count();
+ for (int i = 0; i < child_count; ++i) {
+ Remove(0, folder, 0);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371858.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelEmptyBMFoldEmptyAccountAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddFolder(0, kGenericFolderName) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, GetBookmarkBarNode(0), 0);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371869.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelEmptyBMFoldNonEmptyAccountAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddFolder(0, kGenericFolderName) != NULL);
+ for (int i = 1; i < 15; ++i) {
+ if (base::RandDouble() > 0.6) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedFolderName(i);
+ ASSERT_TRUE(AddFolder(0, i, title) != NULL);
+ }
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, GetBookmarkBarNode(0), 0);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371879.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelBMFoldWithBMsNonEmptyAccountAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 2; i < 10; ++i) {
+ if (base::RandDouble() > 0.6) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedFolderName(i);
+ ASSERT_TRUE(AddFolder(0, i, title) != NULL);
+ }
+ }
+ for (int i = 0; i < 15; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, GetBookmarkBarNode(0), 1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371880.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelBMFoldWithBMsAndBMFoldsNonEmptyACAfterwards) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 2; i < 10; ++i) {
+ if (base::RandDouble() > 0.6) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedFolderName(i);
+ ASSERT_TRUE(AddFolder(0, i, title) != NULL);
+ }
+ }
+ for (int i = 0; i < 10; ++i) {
+ if (base::RandDouble() > 0.6) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedSubfolderName(i);
+ const BookmarkNode* subfolder =
+ AddFolder(0, folder, i, title);
+ ASSERT_TRUE(subfolder != NULL);
+ if (base::RandDouble() > 0.3) {
+ for (int j = 0; j < 10; ++j) {
+ if (base::RandDouble() > 0.6) {
+ std::wstring title = IndexedURLTitle(j);
+ GURL url = GURL(IndexedURL(j));
+ ASSERT_TRUE(AddURL(
+ 0, subfolder, j, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedSubsubfolderName(j);
+ ASSERT_TRUE(AddFolder(
+ 0, subfolder, j, title) != NULL);
+ }
+ }
+ }
+ }
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, GetBookmarkBarNode(0), 1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371882.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_DelBMFoldWithParentAndChildrenBMsAndBMFolds) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 1; i < 11; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ }
+ const BookmarkNode* subfolder =
+ AddFolder(0, folder, 0, kGenericSubfolderName);
+ ASSERT_TRUE(subfolder != NULL);
+ for (int i = 0; i < 30; ++i) {
+ if (base::RandDouble() > 0.2) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, subfolder, i, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedSubsubfolderName(i);
+ ASSERT_TRUE(AddFolder(0, subfolder, i, title) != NULL);
+ }
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Remove(0, folder, 0);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371931.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_ReverseTheOrderOfTwoBMs) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL url0 = GURL(IndexedURL(0));
+ GURL url1 = GURL(IndexedURL(1));
+ std::wstring title0 = IndexedURLTitle(0);
+ std::wstring title1 = IndexedURLTitle(1);
+ const BookmarkNode* bookmark0 = AddURL(0, 0, title0, url0);
+ const BookmarkNode* bookmark1 = AddURL(0, 1, title1, url1);
+ ASSERT_TRUE(bookmark0 != NULL);
+ ASSERT_TRUE(bookmark1 != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Move(0, bookmark0, GetBookmarkBarNode(0), 2);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371933.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_ReverseTheOrderOf10BMs) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ReverseChildOrder(0, GetBookmarkBarNode(0));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371954.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_MovingBMsFromBMBarToBMFolder) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 2; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ int num_bookmarks_to_move =
+ GetBookmarkBarNode(0)->child_count() - 2;
+ for (int i = 0; i < num_bookmarks_to_move; ++i) {
+ Move(
+ 0, GetBookmarkBarNode(0)->GetChild(2), folder, i);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+ }
+}
+
+// Test Scribe ID - 371957.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_MovingBMsFromBMFoldToBMBar) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(0, kGenericURLTitle, GURL(kGenericURL)) != NULL);
+ const BookmarkNode* folder = AddFolder(0, 1, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ int num_bookmarks_to_move = folder->child_count() - 2;
+ for (int i = 0; i < num_bookmarks_to_move; ++i) {
+ Move(0, folder->GetChild(0), GetBookmarkBarNode(0), i);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+ }
+}
+
+// Test Scribe ID - 371961.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_MovingBMsFromParentBMFoldToChildBMFold) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ const BookmarkNode* subfolder =
+ AddFolder(0, folder, 3, kGenericSubfolderName);
+ ASSERT_TRUE(subfolder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i + 3);
+ GURL url = GURL(IndexedURL(i + 3));
+ ASSERT_TRUE(AddURL(0, subfolder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 3; ++i) {
+ GURL url = GURL(IndexedURL(i));
+ Move(0, GetUniqueNodeByURL(0, url), subfolder, i + 10);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371964.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_MovingBMsFromChildBMFoldToParentBMFold) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ const BookmarkNode* subfolder =
+ AddFolder(0, folder, 3, kGenericSubfolderName);
+ ASSERT_TRUE(subfolder != NULL);
+ for (int i = 0; i < 5; ++i) {
+ std::wstring title = IndexedURLTitle(i + 3);
+ GURL url = GURL(IndexedURL(i + 3));
+ ASSERT_TRUE(AddURL(0, subfolder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 3; ++i) {
+ GURL url = GURL(IndexedURL(i + 3));
+ Move(0, GetUniqueNodeByURL(0, url), folder, i + 4);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371967.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_HoistBMs10LevelUp) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = GetBookmarkBarNode(0);
+ const BookmarkNode* folder_L0 = NULL;
+ const BookmarkNode* folder_L10 = NULL;
+ for (int level = 0; level < 15; ++level) {
+ int num_bookmarks = base::RandInt(0, 9);
+ for (int i = 0; i < num_bookmarks; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ std::wstring title = IndexedFolderName(level);
+ folder = AddFolder(0, folder, folder->child_count(), title);
+ ASSERT_TRUE(folder != NULL);
+ if (level == 0) folder_L0 = folder;
+ if (level == 10) folder_L10 = folder;
+ }
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i + 10);
+ GURL url = GURL(IndexedURL(i + 10));
+ ASSERT_TRUE(AddURL(0, folder_L10, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL url10 = GURL(IndexedURL(10));
+ Move(0, GetUniqueNodeByURL(
+ 0, url10), folder_L0, folder_L0->child_count());
+ GURL url11 = GURL(IndexedURL(11));
+ Move(0, GetUniqueNodeByURL(0, url11), folder_L0, 0);
+ GURL url12 = GURL(IndexedURL(12));
+ Move(0, GetUniqueNodeByURL(0, url12), folder_L0, 1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371968.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_SinkBMs10LevelDown) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = GetBookmarkBarNode(0);
+ const BookmarkNode* folder_L0 = NULL;
+ const BookmarkNode* folder_L10 = NULL;
+ for (int level = 0; level < 15; ++level) {
+ int num_bookmarks = base::RandInt(0, 9);
+ for (int i = 0; i < num_bookmarks; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ std::wstring title = IndexedFolderName(level);
+ folder = AddFolder(0, folder, folder->child_count(), title);
+ ASSERT_TRUE(folder != NULL);
+ if (level == 0) folder_L0 = folder;
+ if (level == 10) folder_L10 = folder;
+ }
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i + 10);
+ GURL url = GURL(IndexedURL(i + 10));
+ ASSERT_TRUE(AddURL(0, folder_L0, 0, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ GURL url10 = GURL(IndexedURL(10));
+ Move(0, GetUniqueNodeByURL(0, url10), folder_L10, folder_L10->child_count());
+ GURL url11 = GURL(IndexedURL(11));
+ Move(0, GetUniqueNodeByURL(0, url11), folder_L10, 0);
+ GURL url12 = GURL(IndexedURL(12));
+ Move(0, GetUniqueNodeByURL(0, url12), folder_L10, 1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371980.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_SinkEmptyBMFold5LevelsDown) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = GetBookmarkBarNode(0);
+ const BookmarkNode* folder_L5 = NULL;
+ for (int level = 0; level < 15; ++level) {
+ int num_bookmarks = base::RandInt(0, 9);
+ for (int i = 0; i < num_bookmarks; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ std::wstring title = IndexedFolderName(level);
+ folder = AddFolder(
+ 0, folder, folder->child_count(), title);
+ ASSERT_TRUE(folder != NULL);
+ if (level == 5) folder_L5 = folder;
+ }
+ folder = AddFolder(
+ 0, GetBookmarkBarNode(0)->child_count(), kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Move(0, folder, folder_L5, folder_L5->child_count());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 371997.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_SinkNonEmptyBMFold5LevelsDown) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = GetBookmarkBarNode(0);
+ const BookmarkNode* folder_L5 = NULL;
+ for (int level = 0; level < 6; ++level) {
+ int num_bookmarks = base::RandInt(0, 9);
+ for (int i = 0; i < num_bookmarks; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ std::wstring title = IndexedFolderName(level);
+ folder = AddFolder(0, folder, folder->child_count(), title);
+ ASSERT_TRUE(folder != NULL);
+ if (level == 5) folder_L5 = folder;
+ }
+ folder = AddFolder(
+ 0, GetBookmarkBarNode(0)->child_count(), kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Move(0, folder, folder_L5, folder_L5->child_count());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 372006.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_HoistFolder5LevelsUp) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ const BookmarkNode* folder = GetBookmarkBarNode(0);
+ const BookmarkNode* folder_L5 = NULL;
+ for (int level = 0; level < 6; ++level) {
+ int num_bookmarks = base::RandInt(0, 9);
+ for (int i = 0; i < num_bookmarks; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ std::wstring title = IndexedFolderName(level);
+ folder = AddFolder(
+ 0, folder, folder->child_count(), title);
+ ASSERT_TRUE(folder != NULL);
+ if (level == 5) folder_L5 = folder;
+ }
+ folder = AddFolder(
+ 0, folder_L5, folder_L5->child_count(), kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ Move(0, folder, GetBookmarkBarNode(0), GetBookmarkBarNode(0)->child_count());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 372026.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_ReverseTheOrderOfTwoBMFolders) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 2; ++i) {
+ std::wstring title = IndexedFolderName(i);
+ const BookmarkNode* folder = AddFolder(0, i, title);
+ ASSERT_TRUE(folder != NULL);
+ for (int j = 0; j < 10; ++j) {
+ std::wstring title = IndexedURLTitle(j);
+ GURL url = GURL(IndexedURL(j));
+ ASSERT_TRUE(AddURL(0, folder, j, title, url) != NULL);
+ }
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ReverseChildOrder(0, GetBookmarkBarNode(0));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 372028.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SC_ReverseTheOrderOfTenBMFolders) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ for (int i = 0; i < 10; ++i) {
+ std::wstring title = IndexedFolderName(i);
+ const BookmarkNode* folder = AddFolder(0, i, title);
+ ASSERT_TRUE(folder != NULL);
+ for (int j = 0; j < 10; ++j) {
+ std::wstring title = IndexedURLTitle(1000 * i + j);
+ GURL url = GURL(IndexedURL(j));
+ ASSERT_TRUE(AddURL(0, folder, j, title, url) != NULL);
+ }
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ReverseChildOrder(0, GetBookmarkBarNode(0));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 373379.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_BiDirectionalPushAddingBM) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ DisableVerifier();
+ for (int i = 0; i < 2; ++i) {
+ std::wstring title0 = IndexedURLTitle(2*i);
+ GURL url0 = GURL(IndexedURL(2*i));
+ ASSERT_TRUE(AddURL(0, title0, url0) != NULL);
+ std::wstring title1 = IndexedURLTitle(2*i+1);
+ GURL url1 = GURL(IndexedURL(2*i+1));
+ ASSERT_TRUE(AddURL(1, title1, url1) != NULL);
+ }
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// Test Scribe ID - 373503.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_BiDirectionalPush_AddingSameBMs) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ // Note: When a racy commit is done with identical bookmarks, it is possible
+ // for duplicates to exist after sync completes. See http://crbug.com/19769.
+ DisableVerifier();
+ for (int i = 0; i < 2; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, title, url) != NULL);
+ ASSERT_TRUE(AddURL(1, title, url) != NULL);
+ }
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+}
+
+// Test Scribe ID - 373506.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_BootStrapEmptyStateEverywhere) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// Test Scribe ID - 373505.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_Merge_CaseInsensitivity_InNames) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ const BookmarkNode* folder0 = AddFolder(0, L"Folder");
+ ASSERT_TRUE(folder0 != NULL);
+ ASSERT_TRUE(AddURL(0, folder0, 0, L"Bookmark 0", GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(AddURL(0, folder0, 1, L"Bookmark 1", GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(AddURL(0, folder0, 2, L"Bookmark 2", GURL(kGenericURL)) != NULL);
+
+ const BookmarkNode* folder1 = AddFolder(1, L"fOlDeR");
+ ASSERT_TRUE(folder1 != NULL);
+ ASSERT_TRUE(AddURL(1, folder1, 0, L"bOoKmArK 0", GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(AddURL(1, folder1, 1, L"BooKMarK 1", GURL(kGenericURL)) != NULL);
+ ASSERT_TRUE(AddURL(1, folder1, 2, L"bOOKMARK 2", GURL(kGenericURL)) != NULL);
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// Test Scribe ID - 373508.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_SimpleMergeOfDifferentBMModels) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ ASSERT_TRUE(AddURL(1, i, title, url) != NULL);
+ }
+
+ for (int i = 3; i < 10; ++i) {
+ std::wstring title0 = IndexedURLTitle(i);
+ GURL url0 = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title0, url0) != NULL);
+ std::wstring title1 = IndexedURLTitle(i+7);
+ GURL url1 = GURL(IndexedURL(i+7));
+ ASSERT_TRUE(AddURL(1, i, title1, url1) != NULL);
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// Test Scribe ID - 386586.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_MergeSimpleBMHierarchyUnderBMBar) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ ASSERT_TRUE(AddURL(1, i, title, url) != NULL);
+ }
+
+ for (int i = 3; i < 10; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(1, i, title, url) != NULL);
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// Test Scribe ID - 386589.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_MergeSimpleBMHierarchyEqualSetsUnderBMBar) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ ASSERT_TRUE(AddURL(1, i, title, url) != NULL);
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// Test Scribe ID - 373504 - Merge bookmark folders with different bookmarks.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_MergeBMFoldersWithDifferentBMs) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ const BookmarkNode* folder0 = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder0 != NULL);
+ const BookmarkNode* folder1 = AddFolder(1, kGenericFolderName);
+ ASSERT_TRUE(folder1 != NULL);
+ for (int i = 0; i < 2; ++i) {
+ std::wstring title0 = IndexedURLTitle(2*i);
+ GURL url0 = GURL(IndexedURL(2*i));
+ ASSERT_TRUE(AddURL(0, folder0, i, title0, url0) != NULL);
+ std::wstring title1 = IndexedURLTitle(2*i+1);
+ GURL url1 = GURL(IndexedURL(2*i+1));
+ ASSERT_TRUE(AddURL(1, folder1, i, title1, url1) != NULL);
+ }
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// Test Scribe ID - 373509 - Merge moderately complex bookmark models.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_MergeDifferentBMModelsModeratelyComplex) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < 25; ++i) {
+ std::wstring title0 = IndexedURLTitle(i);
+ GURL url0 = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title0, url0) != NULL);
+ std::wstring title1 = IndexedURLTitle(i+50);
+ GURL url1 = GURL(IndexedURL(i+50));
+ ASSERT_TRUE(AddURL(1, i, title1, url1) != NULL);
+ }
+ for (int i = 25; i < 30; ++i) {
+ std::wstring title0 = IndexedFolderName(i);
+ const BookmarkNode* folder0 = AddFolder(0, i, title0);
+ ASSERT_TRUE(folder0 != NULL);
+ std::wstring title1 = IndexedFolderName(i+50);
+ const BookmarkNode* folder1 = AddFolder(1, i, title1);
+ ASSERT_TRUE(folder1 != NULL);
+ for (int j = 0; j < 5; ++j) {
+ std::wstring title0 = IndexedURLTitle(i+5*j);
+ GURL url0 = GURL(IndexedURL(i+5*j));
+ ASSERT_TRUE(AddURL(0, folder0, j, title0, url0) != NULL);
+ std::wstring title1 = IndexedURLTitle(i+5*j+50);
+ GURL url1 = GURL(IndexedURL(i+5*j+50));
+ ASSERT_TRUE(AddURL(1, folder1, j, title1, url1) != NULL);
+ }
+ }
+ for (int i = 100; i < 125; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, title, url) != NULL);
+ ASSERT_TRUE(AddURL(1, title, url) != NULL);
+ }
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3675271 - Merge simple bookmark subset under bookmark folder.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_MergeSimpleBMHierarchySubsetUnderBMFolder) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < 2; ++i) {
+ const BookmarkNode* folder = AddFolder(i, kGenericFolderName);
+ ASSERT_TRUE(folder != NULL);
+ for (int j = 0; j < 4; ++j) {
+ if (base::RandDouble() < 0.5) {
+ std::wstring title = IndexedURLTitle(j);
+ GURL url = GURL(IndexedURL(j));
+ ASSERT_TRUE(AddURL(i, folder, j, title, url) != NULL);
+ } else {
+ std::wstring title = IndexedFolderName(j);
+ ASSERT_TRUE(AddFolder(i, folder, j, title) != NULL);
+ }
+ }
+ }
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3727284 - Merge subsets of bookmark under bookmark bar.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_MergeSimpleBMHierarchySubsetUnderBookmarkBar) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < 4; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ }
+
+ for (int j = 0; j < 2; ++j) {
+ std::wstring title = IndexedURLTitle(j);
+ GURL url = GURL(IndexedURL(j));
+ ASSERT_TRUE(AddURL(1, j, title, url) != NULL);
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+ ASSERT_FALSE(ContainsDuplicateBookmarks(1));
+}
+
+// TCM ID - 3659294 - Merge simple bookmark hierarchy under bookmark folder.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_Merge_SimpleBMHierarchy_Under_BMFolder) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ const BookmarkNode* folder0 = AddFolder(0, 0, kGenericFolderName);
+ ASSERT_TRUE(folder0 != NULL);
+ ASSERT_TRUE(AddURL(
+ 0, folder0, 0, IndexedURLTitle(1), GURL(IndexedURL(1))) != NULL);
+ ASSERT_TRUE(AddFolder(0, folder0, 1, IndexedSubfolderName(2)) != NULL);
+ ASSERT_TRUE(AddURL(
+ 0, folder0, 2, IndexedURLTitle(3), GURL(IndexedURL(3))) != NULL);
+ ASSERT_TRUE(AddFolder(0, folder0, 3, IndexedSubfolderName(4)) != NULL);
+
+ const BookmarkNode* folder1 = AddFolder(1, 0, kGenericFolderName);
+ ASSERT_TRUE(folder1 != NULL);
+ ASSERT_TRUE(AddFolder(1, folder1, 0, IndexedSubfolderName(0)) != NULL);
+ ASSERT_TRUE(AddFolder(1, folder1, 1, IndexedSubfolderName(2)) != NULL);
+ ASSERT_TRUE(AddURL(
+ 1, folder1, 2, IndexedURLTitle(3), GURL(IndexedURL(3))) != NULL);
+ ASSERT_TRUE(AddFolder(1, folder1, 3, IndexedSubfolderName(5)) != NULL);
+ ASSERT_TRUE(AddURL(
+ 1, folder1, 4, IndexedURLTitle(1), GURL(IndexedURL(1))) != NULL);
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3711273 - Merge disjoint sets of bookmark hierarchy under bookmark
+// folder.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_Merge_SimpleBMHierarchy_DisjointSets_Under_BMFolder) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ const BookmarkNode* folder0 =
+ AddFolder(0, 0, kGenericFolderName);
+ ASSERT_TRUE(folder0 != NULL);
+ ASSERT_TRUE(AddURL(
+ 0, folder0, 0, IndexedURLTitle(1), GURL(IndexedURL(1))) != NULL);
+ ASSERT_TRUE(AddFolder(0, folder0, 1, IndexedSubfolderName(2)) != NULL);
+ ASSERT_TRUE(AddURL(
+ 0, folder0, 2, IndexedURLTitle(3), GURL(IndexedURL(3))) != NULL);
+ ASSERT_TRUE(AddFolder(0, folder0, 3, IndexedSubfolderName(4)) != NULL);
+
+ const BookmarkNode* folder1 = AddFolder(1, 0, kGenericFolderName);
+ ASSERT_TRUE(folder1 != NULL);
+ ASSERT_TRUE(AddFolder(1, folder1, 0, IndexedSubfolderName(5)) != NULL);
+ ASSERT_TRUE(AddFolder(1, folder1, 1, IndexedSubfolderName(6)) != NULL);
+ ASSERT_TRUE(AddURL(
+ 1, folder1, 2, IndexedURLTitle(7), GURL(IndexedURL(7))) != NULL);
+ ASSERT_TRUE(AddURL(
+ 1, folder1, 3, IndexedURLTitle(8), GURL(IndexedURL(8))) != NULL);
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3639296 - Merge disjoint sets of bookmark hierarchy under bookmark
+// bar.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_Merge_SimpleBMHierarchy_DisjointSets_Under_BookmarkBar) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i+1);
+ GURL url = GURL(IndexedURL(i+1));
+ ASSERT_TRUE(AddURL(0, i, title, url) != NULL);
+ }
+
+ for (int j = 0; j < 3; ++j) {
+ std::wstring title = IndexedURLTitle(j+4);
+ GURL url = GURL(IndexedURL(j+4));
+ ASSERT_TRUE(AddURL(0, j, title, url) != NULL);
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3616282 - Merge sets of duplicate bookmarks under bookmark bar.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_Merge_SimpleBMHierarchy_DuplicateBMs_Under_BMBar) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ // Let's add duplicate set of bookmark {1,2,2,3,3,3,4,4,4,4} to client0.
+ int node_index = 0;
+ for (int i = 1; i < 5 ; ++i) {
+ for (int j = 0; j < i; ++j) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, node_index, title, url) != NULL);
+ ++node_index;
+ }
+ }
+ // Let's add a set of bookmarks {1,2,3,4} to client1.
+ for (int i = 0; i < 4; ++i) {
+ std::wstring title = IndexedURLTitle(i+1);
+ GURL url = GURL(IndexedURL(i+1));
+ ASSERT_TRUE(AddURL(1, i, title, url) != NULL);
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+
+ for (int i = 1; i < 5 ; ++i) {
+ ASSERT_TRUE(CountBookmarksWithTitlesMatching(1, IndexedURLTitle(i)) == i);
+ }
+}
+
+// TCM ID - 6593872.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, DisableBookmarks) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForDatatype(syncable::BOOKMARKS));
+ ASSERT_TRUE(AddFolder(1, kGenericFolderName) != NULL);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_FALSE(AllModelsMatch());
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForDatatype(syncable::BOOKMARKS));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+}
+
+// TCM ID - 7343544.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, DisableSync) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForAllDatatypes());
+ ASSERT_TRUE(AddFolder(0, IndexedFolderName(0)) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Added a folder."));
+ ASSERT_FALSE(AllModelsMatch());
+
+ ASSERT_TRUE(AddFolder(1, IndexedFolderName(1)) != NULL);
+ ASSERT_FALSE(AllModelsMatch());
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForAllDatatypes());
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+}
+
+// TCM ID - 3662298 - Test adding duplicate folder - Both with different BMs
+// underneath.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, MC_DuplicateFolders) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ const BookmarkNode* folder0 = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder0 != NULL);
+ const BookmarkNode* folder1 = AddFolder(1, kGenericFolderName);
+ ASSERT_TRUE(folder1 != NULL);
+ for (int i = 0; i < 5; ++i) {
+ std::wstring title0 = IndexedURLTitle(i);
+ GURL url0 = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder0, i, title0, url0) != NULL);
+ std::wstring title1 = IndexedURLTitle(i+5);
+ GURL url1 = GURL(IndexedURL(i+5));
+ ASSERT_TRUE(AddURL(1, folder1, i, title1, url1) != NULL);
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3719307 - Test a scenario of updating the name of the same bookmark
+// from two clients at the same time.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_BookmarkNameChangeConflict) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ const BookmarkNode* folder0 = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder0 != NULL);
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder0, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+
+ DisableVerifier();
+ GURL url(IndexedURL(0));
+ SetTitle(0, GetUniqueNodeByURL(0, url), L"Title++");
+ SetTitle(1, GetUniqueNodeByURL(1, url), L"Title--");
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3672299 - Test a scenario of updating the URL of the same bookmark
+// from two clients at the same time.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_BookmarkURLChangeConflict) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ const BookmarkNode* folder0 = AddFolder(0, kGenericFolderName);
+ ASSERT_TRUE(folder0 != NULL);
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folder0, i, title, url) != NULL);
+ }
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+
+ DisableVerifier();
+ GURL url(IndexedURL(0));
+ ASSERT_TRUE(SetURL(
+ 0, GetUniqueNodeByURL(0, url), GURL("http://www.google.com/00")));
+ ASSERT_TRUE(SetURL(
+ 1, GetUniqueNodeByURL(1, url), GURL("http://www.google.com/11")));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+// TCM ID - 3699290 - Test a scenario of updating the BM Folder name from two
+// clients at the same time.
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ MC_FolderNameChangeConflict) {
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+ DisableVerifier();
+
+ const BookmarkNode* folderA[2];
+ const BookmarkNode* folderB[2];
+ const BookmarkNode* folderC[2];
+
+ // Create empty folder A on both clients.
+ folderA[0] = AddFolder(0, IndexedFolderName(0));
+ ASSERT_TRUE(folderA[0] != NULL);
+ folderA[1] = AddFolder(1, IndexedFolderName(0));
+ ASSERT_TRUE(folderA[1] != NULL);
+
+ // Create folder B with bookmarks on both clients.
+ folderB[0] = AddFolder(0, IndexedFolderName(1));
+ ASSERT_TRUE(folderB[0] != NULL);
+ folderB[1] = AddFolder(1, IndexedFolderName(1));
+ ASSERT_TRUE(folderB[1] != NULL);
+ for (int i = 0; i < 3; ++i) {
+ std::wstring title = IndexedURLTitle(i);
+ GURL url = GURL(IndexedURL(i));
+ ASSERT_TRUE(AddURL(0, folderB[0], i, title, url) != NULL);
+ }
+
+ // Create folder C with bookmarks and subfolders on both clients.
+ folderC[0] = AddFolder(0, IndexedFolderName(2));
+ ASSERT_TRUE(folderC[0] != NULL);
+ folderC[1] = AddFolder(1, IndexedFolderName(2));
+ ASSERT_TRUE(folderC[1] != NULL);
+ for (int i = 0; i < 3; ++i) {
+ std::wstring folder_name = IndexedSubfolderName(i);
+ const BookmarkNode* subfolder = AddFolder(0, folderC[0], i, folder_name);
+ ASSERT_TRUE(subfolder != NULL);
+ for (int j = 0; j < 3; ++j) {
+ std::wstring title = IndexedURLTitle(j);
+ GURL url = GURL(IndexedURL(j));
+ ASSERT_TRUE(AddURL(0, subfolder, j, title, url) != NULL);
+ }
+ }
+
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+
+ // Simultaneously rename folder A on both clients.
+ SetTitle(0, folderA[0], L"Folder A++");
+ SetTitle(1, folderA[1], L"Folder A--");
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+
+ // Simultaneously rename folder B on both clients.
+ SetTitle(0, folderB[0], L"Folder B++");
+ SetTitle(1, folderB[1], L"Folder B--");
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+
+ // Simultaneously rename folder C on both clients.
+ SetTitle(0, folderC[0], L"Folder C++");
+ SetTitle(1, folderC[1], L"Folder C--");
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatch());
+ ASSERT_FALSE(ContainsDuplicateBookmarks(0));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SingleClientEnabledEncryption) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IsEncrypted(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::BOOKMARKS));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SingleClientEnabledEncryptionAndChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IsEncrypted(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::BOOKMARKS));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ BothClientsEnabledEncryption) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(EnableEncryption(1, syncable::BOOKMARKS));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(IsEncrypted(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::BOOKMARKS));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SingleClientEnabledEncryptionBothChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(IsEncrypted(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::BOOKMARKS));
+ ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))) != NULL);
+ ASSERT_TRUE(AddURL(0, IndexedURLTitle(1), GURL(IndexedURL(1))) != NULL);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllModelsMatchVerifier());
+ ASSERT_TRUE(IsEncrypted(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::BOOKMARKS));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+ SingleClientEnabledEncryptionAndChangedMultipleTimes) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))) != NULL);
+ ASSERT_TRUE(EnableEncryption(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IsEncrypted(0, syncable::BOOKMARKS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::BOOKMARKS));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+
+ ASSERT_TRUE(AddURL(0, IndexedURLTitle(1), GURL(IndexedURL(1))) != NULL);
+ ASSERT_TRUE(AddFolder(0, IndexedFolderName(0)) != NULL);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllModelsMatchVerifier());
+}
+
+// TODO(zea): Add first time sync functionality testing. In particular, when
+// there are encrypted types in first time sync we need to ensure we don't
+// duplicate bookmarks.
diff --git a/chrome/browser/sync/test/live_sync/two_client_extensions_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_extensions_sync_test.cc
new file mode 100644
index 0000000..379370d
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_extensions_sync_test.cc
@@ -0,0 +1,283 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/extensions_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using extensions_helper::AllProfilesHaveSameExtensionsAsVerifier;
+using extensions_helper::DisableExtension;
+using extensions_helper::EnableExtension;
+using extensions_helper::HasSameExtensionsAsVerifier;
+using extensions_helper::IncognitoDisableExtension;
+using extensions_helper::IncognitoEnableExtension;
+using extensions_helper::InstallExtension;
+using extensions_helper::InstallExtensionsPendingForSync;
+using extensions_helper::UninstallExtension;
+
+class TwoClientExtensionsSyncTest : public LiveSyncTest {
+ public:
+ TwoClientExtensionsSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+
+ virtual ~TwoClientExtensionsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientExtensionsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, StartWithNoExtensions) {
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, StartWithSameExtensions) {
+ ASSERT_TRUE(SetupClients());
+
+ const int kNumExtensions = 5;
+ for (int i = 0; i < kNumExtensions; ++i) {
+ InstallExtension(GetProfile(0), i);
+ InstallExtension(GetProfile(1), i);
+ InstallExtension(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+ StartWithDifferentExtensions) {
+ ASSERT_TRUE(SetupClients());
+
+ int i = 0;
+
+ const int kNumCommonExtensions = 5;
+ for (int j = 0; j < kNumCommonExtensions; ++i, ++j) {
+ InstallExtension(GetProfile(0), i);
+ InstallExtension(GetProfile(1), i);
+ InstallExtension(verifier(), i);
+ }
+
+ const int kNumProfile0Extensions = 10;
+ for (int j = 0; j < kNumProfile0Extensions; ++i, ++j) {
+ InstallExtension(GetProfile(0), i);
+ InstallExtension(verifier(), i);
+ }
+
+ const int kNumProfile1Extensions = 10;
+ for (int j = 0; j < kNumProfile1Extensions; ++i, ++j) {
+ InstallExtension(GetProfile(1), i);
+ InstallExtension(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallExtensionsPendingForSync(GetProfile(0));
+ InstallExtensionsPendingForSync(GetProfile(1));
+
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+ InstallDifferentExtensions) {
+ ASSERT_TRUE(SetupClients());
+
+ int i = 0;
+
+ const int kNumCommonExtensions = 5;
+ for (int j = 0; j < kNumCommonExtensions; ++i, ++j) {
+ InstallExtension(GetProfile(0), i);
+ InstallExtension(GetProfile(1), i);
+ InstallExtension(verifier(), i);
+ }
+
+ ASSERT_TRUE(SetupSync());
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ const int kNumProfile0Extensions = 10;
+ for (int j = 0; j < kNumProfile0Extensions; ++i, ++j) {
+ InstallExtension(GetProfile(0), i);
+ InstallExtension(verifier(), i);
+ }
+
+ const int kNumProfile1Extensions = 10;
+ for (int j = 0; j < kNumProfile1Extensions; ++i, ++j) {
+ InstallExtension(GetProfile(1), i);
+ InstallExtension(verifier(), i);
+ }
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallExtensionsPendingForSync(GetProfile(0));
+ InstallExtensionsPendingForSync(GetProfile(1));
+
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TCM ID - 3637311.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, Add) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ InstallExtension(GetProfile(0), 0);
+ InstallExtension(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallExtensionsPendingForSync(GetProfile(0));
+ InstallExtensionsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TCM ID - 3724281.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, Uninstall) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ InstallExtension(GetProfile(0), 0);
+ InstallExtension(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ InstallExtensionsPendingForSync(GetProfile(0));
+ InstallExtensionsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ UninstallExtension(GetProfile(0), 0);
+ UninstallExtension(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TCM ID - 3635304.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, Merge) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ InstallExtension(GetProfile(0), 0);
+ InstallExtension(GetProfile(1), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ UninstallExtension(GetProfile(0), 0);
+ InstallExtension(GetProfile(0), 1);
+ InstallExtension(verifier(), 1);
+
+ InstallExtension(GetProfile(0), 2);
+ InstallExtension(GetProfile(1), 2);
+ InstallExtension(verifier(), 2);
+
+ InstallExtension(GetProfile(1), 3);
+ InstallExtension(verifier(), 3);
+
+ ASSERT_TRUE(AwaitQuiescence());
+ InstallExtensionsPendingForSync(GetProfile(0));
+ InstallExtensionsPendingForSync(GetProfile(1));
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TCM ID - 3605300.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+ UpdateEnableDisableExtension) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ InstallExtension(GetProfile(0), 0);
+ InstallExtension(GetProfile(1), 0);
+ InstallExtension(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ DisableExtension(GetProfile(0), 0);
+ DisableExtension(verifier(), 0);
+ ASSERT_TRUE(HasSameExtensionsAsVerifier(0));
+ ASSERT_FALSE(HasSameExtensionsAsVerifier(1));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ EnableExtension(GetProfile(1), 0);
+ EnableExtension(verifier(), 0);
+ ASSERT_TRUE(HasSameExtensionsAsVerifier(1));
+ ASSERT_FALSE(HasSameExtensionsAsVerifier(0));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TCM ID - 3728322.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+ UpdateIncognitoEnableDisable) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ InstallExtension(GetProfile(0), 0);
+ InstallExtension(GetProfile(1), 0);
+ InstallExtension(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ IncognitoEnableExtension(GetProfile(0), 0);
+ IncognitoEnableExtension(verifier(), 0);
+ ASSERT_TRUE(HasSameExtensionsAsVerifier(0));
+ ASSERT_FALSE(HasSameExtensionsAsVerifier(1));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ IncognitoDisableExtension(GetProfile(1), 0);
+ IncognitoDisableExtension(verifier(), 0);
+ ASSERT_TRUE(HasSameExtensionsAsVerifier(1));
+ ASSERT_FALSE(HasSameExtensionsAsVerifier(0));
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TCM ID - 3732278.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, DisableExtensions) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForDatatype(syncable::EXTENSIONS));
+ InstallExtension(GetProfile(0), 1);
+ InstallExtension(verifier(), 1);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_FALSE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForDatatype(syncable::EXTENSIONS));
+ ASSERT_TRUE(AwaitQuiescence());
+ InstallExtensionsPendingForSync(GetProfile(0));
+ InstallExtensionsPendingForSync(GetProfile(1));
+ InstallExtensionsPendingForSync(verifier());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TCM ID - 3606290.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, DisableSync) {
+ ASSERT_TRUE(SetupSync());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForAllDatatypes());
+ InstallExtension(GetProfile(0), 0);
+ InstallExtension(verifier(), 0);
+ ASSERT_TRUE(
+ GetClient(0)->AwaitSyncCycleCompletion("Installed an extension."));
+ ASSERT_TRUE(HasSameExtensionsAsVerifier(0));
+ ASSERT_FALSE(HasSameExtensionsAsVerifier(1));
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForAllDatatypes());
+ ASSERT_TRUE(AwaitQuiescence());
+ InstallExtensionsPendingForSync(GetProfile(0));
+ InstallExtensionsPendingForSync(GetProfile(1));
+ InstallExtensionsPendingForSync(verifier());
+ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
+}
+
+// TODO(akalin): Add tests exercising:
+// - Offline installation/uninstallation behavior
diff --git a/chrome/browser/sync/test/live_sync/two_client_passwords_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_passwords_sync_test.cc
new file mode 100644
index 0000000..7916fa6
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_passwords_sync_test.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/sync/engine/model_safe_worker.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/sessions/session_state.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/passwords_helper.h"
+
+using passwords_helper::AddLogin;
+using passwords_helper::AllProfilesContainSamePasswordForms;
+using passwords_helper::AllProfilesContainSamePasswordFormsAsVerifier;
+using passwords_helper::CreateTestPasswordForm;
+using passwords_helper::GetPasswordCount;
+using passwords_helper::GetPasswordStore;
+using passwords_helper::GetVerifierPasswordCount;
+using passwords_helper::GetVerifierPasswordStore;
+using passwords_helper::ProfileContainsSamePasswordFormsAsVerifier;
+using passwords_helper::RemoveLogin;
+using passwords_helper::RemoveLogins;
+using passwords_helper::SetPassphrase;
+using passwords_helper::UpdateLogin;
+
+using webkit_glue::PasswordForm;
+
+static const char* kValidPassphrase = "passphrase!";
+
+class TwoClientPasswordsSyncTest : public LiveSyncTest {
+ public:
+ TwoClientPasswordsSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+ virtual ~TwoClientPasswordsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientPasswordsSyncTest);
+};
+
+// TCM ID - 3732277
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_Add) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, Add) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+
+ PasswordForm form = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form);
+ ASSERT_EQ(1, GetVerifierPasswordCount());
+ AddLogin(GetPasswordStore(0), form);
+ ASSERT_EQ(1, GetPasswordCount(0));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+}
+
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_Race) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, Race) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordForms());
+
+ PasswordForm form0 = CreateTestPasswordForm(0);
+ AddLogin(GetPasswordStore(0), form0);
+
+ PasswordForm form1 = form0;
+ form1.password_value = ASCIIToUTF16("password1");
+ AddLogin(GetPasswordStore(1), form1);
+
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesContainSamePasswordForms());
+}
+
+// TCM ID - 4577932.
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_DisablePasswords) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DisablePasswords) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForDatatype(syncable::PASSWORDS));
+ PasswordForm form = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form);
+ ASSERT_EQ(1, GetVerifierPasswordCount());
+ AddLogin(GetPasswordStore(0), form);
+ ASSERT_EQ(1, GetPasswordCount(0));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(ProfileContainsSamePasswordFormsAsVerifier(0));
+ ASSERT_FALSE(ProfileContainsSamePasswordFormsAsVerifier(1));
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForDatatype(syncable::PASSWORDS));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+ ASSERT_EQ(1, GetPasswordCount(1));
+}
+
+// TCM ID - 4649281.
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_DisableSync) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DisableSync) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForAllDatatypes());
+ PasswordForm form = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form);
+ ASSERT_EQ(1, GetVerifierPasswordCount());
+ AddLogin(GetPasswordStore(0), form);
+ ASSERT_EQ(1, GetPasswordCount(0));
+
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Added a password."));
+ ASSERT_TRUE(ProfileContainsSamePasswordFormsAsVerifier(0));
+ ASSERT_FALSE(ProfileContainsSamePasswordFormsAsVerifier(1));
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForAllDatatypes());
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+ ASSERT_EQ(1, GetPasswordCount(1));
+}
+
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_SetPassphrase) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, SetPassphrase) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ SetPassphrase(0, kValidPassphrase, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ SetPassphrase(1, kValidPassphrase, false);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Set passphrase."));
+}
+
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
+ DISABLED_SetPassphraseAndAddPassword) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
+ SetPassphraseAndAddPassword) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ SetPassphrase(0, kValidPassphrase, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ SetPassphrase(1, kValidPassphrase, false);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+
+ PasswordForm form = CreateTestPasswordForm(0);
+ AddLogin(GetPasswordStore(0), form);
+ ASSERT_EQ(1, GetPasswordCount(0));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_EQ(1, GetPasswordCount(1));
+}
+
+// TCM ID - 4603879
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_Update) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, Update) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+
+ PasswordForm form = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form);
+ AddLogin(GetPasswordStore(0), form);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ form.password_value = ASCIIToUTF16("updated");
+ UpdateLogin(GetVerifierPasswordStore(), form);
+ UpdateLogin(GetPasswordStore(1), form);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(1, GetVerifierPasswordCount());
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+}
+
+// TCM ID - 3719309
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_Delete) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, Delete) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+
+ PasswordForm form0 = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form0);
+ AddLogin(GetPasswordStore(0), form0);
+ PasswordForm form1 = CreateTestPasswordForm(1);
+ AddLogin(GetVerifierPasswordStore(), form1);
+ AddLogin(GetPasswordStore(0), form1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ RemoveLogin(GetPasswordStore(1), form0);
+ RemoveLogin(GetVerifierPasswordStore(), form0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(1, GetVerifierPasswordCount());
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+}
+
+// TCM ID - 7573511
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_DeleteAll) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DeleteAll) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+
+ PasswordForm form0 = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form0);
+ AddLogin(GetPasswordStore(0), form0);
+ PasswordForm form1 = CreateTestPasswordForm(1);
+ AddLogin(GetVerifierPasswordStore(), form1);
+ AddLogin(GetPasswordStore(0), form1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ RemoveLogins(GetPasswordStore(1));
+ RemoveLogins(GetVerifierPasswordStore());
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(0, GetVerifierPasswordCount());
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+}
+
+// TCM ID - 3694311
+// http://crbug.com/90460
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, DISABLED_Merge) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+
+ PasswordForm form0 = CreateTestPasswordForm(0);
+ AddLogin(GetVerifierPasswordStore(), form0);
+ AddLogin(GetPasswordStore(0), form0);
+ PasswordForm form1 = CreateTestPasswordForm(1);
+ AddLogin(GetVerifierPasswordStore(), form1);
+ AddLogin(GetPasswordStore(0), form1);
+ AddLogin(GetPasswordStore(1), form1);
+ PasswordForm form2 = CreateTestPasswordForm(2);
+ AddLogin(GetVerifierPasswordStore(), form2);
+ AddLogin(GetPasswordStore(1), form2);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(3, GetVerifierPasswordCount());
+ ASSERT_TRUE(AllProfilesContainSamePasswordFormsAsVerifier());
+}
+
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
+ DISABLED_SetPassphraseAndThenSetupSync) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
+ SetPassphraseAndThenSetupSync) {
+#endif
+ ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+ ASSERT_TRUE(GetClient(0)->SetupSync());
+ SetPassphrase(0, kValidPassphrase, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Initial sync."));
+
+ ASSERT_FALSE(GetClient(1)->SetupSync());
+ SetPassphrase(1, kValidPassphrase, false);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Initial sync."));
+
+ // Following ensures types are enabled and active (see bug 87572).
+ browser_sync::ModelSafeRoutingInfo routes;
+ GetClient(0)->service()->GetModelSafeRoutingInfo(&routes);
+ ASSERT_EQ(browser_sync::GROUP_PASSWORD, routes[syncable::PASSWORDS]);
+ routes.clear();
+ GetClient(1)->service()->GetModelSafeRoutingInfo(&routes);
+ ASSERT_EQ(browser_sync::GROUP_PASSWORD, routes[syncable::PASSWORDS]);
+}
+
+// TODO(sync): Enable after MockKeychain is fixed. http://crbug.com/89808.
+#if defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
+ DISABLED_SetPassphraseTwice) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
+ SetPassphraseTwice) {
+#endif
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ SetPassphrase(0, kValidPassphrase, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ SetPassphrase(1, kValidPassphrase, false);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Set passphrase."));
+
+ SetPassphrase(1, kValidPassphrase, false);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Set passphrase again."));
+}
diff --git a/chrome/browser/sync/test/live_sync/two_client_preferences_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_preferences_sync_test.cc
new file mode 100644
index 0000000..60ef890
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_preferences_sync_test.cc
@@ -0,0 +1,669 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/values.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/translate/translate_prefs.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/preferences_helper.h"
+#include "chrome/common/pref_names.h"
+
+using preferences_helper::AppendStringPref;
+using preferences_helper::BooleanPrefMatches;
+using preferences_helper::ChangeBooleanPref;
+using preferences_helper::ChangeIntegerPref;
+using preferences_helper::ChangeListPref;
+using preferences_helper::ChangeStringPref;
+using preferences_helper::GetPrefs;
+using preferences_helper::IntegerPrefMatches;
+using preferences_helper::ListPrefMatches;
+using preferences_helper::StringPrefMatches;
+
+class TwoClientPreferencesSyncTest : public LiveSyncTest {
+ public:
+ TwoClientPreferencesSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+ virtual ~TwoClientPreferencesSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientPreferencesSyncTest);
+};
+
+// TCM ID - 7306186.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kHomePageIsNewTabPage) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+
+ ChangeBooleanPref(0, prefs::kHomePageIsNewTabPage);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+}
+
+// TCM ID - 7260488.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, Race) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(StringPrefMatches(prefs::kHomePage));
+
+ ChangeStringPref(0, prefs::kHomePage,
+ "http://www.google.com/0");
+ ChangeStringPref(1, prefs::kHomePage,
+ "http://www.google.com/1");
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(StringPrefMatches(prefs::kHomePage));
+}
+
+// TCM ID - 3649278.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kPasswordManagerEnabled) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+
+ ChangeBooleanPref(0, prefs::kPasswordManagerEnabled);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+}
+
+// TCM ID - 3699293.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kKeepEverythingSynced) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kKeepEverythingSynced));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncThemes));
+
+ GetClient(0)->DisableSyncForDatatype(syncable::THEMES);
+ ASSERT_FALSE(BooleanPrefMatches(
+ prefs::kKeepEverythingSynced));
+}
+
+// TCM ID - 3661290.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, DisablePreferences) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncPreferences));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+
+ GetClient(1)->DisableSyncForDatatype(syncable::PREFERENCES);
+ ChangeBooleanPref(0, prefs::kPasswordManagerEnabled);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_FALSE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+
+ GetClient(1)->EnableSyncForDatatype(syncable::PREFERENCES);
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+}
+
+// TCM ID - 3664292.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, DisableSync) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncPreferences));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+
+ GetClient(1)->DisableSyncForAllDatatypes();
+ ChangeBooleanPref(0, prefs::kPasswordManagerEnabled);
+ ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Changed a preference."));
+ ASSERT_FALSE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+
+ ChangeBooleanPref(1, prefs::kShowHomeButton);
+ ASSERT_FALSE(BooleanPrefMatches(prefs::kShowHomeButton));
+
+ GetClient(1)->EnableSyncForAllDatatypes();
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kPasswordManagerEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+}
+
+// TCM ID - 3604297.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, SignInDialog) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncPreferences));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncBookmarks));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncThemes));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncExtensions));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kSyncAutofill));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kKeepEverythingSynced));
+
+ GetClient(0)->DisableSyncForDatatype(syncable::PREFERENCES);
+ GetClient(1)->EnableSyncForDatatype(syncable::PREFERENCES);
+ GetClient(0)->DisableSyncForDatatype(syncable::AUTOFILL);
+ GetClient(1)->EnableSyncForDatatype(syncable::AUTOFILL);
+ GetClient(0)->DisableSyncForDatatype(syncable::BOOKMARKS);
+ GetClient(1)->EnableSyncForDatatype(syncable::BOOKMARKS);
+ GetClient(0)->DisableSyncForDatatype(syncable::EXTENSIONS);
+ GetClient(1)->EnableSyncForDatatype(syncable::EXTENSIONS);
+ GetClient(0)->DisableSyncForDatatype(syncable::THEMES);
+ GetClient(1)->EnableSyncForDatatype(syncable::THEMES);
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_FALSE(BooleanPrefMatches(prefs::kSyncPreferences));
+ ASSERT_FALSE(BooleanPrefMatches(prefs::kSyncBookmarks));
+ ASSERT_FALSE(BooleanPrefMatches(prefs::kSyncThemes));
+ ASSERT_FALSE(BooleanPrefMatches(prefs::kSyncExtensions));
+ ASSERT_FALSE(BooleanPrefMatches(prefs::kSyncAutofill));
+ ASSERT_FALSE(BooleanPrefMatches(
+ prefs::kKeepEverythingSynced));
+}
+
+// TCM ID - 3666296.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kShowBookmarkBar) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowBookmarkBar));
+
+ ChangeBooleanPref(0, prefs::kShowBookmarkBar);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowBookmarkBar));
+}
+
+// TCM ID - 3611311.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kCheckDefaultBrowser) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kCheckDefaultBrowser));
+
+ ChangeBooleanPref(0, prefs::kCheckDefaultBrowser);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_FALSE(BooleanPrefMatches(
+ prefs::kCheckDefaultBrowser));
+}
+
+// TCM ID - 3628298.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kHomePage) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(StringPrefMatches(prefs::kHomePage));
+
+ ChangeStringPref(0, prefs::kHomePage,
+ "http://news.google.com");
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(StringPrefMatches(prefs::kHomePage));
+}
+
+// TCM ID - 7297269.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kShowHomeButton) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+
+ ChangeBooleanPref(0, prefs::kShowHomeButton);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+}
+
+// TCM ID - 3710285.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kEnableTranslate) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableTranslate));
+
+ ChangeBooleanPref(0, prefs::kEnableTranslate);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableTranslate));
+}
+
+// TCM ID - 3664293.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kAutofillEnabled) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kAutofillEnabled));
+
+ ChangeBooleanPref(0, prefs::kAutofillEnabled);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kAutofillEnabled));
+}
+
+// TCM ID - 3632259.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kURLsToRestoreOnStartup) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(IntegerPrefMatches(prefs::kRestoreOnStartup));
+ ASSERT_TRUE(ListPrefMatches(
+ prefs::kURLsToRestoreOnStartup));
+
+ ChangeIntegerPref(0, prefs::kRestoreOnStartup, 0);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IntegerPrefMatches(prefs::kRestoreOnStartup));
+
+ ListValue urls;
+ urls.Append(Value::CreateStringValue("http://www.google.com/"));
+ urls.Append(Value::CreateStringValue("http://www.flickr.com/"));
+ ChangeIntegerPref(0, prefs::kRestoreOnStartup, 4);
+ ChangeListPref(0, prefs::kURLsToRestoreOnStartup, urls);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IntegerPrefMatches(prefs::kRestoreOnStartup));
+ ASSERT_TRUE(ListPrefMatches(
+ prefs::kURLsToRestoreOnStartup));
+}
+
+// TCM ID - 3684287.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kRestoreOnStartup) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(IntegerPrefMatches(prefs::kRestoreOnStartup));
+
+ ChangeIntegerPref(0, prefs::kRestoreOnStartup, 1);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IntegerPrefMatches(prefs::kRestoreOnStartup));
+}
+
+// TCM ID - 3703314.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, Privacy) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kAlternateErrorPagesEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kSearchSuggestEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kNetworkPredictionEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kSafeBrowsingEnabled));
+
+ ChangeBooleanPref(0, prefs::kAlternateErrorPagesEnabled);
+ ChangeBooleanPref(0, prefs::kSearchSuggestEnabled);
+ ChangeBooleanPref(0, prefs::kNetworkPredictionEnabled);
+ ChangeBooleanPref(0, prefs::kSafeBrowsingEnabled);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kAlternateErrorPagesEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kSearchSuggestEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kNetworkPredictionEnabled));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kSafeBrowsingEnabled));
+}
+
+// TCM ID - 3649279.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, ClearData) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kDeleteBrowsingHistory));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kDeleteDownloadHistory));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeleteCache));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeleteCookies));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeletePasswords));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeleteFormData));
+
+ ChangeBooleanPref(0, prefs::kDeleteBrowsingHistory);
+ ChangeBooleanPref(0, prefs::kDeleteDownloadHistory);
+ ChangeBooleanPref(0, prefs::kDeleteCache);
+ ChangeBooleanPref(0, prefs::kDeleteCookies);
+ ChangeBooleanPref(0, prefs::kDeletePasswords);
+ ChangeBooleanPref(0, prefs::kDeleteFormData);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kDeleteBrowsingHistory));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kDeleteDownloadHistory));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeleteCache));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeleteCookies));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeletePasswords));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kDeleteFormData));
+}
+
+// TCM ID - 3686300.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kWebKitUsesUniversalDetector) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kWebKitUsesUniversalDetector));
+
+ ChangeBooleanPref(0, prefs::kWebKitUsesUniversalDetector);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kWebKitUsesUniversalDetector));
+}
+
+// TCM ID - 3673298.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kDefaultCharset) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(StringPrefMatches(prefs::kDefaultCharset));
+
+ ChangeStringPref(0, prefs::kDefaultCharset, "Thai");
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(StringPrefMatches(prefs::kDefaultCharset));
+}
+
+// TCM ID - 3653296.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kBlockThirdPartyCookies) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kBlockThirdPartyCookies));
+
+ ChangeBooleanPref(0, prefs::kBlockThirdPartyCookies);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kBlockThirdPartyCookies));
+}
+
+// TCM ID - 7297279.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kClearSiteDataOnExit) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kClearSiteDataOnExit));
+
+ ChangeBooleanPref(0, prefs::kClearSiteDataOnExit);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kClearSiteDataOnExit));
+}
+
+// TCM ID - 7306184.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kSafeBrowsingEnabled) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kSafeBrowsingEnabled));
+
+ ChangeBooleanPref(0, prefs::kSafeBrowsingEnabled);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kSafeBrowsingEnabled));
+}
+
+// TCM ID - 3624302.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kAutofillAuxiliaryProfilesEnabled) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kAutofillAuxiliaryProfilesEnabled));
+
+ ChangeBooleanPref(0,
+ prefs::kAutofillAuxiliaryProfilesEnabled);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // kAutofillAuxiliaryProfilesEnabled is only synced on Mac.
+#if defined(OS_MACOSX)
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kAutofillAuxiliaryProfilesEnabled));
+#else
+ ASSERT_FALSE(BooleanPrefMatches(
+ prefs::kAutofillAuxiliaryProfilesEnabled));
+#endif // OS_MACOSX
+}
+
+// TCM ID - 3717298.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kPromptForDownload) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kPromptForDownload));
+
+ ChangeBooleanPref(0, prefs::kPromptForDownload);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kPromptForDownload));
+}
+
+// TCM ID - 3729263.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kPrefTranslateLanguageBlacklist) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableTranslate));
+
+ TranslatePrefs translate_client0_prefs(GetPrefs(0));
+ TranslatePrefs translate_client1_prefs(GetPrefs(1));
+ ASSERT_FALSE(translate_client0_prefs.IsLanguageBlacklisted("fr"));
+ translate_client0_prefs.BlacklistLanguage("fr");
+ ASSERT_TRUE(translate_client0_prefs.IsLanguageBlacklisted("fr"));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(translate_client1_prefs.IsLanguageBlacklisted("fr"));
+
+ translate_client0_prefs.RemoveLanguageFromBlacklist("fr");
+ ASSERT_FALSE(translate_client0_prefs.IsLanguageBlacklisted("fr"));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_FALSE(translate_client1_prefs.IsLanguageBlacklisted("fr"));
+}
+
+// TCM ID - 7307195.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kPrefTranslateWhitelists) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableTranslate));
+
+ TranslatePrefs translate_client0_prefs(GetPrefs(0));
+ TranslatePrefs translate_client1_prefs(GetPrefs(1));
+ ASSERT_FALSE(translate_client0_prefs.IsLanguagePairWhitelisted("en", "bg"));
+ translate_client0_prefs.WhitelistLanguagePair("en", "bg");
+ ASSERT_TRUE(translate_client0_prefs.IsLanguagePairWhitelisted("en", "bg"));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(translate_client1_prefs.IsLanguagePairWhitelisted("en", "bg"));
+
+ translate_client0_prefs.RemoveLanguagePairFromWhitelist("en", "bg");
+ ASSERT_FALSE(translate_client0_prefs.IsLanguagePairWhitelisted("en", "bg"));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_FALSE(translate_client1_prefs.IsLanguagePairWhitelisted("en", "bg"));
+}
+
+// TCM ID - 3625298.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kPrefTranslateSiteBlacklist) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableTranslate));
+
+ GURL url("http://www.google.com");
+ std::string host(url.host());
+ TranslatePrefs translate_client0_prefs(GetPrefs(0));
+ TranslatePrefs translate_client1_prefs(GetPrefs(1));
+ ASSERT_FALSE(translate_client0_prefs.IsSiteBlacklisted(host));
+ translate_client0_prefs.BlacklistSite(host);
+ ASSERT_TRUE(translate_client0_prefs.IsSiteBlacklisted(host));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(translate_client1_prefs.IsSiteBlacklisted(host));
+
+ translate_client0_prefs.RemoveSiteFromBlacklist(host);
+ ASSERT_FALSE(translate_client0_prefs.IsSiteBlacklisted(host));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_FALSE(translate_client1_prefs.IsSiteBlacklisted(host));
+}
+
+// TCM ID - 6515252.
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kExtensionsUIDeveloperMode) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kExtensionsUIDeveloperMode));
+
+ ChangeBooleanPref(0, prefs::kExtensionsUIDeveloperMode);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kExtensionsUIDeveloperMode));
+}
+
+// TCM ID - 7583816
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kAcceptLanguages) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ DisableVerifier();
+ ASSERT_TRUE(StringPrefMatches(prefs::kAcceptLanguages));
+
+ AppendStringPref(0, prefs::kAcceptLanguages, ",ar");
+ AppendStringPref(1, prefs::kAcceptLanguages, ",fr");
+ ASSERT_TRUE(AwaitQuiescence());
+ // kAcceptLanguages is not synced on Mac.
+#if !defined(OS_MACOSX)
+ ASSERT_TRUE(StringPrefMatches(prefs::kAcceptLanguages));
+#else
+ ASSERT_FALSE(StringPrefMatches(prefs::kAcceptLanguages));
+#endif // OS_MACOSX
+
+ ChangeStringPref(0, prefs::kAcceptLanguages, "en-US");
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+#if !defined(OS_MACOSX)
+ ASSERT_TRUE(StringPrefMatches(prefs::kAcceptLanguages));
+#else
+ ASSERT_FALSE(StringPrefMatches(prefs::kAcceptLanguages));
+#endif // OS_MACOSX
+
+ ChangeStringPref(0, prefs::kAcceptLanguages, "ar,en-US");
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+#if !defined(OS_MACOSX)
+ ASSERT_TRUE(StringPrefMatches(prefs::kAcceptLanguages));
+#else
+ ASSERT_FALSE(StringPrefMatches(prefs::kAcceptLanguages));
+#endif // OS_MACOSX
+}
+
+// TCM ID - 7590682
+#if defined(TOOLKIT_USES_GTK)
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kUsesSystemTheme) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kUsesSystemTheme));
+
+ ChangeBooleanPref(0, prefs::kUsesSystemTheme);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_FALSE(BooleanPrefMatches(prefs::kUsesSystemTheme));
+}
+#endif // TOOLKIT_USES_GTK
+
+// TCM ID - 3636292
+#if defined(TOOLKIT_USES_GTK)
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ kUseCustomChromeFrame) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kUseCustomChromeFrame));
+
+ ChangeBooleanPref(0, prefs::kUseCustomChromeFrame);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kUseCustomChromeFrame));
+}
+#endif // TOOLKIT_USES_GTK
+
+// TCM ID - 6473347.
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kTapToClickEnabled) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kTapToClickEnabled));
+
+ ChangeBooleanPref(0, prefs::kTapToClickEnabled);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kTapToClickEnabled));
+
+ ChangeBooleanPref(1, prefs::kTapToClickEnabled);
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kTapToClickEnabled));
+}
+#endif // OS_CHROMEOS
+
+// TCM ID - 6458824.
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest, kEnableScreenLock) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableScreenLock));
+
+ ChangeBooleanPref(0, prefs::kEnableScreenLock);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableScreenLock));
+
+ ChangeBooleanPref(1, prefs::kEnableScreenLock);
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kEnableScreenLock));
+}
+#endif // OS_CHROMEOS
+
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ SingleClientEnabledEncryption) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::PREFERENCES));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IsEncrypted(0, syncable::PREFERENCES));
+ ASSERT_TRUE(IsEncrypted(1, syncable::PREFERENCES));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ SingleClientEnabledEncryptionAndChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+
+ ChangeBooleanPref(0, prefs::kHomePageIsNewTabPage);
+ ASSERT_TRUE(EnableEncryption(0, syncable::PREFERENCES));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IsEncrypted(0, syncable::PREFERENCES));
+ ASSERT_TRUE(IsEncrypted(1, syncable::PREFERENCES));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ BothClientsEnabledEncryption) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::PREFERENCES));
+ ASSERT_TRUE(EnableEncryption(1, syncable::PREFERENCES));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(IsEncrypted(0, syncable::PREFERENCES));
+ ASSERT_TRUE(IsEncrypted(1, syncable::PREFERENCES));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ SingleClientEnabledEncryptionBothChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+ ASSERT_TRUE(StringPrefMatches(prefs::kHomePage));
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::PREFERENCES));
+ ChangeBooleanPref(0, prefs::kHomePageIsNewTabPage);
+ ChangeStringPref(1, prefs::kHomePage,
+ "http://www.google.com/1");
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(IsEncrypted(0, syncable::PREFERENCES));
+ ASSERT_TRUE(IsEncrypted(1, syncable::PREFERENCES));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+ ASSERT_TRUE(StringPrefMatches(prefs::kHomePage));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientPreferencesSyncTest,
+ SingleClientEnabledEncryptionAndChangedMultipleTimes) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+
+ ChangeBooleanPref(0, prefs::kHomePageIsNewTabPage);
+ ASSERT_TRUE(EnableEncryption(0, syncable::PREFERENCES));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IsEncrypted(0, syncable::PREFERENCES));
+ ASSERT_TRUE(IsEncrypted(1, syncable::PREFERENCES));
+ ASSERT_TRUE(BooleanPrefMatches(
+ prefs::kHomePageIsNewTabPage));
+
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+ ChangeBooleanPref(0, prefs::kShowHomeButton);
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
+}
diff --git a/chrome/browser/sync/test/live_sync/two_client_sessions_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_sessions_sync_test.cc
new file mode 100644
index 0000000..b75f06e
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_sessions_sync_test.cc
@@ -0,0 +1,353 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/sessions/session_state.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sessions_helper.h"
+
+using sessions_helper::CheckInitialState;
+using sessions_helper::GetLocalWindows;
+using sessions_helper::GetSessionData;
+using sessions_helper::OpenTabAndGetLocalWindows;
+using sessions_helper::WindowsMatch;
+
+class TwoClientSessionsSyncTest : public LiveSyncTest {
+ public:
+ TwoClientSessionsSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+ virtual ~TwoClientSessionsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientSessionsSyncTest);
+};
+
+static const char* kValidPassphrase = "passphrase!";
+static const char* kURL1 = "chrome://sync";
+static const char* kURL2 = "chrome://version";
+
+// TODO(zea): Test each individual session command we care about separately.
+// (as well as multi-window). We're currently only checking basic single-window/
+// single-tab functionality.
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest, SingleClientChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ ScopedVector<SessionWindow> client0_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), client0_windows.get()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Get foreign session data from client 1.
+ SyncedSessionVector sessions1;
+ ASSERT_TRUE(GetSessionData(1, &sessions1));
+
+ // Verify client 1's foreign session matches client 0 current window.
+ ASSERT_EQ(1U, sessions1.size());
+ ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows.get()));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ SingleClientEnabledEncryption) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(IsEncrypted(0, syncable::SESSIONS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ SingleClientEnabledEncryptionAndChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ ScopedVector<SessionWindow> client0_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), client0_windows.get()));
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Get foreign session data from client 1.
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+ SyncedSessionVector sessions1;
+ ASSERT_TRUE(GetSessionData(1, &sessions1));
+
+ // Verify client 1's foreign session matches client 0 current window.
+ ASSERT_EQ(1U, sessions1.size());
+ ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows.get()));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ BothClientsEnabledEncryption) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ ASSERT_TRUE(EnableEncryption(1, syncable::SESSIONS));
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(IsEncrypted(0, syncable::SESSIONS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest, BothChanged) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ // Open tabs on both clients and retain window information.
+ ScopedVector<SessionWindow> client0_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL2), client0_windows.get()));
+ ScopedVector<SessionWindow> client1_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(1, GURL(kURL1), client1_windows.get()));
+
+ // Wait for sync.
+ ASSERT_TRUE(AwaitQuiescence());
+
+ // Get foreign session data from client 0 and 1.
+ SyncedSessionVector sessions0;
+ SyncedSessionVector sessions1;
+ ASSERT_TRUE(GetSessionData(0, &sessions0));
+ ASSERT_TRUE(GetSessionData(1, &sessions1));
+
+ // Verify client 1's foreign session matches client 0's current window and
+ // vice versa.
+ ASSERT_EQ(1U, sessions0.size());
+ ASSERT_EQ(1U, sessions1.size());
+ ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows.get()));
+ ASSERT_TRUE(WindowsMatch(sessions0[0]->windows, client1_windows.get()));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ FirstChangesAndSetsPassphrase) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ ScopedVector<SessionWindow> client0_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), client0_windows.get()));
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ GetClient(0)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
+ ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
+ num_blocking_conflicting_updates);
+ // We have 3 non-blocking conflicts due to the two meta nodes (one for each
+ // client, and the one tab node).
+ ASSERT_EQ(3, GetClient(1)->GetLastSessionSnapshot()->
+ num_conflicting_updates); // The encrypted nodes.
+
+ GetClient(1)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
+
+ ASSERT_TRUE(IsEncrypted(0, syncable::SESSIONS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+ // Get foreign session data from client 0 and 1.
+ SyncedSessionVector sessions1;
+ ASSERT_TRUE(GetSessionData(1, &sessions1));
+
+ // Verify client 1's foreign session matches client 0's current window and
+ // vice versa.
+ ASSERT_EQ(1U, sessions1.size());
+ ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows.get()));
+}
+
+// Flaky (number of conflicting nodes is off). http://crbug.com/89604.
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ FLAKY_FirstChangesWhileSecondWaitingForPassphrase) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ GetClient(0)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
+ ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
+ num_blocking_conflicting_updates);
+ // We have two non-blocking conflicts due to the two meta nodes (one for each
+ // client).
+ ASSERT_EQ(2, GetClient(1)->GetLastSessionSnapshot()->
+ num_conflicting_updates); // The encrypted nodes.
+
+ ScopedVector<SessionWindow> client0_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), client0_windows.get()));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
+ num_blocking_conflicting_updates);
+ ASSERT_EQ(3, GetClient(1)->GetLastSessionSnapshot()->
+ num_conflicting_updates); // The encrypted nodes.
+
+ GetClient(1)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
+
+ ASSERT_TRUE(IsEncrypted(0, syncable::SESSIONS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+ // Get foreign session data from client 0 and 1.
+ SyncedSessionVector sessions1;
+ ASSERT_TRUE(GetSessionData(1, &sessions1));
+
+ // Verify client 1's foreign session matches client 0's current window and
+ // vice versa.
+ ASSERT_EQ(1U, sessions1.size());
+ ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows.get()));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ SecondChangesAfterEncrAndPassphraseChange) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ GetClient(0)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
+ ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
+ num_blocking_conflicting_updates);
+ // We have two non-blocking conflicts due to the two meta nodes (one for each
+ // client).
+ ASSERT_EQ(2, GetClient(1)->GetLastSessionSnapshot()->
+ num_conflicting_updates); // The encrypted nodes.
+
+ // These changes are either made with the old passphrase or not encrypted at
+ // all depending on when client 0's changes are propagated.
+ ScopedVector<SessionWindow> client1_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(1, GURL(kURL1), client1_windows.get()));
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+ ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
+ num_blocking_conflicting_updates);
+ ASSERT_EQ(2, GetClient(1)->GetLastSessionSnapshot()->
+ num_conflicting_updates); // The same encrypted nodes.
+
+ // At this point we enter the passphrase, triggering a resync, in which the
+ // local changes of client 1 get overwritten for now.
+ GetClient(1)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
+
+ ASSERT_TRUE(IsEncrypted(0, syncable::SESSIONS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+ // The session data from client 1 got overwritten. As a result, client 0
+ // should have no foreign session data.
+ SyncedSessionVector sessions0;
+ SyncedSessionVector sessions1;
+ ASSERT_FALSE(GetSessionData(0, &sessions0));
+ ASSERT_FALSE(GetSessionData(1, &sessions1));
+}
+
+// Flaky. http://crbug.com/85294
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ FLAKY_SecondChangesBeforeEncrAndPassphraseChange) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ // These changes are either made on client 1 without encryption.
+ ScopedVector<SessionWindow> client1_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(1, GURL(kURL1), client1_windows.get()));
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+
+ // Turn encryption on client 0. Client 1's foreign will be encrypted with the
+ // new passphrase and synced back. It will be unable to decrypt it yet.
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ GetClient(0)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(AwaitQuiescence());
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
+ ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
+ num_blocking_conflicting_updates);
+ // We have three non-blocking conflicts due to the two meta nodes (one for
+ // each client) and the one tab node.
+ ASSERT_GE(3, GetClient(1)->GetLastSessionSnapshot()->
+ num_conflicting_updates); // The encrypted nodes.
+
+ // At this point we enter the passphrase, triggering a resync.
+ GetClient(1)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
+
+ ASSERT_TRUE(IsEncrypted(0, syncable::SESSIONS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+ // Client 0's foreign data should match client 1's local data. Client 1's
+ // foreign data is empty because client 0 did not open any tabs.
+ SyncedSessionVector sessions0;
+ SyncedSessionVector sessions1;
+ ASSERT_TRUE(GetSessionData(0, &sessions0));
+ ASSERT_FALSE(GetSessionData(1, &sessions1));
+ ASSERT_EQ(1U, sessions0.size());
+ ASSERT_TRUE(WindowsMatch(sessions0[0]->windows, client1_windows.get()));
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
+ BothChangeWithEncryptionAndPassphrase) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_TRUE(CheckInitialState(0));
+ ASSERT_TRUE(CheckInitialState(1));
+
+ GetClient(0)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
+
+ // These changes will sync over to client 1, who will be unable to decrypt
+ // them due to the missing passphrase.
+ ScopedVector<SessionWindow> client0_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), client0_windows.get()));
+ ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+ ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
+ num_blocking_conflicting_updates);
+ // We have three non-blocking conflicts due to the two meta nodes (one for
+ // each client) and the one tab node.
+ ASSERT_EQ(3, GetClient(1)->GetLastSessionSnapshot()->
+ num_conflicting_updates); // The encrypted nodes.
+
+ GetClient(1)->service()->SetPassphrase(kValidPassphrase, true, true);
+ ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
+ ASSERT_FALSE(GetClient(1)->service()->IsPassphraseRequired());
+ ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
+
+ // Open windows on client 1, which should automatically be encrypted.
+ ScopedVector<SessionWindow> client1_windows;
+ ASSERT_TRUE(OpenTabAndGetLocalWindows(1, GURL(kURL2), client1_windows.get()));
+ ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
+
+ ASSERT_TRUE(IsEncrypted(0, syncable::SESSIONS));
+ ASSERT_TRUE(IsEncrypted(1, syncable::SESSIONS));
+ // Get foreign session data from client 0 and 1.
+ SyncedSessionVector sessions0;
+ SyncedSessionVector sessions1;
+ ASSERT_TRUE(GetSessionData(0, &sessions0));
+ ASSERT_TRUE(GetSessionData(1, &sessions1));
+
+ // Verify client 1's foreign session matches client 0's current window and
+ // vice versa.
+ ASSERT_EQ(1U, sessions0.size());
+ ASSERT_EQ(1U, sessions1.size());
+ ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows.get()));
+ ASSERT_TRUE(WindowsMatch(sessions0[0]->windows, client1_windows.get()));
+}
diff --git a/chrome/browser/sync/test/live_sync/two_client_themes_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_themes_sync_test.cc
new file mode 100644
index 0000000..2ca7c3f
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_themes_sync_test.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/themes_helper.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+
+using themes_helper::GetCustomTheme;
+using themes_helper::GetThemeID;
+using themes_helper::HasOrWillHaveCustomTheme;
+using themes_helper::ThemeIsPendingInstall;
+using themes_helper::UseCustomTheme;
+using themes_helper::UseDefaultTheme;
+using themes_helper::UseNativeTheme;
+using themes_helper::UsingCustomTheme;
+using themes_helper::UsingDefaultTheme;
+using themes_helper::UsingNativeTheme;
+
+class TwoClientThemesSyncTest : public LiveSyncTest {
+ public:
+ TwoClientThemesSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+ virtual ~TwoClientThemesSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientThemesSyncTest);
+};
+
+// TODO(akalin): Add tests for model association (i.e., tests that
+// start with SetupClients(), change the theme state, then call
+// SetupSync()).
+
+// TCM ID - 3667311.
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, CustomTheme) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ ASSERT_FALSE(UsingCustomTheme(verifier()));
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(verifier(), 0);
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ // TODO(akalin): Add functions to simulate when a pending extension
+ // is installed as well as when a pending extension fails to
+ // install.
+ ASSERT_TRUE(ThemeIsPendingInstall(GetProfile(1), GetCustomTheme(0)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+}
+
+// TCM ID - 3599303.
+// TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, FAILS_NativeTheme) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, NativeTheme) {
+#endif // OS_CHROMEOS
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(GetProfile(1), 0);
+ UseCustomTheme(verifier(), 0);
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ UseNativeTheme(GetProfile(0));
+ UseNativeTheme(verifier());
+ ASSERT_TRUE(UsingNativeTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingNativeTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ ASSERT_TRUE(UsingNativeTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingNativeTheme(GetProfile(1)));
+ ASSERT_TRUE(UsingNativeTheme(verifier()));
+}
+
+// TCM ID - 7247455.
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, DefaultTheme) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(GetProfile(1), 0);
+ UseCustomTheme(verifier(), 0);
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ UseDefaultTheme(GetProfile(0));
+ UseDefaultTheme(verifier());
+ ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingDefaultTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ ASSERT_TRUE(UsingDefaultTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingDefaultTheme(GetProfile(1)));
+ ASSERT_TRUE(UsingDefaultTheme(verifier()));
+}
+
+// TCM ID - 7292065.
+// TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, FAILS_NativeDefaultRace) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, NativeDefaultRace) {
+#endif // OS_CHROMEOS
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ UseNativeTheme(GetProfile(0));
+ UseDefaultTheme(GetProfile(1));
+ ASSERT_TRUE(UsingNativeTheme(GetProfile(0)));
+ ASSERT_TRUE(UsingDefaultTheme(GetProfile(1)));
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ // TODO(akalin): Add function that compares two profiles to see if
+ // they're at the same state.
+
+ ASSERT_EQ(UsingNativeTheme(GetProfile(0)),
+ UsingNativeTheme(GetProfile(1)));
+ ASSERT_EQ(UsingDefaultTheme(GetProfile(0)),
+ UsingDefaultTheme(GetProfile(1)));
+}
+
+// TCM ID - 7294077.
+// TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, FAILS_CustomNativeRace) {
+#else
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, CustomNativeRace) {
+#endif // OS_CHROMEOS
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseNativeTheme(GetProfile(1));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_TRUE(UsingNativeTheme(GetProfile(1)));
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ // TODO(akalin): Add function to wait for pending extensions to be
+ // installed.
+
+ ASSERT_EQ(HasOrWillHaveCustomTheme(GetProfile(0), GetCustomTheme(0)),
+ HasOrWillHaveCustomTheme(GetProfile(1), GetCustomTheme(0)));
+}
+
+// TCM ID - 7307225.
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, CustomDefaultRace) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseDefaultTheme(GetProfile(1));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_TRUE(UsingDefaultTheme(GetProfile(1)));
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(HasOrWillHaveCustomTheme(GetProfile(0), GetCustomTheme(0)),
+ HasOrWillHaveCustomTheme(GetProfile(1), GetCustomTheme(0)));
+}
+
+// TCM ID - 7264758.
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, CustomCustomRace) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // TODO(akalin): Generalize this to n clients.
+
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(GetProfile(1), 1);
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_EQ(GetCustomTheme(1), GetThemeID(GetProfile(1)));
+
+ ASSERT_TRUE(AwaitQuiescence());
+
+ bool using_theme_0 =
+ (GetThemeID(GetProfile(0)) == GetCustomTheme(0)) &&
+ HasOrWillHaveCustomTheme(GetProfile(1), GetCustomTheme(0));
+ bool using_theme_1 =
+ HasOrWillHaveCustomTheme(GetProfile(0), GetCustomTheme(1)) &&
+ (GetThemeID(GetProfile(1)) == GetCustomTheme(1));
+
+ // Equivalent to using_theme_0 xor using_theme_1.
+ ASSERT_NE(using_theme_0, using_theme_1);
+}
+
+// TCM ID - 3723272.
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, DisableThemes) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ ASSERT_FALSE(UsingCustomTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForDatatype(syncable::THEMES));
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(verifier(), 0);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForDatatype(syncable::THEMES));
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ ASSERT_TRUE(ThemeIsPendingInstall(GetProfile(1), GetCustomTheme(0)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+}
+
+// TCM ID - 3687288.
+IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, DisableSync) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ ASSERT_FALSE(UsingCustomTheme(verifier()));
+
+ ASSERT_TRUE(GetClient(1)->DisableSyncForAllDatatypes());
+ UseCustomTheme(GetProfile(0), 0);
+ UseCustomTheme(verifier(), 0);
+ ASSERT_TRUE(
+ GetClient(0)->AwaitSyncCycleCompletion("Installed a custom theme."));
+
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+
+ ASSERT_TRUE(GetClient(1)->EnableSyncForAllDatatypes());
+ ASSERT_TRUE(AwaitQuiescence());
+
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(GetProfile(0)));
+ ASSERT_EQ(GetCustomTheme(0), GetThemeID(verifier()));
+ ASSERT_FALSE(UsingCustomTheme(GetProfile(1)));
+ ASSERT_TRUE(ThemeIsPendingInstall(GetProfile(1), GetCustomTheme(0)));
+}
diff --git a/chrome/browser/sync/test/live_sync/two_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/live_sync/two_client_typed_urls_sync_test.cc
new file mode 100644
index 0000000..ce49633
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/two_client_typed_urls_sync_test.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/number_formatting.h"
+#include "base/memory/scoped_vector.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/typed_urls_helper.h"
+
+using typed_urls_helper::AddUrlToHistory;
+using typed_urls_helper::AssertAllProfilesHaveSameURLsAsVerifier;
+using typed_urls_helper::DeleteUrlFromHistory;
+using typed_urls_helper::GetTypedUrlsFromClient;
+
+class TwoClientTypedUrlsSyncTest : public LiveSyncTest {
+ public:
+ TwoClientTypedUrlsSyncTest() : LiveSyncTest(TWO_CLIENT) {}
+ virtual ~TwoClientTypedUrlsSyncTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TwoClientTypedUrlsSyncTest);
+};
+
+// TCM: 3728323
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, Add) {
+ const string16 kHistoryUrl(
+ ASCIIToUTF16("http://www.add-one-history.google.com/"));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Populate one client with a URL, should sync to the other.
+ GURL new_url(kHistoryUrl);
+ AddUrlToHistory(0, new_url);
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+ ASSERT_EQ(new_url, urls[0].url());
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Both clients should have this URL.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
+
+// TCM: 3705291
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddThenDelete) {
+ const string16 kHistoryUrl(
+ ASCIIToUTF16("http://www.add-one-history.google.com/"));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Populate one client with a URL, should sync to the other.
+ GURL new_url(kHistoryUrl);
+ AddUrlToHistory(0, new_url);
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+ ASSERT_EQ(new_url, urls[0].url());
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Both clients should have this URL.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+
+ // Delete from first client, should delete from second.
+ DeleteUrlFromHistory(0, new_url);
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Neither client should have this URL.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
+
+// TCM: 3643277
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, DisableEnableSync) {
+ const string16 kUrl1(ASCIIToUTF16("http://history1.google.com/"));
+ const string16 kUrl2(ASCIIToUTF16("http://history2.google.com/"));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Disable typed url sync for one client, leave it active for the other.
+ GetClient(0)->DisableSyncForDatatype(syncable::TYPED_URLS);
+
+ // Add one URL to non-syncing client, add a different URL to the other,
+ // wait for sync cycle to complete. No data should be exchanged.
+ GURL url1(kUrl1);
+ GURL url2(kUrl2);
+ AddUrlToHistory(0, url1);
+ AddUrlToHistory(1, url2);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ // Make sure that no data was exchanged.
+ std::vector<history::URLRow> post_sync_urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, post_sync_urls.size());
+ ASSERT_EQ(url1, post_sync_urls[0].url());
+ post_sync_urls = GetTypedUrlsFromClient(1);
+ ASSERT_EQ(1U, post_sync_urls.size());
+ ASSERT_EQ(url2, post_sync_urls[0].url());
+
+ // Enable typed url sync, make both URLs are synced to each client.
+ GetClient(0)->EnableSyncForDatatype(syncable::TYPED_URLS);
+ ASSERT_TRUE(AwaitQuiescence());
+
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddOneDeleteOther) {
+ const string16 kHistoryUrl(
+ ASCIIToUTF16("http://www.add-one-delete-history.google.com/"));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Populate one client with a URL, should sync to the other.
+ GURL new_url(kHistoryUrl);
+ AddUrlToHistory(0, new_url);
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+ ASSERT_EQ(new_url, urls[0].url());
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Both clients should have this URL.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+
+ // Now, delete the URL from the second client.
+ DeleteUrlFromHistory(1, new_url);
+ urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Both clients should have this URL removed.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
+ AddOneDeleteOtherAddAgain) {
+ const string16 kHistoryUrl(
+ ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+ // Populate one client with a URL, should sync to the other.
+ GURL new_url(kHistoryUrl);
+ AddUrlToHistory(0, new_url);
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+ ASSERT_EQ(new_url, urls[0].url());
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Both clients should have this URL.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+
+ // Now, delete the URL from the second client.
+ DeleteUrlFromHistory(1, new_url);
+ urls = GetTypedUrlsFromClient(0);
+ ASSERT_EQ(1U, urls.size());
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Both clients should have this URL removed.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+
+ // Add it to the first client again, should succeed (tests that the deletion
+ // properly disassociates that URL).
+ AddUrlToHistory(0, new_url);
+
+ // Let sync finish.
+ ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
+
+ // Both clients should have this URL added again.
+ AssertAllProfilesHaveSameURLsAsVerifier();
+}
diff --git a/chrome/browser/sync/test/live_sync/typed_urls_helper.cc b/chrome/browser/sync/test/live_sync/typed_urls_helper.cc
new file mode 100644
index 0000000..0a0cc84
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/typed_urls_helper.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/live_sync/typed_urls_helper.h"
+
+#include "base/synchronization/waitable_event.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/history/history_backend.h"
+#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/live_sync/live_sync_test.h"
+#include "chrome/browser/sync/test/live_sync/sync_datatype_helper.h"
+#include "content/browser/cancelable_request.h"
+
+using sync_datatype_helper::test;
+
+namespace {
+
+class FlushHistoryDBQueueTask : public HistoryDBTask {
+ public:
+ explicit FlushHistoryDBQueueTask(base::WaitableEvent* event)
+ : wait_event_(event) {}
+ virtual bool RunOnDBThread(history::HistoryBackend* backend,
+ history::HistoryDatabase* db) {
+ wait_event_->Signal();
+ return true;
+ }
+
+ virtual void DoneRunOnMainThread() {}
+ private:
+ base::WaitableEvent* wait_event_;
+};
+
+class GetTypedUrlsTask : public HistoryDBTask {
+ public:
+ GetTypedUrlsTask(std::vector<history::URLRow>* rows,
+ base::WaitableEvent* event)
+ : rows_(rows), wait_event_(event) {}
+
+ virtual bool RunOnDBThread(history::HistoryBackend* backend,
+ history::HistoryDatabase* db) {
+ // Fetch the typed URLs.
+ backend->GetAllTypedURLs(rows_);
+ wait_event_->Signal();
+ return true;
+ }
+
+ virtual void DoneRunOnMainThread() {}
+ private:
+ std::vector<history::URLRow>* rows_;
+ base::WaitableEvent* wait_event_;
+};
+
+// Waits for the history DB thread to finish executing its current set of
+// tasks.
+void WaitForHistoryDBThread(int index) {
+ CancelableRequestConsumer cancelable_consumer;
+ HistoryService* service =
+ test()->GetProfile(index)->GetHistoryServiceWithoutCreating();
+ base::WaitableEvent wait_event(true, false);
+ service->ScheduleDBTask(new FlushHistoryDBQueueTask(&wait_event),
+ &cancelable_consumer);
+ wait_event.Wait();
+}
+
+// Creates a URLRow in the specified HistoryService.
+void AddToHistory(HistoryService* service,
+ const GURL& url,
+ const base::Time& timestamp) {
+ service->AddPage(url,
+ timestamp,
+ NULL, // scope
+ 1234, // page_id
+ GURL(), // referrer
+ PageTransition::TYPED,
+ history::RedirectList(),
+ history::SOURCE_BROWSED,
+ false);
+}
+
+std::vector<history::URLRow> GetTypedUrlsFromHistoryService(
+ HistoryService* service) {
+ CancelableRequestConsumer cancelable_consumer;
+ std::vector<history::URLRow> rows;
+ base::WaitableEvent wait_event(true, false);
+ service->ScheduleDBTask(new GetTypedUrlsTask(&rows, &wait_event),
+ &cancelable_consumer);
+ wait_event.Wait();
+ return rows;
+}
+
+static base::Time* timestamp = NULL;
+
+} // namespace
+
+namespace typed_urls_helper {
+
+std::vector<history::URLRow> GetTypedUrlsFromClient(int index) {
+ HistoryService* service =
+ test()->GetProfile(index)->GetHistoryServiceWithoutCreating();
+ return GetTypedUrlsFromHistoryService(service);
+}
+
+base::Time GetTimestamp() {
+ // The history subsystem doesn't like identical timestamps for page visits,
+ // and it will massage the visit timestamps if we try to use identical
+ // values, which can lead to spurious errors. So make sure all timestamps
+ // are unique.
+ if (!::timestamp)
+ ::timestamp = new base::Time(base::Time::Now());
+ base::Time original = *::timestamp;
+ *::timestamp += base::TimeDelta::FromMilliseconds(1);
+ return original;
+}
+
+void AddUrlToHistory(int index, const GURL& url) {
+ base::Time timestamp = GetTimestamp();
+ AddToHistory(test()->GetProfile(index)->GetHistoryServiceWithoutCreating(),
+ url,
+ timestamp);
+ if (test()->use_verifier())
+ AddToHistory(
+ test()->verifier()->GetHistoryService(Profile::IMPLICIT_ACCESS),
+ url,
+ timestamp);
+
+ // Wait until the AddPage() request has completed so we know the change has
+ // filtered down to the sync observers (don't need to wait for the
+ // verifier profile since it doesn't sync).
+ WaitForHistoryDBThread(index);
+}
+
+void DeleteUrlFromHistory(int index, const GURL& url) {
+ test()->GetProfile(index)->GetHistoryServiceWithoutCreating()->DeleteURL(url);
+ if (test()->use_verifier())
+ test()->verifier()->GetHistoryService(Profile::IMPLICIT_ACCESS)->
+ DeleteURL(url);
+ WaitForHistoryDBThread(index);
+}
+
+void AssertURLRowVectorsAreEqual(
+ const std::vector<history::URLRow>& left,
+ const std::vector<history::URLRow>& right) {
+ ASSERT_EQ(left.size(), right.size());
+ for (size_t i = 0; i < left.size(); ++i) {
+ // URLs could be out-of-order, so look for a matching URL in the second
+ // array.
+ bool found = false;
+ for (size_t j = 0; j < right.size(); ++j) {
+ if (left[i].url() == right[j].url()) {
+ AssertURLRowsAreEqual(left[i], right[j]);
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ }
+}
+
+void AssertURLRowsAreEqual(
+ const history::URLRow& left, const history::URLRow& right) {
+ ASSERT_EQ(left.url(), right.url());
+ ASSERT_EQ(left.title(), right.title());
+ ASSERT_EQ(left.visit_count(), right.visit_count());
+ ASSERT_EQ(left.typed_count(), right.typed_count());
+ ASSERT_EQ(left.last_visit(), right.last_visit());
+ ASSERT_EQ(left.hidden(), right.hidden());
+}
+
+void AssertAllProfilesHaveSameURLsAsVerifier() {
+ HistoryService* verifier_service =
+ test()->verifier()->GetHistoryService(Profile::IMPLICIT_ACCESS);
+ std::vector<history::URLRow> verifier_urls =
+ GetTypedUrlsFromHistoryService(verifier_service);
+ for (int i = 0; i < test()->num_clients(); ++i) {
+ std::vector<history::URLRow> urls = GetTypedUrlsFromClient(i);
+ AssertURLRowVectorsAreEqual(verifier_urls, urls);
+ }
+}
+
+} // namespace typed_urls_helper
diff --git a/chrome/browser/sync/test/live_sync/typed_urls_helper.h b/chrome/browser/sync/test/live_sync/typed_urls_helper.h
new file mode 100644
index 0000000..5447f62
--- /dev/null
+++ b/chrome/browser/sync/test/live_sync/typed_urls_helper.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_TYPED_URLS_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_TYPED_URLS_HELPER_H_
+#pragma once
+
+#include <vector>
+
+#include "chrome/browser/history/history_types.h"
+
+namespace base {
+class Time;
+}
+
+namespace typed_urls_helper {
+
+// Gets the typed URLs from a specific sync profile.
+std::vector<history::URLRow> GetTypedUrlsFromClient(int index);
+
+// Adds a URL to the history DB for a specific sync profile (just registers a
+// new visit if the URL already exists).
+void AddUrlToHistory(int index, const GURL& url);
+
+// Deletes a URL from the history DB for a specific sync profile.
+void DeleteUrlFromHistory(int index, const GURL& url);
+
+// Returns true if all clients match the verifier profile.
+void AssertAllProfilesHaveSameURLsAsVerifier();
+
+// Checks that the two vectors contain the same set of URLRows (possibly in
+// a different order).
+void AssertURLRowVectorsAreEqual(
+ const std::vector<history::URLRow>& left,
+ const std::vector<history::URLRow>& right);
+
+// Checks that the passed URLRows are equivalent.
+void AssertURLRowsAreEqual(const history::URLRow& left,
+ const history::URLRow& right);
+
+// Returns a unique timestamp to use when generating page visits
+// (HistoryService does not like having identical timestamps and will modify
+// the timestamps behind the scenes if it encounters them, which leads to
+// spurious test failures when the resulting timestamps aren't what we
+// expect).
+base::Time GetTimestamp();
+
+} // namespace typed_urls_helper
+
+#endif // CHROME_BROWSER_SYNC_TEST_LIVE_SYNC_TYPED_URLS_HELPER_H_
diff --git a/chrome/browser/sync/test/null_directory_change_delegate.cc b/chrome/browser/sync/test/null_directory_change_delegate.cc
new file mode 100644
index 0000000..7b2ec71
--- /dev/null
+++ b/chrome/browser/sync/test/null_directory_change_delegate.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/null_directory_change_delegate.h"
+
+namespace syncable {
+
+NullDirectoryChangeDelegate::~NullDirectoryChangeDelegate() {}
+
+void NullDirectoryChangeDelegate::HandleCalculateChangesChangeEventFromSyncApi(
+ const EntryKernelMutationSet& mutations,
+ BaseTransaction* trans) {}
+
+void NullDirectoryChangeDelegate::HandleCalculateChangesChangeEventFromSyncer(
+ const EntryKernelMutationSet& mutations,
+ BaseTransaction* trans) {}
+
+ModelTypeBitSet NullDirectoryChangeDelegate::HandleTransactionEndingChangeEvent(
+ BaseTransaction* trans) {
+ return ModelTypeBitSet();
+}
+
+void NullDirectoryChangeDelegate::HandleTransactionCompleteChangeEvent(
+ const ModelTypeBitSet& models_with_changes) {}
+
+} // namespace syncable
diff --git a/chrome/browser/sync/test/null_directory_change_delegate.h b/chrome/browser/sync/test/null_directory_change_delegate.h
new file mode 100644
index 0000000..1b8518a
--- /dev/null
+++ b/chrome/browser/sync/test/null_directory_change_delegate.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_NULL_DIRECTORY_CHANGE_DELEGATE_H_
+#define CHROME_BROWSER_SYNC_TEST_NULL_DIRECTORY_CHANGE_DELEGATE_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/syncable/directory_change_delegate.h"
+
+namespace syncable {
+
+// DirectoryChangeDelegate that does nothing in all delegate methods.
+class NullDirectoryChangeDelegate : public DirectoryChangeDelegate {
+ public:
+ virtual ~NullDirectoryChangeDelegate();
+
+ virtual void HandleCalculateChangesChangeEventFromSyncApi(
+ const EntryKernelMutationSet& mutations,
+ BaseTransaction* trans) OVERRIDE;
+ virtual void HandleCalculateChangesChangeEventFromSyncer(
+ const EntryKernelMutationSet& mutations,
+ BaseTransaction* trans) OVERRIDE;
+ virtual ModelTypeBitSet HandleTransactionEndingChangeEvent(
+ BaseTransaction* trans) OVERRIDE;
+ virtual void HandleTransactionCompleteChangeEvent(
+ const ModelTypeBitSet& models_with_changes) OVERRIDE;
+};
+
+} // namespace syncable
+
+#endif // CHROME_BROWSER_SYNC_TEST_NULL_DIRECTORY_CHANGE_DELEGATE_H_
diff --git a/chrome/browser/sync/test/sessions/test_scoped_session_event_listener.h b/chrome/browser/sync/test/sessions/test_scoped_session_event_listener.h
new file mode 100644
index 0000000..15fc68c
--- /dev/null
+++ b/chrome/browser/sync/test/sessions/test_scoped_session_event_listener.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_SESSIONS_TEST_SCOPED_SESSION_EVENT_LISTENER_H_
+#define CHROME_BROWSER_SYNC_TEST_SESSIONS_TEST_SCOPED_SESSION_EVENT_LISTENER_H_
+#pragma once
+
+#include "chrome/browser/sync/sessions/sync_session_context.h"
+
+namespace browser_sync {
+namespace sessions {
+
+// Installs a SyncEventListener to a given session context for the lifetime of
+// the TestScopedSessionEventListener.
+class TestScopedSessionEventListener {
+ public:
+ TestScopedSessionEventListener(
+ SyncSessionContext* context,
+ SyncEngineEventListener* listener)
+ : context_(context), listener_(listener) {
+ context->listeners_.AddObserver(listener);
+ }
+ ~TestScopedSessionEventListener() {
+ context_->listeners_.RemoveObserver(listener_);
+ }
+ private:
+ SyncSessionContext* context_;
+ SyncEngineEventListener* listener_;
+ DISALLOW_COPY_AND_ASSIGN(TestScopedSessionEventListener);
+};
+
+} // namespace sessions
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_SESSIONS_TEST_SCOPED_SESSION_EVENT_LISTENER_H_
diff --git a/chrome/browser/sync/test/test_http_bridge_factory.cc b/chrome/browser/sync/test/test_http_bridge_factory.cc
new file mode 100644
index 0000000..a74cf4b
--- /dev/null
+++ b/chrome/browser/sync/test/test_http_bridge_factory.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/test_http_bridge_factory.h"
+
+namespace browser_sync {
+
+bool TestHttpBridge::MakeSynchronousPost(int* os_error_code,
+ int* response_code) {
+ return false;
+}
+
+int TestHttpBridge::GetResponseContentLength() const {
+ return 0;
+}
+
+const char* TestHttpBridge::GetResponseContent() const {
+ return 0;
+}
+
+const std::string TestHttpBridge::GetResponseHeaderValue(
+ const std::string &) const {
+ return std::string();
+}
+
+void TestHttpBridge::Abort() {
+}
+
+TestHttpBridgeFactory::TestHttpBridgeFactory() {}
+
+TestHttpBridgeFactory::~TestHttpBridgeFactory() {}
+
+sync_api::HttpPostProviderInterface* TestHttpBridgeFactory::Create() {
+ return new TestHttpBridge();
+}
+
+void TestHttpBridgeFactory::Destroy(sync_api::HttpPostProviderInterface* http) {
+ delete http;
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/test/test_http_bridge_factory.h b/chrome/browser/sync/test/test_http_bridge_factory.h
new file mode 100644
index 0000000..ebfc577
--- /dev/null
+++ b/chrome/browser/sync/test/test_http_bridge_factory.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_TEST_HTTP_BRIDGE_FACTORY_H_
+#define CHROME_BROWSER_SYNC_TEST_TEST_HTTP_BRIDGE_FACTORY_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/internal_api/http_post_provider_interface.h"
+#include "chrome/browser/sync/internal_api/http_post_provider_factory.h"
+
+namespace browser_sync {
+
+class TestHttpBridge : public sync_api::HttpPostProviderInterface {
+ public:
+ // Begin sync_api::HttpPostProviderInterface implementation:
+ virtual void SetUserAgent(const char* user_agent) OVERRIDE {}
+
+ virtual void SetExtraRequestHeaders(const char * headers) OVERRIDE {}
+
+ virtual void SetURL(const char* url, int port) OVERRIDE {}
+
+ virtual void SetPostPayload(const char* content_type,
+ int content_length,
+ const char* content) OVERRIDE {}
+
+ virtual bool MakeSynchronousPost(int* os_error_code,
+ int* response_code) OVERRIDE;
+
+ virtual int GetResponseContentLength() const OVERRIDE;
+
+ virtual const char* GetResponseContent() const OVERRIDE;
+
+ virtual const std::string GetResponseHeaderValue(
+ const std::string&) const OVERRIDE;
+
+ virtual void Abort();
+ // End sync_api::HttpPostProviderInterface implementation.
+};
+
+class TestHttpBridgeFactory : public sync_api::HttpPostProviderFactory {
+ public:
+ TestHttpBridgeFactory();
+ virtual ~TestHttpBridgeFactory();
+
+ // sync_api::HttpPostProviderFactory:
+ virtual sync_api::HttpPostProviderInterface* Create() OVERRIDE;
+ virtual void Destroy(sync_api::HttpPostProviderInterface* http) OVERRIDE;
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_TEST_TEST_HTTP_BRIDGE_FACTORY_H_
diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc
index 95557ca..ec5ac17 100644
--- a/chrome/browser/sync/test_profile_sync_service.cc
+++ b/chrome/browser/sync/test_profile_sync_service.cc
@@ -14,8 +14,8 @@
#include "chrome/browser/sync/signin_manager.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable.h"
+#include "chrome/browser/sync/test/test_http_bridge_factory.h"
#include "chrome/common/chrome_notification_types.h"
-#include "chrome/test/sync/test_http_bridge_factory.h"
using browser_sync::ModelSafeRoutingInfo;
using browser_sync::sessions::ErrorCounters;
diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h
index 4384723..743cdeb 100644
--- a/chrome/browser/sync/test_profile_sync_service.h
+++ b/chrome/browser/sync/test_profile_sync_service.h
@@ -11,8 +11,8 @@
#include "base/compiler_specific.h"
#include "chrome/browser/sync/glue/data_type_manager_impl.h"
#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/test/engine/test_id_factory.h"
#include "chrome/test/base/profile_mock.h"
-#include "chrome/test/sync/engine/test_id_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
class Profile;