diff options
-rw-r--r-- | ppapi/c/dev/ppb_testing_dev.h | 10 | ||||
-rw-r--r-- | ppapi/ppapi_tests.gypi | 1 | ||||
-rw-r--r-- | ppapi/proxy/plugin_dispatcher.cc | 1 | ||||
-rw-r--r-- | ppapi/proxy/ppb_testing_proxy.cc | 7 | ||||
-rw-r--r-- | ppapi/tests/pp_thread.h | 117 | ||||
-rw-r--r-- | ppapi/tests/test_case.h | 18 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.cc | 82 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.h | 3 | ||||
-rw-r--r-- | webkit/plugins/ppapi/plugin_module.cc | 13 |
9 files changed, 240 insertions, 12 deletions
diff --git a/ppapi/c/dev/ppb_testing_dev.h b/ppapi/c/dev/ppb_testing_dev.h index ec70102..066b50f 100644 --- a/ppapi/c/dev/ppb_testing_dev.h +++ b/ppapi/c/dev/ppb_testing_dev.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 The Chromium Authors. All rights reserved. +/* Copyright (c) 2011 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. */ @@ -12,8 +12,10 @@ struct PP_Point; +// TODO(dmichael): Delete support for 0.6. #define PPB_TESTING_DEV_INTERFACE_0_6 "PPB_Testing(Dev);0.6" -#define PPB_TESTING_DEV_INTERFACE PPB_TESTING_DEV_INTERFACE_0_6 +#define PPB_TESTING_DEV_INTERFACE_0_7 "PPB_Testing(Dev);0.7" +#define PPB_TESTING_DEV_INTERFACE PPB_TESTING_DEV_INTERFACE_0_7 // This interface contains functions used for unit testing. Do not use in // production code. They are not guaranteed to be available in normal plugin @@ -68,6 +70,10 @@ struct PPB_Testing_Dev { // associated with this plugin instance. Used for detecting leaks. Returns // (uint32_t)-1 on failure. uint32_t (*GetLiveObjectsForInstance)(PP_Instance instance); + + // Returns PP_TRUE if the plugin is running out-of-process, PP_FALSE + // otherwise. + PP_Bool (*IsOutOfProcess)(); }; #endif /* PPAPI_C_DEV_PPB_TESTING_DEV_H_ */ diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi index a8a59c2..b3dcb05 100644 --- a/ppapi/ppapi_tests.gypi +++ b/ppapi/ppapi_tests.gypi @@ -72,6 +72,7 @@ 'tests/all_cpp_includes.h', 'tests/arch_dependent_sizes_32.h', 'tests/arch_dependent_sizes_64.h', + 'tests/pp_thread.h', 'tests/test_broker.cc', 'tests/test_broker.h', 'tests/test_buffer.cc', diff --git a/ppapi/proxy/plugin_dispatcher.cc b/ppapi/proxy/plugin_dispatcher.cc index 7412462..58a1017 100644 --- a/ppapi/proxy/plugin_dispatcher.cc +++ b/ppapi/proxy/plugin_dispatcher.cc @@ -109,7 +109,6 @@ bool PluginDispatcher::IsPlugin() const { } bool PluginDispatcher::Send(IPC::Message* msg) { - DCHECK(MessageLoop::current()); TRACE_EVENT2("ppapi proxy", "PluginDispatcher::Send", "Class", IPC_MESSAGE_ID_CLASS(msg->type()), "Line", IPC_MESSAGE_ID_LINE(msg->type())); diff --git a/ppapi/proxy/ppb_testing_proxy.cc b/ppapi/proxy/ppb_testing_proxy.cc index 942521c..564f68f 100644 --- a/ppapi/proxy/ppb_testing_proxy.cc +++ b/ppapi/proxy/ppb_testing_proxy.cc @@ -63,11 +63,16 @@ uint32_t GetLiveObjectsForInstance(PP_Instance instance_id) { return result; } +PP_Bool IsOutOfProcess() { + return PP_TRUE; +} + const PPB_Testing_Dev testing_interface = { &ReadImageData, &RunMessageLoop, &QuitMessageLoop, - &GetLiveObjectsForInstance + &GetLiveObjectsForInstance, + &IsOutOfProcess }; InterfaceProxy* CreateTestingProxy(Dispatcher* dispatcher, diff --git a/ppapi/tests/pp_thread.h b/ppapi/tests/pp_thread.h new file mode 100644 index 0000000..f168d8c --- /dev/null +++ b/ppapi/tests/pp_thread.h @@ -0,0 +1,117 @@ +/* Copyright (c) 2011 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_TESTS_PP_THREAD_H_ +#define PPAPI_TESTS_PP_THREAD_H_ + +#include "ppapi/c/pp_macros.h" + +/* These precompiler names were copied from chromium's build_config.h. */ +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) || defined(__sun) || defined(__native_client__) +#define PPAPI_HAS_POSIX_THREADS 1 +#elif defined (_MSC_VER) +#define PPAPI_HAS_WINDOWS_THREADS 1 +#endif + +#if defined(PPAPI_HAS_POSIX_THREADS) +#include <pthread.h> +#elif defined(PPAPI_HAS_WINDOWS_THREADS) +#include <process.h> +#include <windows.h> +#endif + +/** + * @file + * This file provides platform-independent wrappers around threads. This is for + * use by PPAPI wrappers and tests which need to run on multiple platforms to + * support both trusted platforms (Windows, Mac, Linux) and untrusted (Native + * Client). Apps that use PPAPI only with Native Client should generally use the + * Native Client POSIX implementation instead. + * + * TODO(dmichael): Move this file to ppapi/c and delete this comment, if we end + * up needing platform independent threads in PPAPI C or C++. This file was + * written using inline functions and PPAPI naming conventions with the intent + * of making it possible to put it in to ppapi/c. Currently, however, it's only + * used in ppapi/tests, so is not part of the published API. + */ + +#if defined(PPAPI_HAS_POSIX_THREADS) +typedef pthread_t PP_ThreadType; +#elif defined(PPAPI_HAS_WINDOWS_THREADS) +typedef uintptr_t PP_ThreadType; +#endif + +typedef void (PP_ThreadFunction)(void* data); + +PP_INLINE bool PP_CreateThread(PP_ThreadType* thread, + PP_ThreadFunction function, + void* thread_arg); +PP_INLINE void PP_JoinThread(PP_ThreadType thread); + +#if defined(PPAPI_HAS_POSIX_THREADS) +/* Because POSIX thread functions return void* and Windows thread functions do + * not, we make PPAPI thread functions have the least capability (no returns). + * This struct wraps the user data & function so that we can use the correct + * function type on POSIX platforms. + */ +struct PP_ThreadFunctionArgWrapper { + void* user_data; + PP_ThreadFunction* user_function; +}; + +PP_INLINE void* PP_POSIXThreadFunctionThunk(void* posix_thread_arg) { + PP_ThreadFunctionArgWrapper* arg_wrapper = + (PP_ThreadFunctionArgWrapper*)posix_thread_arg; + arg_wrapper->user_function(arg_wrapper->user_data); + free(posix_thread_arg); + return NULL; +} + +PP_INLINE bool PP_CreateThread(PP_ThreadType* thread, + PP_ThreadFunction function, + void* thread_arg) { + PP_ThreadFunctionArgWrapper* arg_wrapper = + (PP_ThreadFunctionArgWrapper*)malloc(sizeof(PP_ThreadFunctionArgWrapper)); + arg_wrapper->user_function = function; + arg_wrapper->user_data = thread_arg; + return (pthread_create(thread, + NULL, + PP_POSIXThreadFunctionThunk, + arg_wrapper) == 0); +} + +PP_INLINE void PP_JoinThread(PP_ThreadType thread) { + void* exit_status; + pthread_join(thread, &exit_status); +} + +#elif defined(PPAPI_HAS_WINDOWS_THREADS) +typedef DWORD (PP_WindowsThreadFunction)(void* data); + +PP_INLINE bool PP_CreateThread(PP_ThreadType* thread, + PP_ThreadFunction function, + void* thread_arg) { + if (!thread) + return false; + *thread = ::_beginthread(function, + 0, /* Use default stack size. */ + thread_arg); + return (*thread != NULL); +} + +PP_INLINE void PP_JoinThread(PP_ThreadType thread) { + ::WaitForSingleObject((HANDLE)thread, INFINITE); +} + +#endif + + +/** + * @} + */ + +#endif /* PPAPI_TESTS_PP_THREAD_H_ */ + diff --git a/ppapi/tests/test_case.h b/ppapi/tests/test_case.h index 0a3cdc6..06ebcb1 100644 --- a/ppapi/tests/test_case.h +++ b/ppapi/tests/test_case.h @@ -130,18 +130,24 @@ class TestCaseFactory { // RunTest function. This assumes the function name is TestFoo where Foo is the // test |name|. #define RUN_TEST(name) \ - force_async_ = false; \ - instance_->LogTest(#name, Test##name()); + do { \ + force_async_ = false; \ + instance_->LogTest(#name, Test##name()); \ + } while (false) // Like RUN_TEST above but forces functions taking callbacks to complete // asynchronously on success or error. #define RUN_TEST_FORCEASYNC(name) \ - force_async_ = true; \ - instance_->LogTest(#name"ForceAsync", Test##name()); + do { \ + force_async_ = true; \ + instance_->LogTest(#name"ForceAsync", Test##name()); \ + } while (false) #define RUN_TEST_FORCEASYNC_AND_NOT(name) \ - RUN_TEST_FORCEASYNC(name); \ - RUN_TEST(name); + do { \ + RUN_TEST_FORCEASYNC(name); \ + RUN_TEST(name); \ + } while (false) // Helper macros for checking values in tests, and returning a location diff --git a/ppapi/tests/test_post_message.cc b/ppapi/tests/test_post_message.cc index 10bc7a0..f19a3b1 100644 --- a/ppapi/tests/test_post_message.cc +++ b/ppapi/tests/test_post_message.cc @@ -11,6 +11,7 @@ #include "ppapi/cpp/dev/scriptable_object_deprecated.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/var.h" +#include "ppapi/tests/pp_thread.h" #include "ppapi/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" @@ -28,6 +29,25 @@ const bool kTestBool = true; const int32_t kTestInt = 42; const double kTestDouble = 42.0; const int32_t kThreadsToRun = 10; +const int32_t kMessagesToSendPerThread = 50; + +// The struct that invoke_post_message_thread_func expects for its argument. +// It includes the instance on which to invoke PostMessage, and the value to +// pass to PostMessage. +struct InvokePostMessageThreadArg { + InvokePostMessageThreadArg(pp::Instance* i, const pp::Var& v) + : instance(i), value_to_send(v) {} + pp::Instance* instance; + pp::Var value_to_send; +}; + +void InvokePostMessageThreadFunc(void* user_data) { + InvokePostMessageThreadArg* arg = + static_cast<InvokePostMessageThreadArg*>(user_data); + for (int32_t i = 0; i < kMessagesToSendPerThread; ++i) + arg->instance->PostMessage(arg->value_to_send); + delete arg; +} } // namespace @@ -40,6 +60,8 @@ void TestPostMessage::RunTest() { RUN_TEST(MessageEvent); RUN_TEST(NoHandler); RUN_TEST(ExtraParam); + if (testing_interface_->IsOutOfProcess()) + RUN_TEST(NonMainThread); } void TestPostMessage::HandleMessage(const pp::Var& message_data) { @@ -254,3 +276,63 @@ std::string TestPostMessage::TestExtraParam() { PASS(); } +std::string TestPostMessage::TestNonMainThread() { + ASSERT_TRUE(ClearListeners()); + ASSERT_TRUE(AddEchoingListener("message_event.data")); + message_data_.clear(); + + // Set up a thread for each integer from 0 to (kThreadsToRun - 1). Make each + // thread send the number that matches its index kMessagesToSendPerThread + // times. For good measure, call postMessage from the main thread + // kMessagesToSendPerThread times. At the end, we make sure we got all the + // values we expected. + PP_ThreadType threads[kThreadsToRun]; + for (int32_t i = 0; i < kThreadsToRun; ++i) { + // Set up a thread to send a value of i. + void* arg = new InvokePostMessageThreadArg(instance_, pp::Var(i)); + PP_CreateThread(&threads[i], &InvokePostMessageThreadFunc, arg); + } + // Invoke PostMessage right now to send a value of (kThreadsToRun). + for (int32_t i = 0; i < kMessagesToSendPerThread; ++i) + instance_->PostMessage(pp::Var(kThreadsToRun)); + + // Now join all threads. + for (int32_t i = 0; i < kThreadsToRun; ++i) + PP_JoinThread(threads[i]); + + // PostMessage is asynchronous, so we should not receive a response yet. + ASSERT_EQ(message_data_.size(), 0); + + // Make sure we got all values that we expected. Note that because it's legal + // for the JavaScript engine to treat our integers as floating points, we + // can't just use std::find or equality comparison. So we instead, we convert + // each incoming value to an integer, and count them in received_counts. + int32_t expected_num = (kThreadsToRun + 1) * kMessagesToSendPerThread; + // Count how many we receive per-index. + std::vector<int32_t> expected_counts(kThreadsToRun + 1, + kMessagesToSendPerThread); + std::vector<int32_t> received_counts(kThreadsToRun + 1, 0); + for (int32_t i = 0; i < expected_num; ++i) { + // Run the message loop to get the next expected message. + testing_interface_->RunMessageLoop(instance_->pp_instance()); + // Make sure we got another message in. + ASSERT_EQ(message_data_.size(), 1); + pp::Var latest_var(message_data_.back()); + message_data_.clear(); + + ASSERT_TRUE(latest_var.is_int() || latest_var.is_double()); + int32_t received_value = -1; + if (latest_var.is_int()) { + received_value = latest_var.AsInt(); + } else if (latest_var.is_double()) { + received_value = static_cast<int32_t>(latest_var.AsDouble() + 0.5); + } + ASSERT_TRUE(received_value >= 0); + ASSERT_TRUE(received_value <= kThreadsToRun); + ++received_counts[received_value]; + } + ASSERT_EQ(received_counts, expected_counts); + + PASS(); +} + diff --git a/ppapi/tests/test_post_message.h b/ppapi/tests/test_post_message.h index df7256b..f081880 100644 --- a/ppapi/tests/test_post_message.h +++ b/ppapi/tests/test_post_message.h @@ -53,6 +53,9 @@ class TestPostMessage : public TestCase { // nothing happens. std::string TestExtraParam(); + // Test sending messages off of the main thread. + std::string TestNonMainThread(); + typedef std::vector<pp::Var> VarVector; // This is used to store pp::Var objects we receive via a call to diff --git a/webkit/plugins/ppapi/plugin_module.cc b/webkit/plugins/ppapi/plugin_module.cc index b5da771..c4a73e5 100644 --- a/webkit/plugins/ppapi/plugin_module.cc +++ b/webkit/plugins/ppapi/plugin_module.cc @@ -200,11 +200,16 @@ uint32_t GetLiveObjectsForInstance(PP_Instance instance_id) { return ResourceTracker::Get()->GetLiveObjectsForInstance(instance_id); } +PP_Bool IsOutOfProcess() { + return PP_FALSE; +} + const PPB_Testing_Dev testing_interface = { &ReadImageData, &RunMessageLoop, &QuitMessageLoop, - &GetLiveObjectsForInstance + &GetLiveObjectsForInstance, + &IsOutOfProcess }; // GetInterface ---------------------------------------------------------------- @@ -364,7 +369,11 @@ const void* GetInterface(const char* name) { // Only support the testing interface when the command line switch is // specified. This allows us to prevent people from (ab)using this interface // in production code. - if (strcmp(name, PPB_TESTING_DEV_INTERFACE) == 0) { + // TODO(dmichael): Remove support for 0.6. Note that 0.7 only adds a function + // to the end, so returning an 0.7 struct for use by clients of 0.6 just + // works in practice. + if (strcmp(name, PPB_TESTING_DEV_INTERFACE) == 0 || + strcmp(name, PPB_TESTING_DEV_INTERFACE_0_6) == 0) { if (CommandLine::ForCurrentProcess()->HasSwitch("enable-pepper-testing")) return &testing_interface; } |