// 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 chrome.sockets.udp.
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;
}
// Registers a listener on receiving data on |socketId|.
// Calls |callback| with a |cancelled| argument of "false" when receiving data
// of excactly |kTestMessageLength| characters.
// Calls |callback| with a |cancelled| argument of "true" when the caller
// decides to invoke the returned function.
// Returns a function that can be invoked to "cancel" the operation (i.e.
// call |callback| with a |cancelled| argument of "true").
function waitForMessage(socketId, callback) {
var cancelled = false;
var relayCanceller = null;
chrome.sockets.udp.onReceive.addListener(function(info) {
console.log("Data received: " +
"socketId=" + info.socketId +
", bytes=" + info.data.byteLength +
", address=" + info.remoteAddress +
", port=" + info.remotePort);
if (socketId != info.socketId)
return;
if (cancelled)
return;
if (info.data.byteLength == kTestMessageLength * 2 &&
kTestMessage === arrayBufferToString(info.data)) {
callback(false);
} else {
// Restart waiting.
relayCanceller = waitForMessage(socketId, callback);
}
});
return function canceller() {
if (relayCanceller) {
relayCanceller();
} else {
cancelled = true; // prevents callback from being called on receive.
callback(true);
}
};
}
function testMulticastSettings(nextTest) {
console.log("*************** testMulticastSettings");
chrome.sockets.udp.create({}, function (socketInfo) {
var socketId;
if (socketInfo) {
socketId = socketInfo.socketId;
chrome.sockets.udp.setMulticastTimeToLive(socketId, 0,
function (result) {
chrome.test.assertEq(0, result,
"Error setting multicast time to live.");
chrome.sockets.udp.setMulticastTimeToLive(socketId, -3,
function (result) {
chrome.test.assertEq(-4, result,
"Error setting multicast time to live.");
chrome.sockets.udp.setMulticastLoopbackMode(socketId, false,
function (result) {
chrome.test.assertEq(0, result,
"Error setting multicast loop back mode.");
chrome.sockets.udp.setMulticastLoopbackMode(socketId, true,
function (result) {
chrome.test.assertEq(0, result,
"Error setting multicast loop back mode.");
chrome.sockets.udp.close(socketId, function() {});
nextTest();
});
});
});
});
} else {
chrome.test.fail("Cannot create server udp socket");
}
});
}
function testSendMessage(message, address) {
// Send the UDP message to the address with multicast ttl = 0.
chrome.sockets.udp.create({}, function (socketInfo) {
var clientSocketId = socketInfo.socketId;
chrome.test.assertTrue(clientSocketId > 0,
"Cannot create client udp socket.");
chrome.sockets.udp.setMulticastTimeToLive(clientSocketId, 0,
function (result) {
chrome.test.assertEq(0, result,
"Cannot create client udp socket.");
chrome.sockets.udp.bind(clientSocketId, "0.0.0.0", 0,
function (result) {
chrome.test.assertEq(0, result,
"Cannot bind to localhost.");
chrome.sockets.udp.send(clientSocketId,
stringToArrayBuffer(kTestMessage),
address, kPort, function (result) {
console.log("Sent bytes to socket:" +
" socketId=" + clientSocketId +
", bytes=" + result.bytesSent +
", address=" + address +
", port=" + kPort);
chrome.test.assertTrue(result.resultCode >= 0,
"Send to failed. " + JSON.stringify(result));
chrome.sockets.udp.close(clientSocketId, function() {});
});
});
});
});
}
function testRecvBeforeAddMembership(serverSocketId, nextTest) {
console.log("*************** testRecvBeforeAddMembership");
var recvTimeout;
var canceller = waitForMessage(serverSocketId, function (cancelled) {
clearTimeout(recvTimeout);
if (cancelled) {
nextTest();
} else {
chrome.test.fail("Received message before joining the group");
}
});
testSendMessage(kTestMessage, kMulticastAddress); // Meant to be lost.
recvTimeout = setTimeout(function () {
// This is expected to execute.
canceller();
}, 2000);
}
function testRecvWithMembership(serverSocketId, nextTest) {
console.log("*************** testRecvWithMembership");
chrome.sockets.udp.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) {
nextTest();
} else {
chrome.test.fail("Faild to receive message after joining the group");
}
});
testSendMessage(kTestMessage, kMulticastAddress);
recvTimeout = setTimeout(function () {
canceller();
chrome.test.fail("Cannot receive from multicast group.");
}, 2000);
});
}
function testRecvWithoutMembership(serverSocketId, nextTest) {
console.log("*************** testRecvWithoutMembership");
chrome.sockets.udp.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) {
nextTest();
} else {
chrome.test.fail("Received message after leaving the group");
}
});
testSendMessage(request, kMulticastAddress);
recvTimeout = setTimeout(function () {
// This is expected to execute.
canceller();
}, 2000);
});
}
function testMulticastRecv() {
console.log("*************** testMulticastRecv");
chrome.sockets.udp.create({}, function (socketInfo) {
var serverSocketId = socketInfo.socketId;
chrome.sockets.udp.bind(serverSocketId, "0.0.0.0", kPort,
function (result) {
chrome.test.assertEq(0, result, "Bind failed.");
// Test 1
testRecvBeforeAddMembership(serverSocketId, function() {
// Test 2
testRecvWithMembership(serverSocketId, function() {
// Test 3
testRecvWithoutMembership(serverSocketId, function() {
// Success!
chrome.sockets.udp.close(serverSocketId, function() {
console.log("*************** SUCCESS! ");
chrome.test.succeed();
});
});
});
});
});
});
}
setTimeout(function() {
testMulticastSettings(function() {
testMulticastRecv();
})
}, 100);
}