| // Copyright 2016 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. |
| |
| #import "ios/chrome/test/app/sync_test_util.h" |
| |
| #include <set> |
| #include <string> |
| |
| #include "base/logging.h" |
| #import "base/mac/bind_objc_block.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #import "base/test/ios/wait_util.h" |
| #include "components/autofill/core/browser/personal_data_manager.h" |
| #include "components/browser_sync/profile_sync_service.h" |
| #include "components/history/core/browser/history_service.h" |
| #include "components/keyed_service/core/service_access_type.h" |
| #include "components/sync/engine/net/http_bridge_network_resources.h" |
| #include "components/sync/test/fake_server/entity_builder_factory.h" |
| #include "components/sync/test/fake_server/fake_server.h" |
| #include "components/sync/test/fake_server/fake_server_network_resources.h" |
| #include "components/sync/test/fake_server/fake_server_verifier.h" |
| #include "components/sync/test/fake_server/sessions_hierarchy.h" |
| #include "components/sync/test/fake_server/tombstone_entity.h" |
| #include "components/sync/test/fake_server/unique_client_entity.h" |
| #include "ios/chrome/browser/autofill/personal_data_manager_factory.h" |
| #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| #include "ios/chrome/browser/history/history_service_factory.h" |
| #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" |
| #include "ios/chrome/browser/sync/sync_setup_service.h" |
| #include "ios/chrome/browser/sync/sync_setup_service_factory.h" |
| #import "ios/chrome/test/app/chrome_test_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| |
| fake_server::FakeServer* gSyncFakeServer = nullptr; |
| |
| NSString* const kSyncTestErrorDomain = @"SyncTestDomain"; |
| |
| // Overrides the network resources of the current ProfileSyncService with |
| // |resources|. |
| void OverrideSyncNetworkResources( |
| std::unique_ptr<syncer::NetworkResources> resources) { |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| DCHECK(browser_state); |
| browser_sync::ProfileSyncService* service = |
| IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state); |
| service->OverrideNetworkResourcesForTest(std::move(resources)); |
| } |
| |
| } // namespace |
| |
| namespace chrome_test_util { |
| |
| void SetUpFakeSyncServer() { |
| DCHECK(!gSyncFakeServer); |
| gSyncFakeServer = new fake_server::FakeServer(); |
| OverrideSyncNetworkResources(base::WrapUnique<syncer::NetworkResources>( |
| new fake_server::FakeServerNetworkResources( |
| gSyncFakeServer->AsWeakPtr()))); |
| } |
| |
| void TearDownFakeSyncServer() { |
| DCHECK(gSyncFakeServer); |
| delete gSyncFakeServer; |
| gSyncFakeServer = nullptr; |
| OverrideSyncNetworkResources(base::WrapUnique<syncer::NetworkResources>( |
| new syncer::HttpBridgeNetworkResources())); |
| } |
| |
| void StartSync() { |
| DCHECK(!IsSyncInitialized()); |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| SyncSetupService* sync_setup_service = |
| SyncSetupServiceFactory::GetForBrowserState(browser_state); |
| sync_setup_service->SetSyncEnabled(true); |
| } |
| |
| void StopSync() { |
| DCHECK(IsSyncInitialized()); |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| SyncSetupService* sync_setup_service = |
| SyncSetupServiceFactory::GetForBrowserState(browser_state); |
| sync_setup_service->SetSyncEnabled(false); |
| } |
| |
| void TriggerSyncCycle(syncer::ModelType type) { |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| browser_sync::ProfileSyncService* profile_sync_service = |
| IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state); |
| const syncer::ModelTypeSet types(type); |
| profile_sync_service->RefreshTypesForTest(types); |
| } |
| |
| void ClearSyncServerData() { |
| DCHECK(gSyncFakeServer); |
| gSyncFakeServer->ClearServerData(); |
| } |
| |
| int GetNumberOfSyncEntities(syncer::ModelType type) { |
| DCHECK(gSyncFakeServer); |
| std::unique_ptr<base::DictionaryValue> entities = |
| gSyncFakeServer->GetEntitiesAsDictionaryValue(); |
| |
| std::string model_type_string = ModelTypeToString(type); |
| base::ListValue* entity_list = NULL; |
| if (!entities->GetList(model_type_string, &entity_list)) { |
| return 0; |
| } |
| return entity_list->GetSize(); |
| } |
| |
| BOOL VerifyNumberOfSyncEntitiesWithName(syncer::ModelType type, |
| std::string name, |
| size_t count, |
| NSError** error) { |
| DCHECK(gSyncFakeServer); |
| fake_server::FakeServerVerifier verifier(gSyncFakeServer); |
| testing::AssertionResult result = |
| verifier.VerifyEntityCountByTypeAndName(count, type, name); |
| if (result != testing::AssertionSuccess() && error != nil) { |
| NSDictionary* errorInfo = @{ |
| NSLocalizedDescriptionKey : base::SysUTF8ToNSString(result.message()) |
| }; |
| *error = [NSError errorWithDomain:kSyncTestErrorDomain |
| code:0 |
| userInfo:errorInfo]; |
| return NO; |
| } |
| return result == testing::AssertionSuccess(); |
| } |
| |
| void InjectBookmarkOnFakeSyncServer(std::string url, std::string title) { |
| DCHECK(gSyncFakeServer); |
| fake_server::EntityBuilderFactory entity_builder_factory; |
| fake_server::BookmarkEntityBuilder bookmark_builder = |
| entity_builder_factory.NewBookmarkEntityBuilder(title); |
| gSyncFakeServer->InjectEntity(bookmark_builder.BuildBookmark(GURL(url))); |
| } |
| |
| bool IsSyncInitialized() { |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| DCHECK(browser_state); |
| syncer::SyncService* syncService = |
| IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state); |
| return syncService->IsEngineInitialized(); |
| } |
| |
| std::string GetSyncCacheGuid() { |
| DCHECK(IsSyncInitialized()); |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| browser_sync::ProfileSyncService* profile_sync_service = |
| IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state); |
| syncer::LocalDeviceInfoProvider* info_provider = |
| profile_sync_service->GetLocalDeviceInfoProvider(); |
| return info_provider->GetLocalSyncCacheGUID(); |
| } |
| |
| void InjectAutofillProfileOnFakeSyncServer(std::string guid, |
| std::string full_name) { |
| DCHECK(gSyncFakeServer); |
| sync_pb::EntitySpecifics entity_specifics; |
| sync_pb::AutofillProfileSpecifics* autofill_profile = |
| entity_specifics.mutable_autofill_profile(); |
| autofill_profile->add_name_full(full_name); |
| autofill_profile->set_guid(guid); |
| |
| std::unique_ptr<fake_server::FakeServerEntity> entity = |
| fake_server::UniqueClientEntity::CreateForInjection(guid, |
| entity_specifics); |
| gSyncFakeServer->InjectEntity(std::move(entity)); |
| } |
| |
| void DeleteAutofillProfileOnFakeSyncServer(std::string guid) { |
| DCHECK(gSyncFakeServer); |
| std::vector<sync_pb::SyncEntity> autofill_profiles = |
| gSyncFakeServer->GetSyncEntitiesByModelType(syncer::AUTOFILL_PROFILE); |
| std::string entity_id; |
| for (const sync_pb::SyncEntity& autofill_profile : autofill_profiles) { |
| if (autofill_profile.specifics().autofill_profile().guid() == guid) { |
| entity_id = autofill_profile.id_string(); |
| break; |
| } |
| } |
| // Delete the entity if it exists. |
| if (!entity_id.empty()) { |
| std::unique_ptr<fake_server::FakeServerEntity> entity; |
| entity = fake_server::TombstoneEntity::Create(entity_id, std::string()); |
| gSyncFakeServer->InjectEntity(std::move(entity)); |
| } |
| } |
| |
| bool IsAutofillProfilePresent(std::string guid, std::string full_name) { |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| autofill::PersonalDataManager* personal_data_manager = |
| autofill::PersonalDataManagerFactory::GetForBrowserState(browser_state); |
| autofill::AutofillProfile* autofill_profile = |
| personal_data_manager->GetProfileByGUID(guid); |
| |
| if (autofill_profile) { |
| std::string actual_full_name = |
| base::UTF16ToUTF8(autofill_profile->GetRawInfo(autofill::NAME_FULL)); |
| return actual_full_name == full_name; |
| } |
| return false; |
| } |
| |
| void ClearAutofillProfile(std::string guid) { |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| autofill::PersonalDataManager* personal_data_manager = |
| autofill::PersonalDataManagerFactory::GetForBrowserState(browser_state); |
| personal_data_manager->RemoveByGUID(guid); |
| } |
| |
| BOOL VerifySessionsOnSyncServer(const std::multiset<std::string>& expected_urls, |
| NSError** error) { |
| DCHECK(gSyncFakeServer); |
| fake_server::SessionsHierarchy expected_sessions; |
| expected_sessions.AddWindow(expected_urls); |
| fake_server::FakeServerVerifier verifier(gSyncFakeServer); |
| testing::AssertionResult result = verifier.VerifySessions(expected_sessions); |
| if (result != testing::AssertionSuccess() && error != nil) { |
| NSDictionary* errorInfo = @{ |
| NSLocalizedDescriptionKey : base::SysUTF8ToNSString(result.message()) |
| }; |
| *error = [NSError errorWithDomain:kSyncTestErrorDomain |
| code:0 |
| userInfo:errorInfo]; |
| return NO; |
| } |
| return result == testing::AssertionSuccess(); |
| } |
| |
| void AddTypedURLOnClient(const GURL& url) { |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| history::HistoryService* historyService = |
| ios::HistoryServiceFactory::GetForBrowserState( |
| browser_state, ServiceAccessType::EXPLICIT_ACCESS); |
| |
| historyService->AddPage(url, base::Time::Now(), nullptr, 1, GURL(), |
| history::RedirectList(), ui::PAGE_TRANSITION_TYPED, |
| history::SOURCE_BROWSED, false); |
| } |
| |
| void InjectTypedURLOnFakeSyncServer(const std::string& url) { |
| DCHECK(gSyncFakeServer); |
| sync_pb::EntitySpecifics entitySpecifics; |
| sync_pb::TypedUrlSpecifics* typedUrl = entitySpecifics.mutable_typed_url(); |
| typedUrl->set_url(url); |
| typedUrl->set_title(url); |
| typedUrl->add_visits(base::Time::Max().ToInternalValue()); |
| typedUrl->add_visit_transitions(sync_pb::SyncEnums::TYPED); |
| |
| std::unique_ptr<fake_server::FakeServerEntity> entity = |
| fake_server::UniqueClientEntity::CreateForInjection(url, entitySpecifics); |
| gSyncFakeServer->InjectEntity(std::move(entity)); |
| } |
| |
| BOOL IsTypedUrlPresentOnClient(const GURL& url, |
| BOOL expect_present, |
| NSError** error) { |
| // Call the history service. |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| history::HistoryService* history_service = |
| ios::HistoryServiceFactory::GetForBrowserState( |
| browser_state, ServiceAccessType::EXPLICIT_ACCESS); |
| |
| const GURL block_safe_url(url); |
| std::set<GURL> origins; |
| origins.insert(block_safe_url); |
| |
| __block bool history_service_callback_called = false; |
| __block int count = 0; |
| using history::OriginCountAndLastVisitMap; |
| history_service->GetCountsAndLastVisitForOrigins( |
| origins, base::BindBlockArc(^(const OriginCountAndLastVisitMap& result) { |
| auto iter = result.find(block_safe_url); |
| if (iter != result.end()) |
| count = iter->second.first; |
| history_service_callback_called = true; |
| })); |
| |
| NSDate* deadline = [NSDate dateWithTimeIntervalSinceNow:4.0]; |
| while (!history_service_callback_called && |
| [[NSDate date] compare:deadline] != NSOrderedDescending) { |
| base::test::ios::SpinRunLoopWithMaxDelay( |
| base::TimeDelta::FromSecondsD(0.1)); |
| } |
| |
| NSString* error_message = nil; |
| if (!history_service_callback_called) { |
| error_message = @"History::GetCountsAndLastVisitForOrigins callback never " |
| "called, app will probably crash later."; |
| } else if (count == 0 && expect_present) { |
| error_message = @"Typed URL isn't found in HistoryService."; |
| } else if (count > 0 && !expect_present) { |
| error_message = @"Typed URL isn't supposed to be in HistoryService."; |
| } |
| |
| if (error_message != nil && error != nil) { |
| NSDictionary* error_info = @{NSLocalizedDescriptionKey : error_message}; |
| *error = [NSError errorWithDomain:kSyncTestErrorDomain |
| code:0 |
| userInfo:error_info]; |
| return NO; |
| } |
| return error_message == nil; |
| } |
| |
| void DeleteTypedUrlFromClient(const GURL& url) { |
| ios::ChromeBrowserState* browser_state = |
| chrome_test_util::GetOriginalBrowserState(); |
| history::HistoryService* history_service = |
| ios::HistoryServiceFactory::GetForBrowserState( |
| browser_state, ServiceAccessType::EXPLICIT_ACCESS); |
| |
| history_service->DeleteURL(url); |
| } |
| |
| void DeleteTypedUrlFromFakeSyncServer(std::string url) { |
| DCHECK(gSyncFakeServer); |
| std::vector<sync_pb::SyncEntity> typed_urls = |
| gSyncFakeServer->GetSyncEntitiesByModelType(syncer::TYPED_URLS); |
| std::string entity_id; |
| for (const sync_pb::SyncEntity& typed_url : typed_urls) { |
| if (typed_url.specifics().typed_url().url() == url) { |
| entity_id = typed_url.id_string(); |
| break; |
| } |
| } |
| if (!entity_id.empty()) { |
| std::unique_ptr<fake_server::FakeServerEntity> entity; |
| entity = fake_server::TombstoneEntity::Create(entity_id, std::string()); |
| gSyncFakeServer->InjectEntity(std::move(entity)); |
| } |
| } |
| |
| } // namespace chrome_test_util |