| // 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/chromeos/customization/customization_document.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" |
| #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" |
| #include "chrome/browser/extensions/external_provider_impl.h" |
| #include "chrome/browser/prefs/browser_prefs.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/app_list/app_list_syncable_service.h" |
| #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/network/network_handler.h" |
| #include "chromeos/network/network_state.h" |
| #include "chromeos/network/network_state_handler.h" |
| #include "chromeos/system/fake_statistics_provider.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/sync_preferences/pref_service_mock_factory.h" |
| #include "components/sync_preferences/pref_service_syncable.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "extensions/browser/external_install_info.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/manifest.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_status_code.h" |
| #include "net/url_request/test_url_fetcher_factory.h" |
| #include "net/url_request/url_request_status.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::Exactly; |
| using ::testing::Invoke; |
| using ::testing::Mock; |
| using ::testing::_; |
| using extensions::ExternalInstallInfoFile; |
| using extensions::ExternalInstallInfoUpdateUrl; |
| |
| namespace { |
| |
| const char kGoodStartupManifest[] = |
| "{" |
| " \"version\": \"1.0\"," |
| " \"initial_locale\" : \"en-US\"," |
| " \"initial_timezone\" : \"US/Pacific\"," |
| " \"keyboard_layout\" : \"xkb:us::eng\"," |
| " \"setup_content\" : {" |
| " \"en-US\" : {" |
| " \"eula_page\" : \"file:///opt/oem/eula/en-US/eula.html\"," |
| " }," |
| " \"ru-RU\" : {" |
| " \"eula_page\" : \"file:///opt/oem/eula/ru-RU/eula.html\"," |
| " }," |
| " \"default\" : {" |
| " \"eula_page\" : \"file:///opt/oem/eula/en/eula.html\"," |
| " }," |
| " }," |
| " \"hwid_map\" : [" |
| " {" |
| " \"hwid_mask\": \"ZGA*34\"," |
| " \"initial_locale\" : \"ja\"," |
| " \"initial_timezone\" : \"Asia/Tokyo\"," |
| " \"keyboard_layout\" : \"mozc-jp\"," |
| " }," |
| " {" |
| " \"hwid_mask\": \"Mario 1?3*\"," |
| " \"initial_locale\" : \"ru-RU\"," |
| " \"initial_timezone\" : \"Europe/Moscow\"," |
| " \"keyboard_layout\" : \"xkb:ru::rus\"," |
| " }," |
| " ]," |
| "}"; |
| |
| const char kBadManifest[] = "{\"version\": \"1\"}"; |
| |
| const char kGoodServicesManifest[] = |
| "{" |
| " \"version\": \"1.0\"," |
| " \"default_apps\": [\n" |
| " \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n" |
| " {\n" |
| " \"id\": \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\n" |
| " \"do_not_install_for_enterprise\": true\n" |
| " }\n" |
| " ],\n" |
| " \"localized_content\": {\n" |
| " \"en-US\": {\n" |
| " \"default_apps_folder_name\": \"EN-US OEM Name\"\n" |
| " },\n" |
| " \"en\": {\n" |
| " \"default_apps_folder_name\": \"EN OEM Name\"\n" |
| " },\n" |
| " \"default\": {\n" |
| " \"default_apps_folder_name\": \"Default OEM Name\"\n" |
| " }\n" |
| " }\n" |
| "}"; |
| |
| const char kDummyCustomizationID[] = "test-dummy"; |
| |
| } // anonymous namespace |
| |
| namespace chromeos { |
| |
| using ::testing::DoAll; |
| using ::testing::NotNull; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| using ::testing::_; |
| |
| TEST(StartupCustomizationDocumentTest, Basic) { |
| system::ScopedFakeStatisticsProvider fake_statistics_provider; |
| |
| // hardware_class selects the appropriate entry in hwid_map in the manifest. |
| fake_statistics_provider.SetMachineStatistic("hardware_class", "Mario 12345"); |
| StartupCustomizationDocument customization(&fake_statistics_provider, |
| kGoodStartupManifest); |
| EXPECT_EQ("ru-RU", customization.initial_locale()); |
| EXPECT_EQ("Europe/Moscow", customization.initial_timezone()); |
| EXPECT_EQ("xkb:ru::rus", customization.keyboard_layout()); |
| |
| EXPECT_EQ("file:///opt/oem/eula/en-US/eula.html", |
| customization.GetEULAPage("en-US")); |
| EXPECT_EQ("file:///opt/oem/eula/ru-RU/eula.html", |
| customization.GetEULAPage("ru-RU")); |
| EXPECT_EQ("file:///opt/oem/eula/en/eula.html", |
| customization.GetEULAPage("ja")); |
| } |
| |
| TEST(StartupCustomizationDocumentTest, VPD) { |
| system::ScopedFakeStatisticsProvider fake_statistics_provider; |
| |
| // hardware_class selects the appropriate entry in hwid_map in the manifest. |
| fake_statistics_provider.SetMachineStatistic("hardware_class", "Mario 12345"); |
| fake_statistics_provider.SetMachineStatistic("initial_locale", "ja"); |
| fake_statistics_provider.SetMachineStatistic("initial_timezone", |
| "Asia/Tokyo"); |
| fake_statistics_provider.SetMachineStatistic("keyboard_layout", "mozc-jp"); |
| StartupCustomizationDocument customization(&fake_statistics_provider, |
| kGoodStartupManifest); |
| EXPECT_TRUE(customization.IsReady()); |
| EXPECT_EQ("ja", customization.initial_locale()); |
| EXPECT_EQ("Asia/Tokyo", customization.initial_timezone()); |
| EXPECT_EQ("mozc-jp", customization.keyboard_layout()); |
| } |
| |
| TEST(StartupCustomizationDocumentTest, BadManifest) { |
| system::ScopedFakeStatisticsProvider fake_statistics_provider; |
| StartupCustomizationDocument customization(&fake_statistics_provider, |
| kBadManifest); |
| EXPECT_FALSE(customization.IsReady()); |
| } |
| |
| class TestURLLoaderFactoryInterceptor { |
| public: |
| explicit TestURLLoaderFactoryInterceptor( |
| network::TestURLLoaderFactory* factory) { |
| factory->SetInterceptor(base::BindRepeating( |
| &TestURLLoaderFactoryInterceptor::Intercept, base::Unretained(this))); |
| } |
| MOCK_METHOD1(Intercept, void(const network::ResourceRequest&)); |
| }; |
| |
| class MockExternalProviderVisitor |
| : public extensions::ExternalProviderInterface::VisitorInterface { |
| public: |
| MockExternalProviderVisitor() {} |
| |
| MOCK_METHOD1(OnExternalExtensionFileFound, |
| bool(const ExternalInstallInfoFile&)); |
| MOCK_METHOD2(OnExternalExtensionUpdateUrlFound, |
| bool(const ExternalInstallInfoUpdateUrl&, bool)); |
| MOCK_METHOD1(OnExternalProviderReady, |
| void(const extensions::ExternalProviderInterface* provider)); |
| MOCK_METHOD4(OnExternalProviderUpdateComplete, |
| void(const extensions::ExternalProviderInterface*, |
| const std::vector<ExternalInstallInfoUpdateUrl>&, |
| const std::vector<ExternalInstallInfoFile>&, |
| const std::set<std::string>& removed_extensions)); |
| }; |
| |
| class ServicesCustomizationDocumentTest : public testing::Test { |
| protected: |
| ServicesCustomizationDocumentTest() = default; |
| |
| // testing::Test: |
| void SetUp() override { |
| ServicesCustomizationDocument::InitializeForTesting( |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &loader_factory_)); |
| |
| DBusThreadManager::Initialize(); |
| NetworkHandler::Initialize(); |
| RunUntilIdle(); |
| const NetworkState* default_network = |
| NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); |
| std::string default_network_path = |
| default_network ? default_network->path() : ""; |
| |
| network_portal_detector::InitializeForTesting(&network_portal_detector_); |
| NetworkPortalDetector::CaptivePortalState online_state; |
| online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; |
| online_state.response_code = 204; |
| std::string guid = |
| default_network ? default_network->guid() : std::string(); |
| network_portal_detector_.SetDefaultNetworkForTesting(guid); |
| if (!guid.empty()) { |
| network_portal_detector_.SetDetectionResultsForTesting( |
| guid, online_state); |
| } |
| |
| TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_); |
| RegisterLocalState(local_state_.registry()); |
| |
| interceptor_ = |
| std::make_unique<TestURLLoaderFactoryInterceptor>(&loader_factory_); |
| } |
| |
| void TearDown() override { |
| TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr); |
| NetworkHandler::Shutdown(); |
| DBusThreadManager::Shutdown(); |
| network_portal_detector::InitializeForTesting(nullptr); |
| loader_factory_.ClearResponses(); |
| interceptor_.reset(); |
| |
| ServicesCustomizationDocument::ShutdownForTesting(); |
| } |
| |
| void RunUntilIdle() { |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void AddCustomizationIdToVp(const std::string& id) { |
| fake_statistics_provider_.SetMachineStatistic(system::kCustomizationIdKey, |
| id); |
| } |
| |
| void AddExpectedManifest(const std::string& id, |
| const std::string& manifest) { |
| GURL url(base::StringPrintf(ServicesCustomizationDocument::kManifestUrl, |
| id.c_str())); |
| |
| network::ResourceResponseHead response_head; |
| response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); |
| response_head.headers->AddHeader("Content-Type: application/json"); |
| loader_factory_.AddResponse(url, response_head, manifest, |
| network::URLLoaderCompletionStatus(net::OK)); |
| EXPECT_CALL(*interceptor_, Intercept).Times(Exactly(1)); |
| } |
| |
| void AddManifestNotFound(const std::string& id) { |
| GURL url(base::StringPrintf(ServicesCustomizationDocument::kManifestUrl, |
| id.c_str())); |
| |
| network::ResourceResponseHead response_head; |
| response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); |
| response_head.headers->AddHeader("Content-Type: application/json"); |
| response_head.headers->ReplaceStatusLine("HTTP/1.1 404 Not found"); |
| loader_factory_.AddResponse(url, response_head, std::string(), |
| network::URLLoaderCompletionStatus(net::OK)); |
| EXPECT_CALL(*interceptor_, Intercept).Times(Exactly(1)); |
| } |
| |
| std::unique_ptr<TestingProfile> CreateProfile() { |
| TestingProfile::Builder profile_builder; |
| sync_preferences::PrefServiceMockFactory factory; |
| auto registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>(); |
| std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs( |
| factory.CreateSyncable(registry.get())); |
| RegisterUserProfilePrefs(registry.get()); |
| profile_builder.SetPrefService(std::move(prefs)); |
| std::unique_ptr<TestingProfile> profile = profile_builder.Build(); |
| // Make sure we have a Profile Manager. |
| TestingBrowserProcess::GetGlobal()->SetProfileManager( |
| new ProfileManagerWithoutInit(profile->GetPath())); |
| return profile; |
| } |
| |
| private: |
| content::TestBrowserThreadBundle thread_bundle_; |
| system::ScopedFakeStatisticsProvider fake_statistics_provider_; |
| ScopedCrosSettingsTestHelper scoped_cros_settings_test_helper_; |
| TestingPrefServiceSimple local_state_; |
| network::TestURLLoaderFactory loader_factory_; |
| std::unique_ptr<TestURLLoaderFactoryInterceptor> interceptor_; |
| NetworkPortalDetectorTestImpl network_portal_detector_; |
| }; |
| |
| TEST_F(ServicesCustomizationDocumentTest, Basic) { |
| AddCustomizationIdToVp(kDummyCustomizationID); |
| AddExpectedManifest(kDummyCustomizationID, kGoodServicesManifest); |
| |
| ServicesCustomizationDocument* doc = |
| ServicesCustomizationDocument::GetInstance(); |
| EXPECT_FALSE(doc->IsReady()); |
| |
| doc->StartFetching(); |
| RunUntilIdle(); |
| EXPECT_TRUE(doc->IsReady()); |
| |
| GURL wallpaper_url; |
| EXPECT_FALSE(doc->GetDefaultWallpaperUrl(&wallpaper_url)); |
| EXPECT_EQ("", wallpaper_url.spec()); |
| |
| std::unique_ptr<base::DictionaryValue> default_apps(doc->GetDefaultApps()); |
| ASSERT_TRUE(default_apps); |
| EXPECT_EQ(default_apps->size(), 2u); |
| |
| const base::DictionaryValue* app_entry = nullptr; |
| ASSERT_TRUE(default_apps->GetDictionary("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", |
| &app_entry)); |
| EXPECT_EQ(app_entry->size(), 1u); |
| EXPECT_TRUE( |
| app_entry->HasKey(extensions::ExternalProviderImpl::kExternalUpdateUrl)); |
| |
| ASSERT_TRUE(default_apps->GetDictionary("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", |
| &app_entry)); |
| EXPECT_EQ(app_entry->size(), 2u); |
| EXPECT_TRUE( |
| app_entry->HasKey(extensions::ExternalProviderImpl::kExternalUpdateUrl)); |
| EXPECT_TRUE(app_entry->HasKey( |
| extensions::ExternalProviderImpl::kDoNotInstallForEnterprise)); |
| |
| EXPECT_EQ("EN-US OEM Name", doc->GetOemAppsFolderName("en-US")); |
| EXPECT_EQ("EN OEM Name", doc->GetOemAppsFolderName("en")); |
| EXPECT_EQ("Default OEM Name", doc->GetOemAppsFolderName("ru")); |
| } |
| |
| TEST_F(ServicesCustomizationDocumentTest, NoCustomizationIdInVpd) { |
| ServicesCustomizationDocument* doc = |
| ServicesCustomizationDocument::GetInstance(); |
| EXPECT_FALSE(doc->IsReady()); |
| |
| std::unique_ptr<TestingProfile> profile = CreateProfile(); |
| extensions::ExternalLoader* loader = doc->CreateExternalLoader(profile.get()); |
| EXPECT_TRUE(loader); |
| |
| MockExternalProviderVisitor visitor; |
| auto provider = std::make_unique<extensions::ExternalProviderImpl>( |
| &visitor, loader, profile.get(), extensions::Manifest::EXTERNAL_PREF, |
| extensions::Manifest::EXTERNAL_PREF_DOWNLOAD, |
| extensions::Extension::FROM_WEBSTORE | |
| extensions::Extension::WAS_INSTALLED_BY_DEFAULT); |
| |
| EXPECT_CALL(visitor, OnExternalExtensionFileFound(_)).Times(0); |
| EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _)).Times(0); |
| EXPECT_CALL(visitor, OnExternalProviderReady(_)) |
| .Times(1); |
| EXPECT_CALL(visitor, OnExternalProviderUpdateComplete(_, _, _, _)).Times(0); |
| |
| // Manually request a load. |
| RunUntilIdle(); |
| loader->StartLoading(); |
| Mock::VerifyAndClearExpectations(&visitor); |
| |
| RunUntilIdle(); |
| // Empty customization is used when there is no customization ID in VPD. |
| EXPECT_TRUE(doc->IsReady()); |
| } |
| |
| TEST_F(ServicesCustomizationDocumentTest, DefaultApps) { |
| AddCustomizationIdToVp(kDummyCustomizationID); |
| AddExpectedManifest(kDummyCustomizationID, kGoodServicesManifest); |
| |
| ServicesCustomizationDocument* doc = |
| ServicesCustomizationDocument::GetInstance(); |
| EXPECT_FALSE(doc->IsReady()); |
| |
| std::unique_ptr<TestingProfile> profile = CreateProfile(); |
| extensions::ExternalLoader* loader = doc->CreateExternalLoader(profile.get()); |
| EXPECT_TRUE(loader); |
| |
| app_list::AppListSyncableServiceFactory::GetInstance() |
| ->SetTestingFactoryAndUse( |
| profile.get(), |
| base::BindRepeating( |
| &app_list::AppListSyncableServiceFactory::BuildInstanceFor)); |
| |
| MockExternalProviderVisitor visitor; |
| auto provider = std::make_unique<extensions::ExternalProviderImpl>( |
| &visitor, loader, profile.get(), extensions::Manifest::EXTERNAL_PREF, |
| extensions::Manifest::EXTERNAL_PREF_DOWNLOAD, |
| extensions::Extension::FROM_WEBSTORE | |
| extensions::Extension::WAS_INSTALLED_BY_DEFAULT); |
| |
| EXPECT_CALL(visitor, OnExternalExtensionFileFound(_)).Times(0); |
| EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _)).Times(0); |
| EXPECT_CALL(visitor, OnExternalProviderReady(_)) |
| .Times(1); |
| EXPECT_CALL(visitor, OnExternalProviderUpdateComplete(_, _, _, _)).Times(0); |
| |
| // Manually request a load. |
| loader->StartLoading(); |
| Mock::VerifyAndClearExpectations(&visitor); |
| |
| EXPECT_CALL(visitor, OnExternalExtensionFileFound(_)).Times(0); |
| EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _)).Times(2); |
| EXPECT_CALL(visitor, OnExternalProviderReady(_)) |
| .Times(1); |
| EXPECT_CALL(visitor, OnExternalProviderUpdateComplete(_, _, _, _)).Times(0); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(doc->IsReady()); |
| |
| app_list::AppListSyncableService* service = |
| app_list::AppListSyncableServiceFactory::GetForProfile(profile.get()); |
| ASSERT_TRUE(service); |
| EXPECT_EQ("EN OEM Name", service->GetOemFolderNameForTest()); |
| } |
| |
| TEST_F(ServicesCustomizationDocumentTest, CustomizationManifestNotFound) { |
| AddCustomizationIdToVp(kDummyCustomizationID); |
| AddManifestNotFound(kDummyCustomizationID); |
| |
| ServicesCustomizationDocument* doc = |
| ServicesCustomizationDocument::GetInstance(); |
| EXPECT_FALSE(doc->IsReady()); |
| |
| std::unique_ptr<TestingProfile> profile = CreateProfile(); |
| extensions::ExternalLoader* loader = doc->CreateExternalLoader(profile.get()); |
| EXPECT_TRUE(loader); |
| |
| MockExternalProviderVisitor visitor; |
| auto provider = std::make_unique<extensions::ExternalProviderImpl>( |
| &visitor, loader, profile.get(), extensions::Manifest::EXTERNAL_PREF, |
| extensions::Manifest::EXTERNAL_PREF_DOWNLOAD, |
| extensions::Extension::FROM_WEBSTORE | |
| extensions::Extension::WAS_INSTALLED_BY_DEFAULT); |
| |
| EXPECT_CALL(visitor, OnExternalExtensionFileFound(_)).Times(0); |
| EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _)).Times(0); |
| EXPECT_CALL(visitor, OnExternalProviderReady(_)) |
| .Times(1); |
| EXPECT_CALL(visitor, OnExternalProviderUpdateComplete(_, _, _, _)).Times(0); |
| |
| // Manually request a load. |
| loader->StartLoading(); |
| Mock::VerifyAndClearExpectations(&visitor); |
| |
| EXPECT_CALL(visitor, OnExternalExtensionFileFound(_)).Times(0); |
| EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _)).Times(0); |
| EXPECT_CALL(visitor, OnExternalProviderReady(_)) |
| .Times(1); |
| EXPECT_CALL(visitor, OnExternalProviderUpdateComplete(_, _, _, _)).Times(0); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(doc->IsReady()); |
| } |
| |
| } // namespace chromeos |