1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
10 #include "ppapi/c/ppb_console.h"
11 #include "ppapi/cpp/extensions/dev/socket_dev.h"
12 #include "ppapi/cpp/instance.h"
13 #include "ppapi/cpp/module.h"
14 #include "ppapi/cpp/var.h"
15 #include "ppapi/cpp/var_array_buffer.h"
16 #include "ppapi/tests/test_utils.h"
19 using namespace pp::ext;
23 const char* const kSendContents = "0100000005320000005hello";
24 const char* const kReceiveContentsPrefix = "0100000005320000005";
25 const size_t kReceiveContentsSuffixSize = 11;
27 const char* const kMulticastAddress = "237.132.100.133";
28 const int32_t kMulticastPort = 11103;
29 const char* const kMulticastMessage = "hello world!";
33 class MyInstance : public Instance {
35 explicit MyInstance(PP_Instance instance)
37 socket_(InstanceHandle(instance)),
38 console_interface_(NULL),
41 virtual ~MyInstance() {
44 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
45 console_interface_ = static_cast<const PPB_Console*>(
46 Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
48 if (!console_interface_)
51 PostMessage(Var("ready"));
55 virtual void HandleMessage(const pp::Var& message_data) {
58 if (!message_data.is_string()) {
59 output = "Invalid control message.";
63 std::string control_message = message_data.AsString();
64 std::vector<std::string> parts;
66 size_t next_match = 0;
67 while (pos < control_message.size()) {
68 next_match = control_message.find(':', pos);
69 if (next_match == std::string::npos)
70 next_match = control_message.size();
71 parts.push_back(control_message.substr(pos, next_match - pos));
75 if (parts.size() != 3) {
76 output = "Invalid protocol/address/port input.";
80 test_type_ = parts[0];
82 port_ = atoi(parts[2].c_str());
83 Log(PP_LOGLEVEL_LOG, "Running tests, protocol %s, server %s:%d",
84 test_type_.c_str(), address_.c_str(), port_);
86 if (test_type_ == "tcp_server") {
87 output = TestServerSocket();
88 } else if (test_type_ == "multicast") {
89 output = TestMulticast();
91 output = TestClientSocket();
95 NotifyTestDone(output);
99 std::string TestServerSocket() {
100 int32_t socket_id = 0;
102 TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
103 callback(pp_instance());
104 callback.WaitForResult(socket_.Create(
105 socket::SocketType_Dev(socket::SocketType_Dev::TCP),
106 Optional<socket::CreateOptions_Dev>(), callback.GetCallback()));
107 if (callback.result() != PP_OK)
108 return "Create(): failed.";
109 socket_id = callback.output().socket_id();
111 return "Create(): invalid socket ID.";
115 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
116 callback.WaitForResult(socket_.Listen(
117 socket_id, address_, port_, Optional<int32_t>(),
118 callback.GetCallback()));
119 if (callback.result() != PP_OK)
120 return "Listen(): failed.";
121 if (callback.output() != 0)
122 return "Listen(): failed.";
125 int32_t client_socket_id = 0;
127 TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
128 callback(pp_instance());
129 callback.WaitForResult(socket_.Create(
130 socket::SocketType_Dev(socket::SocketType_Dev::TCP),
131 Optional<socket::CreateOptions_Dev>(), callback.GetCallback()));
132 if (callback.result() != PP_OK)
133 return "Create(): failed.";
134 client_socket_id = callback.output().socket_id();
135 if (client_socket_id <= 0)
136 return "Create(): invalid socket ID.";
140 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
141 callback.WaitForResult(socket_.Connect(
142 client_socket_id, address_, port_, callback.GetCallback()));
143 if (callback.result() != PP_OK)
144 return "Connect(): failed.";
145 if (callback.output() != 0)
146 return "Connect(): failed.";
149 int32_t accepted_socket_id = 0;
151 TestExtCompletionCallbackWithOutput<socket::AcceptInfo_Dev>
152 callback(pp_instance());
153 callback.WaitForResult(socket_.Accept(socket_id, callback.GetCallback()));
154 if (callback.result() != PP_OK)
155 return "Accept(): failed.";
156 socket::AcceptInfo_Dev accept_info = callback.output();
157 if (accept_info.result_code() != 0 || !accept_info.socket_id().IsSet())
158 return "Accept(): failed.";
159 accepted_socket_id = *accept_info.socket_id();
162 size_t bytes_written = 0;
164 TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
165 callback(pp_instance());
166 VarArrayBuffer array_buffer = ConvertToArrayBuffer(kSendContents);
167 callback.WaitForResult(socket_.Write(client_socket_id, array_buffer,
168 callback.GetCallback()));
169 if (callback.result() != PP_OK)
170 return "Write(): failed.";
171 socket::WriteInfo_Dev write_info = callback.output();
172 bytes_written = static_cast<size_t>(write_info.bytes_written());
173 if (bytes_written <= 0)
174 return "Write(): did not write any bytes.";
178 TestExtCompletionCallbackWithOutput<socket::ReadInfo_Dev>
179 callback(pp_instance());
180 callback.WaitForResult(socket_.Read(
181 accepted_socket_id, Optional<int32_t>(), callback.GetCallback()));
182 if (callback.result() != PP_OK)
183 return "Read(): failed.";
185 std::string data_string = ConvertFromArrayBuffer(
186 &callback.output().data());
187 if (data_string.compare(0, std::string::npos, kSendContents,
188 bytes_written) != 0) {
189 return "Read(): Received data does not match.";
193 socket_.Destroy(client_socket_id);
194 socket_.Destroy(accepted_socket_id);
195 socket_.Destroy(socket_id);
196 return std::string();
199 std::string TestMulticast() {
200 int32_t socket_id = 0;
202 TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
203 callback(pp_instance());
204 callback.WaitForResult(socket_.Create(
205 socket::SocketType_Dev::UDP, Optional<socket::CreateOptions_Dev>(),
206 callback.GetCallback()));
207 if (callback.result() != PP_OK)
208 return "Create(): failed.";
209 socket_id = callback.output().socket_id();
211 return "Create(): invalid socket ID.";
215 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
216 callback.WaitForResult(socket_.SetMulticastTimeToLive(
217 socket_id, 0, callback.GetCallback()));
218 if (callback.result() != PP_OK || callback.output() != 0)
219 return "SetMulticastTimeToLive(): failed.";
223 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
224 callback.WaitForResult(socket_.SetMulticastTimeToLive(
225 socket_id, -3, callback.GetCallback()));
226 if (callback.result() == PP_OK)
227 return "SetMulticastTimeToLive(): succeeded unexpectedly.";
228 if (callback.output() != -4)
229 return "SetMulticastTimeToLive(): returned unexpected result.";
233 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
234 callback.WaitForResult(socket_.SetMulticastLoopbackMode(
235 socket_id, false, callback.GetCallback()));
236 if (callback.result() != PP_OK || callback.output() != 0)
237 return "SetMulticastLoopbackMode(): failed.";
241 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
242 callback.WaitForResult(socket_.SetMulticastLoopbackMode(
243 socket_id, true, callback.GetCallback()));
244 if (callback.result() != PP_OK || callback.output() != 0)
245 return "SetMulticastLoopbackMode(): failed.";
248 socket_.Destroy(socket_id);
251 int32_t server_socket_id = 0;
253 TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
254 callback(pp_instance());
255 callback.WaitForResult(socket_.Create(
256 socket::SocketType_Dev::UDP, Optional<socket::CreateOptions_Dev>(),
257 callback.GetCallback()));
258 if (callback.result() != PP_OK)
259 return "Create(): failed.";
260 server_socket_id = callback.output().socket_id();
261 if (server_socket_id <= 0)
262 return "Create(): invalid socket ID.";
266 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
267 callback.WaitForResult(socket_.Bind(
268 server_socket_id, "0.0.0.0", kMulticastPort, callback.GetCallback()));
269 if (callback.result() != PP_OK || callback.output() != 0)
270 return "Bind(): failed";
274 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
275 callback.WaitForResult(socket_.JoinGroup(
276 server_socket_id, kMulticastAddress, callback.GetCallback()));
277 if (callback.result() != PP_OK || callback.output() != 0)
278 return "JoinGroup(): failed.";
282 TestExtCompletionCallbackWithOutput<std::vector<std::string> >
283 callback(pp_instance());
284 callback.WaitForResult(socket_.GetJoinedGroups(
285 server_socket_id, callback.GetCallback()));
286 if (callback.result() != PP_OK)
287 return "GetJoinedGroups(): failed.";
288 std::vector<std::string> groups = callback.output();
289 if (groups.size() != 1 || groups[0] != kMulticastAddress) {
290 return "GetJoinedGroups(): the returned groups didn't match those "
295 int32_t client_socket_id = 0;
297 TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
298 callback(pp_instance());
299 callback.WaitForResult(socket_.Create(
300 socket::SocketType_Dev::UDP, Optional<socket::CreateOptions_Dev>(),
301 callback.GetCallback()));
302 if (callback.result() != PP_OK)
303 return "Create(): failed.";
304 client_socket_id = callback.output().socket_id();
305 if (client_socket_id <= 0)
306 return "Create(): invalid socket ID.";
310 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
311 callback.WaitForResult(socket_.SetMulticastTimeToLive(
312 client_socket_id, 0, callback.GetCallback()));
313 if (callback.result() != PP_OK || callback.output() != 0)
314 return "SetMulticastTimeToLive(): failed.";
318 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
319 callback.WaitForResult(socket_.Connect(
320 client_socket_id, kMulticastAddress, kMulticastPort,
321 callback.GetCallback()));
322 if (callback.result() != PP_OK || callback.output() != 0)
323 return "Connnect(): failed.";
327 VarArrayBuffer input_array_buffer =
328 ConvertToArrayBuffer(kMulticastMessage);
329 size_t bytes_written = 0;
330 int32_t result_code = 0;
333 TestExtCompletionCallbackWithOutput<socket::RecvFromInfo_Dev>
334 recv_from_callback(pp_instance());
335 int32_t recv_from_result = socket_.RecvFrom(
336 server_socket_id, 1024, recv_from_callback.GetCallback());
337 if (recv_from_result != PP_OK_COMPLETIONPENDING)
338 return "RecvFrom(): did not wait for data.";
340 TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
341 write_callback(pp_instance());
342 write_callback.WaitForResult(socket_.Write(
343 client_socket_id, input_array_buffer, write_callback.GetCallback()));
344 if (write_callback.result() != PP_OK)
345 return "Write(): failed.";
346 bytes_written = static_cast<size_t>(
347 write_callback.output().bytes_written());
349 recv_from_callback.WaitForResult(recv_from_result);
350 if (recv_from_callback.result() != PP_OK)
351 return "RecvFrom(): failed.";
352 socket::RecvFromInfo_Dev recv_from_info = recv_from_callback.output();
353 result_code = recv_from_info.result_code();
354 data = recv_from_info.data();
356 if (bytes_written != strlen(kMulticastMessage))
357 return "Write(): did not send the whole data buffer.";
359 if (result_code > 0 &&
360 static_cast<uint32_t>(result_code) != data.ByteLength()) {
361 return "RecvFrom(): inconsistent result code and byte length.";
364 std::string output_string = ConvertFromArrayBuffer(&data);
365 if (output_string != kMulticastMessage) {
366 return std::string("RecvFrom(): mismatched data: ").append(
372 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
373 callback.WaitForResult(socket_.LeaveGroup(
374 server_socket_id, kMulticastAddress, callback.GetCallback()));
375 if (callback.result() != PP_OK || callback.output() != 0)
376 return "LeaveGroup(): failed.";
379 socket_.Destroy(server_socket_id);
380 socket_.Destroy(client_socket_id);
381 return std::string();
384 std::string TestClientSocket() {
385 socket::SocketType_Dev socket_type;
386 if (!socket_type.Populate(Var(test_type_).pp_var()))
387 return "Invalid socket type.";
389 int32_t socket_id = 0;
391 TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
392 callback(pp_instance());
393 callback.WaitForResult(socket_.Create(
394 socket_type, Optional<socket::CreateOptions_Dev>(),
395 callback.GetCallback()));
396 if (callback.result() != PP_OK)
397 return "Create(): failed.";
398 socket_id = callback.output().socket_id();
400 return "Create(): invalid socket ID.";
404 TestExtCompletionCallbackWithOutput<socket::SocketInfo_Dev>
405 callback(pp_instance());
406 callback.WaitForResult(socket_.GetInfo(socket_id,
407 callback.GetCallback()));
408 if (callback.result() != PP_OK)
409 return "GetInfo(): failed.";
411 socket::SocketInfo_Dev socket_info = callback.output();
412 if (socket_info.socket_type().value != socket_type.value)
413 return "GetInfo(): inconsistent socket type.";
414 if (socket_info.connected())
415 return "GetInfo(): socket should not be connected.";
416 if (socket_info.peer_address().IsSet() || socket_info.peer_port().IsSet())
417 return "GetInfo(): unconnected socket should not have peer.";
418 if (socket_info.local_address().IsSet() ||
419 socket_info.local_port().IsSet()) {
420 return "GetInfo(): unconnected socket should not have local binding.";
425 if (socket_type.value == socket::SocketType_Dev::TCP) {
426 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
427 callback.WaitForResult(socket_.Connect(
428 socket_id, address_, port_, callback.GetCallback()));
429 if (callback.result() != PP_OK)
430 return "Connect(): failed.";
431 if (callback.output() != 0)
432 return "Connect(): failed.";
434 TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
435 callback.WaitForResult(socket_.Bind(
436 socket_id, "0.0.0.0", 0, callback.GetCallback()));
437 if (callback.result() != PP_OK)
438 return "Bind(): failed.";
439 if (callback.output() != 0)
440 return "Bind(): failed.";
445 TestExtCompletionCallbackWithOutput<socket::SocketInfo_Dev>
446 callback(pp_instance());
447 callback.WaitForResult(socket_.GetInfo(socket_id,
448 callback.GetCallback()));
449 if (callback.result() != PP_OK)
450 return "GetInfo(): failed.";
452 socket::SocketInfo_Dev socket_info = callback.output();
453 if (socket_info.socket_type().value != socket_type.value)
454 return "GetInfo(): inconsistent socket type.";
455 if (!socket_info.local_address().IsSet() ||
456 !socket_info.local_port().IsSet()) {
457 return "GetInfo(): bound socket should have local address and port.";
459 if (socket_type.value == socket::SocketType_Dev::TCP) {
460 if (!socket_info.connected())
461 return "GetInfo(): TCP socket should be connected.";
462 if (!socket_info.peer_address().IsSet() ||
463 !socket_info.peer_port().IsSet()) {
464 return "GetInfo(): connected TCP socket should have peer address and "
467 if (*socket_info.peer_address() != "127.0.0.1" ||
468 *socket_info.peer_port() != port_) {
469 return "GetInfo(): peer address and port should match the listening "
473 if (socket_info.connected())
474 return "GetInfo(): UDP socket should not be connected.";
475 if (socket_info.peer_address().IsSet() ||
476 socket_info.peer_port().IsSet()) {
477 return "GetInfo(): unconnected UDP socket should not have peer "
484 TestExtCompletionCallbackWithOutput<bool> callback(pp_instance());
485 callback.WaitForResult(socket_.SetNoDelay(
486 socket_id, true, callback.GetCallback()));
487 if (callback.result() != PP_OK)
488 return "SetNoDelay(): failed.";
489 if (socket_type.value == socket::SocketType_Dev::TCP) {
490 if (!callback.output())
491 return "SetNoDelay(): failed for TCP.";
493 if (callback.output())
494 return "SetNoDelay(): did not fail for UDP.";
499 TestExtCompletionCallbackWithOutput<bool> callback(pp_instance());
500 callback.WaitForResult(socket_.SetKeepAlive(
501 socket_id, true, 1000, callback.GetCallback()));
502 if (callback.result() != PP_OK)
503 return "SetKeepAlive(): failed.";
504 if (socket_type.value == socket::SocketType_Dev::TCP) {
505 if (!callback.output())
506 return "SetKeepAlive(): failed for TCP.";
508 if (callback.output())
509 return "SetKeepAlive(): did not fail for UDP.";
514 VarArrayBuffer input_array_buffer = ConvertToArrayBuffer(kSendContents);
515 size_t bytes_written = 0;
516 int32_t result_code = 0;
518 if (socket_type.value == socket::SocketType_Dev::TCP) {
519 TestExtCompletionCallbackWithOutput<socket::ReadInfo_Dev>
520 read_callback(pp_instance());
521 int32_t read_result = socket_.Read(socket_id, Optional<int32_t>(),
522 read_callback.GetCallback());
523 if (read_result != PP_OK_COMPLETIONPENDING)
524 return "Read(): did not wait for data.";
526 TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
527 write_callback(pp_instance());
528 write_callback.WaitForResult(socket_.Write(
529 socket_id, input_array_buffer, write_callback.GetCallback()));
530 if (write_callback.result() != PP_OK)
531 return "Write(): failed.";
532 bytes_written = static_cast<size_t>(
533 write_callback.output().bytes_written());
535 read_callback.WaitForResult(read_result);
536 if (read_callback.result() != PP_OK)
537 return "Read(): failed.";
538 socket::ReadInfo_Dev read_info = read_callback.output();
539 result_code = read_info.result_code(),
540 data = read_info.data();
542 TestExtCompletionCallbackWithOutput<socket::RecvFromInfo_Dev>
543 recv_from_callback(pp_instance());
544 int32_t recv_from_result = socket_.RecvFrom(
545 socket_id, Optional<int32_t>(), recv_from_callback.GetCallback());
546 if (recv_from_result != PP_OK_COMPLETIONPENDING)
547 return "RecvFrom(): did not wait for data.";
549 TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
550 send_to_callback(pp_instance());
551 send_to_callback.WaitForResult(socket_.SendTo(
552 socket_id, input_array_buffer, address_, port_,
553 send_to_callback.GetCallback()));
554 if (send_to_callback.result() != PP_OK)
555 return "SendTo(): failed.";
556 bytes_written = static_cast<size_t>(
557 send_to_callback.output().bytes_written());
559 recv_from_callback.WaitForResult(recv_from_result);
560 if (recv_from_callback.result() != PP_OK)
561 return "RecvFrom(): failed.";
562 socket::RecvFromInfo_Dev recv_from_info = recv_from_callback.output();
563 result_code = recv_from_info.result_code();
564 data = recv_from_info.data();
567 if (bytes_written != strlen(kSendContents))
568 return "SendTo() or Write(): did not send the whole data buffer.";
570 if (result_code > 0 &&
571 static_cast<uint32_t>(result_code) != data.ByteLength()) {
572 return "Read() or RecvFrom(): inconsistent result code and byte "
576 std::string output_string = ConvertFromArrayBuffer(&data);
577 size_t prefix_len = strlen(kReceiveContentsPrefix);
578 if (output_string.size() != prefix_len + kReceiveContentsSuffixSize ||
579 output_string.compare(0, prefix_len, kReceiveContentsPrefix) != 0) {
580 return std::string("Read() or RecvFrom(): mismatched data: ").append(
586 TestExtCompletionCallbackWithOutput<
587 std::vector<socket::NetworkInterface_Dev> > callback(pp_instance());
588 callback.WaitForResult(socket_.GetNetworkList(callback.GetCallback()));
589 if (callback.result() != PP_OK)
590 return "GetNetworkList(): failed.";
591 if (callback.output().empty())
592 return "GetNetworkList(): returned an empty list.";
595 socket_.Destroy(socket_id);
596 return std::string();
599 void Log(PP_LogLevel level, const char* format, ...) {
601 va_start(args, format);
603 vsnprintf(buf, sizeof(buf) - 1, format, args);
604 buf[sizeof(buf) - 1] = '\0';
608 console_interface_->Log(pp_instance(), level, value.pp_var());
611 void NotifyTestDone(const std::string& message) {
612 PostMessage(message);
615 VarArrayBuffer ConvertToArrayBuffer(const std::string data) {
616 VarArrayBuffer array_buffer(data.size());
617 memcpy(array_buffer.Map(), data.c_str(), data.size());
618 array_buffer.Unmap();
622 std::string ConvertFromArrayBuffer(VarArrayBuffer* array_buffer) {
623 std::string result(static_cast<const char*>(array_buffer->Map()),
624 array_buffer->ByteLength());
625 array_buffer->Unmap();
629 socket::Socket_Dev socket_;
630 const PPB_Console* console_interface_;
632 std::string test_type_;
633 std::string address_;
637 class MyModule : public Module {
639 MyModule() : Module() {}
640 virtual ~MyModule() {}
642 virtual Instance* CreateInstance(PP_Instance instance) {
643 return new MyInstance(instance);
649 Module* CreateModule() {
650 return new MyModule();