root/ppapi/tests/test_websocket.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. resource_
  2. OnCallback
  3. instance_
  4. WebSocketDidOpen
  5. WebSocketDidClose
  6. HandleWebSocketMessage
  7. HandleWebSocketError
  8. WaitForConnected
  9. WaitForReceived
  10. WaitForClosed
  11. GetSeenEvents
  12. Init
  13. RunTests
  14. GetFullURL
  15. CreateVarString
  16. CreateVarBinary
  17. ReleaseVar
  18. AreEqualWithString
  19. AreEqualWithBinary
  20. Connect
  21. Send
  22. TestIsWebSocket
  23. TestUninitializedPropertiesAccess
  24. TestInvalidConnect
  25. TestProtocols
  26. TestGetURL
  27. TestValidConnect
  28. TestInvalidClose
  29. TestValidClose
  30. TestGetProtocol
  31. TestTextSendReceive
  32. TestTextSendReceiveTwice
  33. TestBinarySendReceive
  34. TestStressedSendReceive
  35. TestBufferedAmount
  36. TestAbortCallsWithCallback
  37. TestAbortSendMessageCall
  38. TestAbortCloseCall
  39. TestAbortReceiveMessageCall
  40. TestCcInterfaces
  41. TestUtilityInvalidConnect
  42. TestUtilityProtocols
  43. TestUtilityGetURL
  44. TestUtilityValidConnect
  45. TestUtilityInvalidClose
  46. TestUtilityValidClose
  47. TestUtilityGetProtocol
  48. TestUtilityTextSendReceive
  49. TestUtilityBinarySendReceive
  50. TestUtilityBufferedAmount

// 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.

#include "ppapi/tests/test_websocket.h"

#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include "ppapi/c/pp_bool.h"
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/c/ppb_var.h"
#include "ppapi/c/ppb_var_array_buffer.h"
#include "ppapi/c/ppb_websocket.h"
#include "ppapi/c/private/ppb_testing_private.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var_array_buffer.h"
#include "ppapi/cpp/websocket.h"
#include "ppapi/tests/test_utils.h"
#include "ppapi/tests/testing_instance.h"
#include "ppapi/utility/websocket/websocket_api.h"

// net::SpawnedTestServer serves WebSocket service for testing.
// Following URLs are handled by pywebsocket handlers in
// net/data/websocket/*_wsh.py.
const char kEchoServerURL[] = "echo-with-no-extension";
const char kCloseServerURL[] = "close";
const char kCloseWithCodeAndReasonServerURL[] = "close-code-and-reason";
const char kProtocolTestServerURL[] = "protocol-test?protocol=";

const char* const kInvalidURLs[] = {
  "http://www.google.com/invalid_scheme",
  "ws://www.google.com/invalid#fragment",
  "ws://www.google.com:65535/invalid_port",
  NULL
};

// Internal packet sizes.
const uint64_t kMessageFrameOverhead = 6;

namespace {

struct WebSocketEvent {
  enum EventType {
    EVENT_OPEN,
    EVENT_MESSAGE,
    EVENT_ERROR,
    EVENT_CLOSE
  };

  WebSocketEvent(EventType type,
                 bool was_clean,
                 uint16_t close_code,
                 const pp::Var& var)
      : event_type(type),
        was_clean(was_clean),
        close_code(close_code),
        var(var) {
  }
  EventType event_type;
  bool was_clean;
  uint16_t close_code;
  pp::Var var;
};

class ReleaseResourceDelegate : public TestCompletionCallback::Delegate {
 public:
  explicit ReleaseResourceDelegate(const PPB_Core* core_interface,
                                   PP_Resource resource)
      : core_interface_(core_interface),
        resource_(resource) {
  }

  // TestCompletionCallback::Delegate implementation.
  virtual void OnCallback(void* user_data, int32_t result) {
    if (resource_)
      core_interface_->ReleaseResource(resource_);
  }

 private:
  const PPB_Core* core_interface_;
  PP_Resource resource_;
};

class TestWebSocketAPI : public pp::WebSocketAPI {
 public:
  explicit TestWebSocketAPI(pp::Instance* instance)
      : pp::WebSocketAPI(instance),
        connected_(false),
        received_(false),
        closed_(false),
        wait_for_connected_(false),
        wait_for_received_(false),
        wait_for_closed_(false),
        instance_(instance->pp_instance()) {
  }

  virtual void WebSocketDidOpen() {
    events_.push_back(
        WebSocketEvent(WebSocketEvent::EVENT_OPEN, true, 0U, pp::Var()));
    connected_ = true;
    if (wait_for_connected_) {
      GetTestingInterface()->QuitMessageLoop(instance_);
      wait_for_connected_ = false;
    }
  }

  virtual void WebSocketDidClose(
      bool was_clean, uint16_t code, const pp::Var& reason) {
    events_.push_back(
        WebSocketEvent(WebSocketEvent::EVENT_CLOSE, was_clean, code, reason));
    connected_ = true;
    closed_ = true;
    if (wait_for_connected_ || wait_for_closed_) {
      GetTestingInterface()->QuitMessageLoop(instance_);
      wait_for_connected_ = false;
      wait_for_closed_ = false;
    }
  }

  virtual void HandleWebSocketMessage(const pp::Var &message) {
    events_.push_back(
        WebSocketEvent(WebSocketEvent::EVENT_MESSAGE, true, 0U, message));
    received_ = true;
    if (wait_for_received_) {
      GetTestingInterface()->QuitMessageLoop(instance_);
      wait_for_received_ = false;
      received_ = false;
    }
  }

  virtual void HandleWebSocketError() {
    events_.push_back(
        WebSocketEvent(WebSocketEvent::EVENT_ERROR, true, 0U, pp::Var()));
  }

  void WaitForConnected() {
    if (!connected_) {
      wait_for_connected_ = true;
      GetTestingInterface()->RunMessageLoop(instance_);
    }
  }

  void WaitForReceived() {
    if (!received_) {
      wait_for_received_ = true;
      GetTestingInterface()->RunMessageLoop(instance_);
    }
  }

  void WaitForClosed() {
    if (!closed_) {
      wait_for_closed_ = true;
      GetTestingInterface()->RunMessageLoop(instance_);
    }
  }

  const std::vector<WebSocketEvent>& GetSeenEvents() const {
    return events_;
  }

 private:
  std::vector<WebSocketEvent> events_;
  bool connected_;
  bool received_;
  bool closed_;
  bool wait_for_connected_;
  bool wait_for_received_;
  bool wait_for_closed_;
  PP_Instance instance_;
};

}  // namespace

REGISTER_TEST_CASE(WebSocket);

bool TestWebSocket::Init() {
  websocket_interface_ = static_cast<const PPB_WebSocket*>(
      pp::Module::Get()->GetBrowserInterface(PPB_WEBSOCKET_INTERFACE));
  var_interface_ = static_cast<const PPB_Var*>(
      pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE));
  arraybuffer_interface_ = static_cast<const PPB_VarArrayBuffer*>(
      pp::Module::Get()->GetBrowserInterface(
          PPB_VAR_ARRAY_BUFFER_INTERFACE));
  core_interface_ = static_cast<const PPB_Core*>(
      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
  if (!websocket_interface_ || !var_interface_ || !arraybuffer_interface_ ||
      !core_interface_)
    return false;

  return CheckTestingInterface();
}

void TestWebSocket::RunTests(const std::string& filter) {
  RUN_TEST_WITH_REFERENCE_CHECK(IsWebSocket, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UninitializedPropertiesAccess, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(InvalidConnect, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(Protocols, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(GetURL, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(ValidConnect, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(InvalidClose, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(ValidClose, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(GetProtocol, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(TextSendReceive, filter);
  RUN_TEST_BACKGROUND(TestWebSocket, TextSendReceiveTwice, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(BinarySendReceive, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(StressedSendReceive, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(BufferedAmount, filter);
  // PP_Resource for WebSocket may be released later because of an internal
  // reference for asynchronous IPC handling. So, suppress reference check on
  // the following AbortCallsWithCallback test.
  RUN_TEST(AbortCallsWithCallback, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(AbortSendMessageCall, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(AbortCloseCall, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(AbortReceiveMessageCall, filter);

  RUN_TEST_WITH_REFERENCE_CHECK(CcInterfaces, filter);

  RUN_TEST_WITH_REFERENCE_CHECK(UtilityInvalidConnect, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityProtocols, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityGetURL, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityValidConnect, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityInvalidClose, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityValidClose, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityGetProtocol, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityTextSendReceive, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityBinarySendReceive, filter);
  RUN_TEST_WITH_REFERENCE_CHECK(UtilityBufferedAmount, filter);
}

std::string TestWebSocket::GetFullURL(const char* url) {
  std::string rv = "ws://";
  // Some WebSocket tests don't start the server so there'll be no host and
  // port.
  if (instance_->websocket_host().empty())
    rv += "127.0.0.1";
  else
    rv += instance_->websocket_host();
  if (instance_->websocket_port() != -1) {
    char buffer[10];
    sprintf(buffer, ":%d", instance_->websocket_port());
    rv += std::string(buffer);
  }
  rv += "/";
  rv += url;
  return rv;
}

PP_Var TestWebSocket::CreateVarString(const std::string& string) {
  return var_interface_->VarFromUtf8(string.c_str(), string.size());
}

PP_Var TestWebSocket::CreateVarBinary(const std::vector<uint8_t>& binary) {
  PP_Var var = arraybuffer_interface_->Create(binary.size());
  uint8_t* var_data = static_cast<uint8_t*>(arraybuffer_interface_->Map(var));
  std::copy(binary.begin(), binary.end(), var_data);
  return var;
}

void TestWebSocket::ReleaseVar(const PP_Var& var) {
  var_interface_->Release(var);
}

bool TestWebSocket::AreEqualWithString(const PP_Var& var,
                                       const std::string& string) {
  if (var.type != PP_VARTYPE_STRING)
    return false;
  uint32_t utf8_length;
  const char* utf8 = var_interface_->VarToUtf8(var, &utf8_length);
  if (utf8_length != string.size())
    return false;
  if (string.compare(utf8))
    return false;
  return true;
}

bool TestWebSocket::AreEqualWithBinary(const PP_Var& var,
                                       const std::vector<uint8_t>& binary) {
  uint32_t buffer_size = 0;
  PP_Bool success = arraybuffer_interface_->ByteLength(var, &buffer_size);
  if (!success || buffer_size != binary.size())
    return false;
  if (!std::equal(binary.begin(), binary.end(),
      static_cast<uint8_t*>(arraybuffer_interface_->Map(var))))
    return false;
  return true;
}

PP_Resource TestWebSocket::Connect(const std::string& url,
                                   int32_t* result,
                                   const std::string& protocol) {
  PP_Var protocols[] = { PP_MakeUndefined() };
  PP_Resource ws = websocket_interface_->Create(instance_->pp_instance());
  if (!ws)
    return 0;
  PP_Var url_var = CreateVarString(url);
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  uint32_t protocol_count = 0U;
  if (protocol.size()) {
    protocols[0] = CreateVarString(protocol);
    protocol_count = 1U;
  }
  callback.WaitForResult(websocket_interface_->Connect(
      ws, url_var, protocols, protocol_count,
      callback.GetCallback().pp_completion_callback()));
  ReleaseVar(url_var);
  if (protocol.size())
    ReleaseVar(protocols[0]);
  *result = callback.result();
  return ws;
}

void TestWebSocket::Send(int32_t /* result */, PP_Resource ws,
                         const std::string& message) {
  PP_Var message_var = CreateVarString(message);
  websocket_interface_->SendMessage(ws, message_var);
  ReleaseVar(message_var);
}

std::string TestWebSocket::TestIsWebSocket() {
  // Test that a NULL resource isn't a websocket.
  pp::Resource null_resource;
  PP_Bool result =
      websocket_interface_->IsWebSocket(null_resource.pp_resource());
  ASSERT_FALSE(result);

  PP_Resource ws = websocket_interface_->Create(instance_->pp_instance());
  ASSERT_TRUE(ws);

  result = websocket_interface_->IsWebSocket(ws);
  ASSERT_TRUE(result);

  core_interface_->ReleaseResource(ws);

  PASS();
}

std::string TestWebSocket::TestUninitializedPropertiesAccess() {
  PP_Resource ws = websocket_interface_->Create(instance_->pp_instance());
  ASSERT_TRUE(ws);

  uint64_t bufferedAmount = websocket_interface_->GetBufferedAmount(ws);
  ASSERT_EQ(0U, bufferedAmount);

  uint16_t close_code = websocket_interface_->GetCloseCode(ws);
  ASSERT_EQ(0U, close_code);

  PP_Var close_reason = websocket_interface_->GetCloseReason(ws);
  ASSERT_TRUE(AreEqualWithString(close_reason, std::string()));
  ReleaseVar(close_reason);

  PP_Bool close_was_clean = websocket_interface_->GetCloseWasClean(ws);
  ASSERT_EQ(PP_FALSE, close_was_clean);

  PP_Var extensions = websocket_interface_->GetExtensions(ws);
  ASSERT_TRUE(AreEqualWithString(extensions, std::string()));
  ReleaseVar(extensions);

  PP_Var protocol = websocket_interface_->GetProtocol(ws);
  ASSERT_TRUE(AreEqualWithString(protocol, std::string()));
  ReleaseVar(protocol);

  PP_WebSocketReadyState ready_state =
      websocket_interface_->GetReadyState(ws);
  ASSERT_EQ(PP_WEBSOCKETREADYSTATE_INVALID, ready_state);

  PP_Var url = websocket_interface_->GetURL(ws);
  ASSERT_TRUE(AreEqualWithString(url, std::string()));
  ReleaseVar(url);

  core_interface_->ReleaseResource(ws);

  PASS();
}

std::string TestWebSocket::TestInvalidConnect() {
  PP_Var protocols[] = { PP_MakeUndefined() };

  PP_Resource ws = websocket_interface_->Create(instance_->pp_instance());
  ASSERT_TRUE(ws);

  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(websocket_interface_->Connect(
      ws, PP_MakeUndefined(), protocols, 1U,
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());

  callback.WaitForResult(websocket_interface_->Connect(
      ws, PP_MakeUndefined(), protocols, 1U,
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_INPROGRESS, callback.result());

  core_interface_->ReleaseResource(ws);

  for (int i = 0; kInvalidURLs[i]; ++i) {
    int32_t result;
    ws = Connect(kInvalidURLs[i], &result, std::string());
    ASSERT_TRUE(ws);
    ASSERT_EQ(PP_ERROR_BADARGUMENT, result);

    core_interface_->ReleaseResource(ws);
  }

  PASS();
}

std::string TestWebSocket::TestProtocols() {
  PP_Var url = CreateVarString(GetFullURL(kEchoServerURL).c_str());
  PP_Var bad_protocols[] = {
    CreateVarString("x-test"),
    CreateVarString("x-test")
  };
  PP_Var good_protocols[] = {
    CreateVarString("x-test"),
    CreateVarString("x-yatest")
  };

  PP_Resource ws = websocket_interface_->Create(instance_->pp_instance());
  ASSERT_TRUE(ws);
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(websocket_interface_->Connect(
      ws, url, bad_protocols, 2U,
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
  core_interface_->ReleaseResource(ws);

  ws = websocket_interface_->Create(instance_->pp_instance());
  ASSERT_TRUE(ws);
  int32_t result = websocket_interface_->Connect(
      ws, url, good_protocols, 2U, PP_BlockUntilComplete());
  ASSERT_EQ(PP_ERROR_BLOCKS_MAIN_THREAD, result);
  core_interface_->ReleaseResource(ws);

  ReleaseVar(url);
  for (int i = 0; i < 2; ++i) {
    ReleaseVar(bad_protocols[i]);
    ReleaseVar(good_protocols[i]);
  }
  core_interface_->ReleaseResource(ws);

  PASS();
}

std::string TestWebSocket::TestGetURL() {
  for (int i = 0; kInvalidURLs[i]; ++i) {
    int32_t result;
    PP_Resource ws = Connect(kInvalidURLs[i], &result, std::string());
    ASSERT_TRUE(ws);
    PP_Var url = websocket_interface_->GetURL(ws);
    ASSERT_TRUE(AreEqualWithString(url, kInvalidURLs[i]));
    ASSERT_EQ(PP_ERROR_BADARGUMENT, result);

    ReleaseVar(url);
    core_interface_->ReleaseResource(ws);
  }

  PASS();
}

std::string TestWebSocket::TestValidConnect() {
  int32_t result;
  PP_Resource ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  PP_Var extensions = websocket_interface_->GetExtensions(ws);
  ASSERT_TRUE(AreEqualWithString(extensions, std::string()));
  core_interface_->ReleaseResource(ws);
  ReleaseVar(extensions);

  PASS();
}

std::string TestWebSocket::TestInvalidClose() {
  PP_Var reason = CreateVarString("close for test");
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  TestCompletionCallback async_callback(instance_->pp_instance(), PP_REQUIRED);

  // Close before connect.
  PP_Resource ws = websocket_interface_->Create(instance_->pp_instance());
  callback.WaitForResult(websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
  core_interface_->ReleaseResource(ws);

  // Close with bad arguments.
  int32_t result;
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  callback.WaitForResult(websocket_interface_->Close(
      ws, 1U, reason, callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_NOACCESS, callback.result());
  core_interface_->ReleaseResource(ws);

  // Close with PP_VARTYPE_NULL.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  callback.WaitForResult(websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeNull(),
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
  core_interface_->ReleaseResource(ws);

  // Close with PP_VARTYPE_NULL and ongoing receive message.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  PP_Var receive_message_var;
  result = websocket_interface_->ReceiveMessage(
      ws, &receive_message_var,
      async_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  callback.WaitForResult(websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeNull(),
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
  const char* send_message = "hi";
  PP_Var send_message_var = CreateVarString(send_message);
  result = websocket_interface_->SendMessage(ws, send_message_var);
  ReleaseVar(send_message_var);
  ASSERT_EQ(PP_OK, result);
  async_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_OK, async_callback.result());
  ASSERT_TRUE(AreEqualWithString(receive_message_var, send_message));
  ReleaseVar(receive_message_var);
  core_interface_->ReleaseResource(ws);

  // Close twice.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      async_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  // Call another Close() before previous one is in progress.
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_ERROR_INPROGRESS, result);
  async_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_OK, async_callback.result());
  // Call another Close() after previous one is completed.
  // This Close() must do nothing and reports no error.
  callback.WaitForResult(websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_OK, callback.result());
  core_interface_->ReleaseResource(ws);

  ReleaseVar(reason);

  PASS();
}

std::string TestWebSocket::TestValidClose() {
  PP_Var reason = CreateVarString("close for test");
  PP_Var url = CreateVarString(GetFullURL(kEchoServerURL).c_str());
  PP_Var protocols[] = { PP_MakeUndefined() };
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  TestCompletionCallback another_callback(
      instance_->pp_instance(), callback_type());

  // Close.
  int32_t result;
  PP_Resource ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  callback.WaitForResult(websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      callback.GetCallback().pp_completion_callback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());
  core_interface_->ReleaseResource(ws);

  // Close without code and reason.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  callback.WaitForResult(websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED, reason,
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_OK, callback.result());
  core_interface_->ReleaseResource(ws);

  // Close with PP_VARTYPE_UNDEFINED.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  callback.WaitForResult(websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeUndefined(),
      callback.GetCallback().pp_completion_callback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());
  core_interface_->ReleaseResource(ws);

  // Close in connecting.
  // The ongoing connect failed with PP_ERROR_ABORTED, then the close is done
  // successfully.
  ws = websocket_interface_->Create(instance_->pp_instance());
  result = websocket_interface_->Connect(
      ws, url, protocols, 0U, callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      another_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
  another_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_OK, another_callback.result());
  core_interface_->ReleaseResource(ws);

  // Close in closing.
  // The first close will be done successfully, then the second one failed with
  // with PP_ERROR_INPROGRESS immediately.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      another_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_ERROR_INPROGRESS, result);
  callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_OK, callback.result());
  core_interface_->ReleaseResource(ws);

  // Close with ongoing receive message.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  PP_Var receive_message_var;
  result = websocket_interface_->ReceiveMessage(
      ws, &receive_message_var,
      callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      another_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
  another_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_OK, another_callback.result());
  core_interface_->ReleaseResource(ws);

  // Close with PP_VARTYPE_UNDEFINED and ongoing receive message.
  ws = Connect(GetFullURL(kEchoServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  result = websocket_interface_->ReceiveMessage(
      ws, &receive_message_var,
      callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeUndefined(),
      another_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
  another_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
  ASSERT_EQ(PP_OK, another_callback.result());
  core_interface_->ReleaseResource(ws);

  // Server initiated closing handshake.
  ws = Connect(
      GetFullURL(kCloseWithCodeAndReasonServerURL), &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  // Text messsage "1000 bye" requests the server to initiate closing handshake
  // with code being 1000 and reason being "bye".
  PP_Var close_request_var = CreateVarString("1000 bye");
  result = websocket_interface_->SendMessage(ws, close_request_var);
  ReleaseVar(close_request_var);
  callback.WaitForResult(websocket_interface_->ReceiveMessage(
      ws, &receive_message_var,
      callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
  core_interface_->ReleaseResource(ws);

  ReleaseVar(reason);
  ReleaseVar(url);

  PASS();
}

std::string TestWebSocket::TestGetProtocol() {
  const char* expected_protocols[] = {
    "x-chat",
    "hoehoe",
    NULL
  };
  for (int i = 0; expected_protocols[i]; ++i) {
    std::string url(GetFullURL(kProtocolTestServerURL));
    url += expected_protocols[i];
    int32_t result;
    PP_Resource ws = Connect(url.c_str(), &result, expected_protocols[i]);
    ASSERT_TRUE(ws);
    ASSERT_EQ(PP_OK, result);

    PP_Var protocol = websocket_interface_->GetProtocol(ws);
    ASSERT_TRUE(AreEqualWithString(protocol, expected_protocols[i]));

    ReleaseVar(protocol);
    core_interface_->ReleaseResource(ws);
  }

  PASS();
}

std::string TestWebSocket::TestTextSendReceive() {
  // Connect to test echo server.
  int32_t connect_result;
  PP_Resource ws =
      Connect(GetFullURL(kEchoServerURL), &connect_result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, connect_result);

  // Send 'hello pepper' text message.
  const char* message = "hello pepper";
  PP_Var message_var = CreateVarString(message);
  int32_t result = websocket_interface_->SendMessage(ws, message_var);
  ReleaseVar(message_var);
  ASSERT_EQ(PP_OK, result);

  // Receive echoed 'hello pepper'.
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  PP_Var received_message;
  callback.WaitForResult(websocket_interface_->ReceiveMessage(
      ws, &received_message, callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_OK, callback.result());
  ASSERT_TRUE(AreEqualWithString(received_message, message));
  ReleaseVar(received_message);
  core_interface_->ReleaseResource(ws);

  PASS();
}

// Run as a BACKGROUND test.
std::string TestWebSocket::TestTextSendReceiveTwice() {
  // Connect to test echo server.
  int32_t connect_result;
  PP_Resource ws =
      Connect(GetFullURL(kEchoServerURL), &connect_result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, connect_result);
  pp::MessageLoop message_loop = pp::MessageLoop::GetCurrent();
  pp::CompletionCallbackFactory<TestWebSocket> factory(this);

  message_loop.PostWork(factory.NewCallback(&TestWebSocket::Send,
                                            ws, std::string("hello")));
  // When the server receives 'Goodbye', it closes the session.
  message_loop.PostWork(factory.NewCallback(&TestWebSocket::Send,
                                            ws, std::string("Goodbye")));
  message_loop.PostQuit(false);
  message_loop.Run();

  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  PP_Var received_message;
  int32_t result = websocket_interface_->ReceiveMessage(
      ws, &received_message, callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK, result);
  // Since we don't run the message loop, the callback will stay
  // "pending and scheduled to run" state.

  // Waiting for the connection close which will be done by the server.
  while (true) {
    PP_WebSocketReadyState ready_state =
        websocket_interface_->GetReadyState(ws);
    if (ready_state != PP_WEBSOCKETREADYSTATE_CONNECTING &&
        ready_state != PP_WEBSOCKETREADYSTATE_OPEN) {
      break;
    }
    PlatformSleep(100);  // 100ms
  }

  // Cleanup the message loop
  message_loop.PostQuit(false);
  message_loop.Run();

  ASSERT_EQ(PP_OK, callback.result());
  ASSERT_TRUE(AreEqualWithString(received_message, "hello"));
  ReleaseVar(received_message);
  core_interface_->ReleaseResource(ws);
  PASS();
}

std::string TestWebSocket::TestBinarySendReceive() {
  // Connect to test echo server.
  int32_t connect_result;
  PP_Resource ws =
      Connect(GetFullURL(kEchoServerURL), &connect_result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, connect_result);

  // Send binary message.
  std::vector<uint8_t> binary(256);
  for (uint32_t i = 0; i < binary.size(); ++i)
    binary[i] = i;
  PP_Var message_var = CreateVarBinary(binary);
  int32_t result = websocket_interface_->SendMessage(ws, message_var);
  ReleaseVar(message_var);
  ASSERT_EQ(PP_OK, result);

  // Receive echoed binary.
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  PP_Var received_message;
  callback.WaitForResult(websocket_interface_->ReceiveMessage(
      ws, &received_message, callback.GetCallback().pp_completion_callback()));
  ASSERT_EQ(PP_OK, callback.result());
  ASSERT_TRUE(AreEqualWithBinary(received_message, binary));
  ReleaseVar(received_message);
  core_interface_->ReleaseResource(ws);

  PASS();
}

std::string TestWebSocket::TestStressedSendReceive() {
  // Connect to test echo server.
  int32_t connect_result;
  PP_Resource ws =
      Connect(GetFullURL(kEchoServerURL), &connect_result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, connect_result);

  // Prepare PP_Var objects to send.
  const char* text = "hello pepper";
  PP_Var text_var = CreateVarString(text);
  std::vector<uint8_t> binary(256);
  for (uint32_t i = 0; i < binary.size(); ++i)
    binary[i] = i;
  PP_Var binary_var = CreateVarBinary(binary);
  // Prepare very large binary data over 64KiB. Object serializer in
  // ppapi_proxy has a limitation of 64KiB as maximum return PP_Var data size
  // to SRPC. In case received data over 64KiB exists, a specific code handles
  // this large data via asynchronous callback from main thread. This data
  // intends to test the code.
  std::vector<uint8_t> large_binary(65 * 1024);
  for (uint32_t i = 0; i < large_binary.size(); ++i)
    large_binary[i] = i & 0xff;
  PP_Var large_binary_var = CreateVarBinary(large_binary);

  // Send many messages.
  int32_t result;
  for (int i = 0; i < 256; ++i) {
    result = websocket_interface_->SendMessage(ws, text_var);
    ASSERT_EQ(PP_OK, result);
    result = websocket_interface_->SendMessage(ws, binary_var);
    ASSERT_EQ(PP_OK, result);
  }
  result = websocket_interface_->SendMessage(ws, large_binary_var);
  ASSERT_EQ(PP_OK, result);
  ReleaseVar(text_var);
  ReleaseVar(binary_var);
  ReleaseVar(large_binary_var);

  // Receive echoed data.
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  for (int i = 0; i <= 512; ++i) {
    PP_Var received_message;
    callback.WaitForResult(websocket_interface_->ReceiveMessage(
        ws, &received_message,
        callback.GetCallback().pp_completion_callback()));
    ASSERT_EQ(PP_OK, callback.result());
    if (i == 512) {
      ASSERT_TRUE(AreEqualWithBinary(received_message, large_binary));
    } else if (i & 1) {
      ASSERT_TRUE(AreEqualWithBinary(received_message, binary));
    } else {
      ASSERT_TRUE(AreEqualWithString(received_message, text));
    }
    ReleaseVar(received_message);
  }
  core_interface_->ReleaseResource(ws);

  PASS();
}

std::string TestWebSocket::TestBufferedAmount() {
  // Connect to test echo server.
  int32_t connect_result;
  PP_Resource ws =
      Connect(GetFullURL(kEchoServerURL), &connect_result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, connect_result);

  // Prepare a large message that is not aligned with the internal buffer
  // sizes.
  std::string message(8193, 'x');
  PP_Var message_var = CreateVarString(message);

  uint64_t buffered_amount = 0;
  int32_t result;
  for (int i = 0; i < 100; i++) {
    result = websocket_interface_->SendMessage(ws, message_var);
    ASSERT_EQ(PP_OK, result);
    buffered_amount = websocket_interface_->GetBufferedAmount(ws);
    // Buffered amount size 262144 is too big for the internal buffer size.
    if (buffered_amount > 262144)
      break;
  }

  // Close connection.
  std::string reason_str = "close while busy";
  PP_Var reason = CreateVarString(reason_str.c_str());
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason,
      callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  ASSERT_EQ(PP_WEBSOCKETREADYSTATE_CLOSING,
      websocket_interface_->GetReadyState(ws));

  callback.WaitForResult(result);
  ASSERT_EQ(PP_OK, callback.result());
  ASSERT_EQ(PP_WEBSOCKETREADYSTATE_CLOSED,
      websocket_interface_->GetReadyState(ws));

  uint64_t base_buffered_amount = websocket_interface_->GetBufferedAmount(ws);

  // After connection closure, all sending requests fail and just increase
  // the bufferedAmount property.
  PP_Var empty_string = CreateVarString(std::string());
  result = websocket_interface_->SendMessage(ws, empty_string);
  ASSERT_EQ(PP_ERROR_FAILED, result);
  buffered_amount = websocket_interface_->GetBufferedAmount(ws);
  ASSERT_EQ(base_buffered_amount + kMessageFrameOverhead, buffered_amount);
  base_buffered_amount = buffered_amount;

  result = websocket_interface_->SendMessage(ws, reason);
  ASSERT_EQ(PP_ERROR_FAILED, result);
  buffered_amount = websocket_interface_->GetBufferedAmount(ws);
  uint64_t reason_frame_size = kMessageFrameOverhead + reason_str.length();
  ASSERT_EQ(base_buffered_amount + reason_frame_size, buffered_amount);

  ReleaseVar(message_var);
  ReleaseVar(reason);
  ReleaseVar(empty_string);
  core_interface_->ReleaseResource(ws);

  PASS();
}

// Test abort behaviors where a WebSocket PP_Resource is released while each
// function is in-flight on the WebSocket PP_Resource.
std::string TestWebSocket::TestAbortCallsWithCallback() {
  // Following tests make sure the behavior for functions which require a
  // callback. The callback must get a PP_ERROR_ABORTED.

  // Test the behavior for Connect().
  PP_Resource ws = websocket_interface_->Create(instance_->pp_instance());
  ASSERT_TRUE(ws);
  std::string url = GetFullURL(kEchoServerURL);
  PP_Var url_var = CreateVarString(url);
  TestCompletionCallback connect_callback(
      instance_->pp_instance(), callback_type());
  int32_t result = websocket_interface_->Connect(
      ws, url_var, NULL, 0,
      connect_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  core_interface_->ReleaseResource(ws);
  connect_callback.WaitForResult(result);
  ASSERT_EQ(PP_ERROR_ABORTED, connect_callback.result());

  // Test the behavior for Close().
  ws = Connect(url, &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  PP_Var reason_var = CreateVarString("abort");
  TestCompletionCallback close_callback(
      instance_->pp_instance(), callback_type());
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason_var,
      close_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  core_interface_->ReleaseResource(ws);
  close_callback.WaitForResult(result);
  ASSERT_EQ(PP_ERROR_ABORTED, close_callback.result());
  ReleaseVar(reason_var);

  // Test the behavior for ReceiveMessage().
  // Make sure the simplest case to wait for data which never arrives, here.
  ws = Connect(url, &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  PP_Var receive_var;
  TestCompletionCallback receive_callback(
      instance_->pp_instance(), callback_type());
  result = websocket_interface_->ReceiveMessage(
      ws, &receive_var,
      receive_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  core_interface_->ReleaseResource(ws);
  receive_callback.WaitForResult(result);
  ASSERT_EQ(PP_ERROR_ABORTED, receive_callback.result());

  // Release the resource in the aborting receive completion callback which is
  // introduced by calling Close().
  ws = Connect(url, &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  result = websocket_interface_->ReceiveMessage(
      ws, &receive_var,
      receive_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  ReleaseResourceDelegate receive_delegate(core_interface_, ws);
  receive_callback.SetDelegate(&receive_delegate);
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeUndefined(),
      close_callback.GetCallback().pp_completion_callback());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  receive_callback.WaitForResult(result);
  CHECK_CALLBACK_BEHAVIOR(receive_callback);
  ASSERT_EQ(PP_ERROR_ABORTED, receive_callback.result());
  close_callback.WaitForResult(result);
  CHECK_CALLBACK_BEHAVIOR(close_callback);
  ASSERT_EQ(PP_ERROR_ABORTED, close_callback.result());

  ReleaseVar(url_var);

  PASS();
}

std::string TestWebSocket::TestAbortSendMessageCall() {
  // Test the behavior for SendMessage().
  // This function doesn't require a callback, but operation will be done
  // asynchronously in WebKit and browser process.
  std::vector<uint8_t> large_binary(65 * 1024);
  PP_Var large_var = CreateVarBinary(large_binary);

  int32_t result;
  std::string url = GetFullURL(kEchoServerURL);
  PP_Resource ws = Connect(url, &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  result = websocket_interface_->SendMessage(ws, large_var);
  ASSERT_EQ(PP_OK, result);
  core_interface_->ReleaseResource(ws);
  ReleaseVar(large_var);

  PASS();
}

std::string TestWebSocket::TestAbortCloseCall() {
  // Release the resource in the close completion callback.
  int32_t result;
  std::string url = GetFullURL(kEchoServerURL);
  PP_Resource ws = Connect(url, &result, std::string());
  ASSERT_TRUE(ws);
  ASSERT_EQ(PP_OK, result);
  TestCompletionCallback close_callback(
      instance_->pp_instance(), callback_type());
  result = websocket_interface_->Close(
      ws, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeUndefined(),
      close_callback.GetCallback().pp_completion_callback());
  ReleaseResourceDelegate close_delegate(core_interface_, ws);
  close_callback.SetDelegate(&close_delegate);
  close_callback.WaitForResult(result);
  CHECK_CALLBACK_BEHAVIOR(close_callback);
  ASSERT_EQ(PP_OK, close_callback.result());

  PASS();
}

std::string TestWebSocket::TestAbortReceiveMessageCall() {
  // Test the behavior where receive process might be in-flight.
  std::vector<uint8_t> large_binary(65 * 1024);
  PP_Var large_var = CreateVarBinary(large_binary);
  const char* text = "yukarin";
  PP_Var text_var = CreateVarString(text);

  std::string url = GetFullURL(kEchoServerURL);
  int32_t result;
  PP_Resource ws;

  // Each trial sends |trial_count| + 1 messages and receives just |trial|
  // number of message(s) before releasing the WebSocket. The WebSocket is
  // released while the next message is going to be received.
  const int trial_count = 8;
  for (int trial = 1; trial <= trial_count; trial++) {
    ws = Connect(url, &result, std::string());
    ASSERT_TRUE(ws);
    ASSERT_EQ(PP_OK, result);
    for (int i = 0; i <= trial_count; ++i) {
      result = websocket_interface_->SendMessage(ws, text_var);
      ASSERT_EQ(PP_OK, result);
    }
    TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    PP_Var var;
    for (int i = 0; i < trial; ++i) {
      callback.WaitForResult(websocket_interface_->ReceiveMessage(
          ws, &var, callback.GetCallback().pp_completion_callback()));
      ASSERT_EQ(PP_OK, callback.result());
      ASSERT_TRUE(AreEqualWithString(var, text));
      ReleaseVar(var);
    }
    result = websocket_interface_->ReceiveMessage(
        ws, &var, callback.GetCallback().pp_completion_callback());
    core_interface_->ReleaseResource(ws);
    if (result != PP_OK) {
      callback.WaitForResult(result);
      ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
    }
  }
  // Same test, but the last receiving message is large message over 64KiB.
  for (int trial = 1; trial <= trial_count; trial++) {
    ws = Connect(url, &result, std::string());
    ASSERT_TRUE(ws);
    ASSERT_EQ(PP_OK, result);
    for (int i = 0; i <= trial_count; ++i) {
      if (i == trial)
        result = websocket_interface_->SendMessage(ws, large_var);
      else
        result = websocket_interface_->SendMessage(ws, text_var);
      ASSERT_EQ(PP_OK, result);
    }
    TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    PP_Var var;
    for (int i = 0; i < trial; ++i) {
      callback.WaitForResult(websocket_interface_->ReceiveMessage(
          ws, &var, callback.GetCallback().pp_completion_callback()));
      ASSERT_EQ(PP_OK, callback.result());
      ASSERT_TRUE(AreEqualWithString(var, text));
      ReleaseVar(var);
    }
    result = websocket_interface_->ReceiveMessage(
        ws, &var, callback.GetCallback().pp_completion_callback());
    core_interface_->ReleaseResource(ws);
    if (result != PP_OK) {
      callback.WaitForResult(result);
      ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
    }
  }

  ReleaseVar(large_var);
  ReleaseVar(text_var);

  PASS();
}

std::string TestWebSocket::TestCcInterfaces() {
  // C++ bindings is simple straightforward, then just verifies interfaces work
  // as a interface bridge fine.
  pp::WebSocket ws(instance_);

  // Check uninitialized properties access.
  ASSERT_EQ(0, ws.GetBufferedAmount());
  ASSERT_EQ(0, ws.GetCloseCode());
  ASSERT_TRUE(AreEqualWithString(ws.GetCloseReason().pp_var(), std::string()));
  ASSERT_FALSE(ws.GetCloseWasClean());
  ASSERT_TRUE(AreEqualWithString(ws.GetExtensions().pp_var(), std::string()));
  ASSERT_TRUE(AreEqualWithString(ws.GetProtocol().pp_var(), std::string()));
  ASSERT_EQ(PP_WEBSOCKETREADYSTATE_INVALID, ws.GetReadyState());
  ASSERT_TRUE(AreEqualWithString(ws.GetURL().pp_var(), std::string()));

  // Check communication interfaces (connect, send, receive, and close).
  TestCompletionCallback connect_callback(
      instance_->pp_instance(), callback_type());
  connect_callback.WaitForResult(ws.Connect(
      pp::Var(GetFullURL(kCloseServerURL)), NULL, 0U,
              connect_callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(connect_callback);
  ASSERT_EQ(PP_OK, connect_callback.result());

  std::string text_message("hello C++");
  int32_t result = ws.SendMessage(pp::Var(text_message));
  ASSERT_EQ(PP_OK, result);

  std::vector<uint8_t> binary(256);
  for (uint32_t i = 0; i < binary.size(); ++i)
    binary[i] = i;
  result = ws.SendMessage(
      pp::Var(pp::PASS_REF, CreateVarBinary(binary)));
  ASSERT_EQ(PP_OK, result);

  pp::Var text_receive_var;
  TestCompletionCallback text_receive_callback(
      instance_->pp_instance(), callback_type());
  text_receive_callback.WaitForResult(
      ws.ReceiveMessage(&text_receive_var,
                        text_receive_callback.GetCallback()));
  ASSERT_EQ(PP_OK, text_receive_callback.result());
  ASSERT_TRUE(
      AreEqualWithString(text_receive_var.pp_var(), text_message.c_str()));

  pp::Var binary_receive_var;
  TestCompletionCallback binary_receive_callback(
      instance_->pp_instance(), callback_type());
  binary_receive_callback.WaitForResult(
      ws.ReceiveMessage(&binary_receive_var,
                        binary_receive_callback.GetCallback()));
  ASSERT_EQ(PP_OK, binary_receive_callback.result());
  ASSERT_TRUE(AreEqualWithBinary(binary_receive_var.pp_var(), binary));

  TestCompletionCallback close_callback(
      instance_->pp_instance(), callback_type());
  std::string reason("bye");
  close_callback.WaitForResult(ws.Close(
      PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var(reason),
      close_callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(close_callback);
  ASSERT_EQ(PP_OK, close_callback.result());

  // Check initialized properties access.
  ASSERT_EQ(0, ws.GetBufferedAmount());
  ASSERT_EQ(PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, ws.GetCloseCode());
  ASSERT_TRUE(
      AreEqualWithString(ws.GetCloseReason().pp_var(), reason.c_str()));
  ASSERT_EQ(true, ws.GetCloseWasClean());
  ASSERT_TRUE(AreEqualWithString(ws.GetProtocol().pp_var(), std::string()));
  ASSERT_EQ(PP_WEBSOCKETREADYSTATE_CLOSED, ws.GetReadyState());
  ASSERT_TRUE(AreEqualWithString(
      ws.GetURL().pp_var(), GetFullURL(kCloseServerURL).c_str()));

  PASS();
}

std::string TestWebSocket::TestUtilityInvalidConnect() {
  const pp::Var protocols[] = { pp::Var() };

  TestWebSocketAPI websocket(instance_);
  int32_t result = websocket.Connect(pp::Var(), protocols, 1U);
  ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
  ASSERT_EQ(0U, websocket.GetSeenEvents().size());

  result = websocket.Connect(pp::Var(), protocols, 1U);
  ASSERT_EQ(PP_ERROR_INPROGRESS, result);
  ASSERT_EQ(0U, websocket.GetSeenEvents().size());

  for (int i = 0; kInvalidURLs[i]; ++i) {
    TestWebSocketAPI ws(instance_);
    result = ws.Connect(pp::Var(std::string(kInvalidURLs[i])), protocols, 0U);
    if (result == PP_OK_COMPLETIONPENDING) {
      ws.WaitForClosed();
      const std::vector<WebSocketEvent>& events = ws.GetSeenEvents();
      ASSERT_EQ(WebSocketEvent::EVENT_ERROR, events[0].event_type);
      ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[1].event_type);
      ASSERT_EQ(2U, ws.GetSeenEvents().size());
    } else {
      ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
      ASSERT_EQ(0U, ws.GetSeenEvents().size());
    }
  }

  PASS();
}

std::string TestWebSocket::TestUtilityProtocols() {
  const pp::Var bad_protocols[] = {
      pp::Var(std::string("x-test")), pp::Var(std::string("x-test")) };
  const pp::Var good_protocols[] = {
      pp::Var(std::string("x-test")), pp::Var(std::string("x-yatest")) };

  {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Connect(
        pp::Var(GetFullURL(kEchoServerURL)), bad_protocols, 2U);
    ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
    ASSERT_EQ(0U, websocket.GetSeenEvents().size());
  }

  {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Connect(
        pp::Var(GetFullURL(kEchoServerURL)), good_protocols, 2U);
    ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    websocket.WaitForConnected();
    const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
    // Protocol arguments are valid, but this test run without a WebSocket
    // server. As a result, OnError() and OnClose() are invoked because of
    // a connection establishment failure.
    ASSERT_EQ(2U, events.size());
    ASSERT_EQ(WebSocketEvent::EVENT_ERROR, events[0].event_type);
    ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[1].event_type);
    ASSERT_FALSE(events[1].was_clean);
  }

  PASS();
}

std::string TestWebSocket::TestUtilityGetURL() {
  const pp::Var protocols[] = { pp::Var() };

  for (int i = 0; kInvalidURLs[i]; ++i) {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Connect(
        pp::Var(std::string(kInvalidURLs[i])), protocols, 0U);
    if (result == PP_OK_COMPLETIONPENDING) {
      websocket.WaitForClosed();
      const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
      ASSERT_EQ(WebSocketEvent::EVENT_ERROR, events[0].event_type);
      ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[1].event_type);
      ASSERT_EQ(2U, events.size());
    } else {
      ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
      ASSERT_EQ(0U, websocket.GetSeenEvents().size());
    }
    pp::Var url = websocket.GetURL();
    ASSERT_TRUE(AreEqualWithString(url.pp_var(), kInvalidURLs[i]));
  }

  PASS();
}

std::string TestWebSocket::TestUtilityValidConnect() {
  const pp::Var protocols[] = { pp::Var() };
  TestWebSocketAPI websocket(instance_);
  int32_t result = websocket.Connect(
      pp::Var(GetFullURL(kEchoServerURL)), protocols, 0U);
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  websocket.WaitForConnected();
  const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
  ASSERT_EQ(1U, events.size());
  ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
  ASSERT_TRUE(
      AreEqualWithString(websocket.GetExtensions().pp_var(), std::string()));

  PASS();
}

std::string TestWebSocket::TestUtilityInvalidClose() {
  const pp::Var reason = pp::Var(std::string("close for test"));

  // Close before connect.
  {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Close(
        PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, reason);
    ASSERT_EQ(PP_ERROR_FAILED, result);
    ASSERT_EQ(0U, websocket.GetSeenEvents().size());
  }

  // Close with bad arguments.
  {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Connect(pp::Var(GetFullURL(kEchoServerURL)),
        NULL, 0);
    ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    websocket.WaitForConnected();
    result = websocket.Close(1U, reason);
    ASSERT_EQ(PP_ERROR_NOACCESS, result);
    const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
    ASSERT_EQ(1U, events.size());
    ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
  }

  PASS();
}

std::string TestWebSocket::TestUtilityValidClose() {
  std::string reason("close for test");
  pp::Var url = pp::Var(GetFullURL(kCloseServerURL));

  // Close.
  {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Connect(url, NULL, 0U);
    ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    websocket.WaitForConnected();
    result = websocket.Close(
        PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var(reason));
    ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    websocket.WaitForClosed();
    const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
    ASSERT_EQ(2U, events.size());
    ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
    ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[1].event_type);
    ASSERT_TRUE(events[1].was_clean);
    ASSERT_EQ(PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, events[1].close_code);
    ASSERT_TRUE(AreEqualWithString(events[1].var.pp_var(), reason.c_str()));
  }

  // Close in connecting.
  // The ongoing connect failed with PP_ERROR_ABORTED, then the close is done
  // successfully.
  {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Connect(url, NULL, 0U);
    ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    result = websocket.Close(
        PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var(reason));
    ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    websocket.WaitForClosed();
    const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
    ASSERT_TRUE(events.size() == 2 || events.size() == 3);
    int index = 0;
    if (events.size() == 3)
      ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[index++].event_type);
    ASSERT_EQ(WebSocketEvent::EVENT_ERROR, events[index++].event_type);
    ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[index].event_type);
    ASSERT_FALSE(events[index].was_clean);
  }

  // Close in closing.
  // The first close will be done successfully, then the second one failed with
  // with PP_ERROR_INPROGRESS immediately.
  {
    TestWebSocketAPI websocket(instance_);
    int32_t result = websocket.Connect(url, NULL, 0U);
    result = websocket.Close(
        PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var(reason));
    ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    result = websocket.Close(
        PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var(reason));
    ASSERT_EQ(PP_ERROR_INPROGRESS, result);
    websocket.WaitForClosed();
    const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
    ASSERT_TRUE(events.size() == 2 || events.size() == 3);
    int index = 0;
    if (events.size() == 3)
      ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[index++].event_type);
    ASSERT_EQ(WebSocketEvent::EVENT_ERROR, events[index++].event_type);
    ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[index].event_type);
    ASSERT_FALSE(events[index].was_clean);
  }

  PASS();
}

std::string TestWebSocket::TestUtilityGetProtocol() {
  const std::string protocol("x-chat");
  const pp::Var protocols[] = { pp::Var(protocol) };
  std::string url(GetFullURL(kProtocolTestServerURL));
  url += protocol;
  TestWebSocketAPI websocket(instance_);
  int32_t result = websocket.Connect(pp::Var(url), protocols, 1U);
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  websocket.WaitForReceived();
  ASSERT_TRUE(AreEqualWithString(
      websocket.GetProtocol().pp_var(), protocol.c_str()));
  const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
  // The server to which this test connect returns the decided protocol as a
  // text frame message. So the WebSocketEvent records EVENT_MESSAGE event
  // after EVENT_OPEN event.
  ASSERT_EQ(2U, events.size());
  ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
  ASSERT_EQ(WebSocketEvent::EVENT_MESSAGE, events[1].event_type);
  ASSERT_TRUE(AreEqualWithString(events[1].var.pp_var(), protocol.c_str()));

  PASS();
}

std::string TestWebSocket::TestUtilityTextSendReceive() {
  const pp::Var protocols[] = { pp::Var() };
  TestWebSocketAPI websocket(instance_);
  int32_t result =
      websocket.Connect(pp::Var(GetFullURL(kEchoServerURL)), protocols, 0U);
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  websocket.WaitForConnected();

  // Send 'hello pepper'.
  std::string message1("hello pepper");
  result = websocket.Send(pp::Var(std::string(message1)));
  ASSERT_EQ(PP_OK, result);

  // Receive echoed 'hello pepper'.
  websocket.WaitForReceived();

  // Send 'goodbye pepper'.
  std::string message2("goodbye pepper");
  result = websocket.Send(pp::Var(std::string(message2)));

  // Receive echoed 'goodbye pepper'.
  websocket.WaitForReceived();

  const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
  ASSERT_EQ(3U, events.size());
  ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
  ASSERT_EQ(WebSocketEvent::EVENT_MESSAGE, events[1].event_type);
  ASSERT_TRUE(AreEqualWithString(events[1].var.pp_var(), message1.c_str()));
  ASSERT_EQ(WebSocketEvent::EVENT_MESSAGE, events[2].event_type);
  ASSERT_TRUE(AreEqualWithString(events[2].var.pp_var(), message2.c_str()));

  PASS();
}

std::string TestWebSocket::TestUtilityBinarySendReceive() {
  const pp::Var protocols[] = { pp::Var() };
  TestWebSocketAPI websocket(instance_);
  int32_t result =
      websocket.Connect(pp::Var(GetFullURL(kEchoServerURL)), protocols, 0U);
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  websocket.WaitForConnected();

  // Send binary message.
  uint32_t len = 256;
  std::vector<uint8_t> binary(len);
  for (uint32_t i = 0; i < len; ++i)
    binary[i] = i;
  pp::VarArrayBuffer message(len);
  uint8_t* var_data = static_cast<uint8_t*>(message.Map());
  std::copy(binary.begin(), binary.end(), var_data);
  result = websocket.Send(message);
  ASSERT_EQ(PP_OK, result);

  // Receive echoed binary message.
  websocket.WaitForReceived();

  const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
  ASSERT_EQ(2U, events.size());
  ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
  ASSERT_EQ(WebSocketEvent::EVENT_MESSAGE, events[1].event_type);
  ASSERT_TRUE(AreEqualWithBinary(events[1].var.pp_var(), binary));

  PASS();
}

std::string TestWebSocket::TestUtilityBufferedAmount() {
  // Connect to test echo server.
  const pp::Var protocols[] = { pp::Var() };
  TestWebSocketAPI websocket(instance_);
  int32_t result =
      websocket.Connect(pp::Var(GetFullURL(kEchoServerURL)), protocols, 0U);
  ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  websocket.WaitForConnected();

  // Prepare a large message that is not aligned with the internal buffer
  // sizes.
  std::string message(8193, 'x');
  uint64_t buffered_amount = 0;
  uint32_t sent;
  for (sent = 0; sent < 100; sent++) {
    result = websocket.Send(pp::Var(message));
    ASSERT_EQ(PP_OK, result);
    buffered_amount = websocket.GetBufferedAmount();
    // Buffered amount size 262144 is too big for the internal buffer size.
    if (buffered_amount > 262144)
      break;
  }

  // Close connection.
  std::string reason = "close while busy";
  result = websocket.Close(
      PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var(reason));
  ASSERT_EQ(PP_WEBSOCKETREADYSTATE_CLOSING, websocket.GetReadyState());
  websocket.WaitForClosed();
  ASSERT_EQ(PP_WEBSOCKETREADYSTATE_CLOSED, websocket.GetReadyState());

  uint64_t base_buffered_amount = websocket.GetBufferedAmount();
  size_t events_on_closed = websocket.GetSeenEvents().size();

  // After connection closure, all sending requests fail and just increase
  // the bufferedAmount property.
  result = websocket.Send(pp::Var(std::string()));
  ASSERT_EQ(PP_ERROR_FAILED, result);
  buffered_amount = websocket.GetBufferedAmount();
  ASSERT_EQ(base_buffered_amount + kMessageFrameOverhead, buffered_amount);
  base_buffered_amount = buffered_amount;

  result = websocket.Send(pp::Var(reason));
  ASSERT_EQ(PP_ERROR_FAILED, result);
  buffered_amount = websocket.GetBufferedAmount();
  uint64_t reason_frame_size = kMessageFrameOverhead + reason.length();
  ASSERT_EQ(base_buffered_amount + reason_frame_size, buffered_amount);

  const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
  ASSERT_EQ(events_on_closed, events.size());
  ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
  size_t last_event = events_on_closed - 1;
  for (uint32_t i = 1; i < last_event; ++i) {
    ASSERT_EQ(WebSocketEvent::EVENT_MESSAGE, events[i].event_type);
    ASSERT_TRUE(AreEqualWithString(events[i].var.pp_var(), message));
  }
  ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[last_event].event_type);
  ASSERT_TRUE(events[last_event].was_clean);

  PASS();
}

/* [<][>][^][v][top][bottom][index][help] */