gpii-windows
Version:
Components of the GPII personalization infrastructure for use on Microsoft's "Windows" ™
407 lines (335 loc) • 13.3 kB
JavaScript
/*
* Window message tests
*
* Copyright 2017 Raising the Floor - International
*
* Licensed under the New BSD license. You may not use this file except in
* compliance with this License.
*
* The R&D leading to these results received funding from the
* Department of Education - Grant H421A150005 (GPII-APCP). However,
* these results do not necessarily represent the policy of the
* Department of Education, and you should not assume endorsement by the
* Federal Government.
*
* You may obtain a copy of the License at
* https://github.com/GPII/universal/blob/master/LICENSE.txt
*/
;
require("../../WindowsUtilities/WindowsUtilities.js");
var fluid = require("gpii-universal"),
ffi = require("ffi-napi");
var jqUnit = fluid.require("node-jqunit");
var gpii = fluid.registerNamespace("gpii");
fluid.registerNamespace("gpii.tests.windowMessages");
require("../index.js");
jqUnit.module("gpii.tests.windowMessages");
gpii.tests.windowMessages.user32 = ffi.Library("user32", {
// https://msdn.microsoft.com/library/ms633499
"FindWindowW": [
gpii.windows.types.HANDLE, ["char*", "int"]
],
// https://msdn.microsoft.com/library/ms644950
"SendMessageW": [
"int", [gpii.windows.types.HANDLE, gpii.windows.types.UINT, gpii.windows.types.UINT, gpii.windows.types.UINT ]
]
});
fluid.defaults("gpii.tests.windowMessages.wrapper", {
gradeNames: ["fluid.component"],
invokers: {
start: "{gpii.windows.messages}.start({that})",
stop: {
func: "{gpii.windows.messages}.stop",
args: "{that}"
},
getWindowHandle: "{gpii.windows.messages}.getWindowHandle",
sendMessage: "{gpii.windows.messages}.sendMessage",
sendData: "{gpii.windows.messages}.sendData"
},
listeners: {
"{gpii.windows.messages}.events.onMessage": "{that}.events.onMessage.fire"
},
events: {
onMessage :null
}
});
/**
* Finds a window, based on the classname.
* @param {String} className The window class name.
* @return {Number} The window handle, or null if not found.
*/
gpii.tests.windowMessages.findWindow = function (className) {
var classNameBuffer = gpii.windows.stringToWideChar(className);
var handle = gpii.tests.windowMessages.user32.FindWindowW(classNameBuffer, 0);
return handle || null;
};
// Test that the message window gets created and destroyed.
jqUnit.asyncTest("Window Messages - Window creation and destruction", function () {
jqUnit.expect(1);
var instances = fluid.queryIoCSelector(fluid.rootComponent, "gpii.windows.messages");
var messages = (instances.length > 0) ? instances[0] : gpii.windows.messages();
var windowExists = function () {
return !!gpii.tests.windowMessages.findWindow(messages.windowClassname);
};
if (windowExists()) {
gpii.windows.messages.destroyMessageWindow(messages);
}
jqUnit.assertFalse("Message window shouldn't exist before start", windowExists());
var work = [
function () {
messages.start(messages);
return gpii.windows.waitForCondition(windowExists, {
timeout: 5000,
error: "Message window should be created (first start)"
});
},
function () {
messages.stop(messages);
return gpii.windows.waitForCondition(windowExists, {
timeout: 5000,
error: "Message window should be destroyed (stop)",
conditionValue: false
});
},
function () {
messages.start(messages);
return gpii.windows.waitForCondition(windowExists, {
timeout: 5000,
error: "Message window should be created (second start)"
});
},
function () {
messages.stop();
return gpii.windows.waitForCondition(windowExists, {
timeout: 5000,
error: "Message window should be destroyed (after stop)",
conditionValue: false
});
}
];
fluid.promise.sequence(work).then(jqUnit.start, jqUnit.fail);
});
// Test that the message window handles a message and fires the event, and can cope with more than one instance.
jqUnit.test("Window Messages - Message receipt", function () {
var WM_USER = 0x400;
var messages = [
{
msg: WM_USER,
wParam: 1234,
lParam: 5678
},
{
msg: WM_USER + 1,
wParam: 42,
lParam: 0
}
];
var instance1 = gpii.tests.windowMessages.wrapper();
var instance2 = gpii.tests.windowMessages.wrapper();
instance1.events.onMessage.addListener(function (hwnd, msg, wParam, lParam) {
var index = null;
if (msg === WM_USER) {
index = 0;
} else if (msg === WM_USER + 1) {
index = 1;
}
if (index !== null) {
jqUnit.assertEquals("wParam, instance1, message " + index, messages[index].wParam, wParam);
jqUnit.assertEquals("lParam, instance1, message " + index, messages[index].lParam, lParam.address());
}
});
instance2.events.onMessage.addListener(function (hwnd, msg, wParam, lParam) {
// Only the first message is expected for this instance. The second message will be received, but it's not
// required to have.
if (msg === WM_USER) {
jqUnit.assertEquals("wParam, instance2", messages[0].wParam, wParam);
jqUnit.assertEquals("lParam, instance2", messages[0].lParam, lParam.address());
}
});
// Start listening for messages.
instance1.start();
instance2.start();
var window = instance1.getWindowHandle();
jqUnit.assertFalse("getWindowHandle should return a number", isNaN(window));
jqUnit.assertEquals("Both instances should point to the same window", window, instance2.getWindowHandle());
// Send a message to the window
gpii.tests.windowMessages.user32.SendMessageW(window, messages[0].msg, messages[0].wParam, messages[0].lParam);
// Stop one instance from listening
instance2.stop();
// Send second message
gpii.tests.windowMessages.user32.SendMessageW(window, messages[1].msg, messages[1].wParam, messages[1].lParam);
instance1.stop();
window = instance1.getWindowHandle();
jqUnit.assertFalse("getWindowHandle should return a zero", 0);
instance1.destroy();
instance2.destroy();
});
jqUnit.test("Window Messages - Registered messages", function () {
// The name needs to be random, because messages remain registered throughout the Windows session.
var name = "gpii-test-" + Math.random();
// Register a message
var id1 = gpii.windows.messages.registeredMessage(name);
jqUnit.assertEquals("registeredMessage should return a number", "number", typeof(id1));
var id2 = gpii.windows.messages.registeredMessage(name);
jqUnit.assertEquals("registeredMessage should be repeatable", id1, id2);
// A message not registered by this process
var shellHook = gpii.windows.messages.registeredMessage("SHELLHOOK");
jqUnit.assertEquals("registeredMessage(shellhook) should return a number", "number", typeof(shellHook));
// Get the name from the id.
var name2 = gpii.windows.messages.registeredMessageName(id1);
jqUnit.assertEquals("registeredMessageName(id) should return the name", name, name2);
// A none-registered message.
var name4 = gpii.windows.messages.registeredMessageName(1);
jqUnit.assertEquals("registeredMessageName(1) should return the id", 1, name4);
});
jqUnit.asyncTest("Window Messages - Message sending", function () {
var WM_GPII_TEST = "GPII_TEST_MESSAGE";
var tests = [
{
wParam: 1234,
lParam: 5678
},
{
wParam: 42,
lParam: 0
},
{
wParam: 1,
lParam: "hello"
},
{
wParam: 1,
lParam: ""
},
{
wParam: 1,
lParam: "2233"
},
{
wParam: 2,
lParam: Buffer.from([0,1,2,3,4])
},
{
wParam: 2,
lParam: Buffer.from([0,1,2,3,4])
},
{
window: "gpii-message-window",
wParam: 11,
lParam: 22
},
{
window: null,
wParam: 1,
lParam: 2
},
{
window: "BROADCAST",
wParam: 3,
lParam: 4
}
];
jqUnit.expect(tests.length * 2);
var instance = gpii.tests.windowMessages.wrapper();
var currentTest;
var currentTestIndex = -1;
instance.events.onMessage.addListener(function (hwnd, msg, wParam, lParam) {
if (currentTest && msg === WM_GPII_TEST) {
jqUnit.assertEquals("wParam should be the correct value", currentTest.wParam, wParam);
var lParamActual;
switch (typeof(currentTest.lParam)) {
case "string":
lParamActual = gpii.windows.stringFromWideChar(lParam);
jqUnit.assertEquals("lParam should be the correct string value", currentTest.lParam, lParamActual);
break;
case "number":
lParamActual = lParam.address();
jqUnit.assertEquals("lParam should be the correct number value", currentTest.lParam, lParamActual);
break;
default:
lParamActual = fluid.makeArray(lParam.reinterpret(currentTest.lParam.length));
var lParamExpect = fluid.makeArray(currentTest.lParam);
jqUnit.assertDeepEq("lParam should be the correct buffer value", lParamExpect, lParamActual);
break;
}
fluid.log("received test:", currentTestIndex);
process.nextTick(nextTest);
}
});
// Start listening for messages.
instance.start();
var messageWindow = instance.getWindowHandle();
// Send the messages
var nextTest = function () {
currentTestIndex++;
if (currentTestIndex >= tests.length) {
instance.stop();
instance.destroy();
jqUnit.start();
} else {
currentTest = tests[currentTestIndex];
fluid.log("currentTestIndex:", currentTestIndex);
var window = currentTest.window === undefined ? messageWindow : currentTest.window;
instance.sendMessage(window, WM_GPII_TEST, currentTest.wParam, currentTest.lParam);
}
};
nextTest();
});
jqUnit.asyncTest("Window Messages - send data", function () {
var tests = [
{
number: 123,
data: "test string"
},
{
number: 111,
data: Buffer.from([0,1,2,3,4])
}
];
jqUnit.expect(tests.length * 4);
var instance = gpii.tests.windowMessages.wrapper();
var currentTest;
var currentTestIndex = -1;
var messageWindow;
instance.events.onMessage.addListener(function (hwnd, msg, wParam, lParam) {
if (msg === gpii.windows.API_constants.WM_COPYDATA) {
jqUnit.assertEquals("wParam should be the correct value", messageWindow, wParam);
var copyData = new gpii.windows.COPYDATASTRUCT(lParam.reinterpret(gpii.windows.COPYDATASTRUCT.size));
jqUnit.assertEquals("number should be the correct value", currentTest.number, copyData.dwData);
var data = copyData.lpData;
var length;
if (typeof(currentTest.data) === "string") {
var dataString = gpii.windows.stringFromWideChar(data);
jqUnit.assertEquals("data should be the correct string value", currentTest.data, dataString);
// Data length should be text length + null terminator (wide chars)
length = (currentTest.data.length + 1) * 2 ;
} else {
var dataIn = copyData.lpData.reinterpret(currentTest.data.length);
length = dataIn.length;
var dataArray = fluid.makeArray(dataIn);
var expectArray = fluid.makeArray(currentTest.data);
jqUnit.assertDeepEq("data should be the correct buffer value", expectArray, dataArray);
}
jqUnit.assertEquals("cbSize should be correct", length, copyData.cbData);
fluid.log("received test:", currentTestIndex);
process.nextTick(nextTest);
}
});
// Start listening for messages.
instance.start();
messageWindow = instance.getWindowHandle();
// Send the messages
var nextTest = function () {
currentTestIndex++;
if (currentTestIndex >= tests.length) {
instance.stop();
instance.destroy();
jqUnit.start();
} else {
currentTest = tests[currentTestIndex];
fluid.log("currentTestIndex:", currentTestIndex);
instance.sendData(messageWindow, currentTest.number, currentTest.data);
}
};
nextTest();
});