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