text: // 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/dev/ppb_testing_dev.h"
  #include "ppapi/c/pp_instance.h"
  #include "ppapi/c/pp_stdint.h"
  #include "ppapi/cpp/completion_callback.h"
  #include "ppapi/cpp/message_loop.h"
  #include "ppapi/utility/completion_callback_factory.h"
  
  // Timeout to wait for some action to complete.
  extern const int kActionTimeoutMs;
  
  const PPB_Testing_Dev* 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);
  
  // 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 message 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 message 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 message 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; }
  
    // Waits for the callback to be called and returns the
    // result. Returns immediately if the callback was previously called
    // and the result wasn't returned (i.e. each result value received
    // by the callback is returned by WaitForResult() once and only
    // once). DEPRECATED: Please use the one below.
    // TODO(dmichael): Remove this one when all the tests are updated.
    int32_t WaitForResult();
  
    // 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) {
    }
  
    TestCompletionCallbackWithOutput(PP_Instance instance, bool force_async) :
      callback_(instance, force_async) {
    }
  
    TestCompletionCallbackWithOutput(PP_Instance instance,
                                     CallbackType callback_type) :
      callback_(instance, callback_type) {
    }
  
    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() { return callback_.Reset(); }
  
   private:
    TestCompletionCallback callback_;
    typename pp::CompletionCallbackWithOutput<OutputT>::OutputStorageType
        output_storage_;
  };
  
  template <typename OutputT>
  pp::CompletionCallbackWithOutput<OutputT>
  TestCompletionCallbackWithOutput<OutputT>::GetCallback() {
    callback_.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
  
  #endif  // PPAPI_TESTS_TEST_UTILS_H_
  