1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/customization_document.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/prefs/testing_pref_service.h"
9#include "base/run_loop.h"
10#include "base/strings/stringprintf.h"
11#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h"
12#include "chrome/browser/extensions/external_provider_impl.h"
13#include "chrome/browser/prefs/browser_prefs.h"
14#include "chrome/browser/prefs/pref_service_mock_factory.h"
15#include "chrome/browser/prefs/pref_service_syncable.h"
16#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
17#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
18#include "chrome/test/base/testing_browser_process.h"
19#include "chrome/test/base/testing_profile.h"
20#include "chromeos/dbus/dbus_thread_manager.h"
21#include "chromeos/network/network_handler.h"
22#include "chromeos/network/network_state.h"
23#include "chromeos/network/network_state_handler.h"
24#include "chromeos/system/mock_statistics_provider.h"
25#include "components/pref_registry/pref_registry_syncable.h"
26#include "content/public/test/test_browser_thread_bundle.h"
27#include "extensions/common/extension.h"
28#include "extensions/common/manifest.h"
29#include "net/http/http_response_headers.h"
30#include "net/http/http_status_code.h"
31#include "net/url_request/test_url_fetcher_factory.h"
32#include "net/url_request/url_request_status.h"
33#include "testing/gmock/include/gmock/gmock.h"
34#include "testing/gtest/include/gtest/gtest.h"
35
36using ::testing::Exactly;
37using ::testing::Invoke;
38using ::testing::Mock;
39using ::testing::_;
40
41namespace {
42
43const char kGoodStartupManifest[] =
44    "{"
45    "  \"version\": \"1.0\","
46    "  \"initial_locale\" : \"en-US\","
47    "  \"initial_timezone\" : \"US/Pacific\","
48    "  \"keyboard_layout\" : \"xkb:us::eng\","
49    "  \"setup_content\" : {"
50    "    \"en-US\" : {"
51    "      \"eula_page\" : \"file:///opt/oem/eula/en-US/eula.html\","
52    "    },"
53    "    \"ru-RU\" : {"
54    "      \"eula_page\" : \"file:///opt/oem/eula/ru-RU/eula.html\","
55    "    },"
56    "    \"default\" : {"
57    "      \"eula_page\" : \"file:///opt/oem/eula/en/eula.html\","
58    "    },"
59    "  },"
60    "  \"hwid_map\" : ["
61    "    {"
62    "      \"hwid_mask\": \"ZGA*34\","
63    "      \"initial_locale\" : \"ja\","
64    "      \"initial_timezone\" : \"Asia/Tokyo\","
65    "      \"keyboard_layout\" : \"mozc-jp\","
66    "    },"
67    "    {"
68    "      \"hwid_mask\": \"Mario 1?3*\","
69    "      \"initial_locale\" : \"ru-RU\","
70    "      \"initial_timezone\" : \"Europe/Moscow\","
71    "      \"keyboard_layout\" : \"xkb:ru::rus\","
72    "    },"
73    "  ],"
74    "}";
75
76const char kBadManifest[] = "{\"version\": \"1\"}";
77
78const char kGoodServicesManifest[] =
79    "{"
80    "  \"version\": \"1.0\","
81    "  \"default_apps\": [\n"
82    "    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n"
83    "    \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n"
84    "  ],\n"
85    "  \"localized_content\": {\n"
86    "    \"en-US\": {\n"
87    "      \"default_apps_folder_name\": \"EN-US OEM Name\"\n"
88    "    },\n"
89    "    \"en\": {\n"
90    "      \"default_apps_folder_name\": \"EN OEM Name\"\n"
91    "    },\n"
92    "    \"default\": {\n"
93    "      \"default_apps_folder_name\": \"Default OEM Name\"\n"
94    "    }\n"
95    "  }\n"
96    "}";
97
98const char kDummyCustomizationID[] = "test-dummy";
99
100}  // anonymous namespace
101
102namespace chromeos {
103
104using ::testing::_;
105using ::testing::DoAll;
106using ::testing::NotNull;
107using ::testing::Return;
108using ::testing::SetArgumentPointee;
109
110TEST(StartupCustomizationDocumentTest, Basic) {
111  system::MockStatisticsProvider mock_statistics_provider;
112  EXPECT_CALL(mock_statistics_provider, GetMachineStatistic(_, NotNull()))
113      .WillRepeatedly(Return(false));
114  EXPECT_CALL(mock_statistics_provider,
115      GetMachineStatistic(std::string("hardware_class"), NotNull()))
116          .WillOnce(DoAll(SetArgumentPointee<1>(std::string("Mario 12345")),
117                          Return(true)));
118  StartupCustomizationDocument customization(&mock_statistics_provider,
119                                             kGoodStartupManifest);
120  EXPECT_EQ("ru-RU", customization.initial_locale());
121  EXPECT_EQ("Europe/Moscow", customization.initial_timezone());
122  EXPECT_EQ("xkb:ru::rus", customization.keyboard_layout());
123
124  EXPECT_EQ("file:///opt/oem/eula/en-US/eula.html",
125            customization.GetEULAPage("en-US"));
126  EXPECT_EQ("file:///opt/oem/eula/ru-RU/eula.html",
127            customization.GetEULAPage("ru-RU"));
128  EXPECT_EQ("file:///opt/oem/eula/en/eula.html",
129            customization.GetEULAPage("ja"));
130}
131
132TEST(StartupCustomizationDocumentTest, VPD) {
133  system::MockStatisticsProvider mock_statistics_provider;
134  EXPECT_CALL(mock_statistics_provider,
135      GetMachineStatistic(std::string("hardware_class"), NotNull()))
136          .WillOnce(DoAll(SetArgumentPointee<1>(std::string("Mario 12345")),
137                          Return(true)));
138  EXPECT_CALL(mock_statistics_provider,
139      GetMachineStatistic(std::string("initial_locale"), NotNull()))
140          .WillOnce(DoAll(SetArgumentPointee<1>(std::string("ja")),
141                          Return(true)));
142  EXPECT_CALL(mock_statistics_provider,
143      GetMachineStatistic(std::string("initial_timezone"), NotNull()))
144          .WillOnce(DoAll(SetArgumentPointee<1>(std::string("Asia/Tokyo")),
145                          Return(true)));
146  EXPECT_CALL(mock_statistics_provider,
147      GetMachineStatistic(std::string("keyboard_layout"), NotNull()))
148          .WillOnce(DoAll(SetArgumentPointee<1>(std::string("mozc-jp")),
149                          Return(true)));
150  StartupCustomizationDocument customization(&mock_statistics_provider,
151                                             kGoodStartupManifest);
152  EXPECT_TRUE(customization.IsReady());
153  EXPECT_EQ("ja", customization.initial_locale());
154  EXPECT_EQ("Asia/Tokyo", customization.initial_timezone());
155  EXPECT_EQ("mozc-jp", customization.keyboard_layout());
156}
157
158TEST(StartupCustomizationDocumentTest, BadManifest) {
159  system::MockStatisticsProvider mock_statistics_provider;
160  StartupCustomizationDocument customization(&mock_statistics_provider,
161                                             kBadManifest);
162  EXPECT_FALSE(customization.IsReady());
163}
164
165class TestURLFetcherCallback {
166 public:
167  scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
168      const GURL& url,
169      net::URLFetcherDelegate* d,
170      const std::string& response_data,
171      net::HttpStatusCode response_code,
172      net::URLRequestStatus::Status status) {
173    scoped_ptr<net::FakeURLFetcher> fetcher(
174        new net::FakeURLFetcher(url, d, response_data, response_code, status));
175    OnRequestCreate(url, fetcher.get());
176    return fetcher.Pass();
177  }
178  MOCK_METHOD2(OnRequestCreate,
179               void(const GURL&, net::FakeURLFetcher*));
180};
181
182void AddMimeHeader(const GURL& url, net::FakeURLFetcher* fetcher) {
183  scoped_refptr<net::HttpResponseHeaders> download_headers =
184      new net::HttpResponseHeaders("");
185  download_headers->AddHeader("Content-Type: application/json");
186  fetcher->set_response_headers(download_headers);
187}
188
189class MockExternalProviderVisitor
190    : public extensions::ExternalProviderInterface::VisitorInterface {
191 public:
192  MockExternalProviderVisitor() {}
193
194  MOCK_METHOD6(OnExternalExtensionFileFound,
195               bool(const std::string&,
196                    const base::Version*,
197                    const base::FilePath&,
198                    extensions::Manifest::Location,
199                    int,
200                    bool));
201  MOCK_METHOD6(OnExternalExtensionUpdateUrlFound,
202               bool(const std::string&,
203                    const std::string&,
204                    const GURL&,
205                    extensions::Manifest::Location,
206                    int,
207                    bool));
208  MOCK_METHOD1(OnExternalProviderReady,
209               void(const extensions::ExternalProviderInterface* provider));
210};
211
212class ServicesCustomizationDocumentTest : public testing::Test {
213 protected:
214  ServicesCustomizationDocumentTest()
215    : factory_(NULL,
216               base::Bind(&TestURLFetcherCallback::CreateURLFetcher,
217               base::Unretained(&url_callback_))) {
218  }
219
220  // testing::Test:
221  virtual void SetUp() OVERRIDE {
222    ServicesCustomizationDocument::InitializeForTesting();
223
224    EXPECT_CALL(mock_statistics_provider_, GetMachineStatistic(_, NotNull()))
225        .WillRepeatedly(Return(false));
226    chromeos::system::StatisticsProvider::SetTestProvider(
227        &mock_statistics_provider_);
228
229    DBusThreadManager::Initialize();
230    NetworkHandler::Initialize();
231    RunUntilIdle();
232    const NetworkState* default_network =
233        NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
234    std::string default_network_path =
235        default_network ? default_network->path() : "";
236
237    NetworkPortalDetector::InitializeForTesting(&network_portal_detector_);
238    NetworkPortalDetector::CaptivePortalState online_state;
239    online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE;
240    online_state.response_code = 204;
241    std::string guid =
242        default_network ? default_network->guid() : std::string();
243    network_portal_detector_.SetDefaultNetworkForTesting(guid);
244    if (!guid.empty()) {
245      network_portal_detector_.SetDetectionResultsForTesting(
246          guid, online_state);
247    }
248
249    TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
250    ServicesCustomizationDocument::RegisterPrefs(local_state_.registry());
251  }
252
253  virtual void TearDown() OVERRIDE {
254    TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
255    NetworkHandler::Shutdown();
256    DBusThreadManager::Shutdown();
257    NetworkPortalDetector::InitializeForTesting(NULL);
258    chromeos::system::StatisticsProvider::SetTestProvider(NULL);
259
260    ServicesCustomizationDocument::ShutdownForTesting();
261  }
262
263  void RunUntilIdle() {
264    base::RunLoop().RunUntilIdle();
265  }
266
267  void AddCustomizationIdToVp(const std::string& id) {
268    EXPECT_CALL(mock_statistics_provider_,
269        GetMachineStatistic(system::kCustomizationIdKey, NotNull()))
270            .WillOnce(DoAll(SetArgumentPointee<1>(id),
271                            Return(true)));
272  }
273
274  void AddExpectedManifest(const std::string& id,
275                           const std::string& manifest) {
276    GURL url(base::StringPrintf(ServicesCustomizationDocument::kManifestUrl,
277                                id.c_str()));
278    factory_.SetFakeResponse(url,
279                             manifest,
280                             net::HTTP_OK,
281                             net::URLRequestStatus::SUCCESS);
282    EXPECT_CALL(url_callback_, OnRequestCreate(url, _))
283      .Times(Exactly(1))
284      .WillRepeatedly(Invoke(AddMimeHeader));
285  }
286
287  void AddManifestNotFound(const std::string& id) {
288    GURL url(base::StringPrintf(ServicesCustomizationDocument::kManifestUrl,
289                                id.c_str()));
290    factory_.SetFakeResponse(url,
291                             std::string(),
292                             net::HTTP_NOT_FOUND,
293                             net::URLRequestStatus::SUCCESS);
294    EXPECT_CALL(url_callback_, OnRequestCreate(url, _))
295      .Times(Exactly(1))
296      .WillRepeatedly(Invoke(AddMimeHeader));
297  }
298
299  scoped_ptr<TestingProfile> CreateProfile() {
300    TestingProfile::Builder profile_builder;
301    PrefServiceMockFactory factory;
302    scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
303        new user_prefs::PrefRegistrySyncable);
304    scoped_ptr<PrefServiceSyncable> prefs(
305        factory.CreateSyncable(registry.get()));
306    chrome::RegisterUserProfilePrefs(registry.get());
307    profile_builder.SetPrefService(prefs.Pass());
308    return profile_builder.Build();
309  }
310
311 private:
312  system::MockStatisticsProvider mock_statistics_provider_;
313  content::TestBrowserThreadBundle thread_bundle_;
314  TestingPrefServiceSimple local_state_;
315  TestURLFetcherCallback url_callback_;
316  net::FakeURLFetcherFactory factory_;
317  NetworkPortalDetectorTestImpl network_portal_detector_;
318};
319
320TEST_F(ServicesCustomizationDocumentTest, Basic) {
321  AddCustomizationIdToVp(kDummyCustomizationID);
322  AddExpectedManifest(kDummyCustomizationID, kGoodServicesManifest);
323
324  ServicesCustomizationDocument* doc =
325      ServicesCustomizationDocument::GetInstance();
326  EXPECT_FALSE(doc->IsReady());
327
328  doc->StartFetching();
329  RunUntilIdle();
330  EXPECT_TRUE(doc->IsReady());
331
332  GURL wallpaper_url;
333  EXPECT_FALSE(doc->GetDefaultWallpaperUrl(&wallpaper_url));
334  EXPECT_EQ("", wallpaper_url.spec());
335
336  std::vector<std::string> default_apps;
337  EXPECT_TRUE(doc->GetDefaultApps(&default_apps));
338  ASSERT_EQ(default_apps.size(), 2u);
339
340  EXPECT_EQ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", default_apps[0]);
341  EXPECT_EQ("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", default_apps[1]);
342
343  EXPECT_EQ("EN-US OEM Name", doc->GetOemAppsFolderName("en-US"));
344  EXPECT_EQ("EN OEM Name", doc->GetOemAppsFolderName("en"));
345  EXPECT_EQ("Default OEM Name", doc->GetOemAppsFolderName("ru"));
346}
347
348TEST_F(ServicesCustomizationDocumentTest, NoCustomizationIdInVpd) {
349  ServicesCustomizationDocument* doc =
350      ServicesCustomizationDocument::GetInstance();
351  EXPECT_FALSE(doc->IsReady());
352
353  scoped_ptr<TestingProfile> profile = CreateProfile();
354  extensions::ExternalLoader* loader = doc->CreateExternalLoader(profile.get());
355  EXPECT_TRUE(loader);
356
357  MockExternalProviderVisitor visitor;
358  scoped_ptr<extensions::ExternalProviderImpl> provider(
359      new extensions::ExternalProviderImpl(
360          &visitor,
361          loader,
362          profile.get(),
363          extensions::Manifest::EXTERNAL_PREF,
364          extensions::Manifest::EXTERNAL_PREF_DOWNLOAD,
365          extensions::Extension::FROM_WEBSTORE |
366              extensions::Extension::WAS_INSTALLED_BY_DEFAULT));
367
368  EXPECT_CALL(visitor, OnExternalExtensionFileFound(_, _, _, _, _, _))
369      .Times(0);
370  EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _, _, _, _, _))
371      .Times(0);
372  EXPECT_CALL(visitor, OnExternalProviderReady(_))
373      .Times(1);
374
375  // Manually request a load.
376  RunUntilIdle();
377  loader->StartLoading();
378  Mock::VerifyAndClearExpectations(&visitor);
379
380  RunUntilIdle();
381  // Empty customization is used when there is no customization ID in VPD.
382  EXPECT_TRUE(doc->IsReady());
383}
384
385TEST_F(ServicesCustomizationDocumentTest, DefaultApps) {
386  AddCustomizationIdToVp(kDummyCustomizationID);
387  AddExpectedManifest(kDummyCustomizationID, kGoodServicesManifest);
388
389  ServicesCustomizationDocument* doc =
390      ServicesCustomizationDocument::GetInstance();
391  EXPECT_FALSE(doc->IsReady());
392
393  scoped_ptr<TestingProfile> profile = CreateProfile();
394  extensions::ExternalLoader* loader = doc->CreateExternalLoader(profile.get());
395  EXPECT_TRUE(loader);
396
397  app_list::AppListSyncableServiceFactory::GetInstance()->
398      SetTestingFactoryAndUse(
399          profile.get(),
400          &app_list::AppListSyncableServiceFactory::BuildInstanceFor);
401
402  MockExternalProviderVisitor visitor;
403  scoped_ptr<extensions::ExternalProviderImpl> provider(
404      new extensions::ExternalProviderImpl(
405          &visitor,
406          loader,
407          profile.get(),
408          extensions::Manifest::EXTERNAL_PREF,
409          extensions::Manifest::EXTERNAL_PREF_DOWNLOAD,
410          extensions::Extension::FROM_WEBSTORE |
411              extensions::Extension::WAS_INSTALLED_BY_DEFAULT));
412
413  EXPECT_CALL(visitor, OnExternalExtensionFileFound(_, _, _, _, _, _))
414      .Times(0);
415  EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _, _, _, _, _))
416      .Times(0);
417  EXPECT_CALL(visitor, OnExternalProviderReady(_))
418      .Times(1);
419
420  // Manually request a load.
421  loader->StartLoading();
422  Mock::VerifyAndClearExpectations(&visitor);
423
424  EXPECT_CALL(visitor, OnExternalExtensionFileFound(_, _, _, _, _, _))
425      .Times(0);
426  EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _, _, _, _, _))
427      .Times(2);
428  EXPECT_CALL(visitor, OnExternalProviderReady(_))
429      .Times(1);
430
431  RunUntilIdle();
432  EXPECT_TRUE(doc->IsReady());
433
434  app_list::AppListSyncableService* service =
435      app_list::AppListSyncableServiceFactory::GetForProfile(profile.get());
436  ASSERT_TRUE(service);
437  EXPECT_EQ("EN OEM Name", service->GetOemFolderNameForTest());
438}
439
440TEST_F(ServicesCustomizationDocumentTest, CustomizationManifestNotFound) {
441  AddCustomizationIdToVp(kDummyCustomizationID);
442  AddManifestNotFound(kDummyCustomizationID);
443
444  ServicesCustomizationDocument* doc =
445      ServicesCustomizationDocument::GetInstance();
446  EXPECT_FALSE(doc->IsReady());
447
448  scoped_ptr<TestingProfile> profile = CreateProfile();
449  extensions::ExternalLoader* loader = doc->CreateExternalLoader(profile.get());
450  EXPECT_TRUE(loader);
451
452  MockExternalProviderVisitor visitor;
453  scoped_ptr<extensions::ExternalProviderImpl> provider(
454      new extensions::ExternalProviderImpl(
455          &visitor,
456          loader,
457          profile.get(),
458          extensions::Manifest::EXTERNAL_PREF,
459          extensions::Manifest::EXTERNAL_PREF_DOWNLOAD,
460          extensions::Extension::FROM_WEBSTORE |
461              extensions::Extension::WAS_INSTALLED_BY_DEFAULT));
462
463  EXPECT_CALL(visitor, OnExternalExtensionFileFound(_, _, _, _, _, _))
464      .Times(0);
465  EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _, _, _, _, _))
466      .Times(0);
467  EXPECT_CALL(visitor, OnExternalProviderReady(_))
468      .Times(1);
469
470  // Manually request a load.
471  loader->StartLoading();
472  Mock::VerifyAndClearExpectations(&visitor);
473
474  EXPECT_CALL(visitor, OnExternalExtensionFileFound(_, _, _, _, _, _))
475      .Times(0);
476  EXPECT_CALL(visitor, OnExternalExtensionUpdateUrlFound(_, _, _, _, _, _))
477      .Times(0);
478  EXPECT_CALL(visitor, OnExternalProviderReady(_))
479      .Times(1);
480
481  RunUntilIdle();
482  EXPECT_TRUE(doc->IsReady());
483}
484
485}  // namespace chromeos
486