gpii-windows
Version:
Components of the GPII personalization infrastructure for use on Microsoft's "Windows" ™
567 lines (471 loc) • 19 kB
JavaScript
/*
* 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);
});