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/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 
     37 using std::string;
     38 
     39 namespace {
     40 
     41 static const char kCustomThemeName[] = "name";
     42 static const char kCustomThemeUrl[] = "http://update.url/foo";
     43 
     44 #if defined(OS_WIN)
     45 const base::FilePath::CharType kExtensionFilePath[] =
     46     FILE_PATH_LITERAL("c:\\foo");
     47 #elif defined(OS_POSIX)
     48 const base::FilePath::CharType kExtensionFilePath[] = FILE_PATH_LITERAL("/oo");
     49 #endif
     50 
     51 class 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 
     72 class 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 
    136 BrowserContextKeyedService* BuildMockThemeService(
    137     content::BrowserContext* profile) {
    138   return new FakeThemeService;
    139 }
    140 
    141 scoped_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 
    163 class 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 
    255 class PolicyInstalledThemeTest : public ThemeSyncableServiceTest {
    256   virtual extensions::Manifest::Location GetThemeLocation() OVERRIDE {
    257     return extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD;
    258   }
    259 };
    260 
    261 TEST_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 
    310 TEST_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 
    323 TEST_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 
    338 TEST_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 
    356 TEST_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 
    369 TEST_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 
    398 TEST_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 
    416 TEST_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 
    450 TEST_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 
    481 TEST_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 
    516 TEST_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)
    542 TEST_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
    580 TEST_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 
    615 TEST_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