| // 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_TESTS_TEST_UTILS_H_ |
| #define PPAPI_TESTS_TEST_UTILS_H_ |
| |
| #include <string> |
| |
| #include "ppapi/c/pp_instance.h" |
| #include "ppapi/c/pp_stdint.h" |
| #include "ppapi/c/private/ppb_testing_private.h" |
| #include "ppapi/cpp/completion_callback.h" |
| #include "ppapi/cpp/message_loop.h" |
| #include "ppapi/utility/completion_callback_factory.h" |
| |
| namespace pp { |
| class NetAddress; |
| } |
| |
| // Timeout to wait for some action to complete. |
| extern const int kActionTimeoutMs; |
| |
| const PPB_Testing_Private* GetTestingInterface(); |
| std::string ReportError(const char* method, int32_t error); |
| void PlatformSleep(int duration_ms); |
| bool GetLocalHostPort(PP_Instance instance, std::string* host, uint16_t* port); |
| |
| uint16_t ConvertFromNetEndian16(uint16_t x); |
| uint16_t ConvertToNetEndian16(uint16_t x); |
| bool EqualNetAddress(const pp::NetAddress& addr1, const pp::NetAddress& addr2); |
| // Only returns the first address if there are more than one available. |
| bool ResolveHost(PP_Instance instance, |
| const std::string& host, |
| uint16_t port, |
| pp::NetAddress* addr); |
| bool ReplacePort(PP_Instance instance, |
| const pp::NetAddress& input_addr, |
| uint16_t port, |
| pp::NetAddress* output_addr); |
| uint16_t GetPort(const pp::NetAddress& addr); |
| |
| // NestedEvent allows you to run a nested MessageLoop and wait for a particular |
| // event to complete. For example, you can use it to wait for a callback on a |
| // PPP interface, which will "Signal" the event and make the loop quit. |
| // "Wait()" will return immediately if it has already been signalled. Otherwise, |
| // it will run a nested run loop (using PPB_Testing.RunMessageLoop) and will |
| // return only after it has been signalled. |
| // Example: |
| // std::string TestFullscreen::TestNormalToFullscreen() { |
| // pp::Fullscreen screen_mode(instance); |
| // screen_mode.SetFullscreen(true); |
| // SimulateUserGesture(); |
| // // Let DidChangeView run in a nested run loop. |
| // nested_event_.Wait(); |
| // Pass(); |
| // } |
| // |
| // void TestFullscreen::DidChangeView(const pp::View& view) { |
| // nested_event_.Signal(); |
| // } |
| // |
| // All methods except Signal and PostSignal must be invoked on the main thread. |
| // It's OK to signal from a background thread, so you can (for example) Signal() |
| // from the Audio thread. |
| class NestedEvent { |
| public: |
| explicit NestedEvent(PP_Instance instance) |
| : instance_(instance), waiting_(false), signalled_(false) { |
| } |
| // Run a nested run loop and wait until Signal() is called. If Signal() |
| // has already been called, return immediately without running a nested loop. |
| void Wait(); |
| // Signal the NestedEvent. If Wait() has been called, quit the message loop. |
| // This can be called from any thread. |
| void Signal(); |
| // Signal the NestedEvent in |wait_ms| milliseconds. This can be called from |
| // any thread. |
| void PostSignal(int32_t wait_ms); |
| |
| // Reset the NestedEvent so it can be used again. |
| void Reset(); |
| private: |
| void SignalOnMainThread(); |
| static void SignalThunk(void* async_event, int32_t result); |
| |
| PP_Instance instance_; |
| bool waiting_; |
| bool signalled_; |
| // Disable copy and assign. |
| NestedEvent(const NestedEvent&); |
| NestedEvent& operator=(const NestedEvent&); |
| }; |
| |
| enum CallbackType { PP_REQUIRED, PP_OPTIONAL, PP_BLOCKING }; |
| class TestCompletionCallback { |
| public: |
| class Delegate { |
| public: |
| virtual ~Delegate() {} |
| virtual void OnCallback(void* user_data, int32_t result) = 0; |
| }; |
| explicit TestCompletionCallback(PP_Instance instance); |
| // TODO(dmichael): Remove this constructor. |
| TestCompletionCallback(PP_Instance instance, bool force_async); |
| |
| TestCompletionCallback(PP_Instance instance, CallbackType callback_type); |
| |
| // Sets a Delegate instance. OnCallback() of this instance will be invoked |
| // when the completion callback is invoked. |
| // The delegate will be reset when Reset() or GetCallback() is called. |
| void SetDelegate(Delegate* delegate) { delegate_ = delegate; } |
| |
| // Wait for a result, given the return from the call which took this callback |
| // as a parameter. If |result| is PP_OK_COMPLETIONPENDING, WaitForResult will |
| // block until its callback has been invoked (in some cases, this will already |
| // have happened, and WaitForCallback can return immediately). |
| // For any other values, WaitForResult will simply set its internal "result_" |
| // field. To retrieve the final result of the operation (i.e., the result |
| // the callback has run, if necessary), call result(). You can call result() |
| // as many times as necessary until a new pp::CompletionCallback is retrieved. |
| // |
| // In some cases, you may want to check that the callback was invoked in the |
| // expected way (i.e., if the callback was "Required", then it should be |
| // invoked asynchronously). Within the body of a test (where returning a non- |
| // empty string indicates test failure), you can use the |
| // CHECK_CALLBACK_BEHAVIOR(callback) macro. From within a helper function, |
| // you can use failed() and errors(). |
| // |
| // Example usage within a test: |
| // callback.WaitForResult(foo.DoSomething(callback)); |
| // CHECK_CALLBACK_BEHAVIOR(callback); |
| // ASSERT_EQ(PP_OK, callback.result()); |
| // |
| // Example usage within a helper function: |
| // void HelperFunction(std::string* error_message) { |
| // callback.WaitForResult(foo.DoSomething(callback)); |
| // if (callback.failed()) |
| // error_message->assign(callback.errors()); |
| // } |
| void WaitForResult(int32_t result); |
| |
| // Used when you expect to receive either synchronous completion with PP_OK |
| // or a PP_ERROR_ABORTED asynchronously. |
| // Example usage: |
| // int32_t result = 0; |
| // { |
| // pp::URLLoader temp(instance_); |
| // result = temp.Open(request, callback); |
| // } |
| // callback.WaitForAbortResult(result); |
| // CHECK_CALLBACK_BEHAVIOR(callback); |
| void WaitForAbortResult(int32_t result); |
| |
| // Retrieve a pp::CompletionCallback for use in testing. This Reset()s the |
| // TestCompletionCallback. |
| pp::CompletionCallback GetCallback(); |
| |
| bool failed() { return !errors_.empty(); } |
| const std::string& errors() { return errors_; } |
| |
| int32_t result() const { return result_; } |
| |
| // Reset so that this callback can be used again. |
| void Reset(); |
| |
| CallbackType callback_type() { return callback_type_; } |
| void set_target_loop(const pp::MessageLoop& loop) { target_loop_ = loop; } |
| static void Handler(void* user_data, int32_t result); |
| |
| protected: |
| void RunMessageLoop(); |
| void QuitMessageLoop(); |
| |
| // Used to check that WaitForResult is only called once for each usage of the |
| // callback. |
| bool wait_for_result_called_; |
| // Indicates whether we have already been invoked. |
| bool have_result_; |
| // The last result received (or PP_OK_COMPLETIONCALLBACK if none). |
| int32_t result_; |
| CallbackType callback_type_; |
| bool post_quit_task_; |
| std::string errors_; |
| PP_Instance instance_; |
| Delegate* delegate_; |
| pp::MessageLoop target_loop_; |
| }; |
| |
| template <typename OutputT> |
| class TestCompletionCallbackWithOutput { |
| public: |
| explicit TestCompletionCallbackWithOutput(PP_Instance instance) |
| : callback_(instance), |
| output_storage_() { |
| pp::internal::CallbackOutputTraits<OutputT>::Initialize(&output_storage_); |
| } |
| |
| TestCompletionCallbackWithOutput(PP_Instance instance, bool force_async) |
| : callback_(instance, force_async), |
| output_storage_() { |
| pp::internal::CallbackOutputTraits<OutputT>::Initialize(&output_storage_); |
| } |
| |
| TestCompletionCallbackWithOutput(PP_Instance instance, |
| CallbackType callback_type) |
| : callback_(instance, callback_type), |
| output_storage_() { |
| pp::internal::CallbackOutputTraits<OutputT>::Initialize(&output_storage_); |
| } |
| |
| pp::CompletionCallbackWithOutput<OutputT> GetCallback(); |
| OutputT output() { |
| return pp::internal::CallbackOutputTraits<OutputT>::StorageToPluginArg( |
| output_storage_); |
| } |
| |
| // Delegate functions to TestCompletionCallback |
| void SetDelegate(TestCompletionCallback::Delegate* delegate) { |
| callback_.SetDelegate(delegate); |
| } |
| void WaitForResult(int32_t result) { callback_.WaitForResult(result); } |
| void WaitForAbortResult(int32_t result) { |
| callback_.WaitForAbortResult(result); |
| } |
| bool failed() { return callback_.failed(); } |
| const std::string& errors() { return callback_.errors(); } |
| int32_t result() const { return callback_.result(); } |
| void Reset() { |
| pp::internal::CallbackOutputTraits<OutputT>::Initialize(&output_storage_); |
| return callback_.Reset(); |
| } |
| |
| private: |
| TestCompletionCallback callback_; |
| typename pp::CompletionCallbackWithOutput<OutputT>::OutputStorageType |
| output_storage_; |
| }; |
| |
| template <typename OutputT> |
| pp::CompletionCallbackWithOutput<OutputT> |
| TestCompletionCallbackWithOutput<OutputT>::GetCallback() { |
| this->Reset(); |
| if (callback_.callback_type() == PP_BLOCKING) { |
| pp::CompletionCallbackWithOutput<OutputT> cc(&output_storage_); |
| return cc; |
| } |
| |
| callback_.set_target_loop(pp::MessageLoop::GetCurrent()); |
| pp::CompletionCallbackWithOutput<OutputT> cc( |
| &TestCompletionCallback::Handler, |
| this, |
| &output_storage_); |
| if (callback_.callback_type() == PP_OPTIONAL) |
| cc.set_flags(PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); |
| return cc; |
| } |
| |
| // Verifies that the callback didn't record any errors. If the callback is run |
| // in an unexpected way (e.g., if it's invoked asynchronously when the call |
| // should have blocked), this returns an appropriate error string. |
| #define CHECK_CALLBACK_BEHAVIOR(callback) \ |
| do { \ |
| if ((callback).failed()) \ |
| return MakeFailureMessage(__FILE__, __LINE__, \ |
| (callback).errors().c_str()); \ |
| } while (false) |
| |
| /* |
| * A set of macros to use for platform detection. These were largely copied |
| * from chromium's build_config.h. |
| */ |
| #if defined(__APPLE__) |
| #define PPAPI_OS_MACOSX 1 |
| #elif defined(ANDROID) |
| #define PPAPI_OS_ANDROID 1 |
| #elif defined(__native_client__) |
| #define PPAPI_OS_NACL 1 |
| #elif defined(__linux__) |
| #define PPAPI_OS_LINUX 1 |
| #elif defined(_WIN32) |
| #define PPAPI_OS_WIN 1 |
| #elif defined(__FreeBSD__) |
| #define PPAPI_OS_FREEBSD 1 |
| #elif defined(__OpenBSD__) |
| #define PPAPI_OS_OPENBSD 1 |
| #elif defined(__sun) |
| #define PPAPI_OS_SOLARIS 1 |
| #else |
| #error Please add support for your platform in ppapi/tests/test_utils.h |
| #endif |
| |
| /* These are used to determine POSIX-like implementations vs Windows. */ |
| #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ |
| defined(__OpenBSD__) || defined(__sun) || defined(__native_client__) |
| #define PPAPI_POSIX 1 |
| #endif |
| |
| // By default, ArrayBuffers over a certain size are sent via shared memory. In |
| // order to test for this without sending huge buffers, tests can use this |
| // class to set the minimum array buffer size used for shared memory temporarily |
| // lower. |
| class ScopedArrayBufferSizeSetter { |
| public: |
| ScopedArrayBufferSizeSetter(const PPB_Testing_Private* interface, |
| PP_Instance instance, |
| uint32_t threshold) |
| : interface_(interface), |
| instance_(instance) { |
| interface_->SetMinimumArrayBufferSizeForShmem(instance_, threshold); |
| } |
| ~ScopedArrayBufferSizeSetter() { |
| interface_->SetMinimumArrayBufferSizeForShmem(instance_, 0); |
| } |
| private: |
| const PPB_Testing_Private* interface_; |
| PP_Instance instance_; |
| }; |
| |
| #endif // PPAPI_TESTS_TEST_UTILS_H_ |