Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'android'
- Manifest: dom/push/test/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
// Create the profile directory early to ensure pushBroadcastService
// is initialized with the correct path
do_get_profile();
const { BroadcastService } = ChromeUtils.importESModule(
"resource://gre/modules/PushBroadcastService.sys.mjs"
);
const { JSONFile } = ChromeUtils.importESModule(
"resource://gre/modules/JSONFile.sys.mjs"
);
const { FileTestUtils } = ChromeUtils.importESModule(
);
const { broadcastHandler } = ChromeUtils.importESModule(
);
const broadcastService = pushBroadcastService;
const assert = Assert;
const userAgentID = "bd744428-f125-436a-b6d0-dd0c9845837f";
const channelID = "0ef2ad4a-6c49-41ad-af6e-95d2425276bf";
function run_test() {
setPrefs({
userAgentID,
alwaysConnect: true,
requestTimeout: 1000,
retryBaseInterval: 150,
});
run_next_test();
}
function getPushServiceMock() {
return {
subscribed: [],
subscribeBroadcast(broadcastId, version) {
this.subscribed.push([broadcastId, version]);
},
};
}
add_task(async function test_register_success() {
await broadcastService._resetListeners();
const db = PushServiceWebSocket.newPushDB();
broadcastHandler.reset();
const notifications = broadcastHandler.notifications;
let socket;
registerCleanupFunction(() => {
return db.drop().then(_ => db.close());
});
await broadcastService.addListener("broadcast-test", "2018-02-01", {
symbolName: "broadcastHandler",
});
PushServiceWebSocket._generateID = () => channelID;
var broadcastSubscriptions = [];
let handshakeDone;
let handshakePromise = new Promise(resolve => (handshakeDone = resolve));
await PushService.init({
db,
makeWebSocket(uri) {
return new MockWebSocket(uri, {
onHello(data) {
socket = this;
deepEqual(
data.broadcasts,
{ "broadcast-test": "2018-02-01" },
"Handshake: doesn't consult listeners"
);
equal(data.messageType, "hello", "Handshake: wrong message type");
ok(
!data.uaid,
"Should not send UAID in handshake without local subscriptions"
);
this.serverSendMsg(
JSON.stringify({
messageType: "hello",
status: 200,
uaid: userAgentID,
})
);
handshakeDone();
},
onBroadcastSubscribe(data) {
broadcastSubscriptions.push(data);
},
});
},
});
await handshakePromise;
socket.serverSendMsg(
JSON.stringify({
messageType: "broadcast",
broadcasts: {
"broadcast-test": "2018-03-02",
},
})
);
await broadcastHandler.wasNotified;
deepEqual(
notifications,
[
[
"2018-03-02",
"broadcast-test",
{ phase: broadcastService.PHASES.BROADCAST },
],
],
"Broadcast notification didn't get delivered"
);
deepEqual(
await broadcastService.getListeners(),
{
"broadcast-test": "2018-03-02",
},
"Broadcast version wasn't updated"
);
await broadcastService.addListener("example-listener", "2018-03-01", {
// NOTE: This file is non-existing.
symbolName: "doesntExist",
});
deepEqual(broadcastSubscriptions, [
{
messageType: "broadcast_subscribe",
broadcasts: { "example-listener": "2018-03-01" },
},
]);
});
add_task(async function test_handle_hello_broadcasts() {
PushService.uninit();
await broadcastService._resetListeners();
let db = PushServiceWebSocket.newPushDB();
broadcastHandler.reset();
let notifications = broadcastHandler.notifications;
registerCleanupFunction(() => {
return db.drop().then(_ => db.close());
});
await broadcastService.addListener("broadcast-test", "2018-02-01", {
symbolName: "broadcastHandler",
});
PushServiceWebSocket._generateID = () => channelID;
await PushService.init({
db,
makeWebSocket(uri) {
return new MockWebSocket(uri, {
onHello(data) {
deepEqual(
data.broadcasts,
{ "broadcast-test": "2018-02-01" },
"Handshake: doesn't consult listeners"
);
equal(data.messageType, "hello", "Handshake: wrong message type");
ok(
!data.uaid,
"Should not send UAID in handshake without local subscriptions"
);
this.serverSendMsg(
JSON.stringify({
messageType: "hello",
status: 200,
uaid: userAgentID,
broadcasts: {
"broadcast-test": "2018-02-02",
},
})
);
},
onBroadcastSubscribe() {},
});
},
});
await broadcastHandler.wasNotified;
deepEqual(
notifications,
[
[
"2018-02-02",
"broadcast-test",
{ phase: broadcastService.PHASES.HELLO },
],
],
"Broadcast notification on hello was delivered"
);
deepEqual(
await broadcastService.getListeners(),
{
"broadcast-test": "2018-02-02",
},
"Broadcast version wasn't updated"
);
});
add_task(async function test_broadcast_context() {
await broadcastService._resetListeners();
const db = PushServiceWebSocket.newPushDB();
broadcastHandler.reset();
registerCleanupFunction(() => {
return db.drop().then(() => db.close());
});
const serviceId = "broadcast-test";
const version = "2018-02-01";
await broadcastService.addListener(serviceId, version, {
symbolName: "broadcastHandler",
});
// PushServiceWebSocket._generateID = () => channelID;
await PushService.init({
db,
makeWebSocket(uri) {
return new MockWebSocket(uri, {
onHello() {},
});
},
});
// Simulate registration.
PushServiceWebSocket.sendSubscribeBroadcast(serviceId, version);
// Simulate broadcast reply received by PushWebSocketListener.
const message = JSON.stringify({
messageType: "broadcast",
broadcasts: {
[serviceId]: version,
},
});
PushServiceWebSocket._wsOnMessageAvailable({}, message);
await broadcastHandler.wasNotified;
deepEqual(
broadcastHandler.notifications,
[[version, serviceId, { phase: broadcastService.PHASES.REGISTER }]],
"Broadcast passes REGISTER context"
);
// Simulate broadcast reply, without previous registration.
broadcastHandler.reset();
PushServiceWebSocket._wsOnMessageAvailable({}, message);
await broadcastHandler.wasNotified;
deepEqual(
broadcastHandler.notifications,
[[version, serviceId, { phase: broadcastService.PHASES.BROADCAST }]],
"Broadcast passes BROADCAST context"
);
});
add_task(async function test_broadcast_unit() {
const fakeListenersData = {
abc: {
version: "2018-03-04",
sourceInfo: {
// NOTE: This file is non-existing.
symbolName: "getAbc",
},
},
def: {
version: "2018-04-05",
sourceInfo: {
// NOTE: This file is non-existing.
symbolName: "getDef",
},
},
};
const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
const jsonFile = new JSONFile({ path });
jsonFile.data = {
listeners: fakeListenersData,
};
await jsonFile._save();
const pushServiceMock = getPushServiceMock();
const mockBroadcastService = new BroadcastService(pushServiceMock, path);
const listeners = await mockBroadcastService.getListeners();
deepEqual(listeners, {
abc: "2018-03-04",
def: "2018-04-05",
});
await mockBroadcastService.addListener("ghi", "2018-05-06", {
// NOTE: This file is non-existing.
symbolName: "getGhi",
});
deepEqual(pushServiceMock.subscribed, [["ghi", "2018-05-06"]]);
await mockBroadcastService._saveImmediately();
const newJSONFile = new JSONFile({ path });
await newJSONFile.load();
deepEqual(newJSONFile.data, {
listeners: {
...fakeListenersData,
ghi: {
version: "2018-05-06",
sourceInfo: {
// NOTE: This file is non-existing.
symbolName: "getGhi",
},
},
},
version: 1,
});
deepEqual(await mockBroadcastService.getListeners(), {
abc: "2018-03-04",
def: "2018-04-05",
ghi: "2018-05-06",
});
});
add_task(async function test_broadcast_initialize_sane() {
const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
const mockBroadcastService = new BroadcastService(getPushServiceMock(), path);
deepEqual(
await mockBroadcastService.getListeners(),
{},
"listeners should start out sane"
);
await mockBroadcastService._saveImmediately();
let onDiskJSONFile = new JSONFile({ path });
await onDiskJSONFile.load();
deepEqual(
onDiskJSONFile.data,
{ listeners: {}, version: 1 },
"written JSON file has listeners and version fields"
);
await mockBroadcastService.addListener("ghi", "2018-05-06", {
// NOTE: This file is non-existing.
symbolName: "getGhi",
});
await mockBroadcastService._saveImmediately();
onDiskJSONFile = new JSONFile({ path });
await onDiskJSONFile.load();
deepEqual(
onDiskJSONFile.data,
{
listeners: {
ghi: {
version: "2018-05-06",
sourceInfo: {
// NOTE: This file is non-existing.
symbolName: "getGhi",
},
},
},
version: 1,
},
"adding listeners to initial state is written OK"
);
});
add_task(async function test_broadcast_reject_invalid_sourceinfo() {
const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
const mockBroadcastService = new BroadcastService(getPushServiceMock(), path);
await assert.rejects(
mockBroadcastService.addListener("ghi", "2018-05-06", {
// NOTE: This file is non-existing.
symbolName: "getGhi",
}),
/moduleURI must be a string/,
"rejects sourceInfo that doesn't have moduleURI"
);
});
add_task(async function test_broadcast_reject_version_not_string() {
await assert.rejects(
broadcastService.addListener(
"ghi",
{},
{
// NOTE: This file is non-existing.
symbolName: "getGhi",
}
),
/version should be a string/,
"rejects version that isn't a string"
);
});
add_task(async function test_broadcast_reject_version_empty_string() {
await assert.rejects(
broadcastService.addListener("ghi", "", {
// NOTE: This file is non-existing.
symbolName: "getGhi",
}),
/version should not be an empty string/,
"rejects version that is an empty string"
);
});