chromium/chrome/test/data/extensions/api_test/socket/api/multicast.js

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Tests for multicast UDP socket.
function testMulticast() {
  function randomHexString(count) {
    var result = '';
    for (var i = 0; i < count; i++) {
      result += (Math.random() * 16 >> 0).toString(16);
    }
    return result;
  }

  var kMulticastAddress = "237.132.100.133";
  var kTestMessageLength = 128;
  var kTestMessage = randomHexString(128);
  var kPort = 11103;

  function arrayBufferToString(arrayBuffer) {
    // UTF-16LE
    return String.fromCharCode.apply(String, new Uint16Array(arrayBuffer));
  }

  function stringToArrayBuffer(string) {
    // UTF-16LE
    var buf = new ArrayBuffer(string.length * 2);
    var bufView = new Uint16Array(buf);
    for (var i = 0, strLen = string.length; i < strLen; i++) {
      bufView[i] = string.charCodeAt(i);
    }
    return buf;
  }

  function waitForMessage(socketId, callback) {
    var cancelled = false;
    var relayCanceller = null;
    socket.recvFrom(socketId, 1024, function (result) {
      if (cancelled)
        return;

      if (result.resultCode == kTestMessageLength * 2 &&
          kTestMessage === arrayBufferToString(result.data)) {
        callback(cancelled);
      } else {
        // Restart waiting.
        relayCanceller = waitForMessage(socketId, callback);
      }
    });
    return function canceller() {
      if (relayCanceller) {
        relayCanceller();
      } else {
        cancelled = true;
        callback(true);
      }
    };
  }

  function testMulticastSettings() {
    socket.create('udp', {}, function (socketInfo) {
      var socket_id;
      if (socketInfo) {
        socket_id = socketInfo.socketId;
        socket.setMulticastTimeToLive(socket_id, 0, function (result) {
          chrome.test.assertEq(0, result,
              "Error setting multicast time to live.");
          socket.setMulticastTimeToLive(socket_id, -3, function (result) {
            chrome.test.assertEq(-4, result,
                "Error setting multicast time to live.");
            socket.setMulticastLoopbackMode(socket_id, false,
                function (result) {
              chrome.test.assertEq(0, result,
                  "Error setting multicast loop back mode.");
              socket.setMulticastLoopbackMode(socket_id, true,
                  function (result) {
                chrome.test.assertEq(0, result,
                    "Error setting multicast loop back mode.");
                socket.destroy(socket_id);
                testMulticastRecv();
              });
            });
          });
        });
      } else {
        chrome.test.fail("Cannot create server udp socket");
      }
    });
  }

  function testSendMessage(message, address) {
    // Send the UDP message to the address with multicast ttl = 0.
    address = address || kMulticastAddress;
    socket.create('udp', {}, function (socketInfo) {
      var clientSocketId;
      if (socketInfo) {
        clientSocketId = socketInfo.socketId;
        chrome.test.assertTrue(clientSocketId > 0,
            "Cannot create client udp socket.");
        socket.setMulticastTimeToLive(clientSocketId, 0, function (result) {
          chrome.test.assertEq(0, result,
              "Cannot create client udp socket.");
          socket.connect(clientSocketId, address, kPort, function (result) {
            chrome.test.assertEq(0, result,
                "Cannot connect to localhost.");
            socket.write(clientSocketId, stringToArrayBuffer(kTestMessage),
                function (result) {
                  chrome.test.assertTrue(result.bytesWritten >= 0,
                      "Send to failed. " + JSON.stringify(result));
                  socket.destroy(clientSocketId);
                });
          });
        });
      } else {
        chrome.test.fail("Cannot create client udp socket");
      }
    });
  }

  function recvBeforeAddMembership(serverSocketId) {
    var recvTimeout;
    var canceller = waitForMessage(serverSocketId, function (cancelled) {
      clearTimeout(recvTimeout);
      if (cancelled) {
        recvWithMembership(serverSocketId);
      } else {
        chrome.test.fail("Received message before joining the group");
      }
    });
    testSendMessage(kTestMessage); // Meant to be lost.
    recvTimeout = setTimeout(function () {
      canceller();
      testSendMessage(kTestMessage, "127.0.0.1", kPort);
    }, 2000);
  }

  function recvWithMembership(serverSocketId) {
    // Join group.
    socket.joinGroup(serverSocketId, kMulticastAddress, function (result) {
      chrome.test.assertEq(0, result, "Join group failed.");
      var recvTimeout;
      var canceller = waitForMessage(serverSocketId, function (cancelled) {
        clearTimeout(recvTimeout);
        if (!cancelled) {
          recvWithoutMembership(serverSocketId);
        } else {
          chrome.test.fail("Faild to receive message after joining the group");
        }
      });
      testSendMessage(kTestMessage);
      recvTimeout = setTimeout(function () {
        canceller();
        socket.destroy(serverSocketId);
        chrome.test.fail("Cannot receive from multicast group.");
      }, 2000);
    });
  }

  function recvWithoutMembership(serverSocketId) {
    socket.leaveGroup(serverSocketId, kMulticastAddress, function (result) {
      chrome.test.assertEq(0, result, "leave group failed.");
      var recvTimeout;
      var canceller = waitForMessage(serverSocketId, function (cancelled) {
        clearTimeout(recvTimeout);
        if (cancelled) {
          leaveGroupAndDisconnect(serverSocketId);
        } else {
          chrome.test.fail("Received message after leaving the group");
          socket.destroy(serverSocketId);
        }
      });
      testSendMessage(request);
      recvTimeout = setTimeout(function () {
        canceller();
      }, 2000);
    });
  }

  function leaveGroupAndDisconnect(serverSocketId) {
    socket.joinGroup(serverSocketId, kMulticastAddress, function (result) {
      chrome.test.assertNoLastError();
      chrome.test.assertEq(0, result, "Join group failed.");
      socket.leaveGroup(serverSocketId, kMulticastAddress, () => {
        chrome.test.assertEq(0, result, "Leave group failed.");
        socket.destroy(serverSocketId);
        chrome.test.succeed();
      });
      socket.disconnect(serverSocketId);
    });
  }

  function testMulticastRecv() {
    socket.create('udp', {}, function (socketInfo) {
      var serverSocketId;
      if (socketInfo) {
        serverSocketId = socketInfo.socketId;
        socket.bind(serverSocketId, "0.0.0.0", kPort, function (result) {
          chrome.test.assertEq(0, result, "Bind failed.");
          recvBeforeAddMembership(serverSocketId);
        });
      } else {
        chrome.test.fail("Cannot create server udp socket");
      }
    });
  }

  testMulticastSettings();
}