chromium/ppapi/tests/test_utils.h

// Copyright 2012 The Chromium Authors
// 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;
class URLLoader;
class URLRequestInfo;
}

// 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);

// Returns the host and port of the current document's URL (Which is generally
// served by an EmbeddedTestServer). Returns false on failure.
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&);
};

// Returns a callback that does nothing, so can be invoked when the current
// function is out of scope, unlike TestCompletionCallback.
pp::CompletionCallback DoNothingCallback();

template <typename OutputT>
void DeleteStorage(void* user_data, int32_t flags) {
  typename pp::CompletionCallbackWithOutput<OutputT>::OutputStorageType*
      storage = reinterpret_cast<typename pp::CompletionCallbackWithOutput<
          OutputT>::OutputStorageType*>(user_data);
  delete storage;
}

// Same as DoNothingCallback(), but with an OutputStorageType, which it deletes
// when the callback is invoked.
template <typename OutputT>
pp::CompletionCallbackWithOutput<OutputT> DoNothingCallbackWithOutput() {
  typename pp::CompletionCallbackWithOutput<OutputT>::OutputStorageType*
      storage = new
      typename pp::CompletionCallbackWithOutput<OutputT>::OutputStorageType();
  return pp::CompletionCallbackWithOutput<OutputT>(
      &DeleteStorage<OutputT>, storage, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL,
      storage);
}

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_;
};

// Opens |request| in |loader| and returns the results of the URLRequest.  The
// caller may provide the optional |response_body| argument to get the contents
// of the body of the response to the URLRequest.
//
// Returns PP_OK upon success.
int32_t OpenURLRequest(PP_Instance instance,
                       pp::URLLoader* loader,
                       const pp::URLRequestInfo& request,
                       CallbackType callback_type,
                       std::string* response_body);

#endif  // PPAPI_TESTS_TEST_UTILS_H_