UNPKG

gpii-windows

Version:

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

567 lines (471 loc) 19 kB
/* * Windows Utilities Tests * * Copyright 2018 Raising the Floor - International * * Licensed under the New BSD license. You may not use this file except in * compliance with this License. * * You may obtain a copy of the License at * https://github.com/GPII/universal/blob/master/LICENSE.txt */ "use strict"; var fluid = require("gpii-universal"), jqUnit = fluid.require("node-jqunit"), os = require("os"), fs = require("fs"), path = require("path"), rimraf = require("rimraf"); require("../WindowsUtilities.js"); var gpii = fluid.registerNamespace("gpii"); var windows = fluid.registerNamespace("gpii.windows"); fluid.registerNamespace("gpii.tests.windows"); var teardowns = []; jqUnit.module("gpii.tests.windows.windowsUtilities", { teardown: function () { while (teardowns.length) { teardowns.pop()(); } } }); jqUnit.test("Testing 'windows.resolveFlags' function with SpiFlags", function () { jqUnit.expect(1); var expected = windows.API_constants.SpiFlags.SPIF_UPDATEINIFILE | windows.API_constants.SpiFlags.SPIF_SENDCHANGE; var fWinIni = windows.resolveFlags("SPIF_UPDATEINIFILE | SPIF_SENDCHANGE", windows.API_constants.SpiFlags); jqUnit.assertDeepEq("Checking return value", expected, fWinIni); }); jqUnit.test("Testing 'windows.resolveFlags' function with 'virtualKeyCodes'", function () { jqUnit.expect(2); var expected = windows.API_constants.virtualKeyCodes.VK_CONTROL & windows.API_constants.virtualKeyCodes.VK_PAGEUP; var fResult = windows.resolveFlags("VK_CONTROL & VK_PAGEUP", windows.API_constants.virtualKeyCodes); jqUnit.assertDeepEq("Checking return value", expected, fResult); var flags = "VK_CONTROL & VK_PAGEUP & VK_RWIN & VK_PAGEDOWN & VK_F7"; expected = windows.API_constants.virtualKeyCodes.VK_CONTROL & windows.API_constants.virtualKeyCodes.VK_PAGEUP & windows.API_constants.virtualKeyCodes.VK_RWIN & windows.API_constants.virtualKeyCodes.VK_PAGEDOWN & windows.API_constants.virtualKeyCodes.VK_F7; fResult = windows.resolveFlags(flags, windows.API_constants.virtualKeyCodes); jqUnit.assertDeepEq("Checking return value", expected, fResult); }); jqUnit.test("Testing 'windows.resolveFlags' function with array of flags", function () { jqUnit.expect(1); var expected = windows.API_constants.virtualKeyCodes.VK_F1 | windows.API_constants.virtualKeyCodes.VK_F2 | windows.API_constants.virtualKeyCodes.VK_F3 | windows.API_constants.virtualKeyCodes.VK_F8 | windows.API_constants.virtualKeyCodes.VK_ESCAPE; var arrayValues = ["VK_F1", "VK_F2", "VK_F3", "VK_F8", "VK_ESCAPE"]; var fResult = windows.resolveFlags(arrayValues, windows.API_constants.virtualKeyCodes); jqUnit.assertDeepEq("Checking return value", expected, fResult); }); jqUnit.test("Testing 'windows.translateLastError' function", function () { jqUnit.expect(5); var msgMaps = [ [1, "Incorrect function.\r\n"], [3, "The system cannot find the path specified.\r\n"], [5, "Access is denied.\r\n"], [10, "The environment is incorrect.\r\n"], [15, "The system cannot find the drive specified.\r\n"] ]; fluid.each(msgMaps, function (msgMap) { var msgRes = windows.translateLastError(msgMap[0]); jqUnit.assertDeepEq("Checking expected message with error translation", msgMap[1], msgRes); }); }); jqUnit.test("Testing getAllDrives", function () { var drives = gpii.windows.getAllDrives(); jqUnit.assertTrue("getAllDrives should return an array", Array.isArray(drives)); jqUnit.assertTrue("There should be at least one drive", drives.length > 0); fluid.log("drives:", drives); var gotSystem = false; var systemPath = process.env.SystemRoot.substr(0, 3).toUpperCase(); fluid.each(drives, function (path) { if (!gotSystem) { gotSystem = path === systemPath; } jqUnit.assertTrue("All paths should look like a drive root", path.match(/^[A-Z]:\\/)); }); // Check some sanity of the list. jqUnit.assertTrue("SystemRoot drive should have been found (" + systemPath + ")", gotSystem); }); gpii.tests.windows.driveTypes = { DRIVE_REMOVABLE: 2, DRIVE_FIXED: 3, DRIVE_REMOTE: 4, DRIVE_CDROM: 5 }; gpii.tests.windows.getUsbDrivesTests = fluid.freezeRecursive({ "C:\\": { // Fixed drive, not usb volumePath: "\\\\.\\C:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_FIXED, CreateFileW: 1, DeviceIoControl: 1, usb: false, expected: false }, "D:\\": { // cd-rom drive volumePath: "\\\\.\\D:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_CDROM, CreateFileW: "unexpected", DeviceIoControl: "unexpected", usb: false, expected: false }, "E:\\": { // cd-rom drive, usb volumePath: "\\\\.\\E:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_CDROM, CreateFileW: "unexpected", DeviceIoControl: "unexpected", usb: false, expected: false }, "F:\\": { // network drive volumePath: "\\\\.\\F:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_REMOTE, CreateFileW: "unexpected", DeviceIoControl: "unexpected", usb: false, expected: false }, "G:\\": { // fixed drive, usb volumePath: "\\\\.\\G:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_FIXED, CreateFileW: 1, DeviceIoControl: 1, usb: true, expected: true }, "H:\\": { // removable drive, non-usb volumePath: "\\\\.\\H:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_REMOVABLE, CreateFileW: 1, DeviceIoControl: 1, usb: false, expected: false }, "I:\\": { // removable drive, usb volumePath: "\\\\.\\I:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_REMOVABLE, CreateFileW: 1, DeviceIoControl: 1, usb: true, expected: true }, "J:\\": { // another fixed drive, usb volumePath: "\\\\.\\J:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_FIXED, CreateFileW: 1, DeviceIoControl: 1, usb: true, expected: true }, // Errors "O:\\": { // CreateFile failed volumePath: "\\\\.\\O:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_FIXED, CreateFileW: gpii.windows.API_constants.INVALID_HANDLE_VALUE, DeviceIoControl: "unexpected", expected: false }, "P:\\": { // DeviceIoControl failed volumePath: "\\\\.\\P:", GetDriveTypeW: gpii.tests.windows.driveTypes.DRIVE_FIXED, CreateFileW: 1, DeviceIoControl: 0, expected: false } }); jqUnit.test("Testing getUsbDrives", function () { // Call it normally - the result is unpredictable, but it shouldn't crash. There should be at least one fixed drive, // so that will flow mostly through the happy path, calling all real API functions. var drives = gpii.windows.getUsbDrives(); fluid.log("gpii.windows.getUsbDrives:", drives); jqUnit.assertTrue("getUsbDrives returns an array", Array.isArray(drives)); // Mock the API calls to return the expected values. var origValues = { GetDriveTypeW: gpii.windows.kernel32.GetDriveTypeW, CreateFileW: gpii.windows.kernel32.CreateFileW, DeviceIoControl: gpii.windows.kernel32.DeviceIoControl }; var tests = gpii.tests.windows.getUsbDrivesTests; var currentTest; gpii.windows.kernel32.GetDriveTypeW = function (lpRootPathName) { var drivePath = gpii.windows.stringFromWideChar(lpRootPathName); fluid.log("Testing drive " + drivePath); currentTest = tests[drivePath]; jqUnit.assertNotNull("GetDriveTypeW called with a test drive", currentTest); return currentTest.GetDriveTypeW; }; gpii.windows.kernel32.CreateFileW = function (lpFileName) { jqUnit.assertNotEquals("CreateFileW should only be called if expected", "unexpected", currentTest.CreateFileW); jqUnit.assertEquals("CreateFileW called with lpFileName as the volume path", currentTest.volumePath, gpii.windows.stringFromWideChar(lpFileName)); return currentTest.CreateFileW; }; gpii.windows.kernel32.DeviceIoControl = function ( hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize) { jqUnit.assertNotEquals("DeviceIoControl should only be called if expected", "unexpected", currentTest.DeviceIoControl); jqUnit.assertEquals("DeviceIoControl called with hDevice as the CreateFile result", currentTest.CreateFileW, hDevice); jqUnit.assertTrue("DeviceIoControl called with a buffer for lpInBuffer", lpInBuffer instanceof Buffer); jqUnit.assertTrue("DeviceIoControl called with a buffer for lpOutBuffer", lpOutBuffer instanceof Buffer); jqUnit.assertEquals("DeviceIoControl called with matching nInBufferSize and lpInBuffer.length", lpInBuffer.length, nInBufferSize); jqUnit.assertEquals("DeviceIoControl called with matching nOutBufferSize and lpOutBuffer.length", lpOutBuffer.length, nOutBufferSize); var BusTypeUsb = 7; var busType = currentTest.usb ? BusTypeUsb : 1; lpOutBuffer.writeInt32LE(busType, 28); return currentTest.DeviceIoControl; }; try { var result = gpii.windows.getUsbDrives(Object.keys(tests)); var expected = []; fluid.each(tests, function (test, key) { if (test.expected) { expected.push(key); } }); jqUnit.assertDeepEq("getUsbDrives should return the expected drive paths", expected, result); } finally { fluid.each(Object.keys(origValues), function (funcName) { gpii.windows.kernel32[funcName] = origValues[funcName]; }); } }); gpii.tests.windows.getUserUsbDrivesTests = fluid.freezeRecursive({ "single drive, with token": { drives: [true], expect: ["A"] }, "single drive, no token": { drives: [false], expect: ["A"] }, "two drives, no tokens": { drives: [false, false], expect: ["A", "B"] }, "two drives, 1st with token": { drives: [true, false], expect: ["B"] }, "two drives, 2nd with token": { drives: [false, true], expect: ["A"] }, "two drives, both with token": { drives: [true, true], expect: ["A", "B"] }, "three drives, no token": { drives: [false, false, false], expect: ["A", "B", "C"] }, "three drives, 1 token": { drives: [false, true, false], expect: ["A", "C"] }, "three drives, 2 tokens": { drives: [true, true, false], expect: ["C"] }, "three drives, 3 tokens": { drives: [true, true, true], expect: ["A", "B", "C"] }, "no drives": { drives: [], expect: [] } }); jqUnit.asyncTest("Testing getUserUsbDrives", function () { var tests = fluid.hashToArray(gpii.tests.windows.getUserUsbDrivesTests, "name"); var testRoot = fs.mkdtempSync(path.join(os.tmpdir(), "gpii-usb-test")); teardowns.push(function () { rimraf.sync(testRoot); }); var tokenFile = ".gpii-user-token.txt"; jqUnit.expect(tests.length * 2); // Create a few directories (some containing a token file) and pass them to getUserUsbDrives as though they're // drive roots. var doTest = function (testIndex) { var currentTest = tests[testIndex]; if (!currentTest) { jqUnit.start(); return; } var letters = "ABCDE"; var testDir = fs.mkdtempSync(path.join(testRoot, "test")); var testDrives = []; // Create some directories to act as drives. fluid.each(currentTest.drives, function (hasToken, index) { var testDrive = path.join(testDir, letters[index]); testDrives.push(testDrive); fs.mkdirSync(testDrive); if (hasToken) { // Add the token, if required. fs.writeFileSync(path.join(testDrive, tokenFile)); } }); var promise = gpii.windows.getUserUsbDrives(testDrives); jqUnit.assertTrue("getUserUsbDrives should return a promise", fluid.isPromise(promise)); promise.then(function (drives) { var driveLetters = fluid.transform(drives, function (path) { return path.substr(path.length - 1, 1); }); fluid.log(currentTest.name, driveLetters); jqUnit.assertDeepEq("getUserUsbDrives should resolve with the expected value - " + currentTest.name, currentTest.expect, driveLetters.sort()); doTest(testIndex + 1); }); }; doTest(0); }); jqUnit.test("Testing removeFiles", function () { var testRoot = fs.mkdtempSync(path.join(os.tmpdir(), "gpii-file-test")); var testFiles = ["file.txt", "file", "json"]; var createNonEmptyDir = function () { var dir = fs.mkdtempSync(path.join(testRoot, "nonEmpty")); fluid.each(testFiles, function (fileName) { try { fs.appendFileSync(path.join(dir, fileName), ""); } catch (error) { jqUnit.fail("Error: Unable to create necessary files for testing."); } }); return dir; }; var createTestFile = function () { var file = path.join(testRoot, "file.txt"); try { fs.appendFileSync(file, ""); } catch (error) { jqUnit.fail("Error: Unable to create necessary test file."); } return file; }; var nonEmptyDirectory = createNonEmptyDir(); var checkRemoveAsFile = function (promise) { if (promise.disposition === undefined) { jqUnit.fail("Error: Operation isn't completed by this point."); } promise.then( function () { jqUnit.fail("Error: Removing directory as file should fail."); }, function (err) { if (err.code === "EPERM") { jqUnit.assert("Removing directory as file failed as expected."); } else { jqUnit.fail("Error: Removing directory as file should fail with EPERM error code."); } } ); }; // Trying to remove directory as file should fail with EPERM var pr1 = gpii.windows.rm(nonEmptyDirectory, { recursive: false, silent: false }); var pr2 = gpii.windows.rm(nonEmptyDirectory, { recursive: false, silent: true }); checkRemoveAsFile(pr1); checkRemoveAsFile(pr2); var checkRemoveNonEmpty = function (promise) { promise.then( function () { jqUnit.assert("Directory succesfully removed."); }, function (err) { jqUnit.fail("Error: Non-empty directory removing failed with error '" + err.message + "'"); } ); }; // Trying to remove non-empty directory should succeed. pr1 = gpii.windows.rm(nonEmptyDirectory, { recursive: true, silent: false }); nonEmptyDirectory = createNonEmptyDir(); pr2 = gpii.windows.rm(nonEmptyDirectory, { recursive: true, silent: true }); checkRemoveNonEmpty(pr1); checkRemoveNonEmpty(pr2); var checkRemoveFile = function (promise) { if (promise.disposition === undefined) { jqUnit.fail("Error: Operation isn't completed by this point."); } promise.then( function () { jqUnit.assert("File succesfully removed."); }, function (err) { jqUnit.fail("Error: File failed to be removed as directory with error '" + err.message + "'"); } ); }; // Trying to remove file should succeed. var f1 = createTestFile(); pr1 = gpii.windows.rm(f1, { recursive: false, silent: false }); checkRemoveFile(pr1); f1 = createTestFile(); pr1 = gpii.windows.rm(f1, { recursive: false, silent: true }); checkRemoveFile(pr1); // Trying to remove file recursively should also succeed. f1 = createTestFile(); pr1 = gpii.windows.rm(f1, { recursive: true, silent: false }); checkRemoveFile(pr1); f1 = createTestFile(); pr1 = gpii.windows.rm(f1, { recursive: true, silent: true }); checkRemoveFile(pr1); var checkRemoveNonExistent = function (promise) { if (promise.disposition === undefined) { jqUnit.fail("Error: Operation isn't completed by this point."); } promise.then( function () { jqUnit.fail("Error: Removing non-existing elements should fail."); }, function (err) { if (err.code === "ENOENT") { jqUnit.assert("Trying to remove non-existing element failed as expected."); } else { jqUnit.fail("Error: File failed to be removed as directory with error '" + err.message + "'"); } } ); }; // Trying to remove non-existing file and non-existing dir should fail with ENOENT. var nonExistingFile = path.join(testRoot, "nonExistingFile.txt"); var nonExistingDir = path.join(testRoot, "nonExistingDir"); pr1 = gpii.windows.rm(nonExistingDir, {recursive: true, silent: false}); pr2 = gpii.windows.rm(nonExistingFile, {recursive: true, silent: false}); checkRemoveNonExistent(pr1); checkRemoveNonExistent(pr2); // Trying to remove non-existing file and non-existing dir should not fail when silent is 'true'. var checkRemoveSilent = function (promise) { if (promise.disposition === undefined) { jqUnit.fail("Error: Operation isn't completed by this point."); } promise.then( function () { jqUnit.fail("Error: Removing non-existing elements should fail."); }, function (err) { if (err.code === "ENOENT") { jqUnit.assert("Trying to remove non-existing element failed as expected."); } else { jqUnit.fail("Error: File failed to be removed as directory with error '" + err.message + "'"); } } ); }; pr1 = gpii.windows.rm(nonExistingDir, {recursive: true, silent: false}); pr2 = gpii.windows.rm(nonExistingFile, {recursive: true, silent: false}); checkRemoveSilent(pr1); checkRemoveSilent(pr2); });