// 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 #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/bluetooth/bluetooth_event_router.h" #include "chrome/browser/extensions/event_names.h" #include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/common/extensions/api/bluetooth.h" #include "chrome/test/base/testing_profile.h" #include "content/public/browser/notification_service.h" #include "content/public/test/test_browser_thread.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "device/bluetooth/test/mock_bluetooth_device.h" #include "device/bluetooth/test/mock_bluetooth_profile.h" #include "device/bluetooth/test/mock_bluetooth_socket.h" #include "extensions/browser/event_router.h" #include "extensions/common/extension_builder.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace { const char kTestExtensionId[] = "test extension id"; const char kAudioProfileUuid[] = "audio profile uuid"; const char kHealthProfileUuid[] = "health profile uuid"; class FakeEventRouter : public extensions::EventRouter { public: explicit FakeEventRouter(Profile* profile) : EventRouter(profile, NULL) {} virtual void DispatchEventToExtension( const std::string& extension_id, scoped_ptr event) OVERRIDE { extension_id_ = extension_id; event_ = event.Pass(); } std::string extension_id() const { return extension_id_; } const extensions::Event* event() const { return event_.get(); } private: std::string extension_id_; scoped_ptr event_; DISALLOW_COPY_AND_ASSIGN(FakeEventRouter); }; class FakeExtensionSystem : public extensions::TestExtensionSystem { public: explicit FakeExtensionSystem(Profile* profile) : extensions::TestExtensionSystem(profile) {} virtual extensions::EventRouter* event_router() OVERRIDE { if (!fake_event_router_) fake_event_router_.reset(new FakeEventRouter(profile_)); return fake_event_router_.get(); } private: scoped_ptr fake_event_router_; DISALLOW_COPY_AND_ASSIGN(FakeExtensionSystem); }; BrowserContextKeyedService* BuildFakeExtensionSystem( content::BrowserContext* profile) { return new FakeExtensionSystem(static_cast(profile)); } } // namespace namespace extensions { namespace bluetooth = api::bluetooth; class ExtensionBluetoothEventRouterTest : public testing::Test { public: ExtensionBluetoothEventRouterTest() : mock_adapter_(new testing::StrictMock()), test_profile_(new TestingProfile()), router_(test_profile_.get()), ui_thread_(content::BrowserThread::UI, &message_loop_) { router_.SetAdapterForTest(mock_adapter_); } virtual void TearDown() OVERRIDE { // Some profile-dependent services rely on UI thread to clean up. We make // sure they are properly cleaned up by running the UI message loop until // idle. test_profile_.reset(NULL); base::RunLoop run_loop; run_loop.RunUntilIdle(); } protected: testing::StrictMock* mock_adapter_; testing::NiceMock mock_audio_profile_; testing::NiceMock mock_health_profile_; scoped_ptr test_profile_; ExtensionBluetoothEventRouter router_; base::MessageLoopForUI message_loop_; content::TestBrowserThread ui_thread_; }; TEST_F(ExtensionBluetoothEventRouterTest, BluetoothEventListener) { router_.OnListenerAdded(); EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)).Times(1); router_.OnListenerRemoved(); } TEST_F(ExtensionBluetoothEventRouterTest, MultipleBluetoothEventListeners) { router_.OnListenerAdded(); router_.OnListenerAdded(); router_.OnListenerAdded(); router_.OnListenerRemoved(); router_.OnListenerRemoved(); EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)).Times(1); router_.OnListenerRemoved(); } TEST_F(ExtensionBluetoothEventRouterTest, Profiles) { EXPECT_FALSE(router_.HasProfile(kAudioProfileUuid)); EXPECT_FALSE(router_.HasProfile(kHealthProfileUuid)); router_.AddProfile( kAudioProfileUuid, kTestExtensionId, &mock_audio_profile_); router_.AddProfile( kHealthProfileUuid, kTestExtensionId, &mock_health_profile_); EXPECT_TRUE(router_.HasProfile(kAudioProfileUuid)); EXPECT_TRUE(router_.HasProfile(kHealthProfileUuid)); EXPECT_CALL(mock_audio_profile_, Unregister()).Times(1); router_.RemoveProfile(kAudioProfileUuid); EXPECT_FALSE(router_.HasProfile(kAudioProfileUuid)); EXPECT_TRUE(router_.HasProfile(kHealthProfileUuid)); // Make sure remaining profiles are unregistered in destructor. EXPECT_CALL(mock_health_profile_, Unregister()).Times(1); EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)).Times(1); } TEST_F(ExtensionBluetoothEventRouterTest, UnloadExtension) { scoped_refptr extension = extensions::ExtensionBuilder() .SetManifest(extensions::DictionaryBuilder() .Set("name", "BT event router test") .Set("version", "1.0") .Set("manifest_version", 2)) .SetID(kTestExtensionId) .Build(); router_.AddProfile( kAudioProfileUuid, kTestExtensionId, &mock_audio_profile_); router_.AddProfile( kHealthProfileUuid, kTestExtensionId, &mock_health_profile_); EXPECT_TRUE(router_.HasProfile(kAudioProfileUuid)); EXPECT_TRUE(router_.HasProfile(kHealthProfileUuid)); // Unloading the extension should unregister all profiles added by it. EXPECT_CALL(mock_audio_profile_, Unregister()).Times(1); EXPECT_CALL(mock_health_profile_, Unregister()).Times(1); content::NotificationService* notifier = content::NotificationService::current(); UnloadedExtensionInfo details( extension, UnloadedExtensionInfo::REASON_DISABLE); notifier->Notify(chrome::NOTIFICATION_EXTENSION_UNLOADED, content::Source(test_profile_.get()), content::Details(&details)); EXPECT_FALSE(router_.HasProfile(kAudioProfileUuid)); EXPECT_FALSE(router_.HasProfile(kHealthProfileUuid)); EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)).Times(1); } TEST_F(ExtensionBluetoothEventRouterTest, DispatchConnectionEvent) { router_.AddProfile( kAudioProfileUuid, kTestExtensionId, &mock_audio_profile_); FakeExtensionSystem* fake_extension_system = static_cast(ExtensionSystemFactory::GetInstance()-> SetTestingFactoryAndUse(test_profile_.get(), &BuildFakeExtensionSystem)); testing::NiceMock mock_device( mock_adapter_, 0, "device name", "device address", true, false); scoped_refptr > mock_socket( new testing::NiceMock()); router_.DispatchConnectionEvent(kTestExtensionId, kAudioProfileUuid, &mock_device, mock_socket); FakeEventRouter* fake_event_router = static_cast(fake_extension_system->event_router()); EXPECT_STREQ(kTestExtensionId, fake_event_router->extension_id().c_str()); EXPECT_STREQ(bluetooth::OnConnection::kEventName, fake_event_router->event()->event_name.c_str()); base::ListValue* event_args = fake_event_router->event()->event_args.get(); base::DictionaryValue* socket_value = NULL; ASSERT_TRUE(event_args->GetDictionary(0, &socket_value)); int socket_id; ASSERT_TRUE(socket_value->GetInteger("id", &socket_id)); EXPECT_EQ(mock_socket.get(), router_.GetSocket(socket_id).get()); base::DictionaryValue* profile_value = NULL; ASSERT_TRUE(socket_value->GetDictionary("profile", &profile_value)); std::string profile_uuid; ASSERT_TRUE(profile_value->GetString("uuid", &profile_uuid)); EXPECT_STREQ(kAudioProfileUuid, profile_uuid.c_str()); EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)).Times(1); router_.ReleaseSocket(socket_id); } TEST_F(ExtensionBluetoothEventRouterTest, DoNotDispatchConnectionEvent) { FakeExtensionSystem* fake_extension_system = static_cast(ExtensionSystemFactory::GetInstance()-> SetTestingFactoryAndUse(test_profile_.get(), &BuildFakeExtensionSystem)); testing::NiceMock mock_device( mock_adapter_, 0, "device name", "device address", true, false); scoped_refptr > mock_socket( new testing::NiceMock()); // Connection event won't be dispatched for non-registered profiles. router_.DispatchConnectionEvent("test extension id", kAudioProfileUuid, &mock_device, mock_socket); FakeEventRouter* fake_event_router = static_cast(fake_extension_system->event_router()); EXPECT_TRUE(fake_event_router->event() == NULL); EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)).Times(1); } } // namespace extensions