// 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. #ifndef PPAPI_PROXY_PPAPI_PROXY_TEST_H_ #define PPAPI_PROXY_PPAPI_PROXY_TEST_H_ #include #include #include #include #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" #include "base/synchronization/waitable_event.h" #include "base/task_runner.h" #include "base/threading/simple_thread.h" #include "base/threading/thread.h" #include "ppapi/c/pp_instance.h" #include "ppapi/proxy/host_dispatcher.h" #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/plugin_globals.h" #include "ppapi/proxy/plugin_proxy_delegate.h" #include "ppapi/proxy/plugin_resource_tracker.h" #include "ppapi/proxy/plugin_var_tracker.h" #include "ppapi/proxy/resource_message_test_sink.h" #include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/test_globals.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { class RunLoop; class SingleThreadTaskRunner; } namespace ppapi { namespace proxy { class MessageLoopResource; // Base class for plugin and host test harnesses. Tests will not use this // directly. Instead, use the PluginProxyTest, HostProxyTest, or TwoWayTest. class ProxyTestHarnessBase { public: enum GlobalsConfiguration { PER_THREAD_GLOBALS, SINGLETON_GLOBALS }; ProxyTestHarnessBase(); virtual ~ProxyTestHarnessBase(); PP_Module pp_module() const { return pp_module_; } PP_Instance pp_instance() const { return pp_instance_; } ResourceMessageTestSink& sink() { return sink_; } virtual PpapiGlobals* GetGlobals() = 0; // Returns either the plugin or host dispatcher, depending on the test. virtual Dispatcher* GetDispatcher() = 0; // Set up the harness using an IPC::TestSink to capture messages. virtual void SetUpHarness() = 0; // Set up the harness using a real IPC channel. virtual void SetUpHarnessWithChannel( const IPC::ChannelHandle& channel_handle, base::SingleThreadTaskRunner* ipc_task_runner, base::WaitableEvent* shutdown_event, bool is_client) = 0; virtual void TearDownHarness() = 0; // Implementation of GetInterface for the dispatcher. This will // return NULL for all interfaces unless one is registered by calling // RegisterTestInterface(); const void* GetInterface(const char* name); // Allows the test to specify an interface implementation for a given // interface name. This will be returned when any of the proxy logic // requests a local interface. void RegisterTestInterface(const char* name, const void* test_interface); // Sends a "supports interface" message to the current dispatcher and returns // true if it's supported. This is just for the convenience of tests. bool SupportsInterface(const char* name); private: // Destination for IPC messages sent by the test. ResourceMessageTestSink sink_; // The module and instance ID associated with the plugin dispatcher. PP_Module pp_module_; PP_Instance pp_instance_; // Stores the data for GetInterface/RegisterTestInterface. std::map registered_interfaces_; }; // Test harness for the plugin side of the proxy. class PluginProxyTestHarness : public ProxyTestHarnessBase { public: explicit PluginProxyTestHarness(GlobalsConfiguration globals_config); virtual ~PluginProxyTestHarness(); PluginDispatcher* plugin_dispatcher() { return plugin_dispatcher_.get(); } PluginResourceTracker& resource_tracker() { return *plugin_globals_->plugin_resource_tracker(); } PluginVarTracker& var_tracker() { return *plugin_globals_->plugin_var_tracker(); } // ProxyTestHarnessBase implementation. virtual PpapiGlobals* GetGlobals(); virtual Dispatcher* GetDispatcher(); virtual void SetUpHarness(); virtual void SetUpHarnessWithChannel( const IPC::ChannelHandle& channel_handle, base::SingleThreadTaskRunner* ipc_task_runner, base::WaitableEvent* shutdown_event, bool is_client); virtual void TearDownHarness(); class PluginDelegateMock : public PluginDispatcher::PluginDelegate, public PluginProxyDelegate { public: PluginDelegateMock() : ipc_task_runner_(NULL), shutdown_event_() {} ~PluginDelegateMock() override {} void Init(base::SingleThreadTaskRunner* ipc_task_runner, base::WaitableEvent* shutdown_event) { ipc_task_runner_ = ipc_task_runner; shutdown_event_ = shutdown_event; } void set_browser_sender(IPC::Sender* browser_sender) { browser_sender_ = browser_sender; } // ProxyChannel::Delegate implementation. base::SingleThreadTaskRunner* GetIPCTaskRunner() override; base::WaitableEvent* GetShutdownEvent() override; IPC::PlatformFileForTransit ShareHandleWithRemote( base::PlatformFile handle, base::ProcessId remote_pid, bool should_close_source) override; base::SharedMemoryHandle ShareSharedMemoryHandleWithRemote( const base::SharedMemoryHandle& handle, base::ProcessId remote_pid) override; base::UnsafeSharedMemoryRegion ShareUnsafeSharedMemoryRegionWithRemote( const base::UnsafeSharedMemoryRegion& region, base::ProcessId remote_pid) override; base::ReadOnlySharedMemoryRegion ShareReadOnlySharedMemoryRegionWithRemote( const base::ReadOnlySharedMemoryRegion& region, base::ProcessId remote_pid) override; // PluginDispatcher::PluginDelegate implementation. std::set* GetGloballySeenInstanceIDSet() override; uint32_t Register(PluginDispatcher* plugin_dispatcher) override; void Unregister(uint32_t plugin_dispatcher_id) override; // PluginProxyDelegate implementation. IPC::Sender* GetBrowserSender() override; std::string GetUILanguage() override; void PreCacheFontForFlash(const void* logfontw) override; void SetActiveURL(const std::string& url) override; PP_Resource CreateBrowserFont( Connection connection, PP_Instance instance, const PP_BrowserFont_Trusted_Description& desc, const Preferences& prefs) override; private: base::SingleThreadTaskRunner* ipc_task_runner_; // Weak base::WaitableEvent* shutdown_event_; // Weak std::set instance_id_set_; IPC::Sender* browser_sender_; DISALLOW_COPY_AND_ASSIGN(PluginDelegateMock); }; private: void CreatePluginGlobals( const scoped_refptr& ipc_task_runner); GlobalsConfiguration globals_config_; std::unique_ptr plugin_globals_; std::unique_ptr plugin_dispatcher_; PluginDelegateMock plugin_delegate_mock_; }; class PluginProxyTest : public PluginProxyTestHarness, public testing::Test { public: PluginProxyTest(); virtual ~PluginProxyTest(); // testing::Test implementation. virtual void SetUp(); virtual void TearDown(); private: base::MessageLoop message_loop_; }; // This class provides support for multi-thread testing. A secondary thread is // created with a Pepper message loop. // Subclasses need to implement the two SetUpTestOn*Thread() methods to do the // actual testing work; and call both PostQuitFor*Thread() when testing is // done. class PluginProxyMultiThreadTest : public PluginProxyTest, public base::DelegateSimpleThread::Delegate { public: PluginProxyMultiThreadTest(); ~PluginProxyMultiThreadTest() override; // Called before the secondary thread is started, but after all the member // variables, including |secondary_thread_| and // |secondary_thread_message_loop_|, are initialized. virtual void SetUpTestOnMainThread() = 0; virtual void SetUpTestOnSecondaryThread() = 0; // TEST_F() should call this method. void RunTest(); enum ThreadType { MAIN_THREAD, SECONDARY_THREAD }; void CheckOnThread(ThreadType thread_type); // These can be called on any thread. void PostQuitForMainThread(); void PostQuitForSecondaryThread(); protected: scoped_refptr secondary_thread_message_loop_; scoped_refptr main_thread_task_runner_; private: // base::DelegateSimpleThread::Delegate implementation. void Run() override; void QuitNestedLoop(); static void InternalSetUpTestOnSecondaryThread(void* user_data, int32_t result); std::unique_ptr secondary_thread_; std::unique_ptr nested_main_thread_message_loop_; }; class HostProxyTestHarness : public ProxyTestHarnessBase { public: explicit HostProxyTestHarness(GlobalsConfiguration globals_config); virtual ~HostProxyTestHarness(); HostDispatcher* host_dispatcher() { return host_dispatcher_.get(); } ResourceTracker& resource_tracker() { return *host_globals_->GetResourceTracker(); } VarTracker& var_tracker() { return *host_globals_->GetVarTracker(); } // ProxyTestBase implementation. virtual PpapiGlobals* GetGlobals(); virtual Dispatcher* GetDispatcher(); virtual void SetUpHarness(); virtual void SetUpHarnessWithChannel( const IPC::ChannelHandle& channel_handle, base::SingleThreadTaskRunner* ipc_task_runner, base::WaitableEvent* shutdown_event, bool is_client); virtual void TearDownHarness(); class DelegateMock : public ProxyChannel::Delegate { public: DelegateMock() : ipc_task_runner_(NULL), shutdown_event_(NULL) {} ~DelegateMock() override {} void Init(base::SingleThreadTaskRunner* ipc_task_runner, base::WaitableEvent* shutdown_event) { ipc_task_runner_ = ipc_task_runner; shutdown_event_ = shutdown_event; } // ProxyChannel::Delegate implementation. base::SingleThreadTaskRunner* GetIPCTaskRunner() override; base::WaitableEvent* GetShutdownEvent() override; IPC::PlatformFileForTransit ShareHandleWithRemote( base::PlatformFile handle, base::ProcessId remote_pid, bool should_close_source) override; base::SharedMemoryHandle ShareSharedMemoryHandleWithRemote( const base::SharedMemoryHandle& handle, base::ProcessId remote_pid) override; base::UnsafeSharedMemoryRegion ShareUnsafeSharedMemoryRegionWithRemote( const base::UnsafeSharedMemoryRegion& region, base::ProcessId remote_pid) override; base::ReadOnlySharedMemoryRegion ShareReadOnlySharedMemoryRegionWithRemote( const base::ReadOnlySharedMemoryRegion& region, base::ProcessId remote_pid) override; private: base::SingleThreadTaskRunner* ipc_task_runner_; // Weak base::WaitableEvent* shutdown_event_; // Weak DISALLOW_COPY_AND_ASSIGN(DelegateMock); }; private: void CreateHostGlobals(); GlobalsConfiguration globals_config_; std::unique_ptr host_globals_; std::unique_ptr host_dispatcher_; // The host side of the real proxy doesn't lock, so this disables locking for // the thread the host side of the test runs on. std::unique_ptr disable_locking_; DelegateMock delegate_mock_; }; class HostProxyTest : public HostProxyTestHarness, public testing::Test { public: HostProxyTest(); virtual ~HostProxyTest(); // testing::Test implementation. virtual void SetUp(); virtual void TearDown(); private: base::MessageLoop message_loop_; }; // Use this base class to test both sides of a proxy. class TwoWayTest : public testing::Test { public: enum TwoWayTestMode { TEST_PPP_INTERFACE, TEST_PPB_INTERFACE }; TwoWayTest(TwoWayTestMode test_mode); virtual ~TwoWayTest(); HostProxyTestHarness& host() { return host_; } PluginProxyTestHarness& plugin() { return plugin_; } PP_Module pp_module() const { return host_.pp_module(); } PP_Instance pp_instance() const { return host_.pp_instance(); } TwoWayTestMode test_mode() { return test_mode_; } // testing::Test implementation. virtual void SetUp(); virtual void TearDown(); protected: // Post a task to the thread where the remote harness lives. This // is typically used to test the state of the var tracker on the plugin // thread. This runs the task synchronously for convenience. void PostTaskOnRemoteHarness(const base::Closure& task); private: TwoWayTestMode test_mode_; HostProxyTestHarness host_; PluginProxyTestHarness plugin_; // In order to use sync IPC, we need to have an IO thread. base::Thread io_thread_; // The plugin side of the proxy runs on its own thread. base::Thread plugin_thread_; // The message loop for the main (host) thread. base::MessageLoop message_loop_; // Aliases for the host and plugin harnesses; if we're testing a PPP // interface, remote_harness will point to plugin_, and local_harness // will point to host_. This makes it convenient when we're starting and // stopping the harnesses. ProxyTestHarnessBase* remote_harness_; ProxyTestHarnessBase* local_harness_; base::WaitableEvent channel_created_; base::WaitableEvent shutdown_event_; }; // Used during Gtests when you have a PP_Var that you want to EXPECT is equal // to a certain constant string value: // // EXPECT_VAR_IS_STRING("foo", my_var); #define EXPECT_VAR_IS_STRING(str, var) { \ StringVar* sv = StringVar::FromPPVar(var); \ EXPECT_TRUE(sv); \ if (sv) \ EXPECT_EQ(str, sv->value()); \ } } // namespace proxy } // namespace ppapi #endif // PPAPI_PROXY_PPAPI_PROXY_TEST_H_