1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/themes/theme_syncable_service.h"
6
7#include "base/command_line.h"
8#include "base/compiler_specific.h"
9#include "base/files/file_path.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/time/time.h"
12#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/browser/extensions/test_extension_system.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/themes/theme_service.h"
16#include "chrome/browser/themes/theme_service_factory.h"
17#include "chrome/common/extensions/extension.h"
18#include "chrome/common/extensions/extension_manifest_constants.h"
19#include "chrome/common/extensions/extension_messages.h"
20#include "chrome/common/extensions/manifest_url_handler.h"
21#include "chrome/common/extensions/permissions/api_permission_set.h"
22#include "chrome/common/extensions/permissions/permission_set.h"
23#include "chrome/test/base/testing_profile.h"
24#include "content/public/test/test_browser_thread.h"
25#include "sync/api/sync_error.h"
26#include "sync/api/sync_error_factory_mock.h"
27#include "sync/protocol/sync.pb.h"
28#include "sync/protocol/theme_specifics.pb.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
31#if defined(OS_CHROMEOS)
32#include "chrome/browser/chromeos/login/user_manager.h"
33#include "chrome/browser/chromeos/settings/cros_settings.h"
34#include "chrome/browser/chromeos/settings/device_settings_service.h"
35#endif
36
37using std::string;
38
39namespace {
40
41static const char kCustomThemeName[] = "name";
42static const char kCustomThemeUrl[] = "http://update.url/foo";
43
44#if defined(OS_WIN)
45const base::FilePath::CharType kExtensionFilePath[] =
46    FILE_PATH_LITERAL("c:\\foo");
47#elif defined(OS_POSIX)
48const base::FilePath::CharType kExtensionFilePath[] = FILE_PATH_LITERAL("/oo");
49#endif
50
51class FakeSyncChangeProcessor : public syncer::SyncChangeProcessor {
52 public:
53  FakeSyncChangeProcessor() : change_output_(NULL) {}
54
55  // syncer::SyncChangeProcessor implementation.
56  virtual syncer::SyncError ProcessSyncChanges(
57        const tracked_objects::Location& from_here,
58        const syncer::SyncChangeList& change_list) OVERRIDE {
59    change_output_->insert(change_output_->end(), change_list.begin(),
60                           change_list.end());
61    return syncer::SyncError();
62  }
63
64  void SetChangeOutput(syncer::SyncChangeList *change_output) {
65    change_output_ = change_output;
66  }
67
68 private:
69  syncer::SyncChangeList *change_output_;
70};
71
72class FakeThemeService : public ThemeService {
73 public:
74  FakeThemeService() :
75    using_native_theme_(false),
76    using_default_theme_(false),
77    theme_extension_(NULL),
78    is_dirty_(false) {}
79
80  // ThemeService implementation
81  virtual void SetTheme(const extensions::Extension* extension) OVERRIDE {
82    is_dirty_ = true;
83    theme_extension_ = extension;
84    using_native_theme_ = false;
85    using_default_theme_ = false;
86  }
87
88  virtual void UseDefaultTheme() OVERRIDE {
89    is_dirty_ = true;
90    using_default_theme_ = true;
91    using_native_theme_ = false;
92    theme_extension_ = NULL;
93  }
94
95  virtual void SetNativeTheme() OVERRIDE {
96    is_dirty_ = true;
97    using_native_theme_ = true;
98    using_default_theme_ = false;
99    theme_extension_ = NULL;
100  }
101
102  virtual bool UsingDefaultTheme() const OVERRIDE {
103    return using_default_theme_;
104  }
105
106  virtual bool UsingNativeTheme() const OVERRIDE {
107    return using_native_theme_;
108  }
109
110  virtual string GetThemeID() const OVERRIDE {
111    if (theme_extension_.get())
112      return theme_extension_->id();
113    else
114      return std::string();
115  }
116
117  const extensions::Extension* theme_extension() const {
118    return theme_extension_.get();
119  }
120
121  bool is_dirty() const {
122    return is_dirty_;
123  }
124
125  void MarkClean() {
126    is_dirty_ = false;
127  }
128
129 private:
130  bool using_native_theme_;
131  bool using_default_theme_;
132  scoped_refptr<const extensions::Extension> theme_extension_;
133  bool is_dirty_;
134};
135
136BrowserContextKeyedService* BuildMockThemeService(
137    content::BrowserContext* profile) {
138  return new FakeThemeService;
139}
140
141scoped_refptr<extensions::Extension> MakeThemeExtension(
142    const base::FilePath& extension_path,
143    const string& name,
144    extensions::Manifest::Location location,
145    const string& update_url) {
146  DictionaryValue source;
147  source.SetString(extension_manifest_keys::kName, name);
148  source.Set(extension_manifest_keys::kTheme, new DictionaryValue());
149  source.SetString(extension_manifest_keys::kUpdateURL, update_url);
150  source.SetString(extension_manifest_keys::kVersion, "0.0.0.0");
151  string error;
152  scoped_refptr<extensions::Extension> extension =
153      extensions::Extension::Create(
154          extension_path, location, source,
155          extensions::Extension::NO_FLAGS, &error);
156  EXPECT_TRUE(extension.get());
157  EXPECT_EQ("", error);
158  return extension;
159}
160
161}  // namespace
162
163class ThemeSyncableServiceTest : public testing::Test {
164 protected:
165  ThemeSyncableServiceTest()
166      : loop_(base::MessageLoop::TYPE_DEFAULT),
167        ui_thread_(content::BrowserThread::UI, &loop_),
168        file_thread_(content::BrowserThread::FILE, &loop_),
169        fake_theme_service_(NULL) {}
170
171  virtual ~ThemeSyncableServiceTest() {}
172
173  virtual void SetUp() {
174    profile_.reset(new TestingProfile);
175    fake_theme_service_ = BuildForProfile(profile_.get());
176    theme_sync_service_.reset(new ThemeSyncableService(profile_.get(),
177                                                       fake_theme_service_));
178    fake_change_processor_.reset(new FakeSyncChangeProcessor);
179    SetUpExtension();
180  }
181
182  virtual void TearDown() {
183    profile_.reset();
184    loop_.RunUntilIdle();
185  }
186
187  void SetUpExtension() {
188    CommandLine command_line(CommandLine::NO_PROGRAM);
189    extensions::TestExtensionSystem* test_ext_system =
190        static_cast<extensions::TestExtensionSystem*>(
191                extensions::ExtensionSystem::Get(profile_.get()));
192    ExtensionService* service = test_ext_system->CreateExtensionService(
193        &command_line, base::FilePath(kExtensionFilePath), false);
194    EXPECT_TRUE(service->extensions_enabled());
195    service->Init();
196    loop_.RunUntilIdle();
197
198    // Create and add custom theme extension so the ThemeSyncableService can
199    // find it.
200    theme_extension_ = MakeThemeExtension(base::FilePath(kExtensionFilePath),
201                                          kCustomThemeName,
202                                          GetThemeLocation(),
203                                          kCustomThemeUrl);
204    extensions::APIPermissionSet empty_set;
205    extensions::URLPatternSet empty_extent;
206    scoped_refptr<extensions::PermissionSet> permissions =
207        new extensions::PermissionSet(empty_set, empty_extent, empty_extent);
208    service->extension_prefs()->AddGrantedPermissions(
209        theme_extension_->id(), permissions.get());
210    service->AddExtension(theme_extension_.get());
211    ASSERT_EQ(1u, service->extensions()->size());
212  }
213
214  // Overridden in PolicyInstalledThemeTest below.
215  virtual extensions::Manifest::Location GetThemeLocation() {
216    return extensions::Manifest::INTERNAL;
217  }
218
219  FakeThemeService* BuildForProfile(Profile* profile) {
220    return static_cast<FakeThemeService*>(
221        ThemeServiceFactory::GetInstance()->SetTestingFactoryAndUse(
222            profile, &BuildMockThemeService));
223  }
224
225  syncer::SyncDataList MakeThemeDataList(
226      const sync_pb::ThemeSpecifics& theme_specifics) {
227    syncer::SyncDataList list;
228    sync_pb::EntitySpecifics entity_specifics;
229    entity_specifics.mutable_theme()->CopyFrom(theme_specifics);
230    list.push_back(syncer::SyncData::CreateLocalData(
231        ThemeSyncableService::kCurrentThemeClientTag,
232        ThemeSyncableService::kCurrentThemeNodeTitle,
233        entity_specifics));
234    return list;
235  }
236
237  // Needed for setting up extension service.
238  base::MessageLoop loop_;
239  content::TestBrowserThread ui_thread_;
240  content::TestBrowserThread file_thread_;
241
242#if defined OS_CHROMEOS
243  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
244  chromeos::ScopedTestCrosSettings test_cros_settings_;
245  chromeos::ScopedTestUserManager test_user_manager_;
246#endif
247
248  scoped_ptr<TestingProfile> profile_;
249  FakeThemeService* fake_theme_service_;
250  scoped_refptr<extensions::Extension> theme_extension_;
251  scoped_ptr<ThemeSyncableService> theme_sync_service_;
252  scoped_ptr<syncer::SyncChangeProcessor> fake_change_processor_;
253};
254
255class PolicyInstalledThemeTest : public ThemeSyncableServiceTest {
256  virtual extensions::Manifest::Location GetThemeLocation() OVERRIDE {
257    return extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD;
258  }
259};
260
261TEST_F(ThemeSyncableServiceTest, AreThemeSpecificsEqual) {
262  sync_pb::ThemeSpecifics a, b;
263  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
264  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
265
266  // Custom vs. non-custom.
267
268  a.set_use_custom_theme(true);
269  EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
270  EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
271
272  // Custom theme equality.
273
274  b.set_use_custom_theme(true);
275  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
276  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
277
278  a.set_custom_theme_id("id");
279  EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
280  EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
281
282  b.set_custom_theme_id("id");
283  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
284  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
285
286  a.set_custom_theme_update_url("http://update.url");
287  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
288  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
289
290  a.set_custom_theme_name("name");
291  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
292  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
293
294  // Non-custom theme equality.
295
296  a.set_use_custom_theme(false);
297  b.set_use_custom_theme(false);
298  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
299  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
300
301  a.set_use_system_theme_by_default(true);
302  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
303  EXPECT_FALSE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
304
305  b.set_use_system_theme_by_default(true);
306  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, false));
307  EXPECT_TRUE(ThemeSyncableService::AreThemeSpecificsEqual(a, b, true));
308}
309
310TEST_F(ThemeSyncableServiceTest, SetCurrentThemeDefaultTheme) {
311  // Set up theme service to use custom theme.
312  fake_theme_service_->SetTheme(theme_extension_.get());
313
314  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
315      syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
316      fake_change_processor_.Pass(),
317      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
318          error();
319  EXPECT_FALSE(error.IsSet()) << error.message();
320  EXPECT_TRUE(fake_theme_service_->UsingDefaultTheme());
321}
322
323TEST_F(ThemeSyncableServiceTest, SetCurrentThemeSystemTheme) {
324  sync_pb::ThemeSpecifics theme_specifics;
325  theme_specifics.set_use_system_theme_by_default(true);
326
327  // Set up theme service to use custom theme.
328  fake_theme_service_->SetTheme(theme_extension_.get());
329  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
330      syncer::THEMES, MakeThemeDataList(theme_specifics),
331      fake_change_processor_.Pass(),
332      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
333          error();
334  EXPECT_FALSE(error.IsSet()) << error.message();
335  EXPECT_TRUE(fake_theme_service_->UsingNativeTheme());
336}
337
338TEST_F(ThemeSyncableServiceTest, SetCurrentThemeCustomTheme) {
339  sync_pb::ThemeSpecifics theme_specifics;
340  theme_specifics.set_use_custom_theme(true);
341  theme_specifics.set_custom_theme_id(theme_extension_->id());
342  theme_specifics.set_custom_theme_name(kCustomThemeName);
343  theme_specifics.set_custom_theme_name(kCustomThemeUrl);
344
345  // Set up theme service to use default theme.
346  fake_theme_service_->UseDefaultTheme();
347  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
348      syncer::THEMES, MakeThemeDataList(theme_specifics),
349      fake_change_processor_.Pass(),
350      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
351          error();
352  EXPECT_FALSE(error.IsSet()) << error.message();
353  EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
354}
355
356TEST_F(ThemeSyncableServiceTest, DontResetThemeWhenSpecificsAreEqual) {
357  // Set up theme service to use default theme and expect no changes.
358  fake_theme_service_->UseDefaultTheme();
359  fake_theme_service_->MarkClean();
360  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
361      syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
362      fake_change_processor_.Pass(),
363      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
364          error();
365  EXPECT_FALSE(error.IsSet()) << error.message();
366  EXPECT_FALSE(fake_theme_service_->is_dirty());
367}
368
369TEST_F(ThemeSyncableServiceTest, UpdateThemeSpecificsFromCurrentTheme) {
370  // Set up theme service to use custom theme.
371  fake_theme_service_->SetTheme(theme_extension_.get());
372
373  syncer::SyncChangeList change_list;
374  static_cast<FakeSyncChangeProcessor*>(fake_change_processor_.get())->
375      SetChangeOutput(&change_list);
376
377  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
378      syncer::THEMES, syncer::SyncDataList(), fake_change_processor_.Pass(),
379      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
380          error();
381  EXPECT_FALSE(error.IsSet()) << error.message();
382
383  ASSERT_EQ(1u, change_list.size());
384  EXPECT_TRUE(change_list[0].IsValid());
385  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change_list[0].change_type());
386  EXPECT_EQ(syncer::THEMES, change_list[0].sync_data().GetDataType());
387
388  const sync_pb::ThemeSpecifics& theme_specifics =
389      change_list[0].sync_data().GetSpecifics().theme();
390  EXPECT_TRUE(theme_specifics.use_custom_theme());
391  EXPECT_EQ(theme_extension_->id(), theme_specifics.custom_theme_id());
392  EXPECT_EQ(theme_extension_->name(), theme_specifics.custom_theme_name());
393  EXPECT_EQ(
394      extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
395      theme_specifics.custom_theme_update_url());
396}
397
398TEST_F(ThemeSyncableServiceTest, GetAllSyncData) {
399  // Set up theme service to use custom theme.
400  fake_theme_service_->SetTheme(theme_extension_.get());
401
402  syncer::SyncDataList data_list =
403      theme_sync_service_->GetAllSyncData(syncer::THEMES);
404
405  ASSERT_EQ(1u, data_list.size());
406  const sync_pb::ThemeSpecifics& theme_specifics =
407      data_list[0].GetSpecifics().theme();
408  EXPECT_TRUE(theme_specifics.use_custom_theme());
409  EXPECT_EQ(theme_extension_->id(), theme_specifics.custom_theme_id());
410  EXPECT_EQ(theme_extension_->name(), theme_specifics.custom_theme_name());
411  EXPECT_EQ(
412      extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
413      theme_specifics.custom_theme_update_url());
414}
415
416TEST_F(ThemeSyncableServiceTest, ProcessSyncThemeChange) {
417  // Set up theme service to use default theme.
418  fake_theme_service_->UseDefaultTheme();
419  fake_theme_service_->MarkClean();
420
421  // Start syncing.
422  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
423      syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
424      fake_change_processor_.Pass(),
425      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
426          error();
427  EXPECT_FALSE(error.IsSet()) << error.message();
428  // Don't expect theme change initially because specifics are equal.
429  EXPECT_FALSE(fake_theme_service_->is_dirty());
430
431  // Change specifics to use custom theme and update.
432  sync_pb::ThemeSpecifics theme_specifics;
433  theme_specifics.set_use_custom_theme(true);
434  theme_specifics.set_custom_theme_id(theme_extension_->id());
435  theme_specifics.set_custom_theme_name(kCustomThemeName);
436  theme_specifics.set_custom_theme_name(kCustomThemeUrl);
437  sync_pb::EntitySpecifics entity_specifics;
438  entity_specifics.mutable_theme()->CopyFrom(theme_specifics);
439  syncer::SyncChangeList change_list;
440  change_list.push_back(syncer::SyncChange(
441      FROM_HERE,
442      syncer::SyncChange::ACTION_UPDATE,
443      syncer::SyncData::CreateRemoteData(
444          1, entity_specifics, base::Time())));
445  error = theme_sync_service_->ProcessSyncChanges(FROM_HERE, change_list);
446  EXPECT_FALSE(error.IsSet()) << error.message();
447  EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
448}
449
450TEST_F(ThemeSyncableServiceTest, OnThemeChangeByUser) {
451  syncer::SyncChangeList change_list;
452  static_cast<FakeSyncChangeProcessor*>(fake_change_processor_.get())->
453      SetChangeOutput(&change_list);
454
455  // Set up theme service to use default theme.
456  fake_theme_service_->UseDefaultTheme();
457
458  // Start syncing.
459  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
460      syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
461      fake_change_processor_.Pass(),
462      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
463          error();
464  EXPECT_FALSE(error.IsSet()) << error.message();
465  EXPECT_EQ(0u, change_list.size());
466
467  // Change current theme to custom theme and notify theme_sync_service_.
468  fake_theme_service_->SetTheme(theme_extension_.get());
469  theme_sync_service_->OnThemeChange();
470  EXPECT_EQ(1u, change_list.size());
471  const sync_pb::ThemeSpecifics& change_specifics =
472      change_list[0].sync_data().GetSpecifics().theme();
473  EXPECT_TRUE(change_specifics.use_custom_theme());
474  EXPECT_EQ(theme_extension_->id(), change_specifics.custom_theme_id());
475  EXPECT_EQ(theme_extension_->name(), change_specifics.custom_theme_name());
476  EXPECT_EQ(
477      extensions::ManifestURL::GetUpdateURL(theme_extension_.get()).spec(),
478      change_specifics.custom_theme_update_url());
479}
480
481TEST_F(ThemeSyncableServiceTest, StopSync) {
482  syncer::SyncChangeList change_list;
483  static_cast<FakeSyncChangeProcessor*>(fake_change_processor_.get())->
484      SetChangeOutput(&change_list);
485
486  // Set up theme service to use default theme.
487  fake_theme_service_->UseDefaultTheme();
488
489  // Start syncing.
490  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
491      syncer::THEMES, MakeThemeDataList(sync_pb::ThemeSpecifics()),
492      fake_change_processor_.Pass(),
493      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
494          error();
495  EXPECT_FALSE(error.IsSet()) << error.message();
496  EXPECT_EQ(0u, change_list.size());
497
498  // Stop syncing.
499  theme_sync_service_->StopSyncing(syncer::THEMES);
500
501  // Change current theme to custom theme and notify theme_sync_service_.
502  // No change is output because sync has stopped.
503  fake_theme_service_->SetTheme(theme_extension_.get());
504  theme_sync_service_->OnThemeChange();
505  EXPECT_EQ(0u, change_list.size());
506
507  // ProcessSyncChanges() should return error when sync has stopped.
508  error = theme_sync_service_->ProcessSyncChanges(FROM_HERE, change_list);
509  EXPECT_TRUE(error.IsSet());
510  EXPECT_EQ(syncer::THEMES, error.model_type());
511  EXPECT_EQ("datatype error was encountered: Theme syncable service is not "
512                "started.",
513            error.message());
514}
515
516TEST_F(ThemeSyncableServiceTest, RestoreSystemThemeBitWhenChangeToCustomTheme) {
517  syncer::SyncChangeList change_list;
518  static_cast<FakeSyncChangeProcessor*>(fake_change_processor_.get())->
519      SetChangeOutput(&change_list);
520
521  // Initialize to use system theme.
522  fake_theme_service_->UseDefaultTheme();
523  sync_pb::ThemeSpecifics theme_specifics;
524  theme_specifics.set_use_system_theme_by_default(true);
525  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
526      syncer::THEMES, MakeThemeDataList(theme_specifics),
527      fake_change_processor_.Pass(),
528      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
529          error();
530
531  // Change to custom theme and notify theme_sync_service_.
532  // use_system_theme_by_default bit should be preserved.
533  fake_theme_service_->SetTheme(theme_extension_.get());
534  theme_sync_service_->OnThemeChange();
535  EXPECT_EQ(1u, change_list.size());
536  const sync_pb::ThemeSpecifics& change_specifics =
537      change_list[0].sync_data().GetSpecifics().theme();
538  EXPECT_TRUE(change_specifics.use_system_theme_by_default());
539}
540
541#if defined(TOOLKIT_GTK)
542TEST_F(ThemeSyncableServiceTest,
543       GtkUpdateSystemThemeBitWhenChangeBetweenSystemAndDefault) {
544  syncer::SyncChangeList change_list;
545  static_cast<FakeSyncChangeProcessor*>(fake_change_processor_.get())->
546      SetChangeOutput(&change_list);
547
548  // Initialize to use native theme.
549  fake_theme_service_->SetNativeTheme();
550  fake_theme_service_->MarkClean();
551  sync_pb::ThemeSpecifics theme_specifics;
552  theme_specifics.set_use_system_theme_by_default(true);
553  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
554      syncer::THEMES, MakeThemeDataList(theme_specifics),
555      fake_change_processor_.Pass(),
556      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
557          error();
558  EXPECT_FALSE(fake_theme_service_->is_dirty());
559
560  // Change to default theme and notify theme_sync_service_.
561  // use_system_theme_by_default bit should be false.
562  fake_theme_service_->UseDefaultTheme();
563  theme_sync_service_->OnThemeChange();
564  EXPECT_EQ(1u, change_list.size());
565  EXPECT_FALSE(change_list[0].sync_data().GetSpecifics().theme()
566               .use_system_theme_by_default());
567
568  // Change to native theme and notify theme_sync_service_.
569  // use_system_theme_by_default bit should be true.
570  change_list.clear();
571  fake_theme_service_->SetNativeTheme();
572  theme_sync_service_->OnThemeChange();
573  EXPECT_EQ(1u, change_list.size());
574  EXPECT_TRUE(change_list[0].sync_data().GetSpecifics().theme()
575              .use_system_theme_by_default());
576}
577#endif
578
579#ifndef TOOLKIT_GTK
580TEST_F(ThemeSyncableServiceTest,
581       NonGtkPreserveSystemThemeBitWhenChangeToDefaultTheme) {
582  syncer::SyncChangeList change_list;
583  static_cast<FakeSyncChangeProcessor*>(fake_change_processor_.get())->
584      SetChangeOutput(&change_list);
585
586  // Set up theme service to use default theme.
587  fake_theme_service_->UseDefaultTheme();
588
589  // Initialize to use custom theme with use_system_theme_by_default set true.
590  sync_pb::ThemeSpecifics theme_specifics;
591  theme_specifics.set_use_custom_theme(true);
592  theme_specifics.set_custom_theme_id(theme_extension_->id());
593  theme_specifics.set_custom_theme_name(kCustomThemeName);
594  theme_specifics.set_custom_theme_name(kCustomThemeUrl);
595  theme_specifics.set_use_system_theme_by_default(true);
596  syncer::SyncError error = theme_sync_service_->MergeDataAndStartSyncing(
597      syncer::THEMES, MakeThemeDataList(theme_specifics),
598      fake_change_processor_.Pass(),
599      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())).
600          error();
601  EXPECT_EQ(fake_theme_service_->theme_extension(), theme_extension_.get());
602
603  // Change to default theme and notify theme_sync_service_.
604  // use_system_theme_by_default bit should be preserved.
605  fake_theme_service_->UseDefaultTheme();
606  theme_sync_service_->OnThemeChange();
607  EXPECT_EQ(1u, change_list.size());
608  const sync_pb::ThemeSpecifics& change_specifics =
609      change_list[0].sync_data().GetSpecifics().theme();
610  EXPECT_FALSE(change_specifics.use_custom_theme());
611  EXPECT_TRUE(change_specifics.use_system_theme_by_default());
612}
613#endif
614
615TEST_F(PolicyInstalledThemeTest, InstallThemeByPolicy) {
616  // Set up theme service to use custom theme that was installed by policy.
617  fake_theme_service_->SetTheme(theme_extension_.get());
618
619  syncer::SyncDataList data_list =
620      theme_sync_service_->GetAllSyncData(syncer::THEMES);
621
622  ASSERT_EQ(0u, data_list.size());
623}
624