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