| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/json/json_writer.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/settings/settings_frontend.h" |
| #include "chrome/browser/extensions/settings/settings_namespace.h" |
| #include "chrome/browser/extensions/settings/settings_sync_util.h" |
| #include "chrome/browser/extensions/extension_test_message_listener.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sync/api/sync_change.h" |
| #include "chrome/browser/sync/api/sync_change_processor.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| |
| namespace extensions { |
| |
| using settings_namespace::FromString; |
| using settings_namespace::LOCAL; |
| using settings_namespace::Namespace; |
| using settings_namespace::SYNC; |
| using settings_namespace::ToString; |
| |
| namespace { |
| |
| // TODO(kalman): test both EXTENSION_SETTINGS and APP_SETTINGS. |
| const syncable::ModelType kModelType = syncable::EXTENSION_SETTINGS; |
| |
| class NoopSyncChangeProcessor : public SyncChangeProcessor { |
| public: |
| virtual SyncError ProcessSyncChanges( |
| const tracked_objects::Location& from_here, |
| const SyncChangeList& change_list) OVERRIDE { |
| return SyncError(); |
| } |
| |
| virtual ~NoopSyncChangeProcessor() {}; |
| }; |
| |
| } // namespace |
| |
| class ExtensionSettingsApiTest : public ExtensionApiTest { |
| protected: |
| void ReplyWhenSatisfied( |
| Namespace settings_namespace, |
| const std::string& normal_action, |
| const std::string& incognito_action) { |
| MaybeLoadAndReplyWhenSatisfied( |
| settings_namespace, normal_action, incognito_action, NULL, false); |
| } |
| |
| const Extension* LoadAndReplyWhenSatisfied( |
| Namespace settings_namespace, |
| const std::string& normal_action, |
| const std::string& incognito_action, |
| const std::string& extension_dir) { |
| return MaybeLoadAndReplyWhenSatisfied( |
| settings_namespace, |
| normal_action, |
| incognito_action, |
| &extension_dir, |
| false); |
| } |
| |
| void FinalReplyWhenSatisfied( |
| Namespace settings_namespace, |
| const std::string& normal_action, |
| const std::string& incognito_action) { |
| MaybeLoadAndReplyWhenSatisfied( |
| settings_namespace, normal_action, incognito_action, NULL, true); |
| } |
| |
| void InitSync(SyncChangeProcessor* sync_processor) { |
| MessageLoop::current()->RunAllPending(); |
| InitSyncWithSyncableService( |
| sync_processor, |
| browser()->profile()->GetExtensionService()->settings_frontend()-> |
| GetBackendForSync(kModelType)); |
| } |
| |
| void SendChanges(const SyncChangeList& change_list) { |
| MessageLoop::current()->RunAllPending(); |
| SendChangesToSyncableService( |
| change_list, |
| browser()->profile()->GetExtensionService()->settings_frontend()-> |
| GetBackendForSync(kModelType)); |
| } |
| |
| private: |
| const Extension* MaybeLoadAndReplyWhenSatisfied( |
| Namespace settings_namespace, |
| const std::string& normal_action, |
| const std::string& incognito_action, |
| // May be NULL to imply not loading the extension. |
| const std::string* extension_dir, |
| bool is_final_action) { |
| ExtensionTestMessageListener listener("waiting", true); |
| ExtensionTestMessageListener listener_incognito("waiting_incognito", true); |
| |
| // Only load the extension after the listeners have been set up, to avoid |
| // initialisation race conditions. |
| const Extension* extension = NULL; |
| if (extension_dir) { |
| extension = LoadExtensionIncognito( |
| test_data_dir_.AppendASCII("settings").AppendASCII(*extension_dir)); |
| EXPECT_TRUE(extension); |
| } |
| |
| EXPECT_TRUE(listener.WaitUntilSatisfied()); |
| EXPECT_TRUE(listener_incognito.WaitUntilSatisfied()); |
| |
| listener.Reply( |
| CreateMessage(settings_namespace, normal_action, is_final_action)); |
| listener_incognito.Reply( |
| CreateMessage(settings_namespace, incognito_action, is_final_action)); |
| return extension; |
| } |
| |
| std::string CreateMessage( |
| Namespace settings_namespace, |
| const std::string& action, |
| bool is_final_action) { |
| scoped_ptr<DictionaryValue> message(new DictionaryValue()); |
| message->SetString("namespace", ToString(settings_namespace)); |
| message->SetString("action", action); |
| message->SetBoolean("isFinalAction", is_final_action); |
| std::string message_json; |
| base::JSONWriter::Write(message.get(), &message_json); |
| return message_json; |
| } |
| |
| void InitSyncWithSyncableService( |
| SyncChangeProcessor* sync_processor, SyncableService* settings_service) { |
| EXPECT_FALSE(settings_service->MergeDataAndStartSyncing( |
| kModelType, |
| SyncDataList(), |
| sync_processor).IsSet()); |
| } |
| |
| void SendChangesToSyncableService( |
| const SyncChangeList& change_list, SyncableService* settings_service) { |
| EXPECT_FALSE( |
| settings_service->ProcessSyncChanges(FROM_HERE, change_list).IsSet()); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SimpleTest) { |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| ASSERT_TRUE(RunExtensionTest("settings/simple_test")) << message_; |
| } |
| |
| // Structure of this test taken from IncognitoSplitMode. |
| // Note that only split-mode incognito is tested, because spanning mode |
| // incognito looks the same as normal mode when the only API activity comes |
| // from background pages. |
| IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SplitModeIncognito) { |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| |
| // We need 2 ResultCatchers because we'll be running the same test in both |
| // regular and incognito mode. |
| ResultCatcher catcher, catcher_incognito; |
| catcher.RestrictToProfile(browser()->profile()); |
| catcher_incognito.RestrictToProfile( |
| browser()->profile()->GetOffTheRecordProfile()); |
| |
| LoadAndReplyWhenSatisfied(SYNC, |
| "assertEmpty", "assertEmpty", "split_incognito"); |
| ReplyWhenSatisfied(SYNC, "noop", "setFoo"); |
| ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); |
| ReplyWhenSatisfied(SYNC, "clear", "noop"); |
| ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); |
| ReplyWhenSatisfied(SYNC, "setFoo", "noop"); |
| ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); |
| ReplyWhenSatisfied(SYNC, "noop", "removeFoo"); |
| FinalReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); |
| |
| EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, |
| OnChangedNotificationsBetweenBackgroundPages) { |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| |
| // We need 2 ResultCatchers because we'll be running the same test in both |
| // regular and incognito mode. |
| ResultCatcher catcher, catcher_incognito; |
| catcher.RestrictToProfile(browser()->profile()); |
| catcher_incognito.RestrictToProfile( |
| browser()->profile()->GetOffTheRecordProfile()); |
| |
| LoadAndReplyWhenSatisfied(SYNC, |
| "assertNoNotifications", "assertNoNotifications", "split_incognito"); |
| ReplyWhenSatisfied(SYNC, "noop", "setFoo"); |
| ReplyWhenSatisfied(SYNC, |
| "assertAddFooNotification", "assertAddFooNotification"); |
| ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); |
| ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); |
| FinalReplyWhenSatisfied(SYNC, |
| "assertDeleteFooNotification", "assertDeleteFooNotification"); |
| |
| EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, |
| SyncAndLocalAreasAreSeparate) { |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| |
| // We need 2 ResultCatchers because we'll be running the same test in both |
| // regular and incognito mode. |
| ResultCatcher catcher, catcher_incognito; |
| catcher.RestrictToProfile(browser()->profile()); |
| catcher_incognito.RestrictToProfile( |
| browser()->profile()->GetOffTheRecordProfile()); |
| |
| LoadAndReplyWhenSatisfied(SYNC, |
| "assertNoNotifications", "assertNoNotifications", "split_incognito"); |
| |
| ReplyWhenSatisfied(SYNC, "noop", "setFoo"); |
| ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); |
| ReplyWhenSatisfied(SYNC, |
| "assertAddFooNotification", "assertAddFooNotification"); |
| ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); |
| ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); |
| |
| ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); |
| |
| ReplyWhenSatisfied(LOCAL, "setFoo", "noop"); |
| ReplyWhenSatisfied(LOCAL, "assertFoo", "assertFoo"); |
| ReplyWhenSatisfied(LOCAL, |
| "assertAddFooNotification", "assertAddFooNotification"); |
| ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); |
| ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); |
| |
| ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); |
| |
| ReplyWhenSatisfied(LOCAL, "noop", "removeFoo"); |
| ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); |
| ReplyWhenSatisfied(LOCAL, |
| "assertDeleteFooNotification", "assertDeleteFooNotification"); |
| ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); |
| ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); |
| |
| ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); |
| |
| ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); |
| ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); |
| ReplyWhenSatisfied(SYNC, |
| "assertDeleteFooNotification", "assertDeleteFooNotification"); |
| ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); |
| FinalReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); |
| |
| EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); |
| } |
| |
| // Disabled, see crbug.com/101110 |
| IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, |
| DISABLED_OnChangedNotificationsFromSync) { |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| |
| // We need 2 ResultCatchers because we'll be running the same test in both |
| // regular and incognito mode. |
| ResultCatcher catcher, catcher_incognito; |
| catcher.RestrictToProfile(browser()->profile()); |
| catcher_incognito.RestrictToProfile( |
| browser()->profile()->GetOffTheRecordProfile()); |
| |
| const Extension* extension = |
| LoadAndReplyWhenSatisfied(SYNC, |
| "assertNoNotifications", "assertNoNotifications", "split_incognito"); |
| const std::string& extension_id = extension->id(); |
| |
| NoopSyncChangeProcessor sync_processor; |
| InitSync(&sync_processor); |
| |
| // Set "foo" to "bar" via sync. |
| SyncChangeList sync_changes; |
| StringValue bar("bar"); |
| sync_changes.push_back(settings_sync_util::CreateAdd( |
| extension_id, "foo", bar, kModelType)); |
| SendChanges(sync_changes); |
| |
| ReplyWhenSatisfied(SYNC, |
| "assertAddFooNotification", "assertAddFooNotification"); |
| ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); |
| |
| // Remove "foo" via sync. |
| sync_changes.clear(); |
| sync_changes.push_back(settings_sync_util::CreateDelete( |
| extension_id, "foo", kModelType)); |
| SendChanges(sync_changes); |
| |
| FinalReplyWhenSatisfied(SYNC, |
| "assertDeleteFooNotification", "assertDeleteFooNotification"); |
| |
| EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); |
| } |
| |
| // Disabled, see crbug.com/101110 |
| // |
| // TODO: boring test, already done in the unit tests. What we really should be |
| // be testing is that the areas don't overlap. |
| IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, |
| DISABLED_OnChangedNotificationsFromSyncNotSentToLocal) { |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| |
| // We need 2 ResultCatchers because we'll be running the same test in both |
| // regular and incognito mode. |
| ResultCatcher catcher, catcher_incognito; |
| catcher.RestrictToProfile(browser()->profile()); |
| catcher_incognito.RestrictToProfile( |
| browser()->profile()->GetOffTheRecordProfile()); |
| |
| const Extension* extension = |
| LoadAndReplyWhenSatisfied(LOCAL, |
| "assertNoNotifications", "assertNoNotifications", "split_incognito"); |
| const std::string& extension_id = extension->id(); |
| |
| NoopSyncChangeProcessor sync_processor; |
| InitSync(&sync_processor); |
| |
| // Set "foo" to "bar" via sync. |
| SyncChangeList sync_changes; |
| StringValue bar("bar"); |
| sync_changes.push_back(settings_sync_util::CreateAdd( |
| extension_id, "foo", bar, kModelType)); |
| SendChanges(sync_changes); |
| |
| ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); |
| |
| // Remove "foo" via sync. |
| sync_changes.clear(); |
| sync_changes.push_back(settings_sync_util::CreateDelete( |
| extension_id, "foo", kModelType)); |
| SendChanges(sync_changes); |
| |
| FinalReplyWhenSatisfied(LOCAL, |
| "assertNoNotifications", "assertNoNotifications"); |
| |
| EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); |
| } |
| |
| } // namespace extensions |