UNPKG

gpii-windows

Version:

Components of the GPII personalization infrastructure for use on Microsoft's "Windows" ™

407 lines (335 loc) 13.3 kB
/* * 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 */ "use strict"; 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(); });