diff options
Diffstat (limited to 'toolkit/components/taskscheduler/tests/xpcshell')
4 files changed, 441 insertions, 0 deletions
diff --git a/toolkit/components/taskscheduler/tests/xpcshell/test_TaskScheduler.js b/toolkit/components/taskscheduler/tests/xpcshell/test_TaskScheduler.js new file mode 100644 index 0000000000..47e641530b --- /dev/null +++ b/toolkit/components/taskscheduler/tests/xpcshell/test_TaskScheduler.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +// Cross-platform task scheduler tests. +// +// There's not much that can be done here without allowing the task to run, so this +// only touches on the basics of argument checking. On platforms without a task +// scheduler implementation, these interfaces currently do nothing else. + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const { updateAppInfo } = ChromeUtils.import( + "resource://testing-common/AppInfo.jsm" +); +updateAppInfo(); + +const { TaskScheduler } = ChromeUtils.import( + "resource://gre/modules/TaskScheduler.jsm" +); + +registerCleanupFunction(() => { + TaskScheduler.deleteAllTasks(); +}); + +add_task(async function test_gen() { + TaskScheduler.registerTask("FOO", "xyz", TaskScheduler.MIN_INTERVAL_SECONDS, { + disabled: true, + }); + TaskScheduler.deleteTask("FOO"); + + Assert.throws( + () => + TaskScheduler.registerTask("BAR", "123", 1, { + disabled: true, + }), + /Interval is too short/ + ); +}); diff --git a/toolkit/components/taskscheduler/tests/xpcshell/test_TaskSchedulerWinImpl.js b/toolkit/components/taskscheduler/tests/xpcshell/test_TaskSchedulerWinImpl.js new file mode 100644 index 0000000000..19c6a86abc --- /dev/null +++ b/toolkit/components/taskscheduler/tests/xpcshell/test_TaskSchedulerWinImpl.js @@ -0,0 +1,205 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +// Unit tests for Windows scheduled task generation. + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const { updateAppInfo } = ChromeUtils.import( + "resource://testing-common/AppInfo.jsm" +); +updateAppInfo(); + +const { TaskScheduler } = ChromeUtils.import( + "resource://gre/modules/TaskScheduler.jsm" +); + +const { _TaskSchedulerWinImpl: WinImpl } = ChromeUtils.import( + "resource://gre/modules/TaskSchedulerWinImpl.jsm" +); + +const WinSvc = Cc["@mozilla.org/win-task-scheduler-service;1"].getService( + Ci.nsIWinTaskSchedulerService +); + +const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService( + Ci.nsIUUIDGenerator +); + +function randomName() { + return ( + "moz-taskschd-test-" + + uuidGenerator + .generateUUID() + .toString() + .slice(1, -1) + ); +} + +const gFolderName = randomName(); + +// Override task folder name, to prevent colliding with other tests. +WinImpl._taskFolderName = function() { + return gFolderName; +}; +WinImpl._taskFolderNameParts = function() { + return { + parentName: "\\", + subName: gFolderName, + }; +}; + +registerCleanupFunction(() => { + TaskScheduler.deleteAllTasks(); +}); + +add_task(function test_create() { + const taskName = "test-task-1"; + const rawTaskName = WinImpl._formatTaskName(taskName); + const folderName = WinImpl._taskFolderName(); + const exePath = "C:\\Program Files\\XYZ\\123.exe"; + const workingDir = "C:\\Program Files\\XYZ"; + const argsIn = [ + "x.txt", + "c:\\x.txt", + 'C:\\"HELLO WORLD".txt', + "only space.txt", + ]; + const expectedArgsOutStr = [ + "x.txt", + "c:\\x.txt", + '"C:\\\\\\"HELLO WORLD\\".txt"', + '"only space.txt"', + ].join(" "); + const description = "Entities: < &. Non-ASCII: abc😀def."; + const intervalSecsIn = 2 * 60 * 60; // 2 hours + const expectedIntervalOutWin10 = "PT2H"; // Windows 10 regroups by hours and minutes + const expectedIntervalOutWin7 = `PT${intervalSecsIn}S`; // Windows 7 doesn't regroup + + TaskScheduler.registerTask(taskName, exePath, intervalSecsIn, { + disabled: true, + args: argsIn, + description, + workingDirectory: workingDir, + }); + + // Read back the task + const readBackXML = WinSvc.getTaskXML(folderName, rawTaskName); + const parser = new DOMParser(); + const doc = parser.parseFromString(readBackXML, "text/xml"); + Assert.equal(doc.documentElement.tagName, "Task"); + + // Check for the values set above + Assert.equal(doc.querySelector("Actions Exec Command").textContent, exePath); + Assert.equal( + doc.querySelector("Actions Exec WorkingDirectory").textContent, + workingDir + ); + Assert.equal( + doc.querySelector("Actions Exec Arguments").textContent, + expectedArgsOutStr + ); + Assert.equal( + doc.querySelector("RegistrationInfo Description").textContent, + description + ); + Assert.equal( + doc.querySelector("RegistrationInfo Author").textContent, + Services.appinfo.vendor + ); + + Assert.equal(doc.querySelector("Settings Enabled").textContent, "false"); + + // Note: It's a little too tricky to check for a specific StartBoundary value reliably here, given + // that it gets set relative to Date.now(), so I'm skipping that. + const intervalOut = doc.querySelector( + "Triggers TimeTrigger Repetition Interval" + ).textContent; + Assert.ok( + intervalOut == expectedIntervalOutWin7 || + intervalOut == expectedIntervalOutWin10 + ); + + // Validate the XML + WinSvc.validateTaskDefinition(readBackXML); + + // Update + const updatedExePath = "C:\\Program Files (x86)\\ABC\\foo.exe"; + const updatedIntervalSecsIn = 3 * 60 * 60; // 3 hours + const expectedUpdatedIntervalOutWin10 = "PT3H"; + const expectedUpdatedIntervalOutWin7 = `PT${updatedIntervalSecsIn}S`; + + TaskScheduler.registerTask(taskName, updatedExePath, updatedIntervalSecsIn, { + disabled: true, + args: argsIn, + description, + workingDirectory: workingDir, + }); + + // Read back the updated task + const readBackUpdatedXML = WinSvc.getTaskXML(folderName, rawTaskName); + const updatedDoc = parser.parseFromString(readBackUpdatedXML, "text/xml"); + Assert.equal(updatedDoc.documentElement.tagName, "Task"); + + // Check for updated values + Assert.equal( + updatedDoc.querySelector("Actions Exec Command").textContent, + updatedExePath + ); + + Assert.notEqual( + doc.querySelector("Triggers TimeTrigger StartBoundary").textContent, + updatedDoc.querySelector("Triggers TimeTrigger StartBoundary").textContent + ); + const updatedIntervalOut = updatedDoc.querySelector( + "Triggers TimeTrigger Repetition Interval" + ).textContent; + Assert.ok( + updatedIntervalOut == expectedUpdatedIntervalOutWin7 || + updatedIntervalOut == expectedUpdatedIntervalOutWin10 + ); + + // Check that the folder really was there + { + const { parentName, subName } = WinImpl._taskFolderNameParts(); + let threw; + try { + WinSvc.deleteFolder(parentName, subName); + } catch (ex) { + threw = ex; + } + Assert.equal(threw.result, Cr.NS_ERROR_FILE_DIR_NOT_EMPTY); + } + + // Delete + TaskScheduler.deleteAllTasks(); + + // Check that the folder is gone + { + const { parentName, subName } = WinImpl._taskFolderNameParts(); + let threw; + try { + WinSvc.deleteFolder(parentName, subName); + } catch (ex) { + threw = ex; + } + Assert.equal(threw.result, Cr.NS_ERROR_FILE_NOT_FOUND); + } + + // Format and validate the XML with the task not disabled + const enabledXML = WinImpl._formatTaskDefinitionXML(exePath, intervalSecsIn, { + args: argsIn, + description, + workingDirectory: workingDir, + }); + Assert.equal(WinSvc.validateTaskDefinition(enabledXML), 0 /* S_OK */); + + // Format and validate with no options + const basicXML = WinImpl._formatTaskDefinitionXML( + "foo", + TaskScheduler.MIN_INTERVAL_SECONDS + ); + Assert.equal(WinSvc.validateTaskDefinition(basicXML), 0 /* S_OK */); +}); diff --git a/toolkit/components/taskscheduler/tests/xpcshell/test_WinTaskSchedulerService.js b/toolkit/components/taskscheduler/tests/xpcshell/test_WinTaskSchedulerService.js new file mode 100644 index 0000000000..5ffe018573 --- /dev/null +++ b/toolkit/components/taskscheduler/tests/xpcshell/test_WinTaskSchedulerService.js @@ -0,0 +1,191 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +// Unit tests for access to the Windows Task Scheduler via nsIWinTaskSchedulerService. + +const svc = Cc["@mozilla.org/win-task-scheduler-service;1"].getService( + Ci.nsIWinTaskSchedulerService +); + +const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService( + Ci.nsIUUIDGenerator +); + +function randomName() { + return ( + "moz-taskschd-test-" + + uuidGenerator + .generateUUID() + .toString() + .slice(1, -1) + ); +} + +const gParentFolderName = randomName(); +const gParentFolderPath = `\\${gParentFolderName}`; +const gSubFolderName = randomName(); +const gSubFolderPath = `\\${gParentFolderName}\\${gSubFolderName}`; +// This folder will not be created +const gMissingFolderName = randomName(); +const gMissingFolderPath = `\\${gParentFolderName}\\${gMissingFolderName}`; + +const gValidTaskXML = `<Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> + <Triggers /> + <Settings> + <Enabled>false</Enabled> + </Settings> + <Actions> + <Exec> + <Command>xyz123.exe</Command> + </Exec> + </Actions> +</Task>`; + +// Missing actions +const gInvalidTaskXML = `<Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> + <Triggers /> + <Settings> + <Enabled>false</Enabled> + </Settings> +</Task>`; + +function cleanup() { + let tasksToDelete = svc.getFolderTasks(gSubFolderPath); + + for (const task of tasksToDelete) { + svc.deleteTask(gSubFolderPath, task); + } + + svc.deleteFolder(gParentFolderPath, gSubFolderName); + + svc.deleteFolder("\\", gParentFolderPath); +} + +registerCleanupFunction(() => { + try { + cleanup(); + } catch (_ex) { + // Folders may not exist + } +}); + +add_task(async function test_svc() { + /***** FOLDERS *****/ + + // Try creating subfolder before parent folder exists + Assert.throws( + () => svc.createFolder(gParentFolderPath, gSubFolderName), + /NS_ERROR_FILE_NOT_FOUND/ + ); + + // Create parent folder + svc.createFolder("\\", gParentFolderName); + + // Create subfolder + svc.createFolder(gParentFolderPath, gSubFolderName); + + // Try creating existing folder + Assert.throws( + () => svc.createFolder(gParentFolderPath, gSubFolderName), + /NS_ERROR_FILE_ALREADY_EXISTS/ + ); + + // Try deleting nonexistent subfolder + Assert.throws( + () => svc.deleteFolder(gParentFolderPath, gMissingFolderName), + /NS_ERROR_FILE_NOT_FOUND/ + ); + + /***** TASKS *****/ + const taskNames = [randomName(), randomName(), randomName()]; + + // Try enumerating nonexistent subfolder + Assert.throws( + () => svc.getFolderTasks(gMissingFolderPath), + /NS_ERROR_FILE_NOT_FOUND/ + ); + + // List empty subfolder + Assert.deepEqual(svc.getFolderTasks(gSubFolderPath), []); + + // Try to create task in nonexistent subfolder + Assert.throws( + () => svc.registerTask(gMissingFolderPath, taskNames[0], gValidTaskXML), + /NS_ERROR_FILE_NOT_FOUND/ + ); + + // Create task 0 + + svc.registerTask(gSubFolderPath, taskNames[0], gValidTaskXML); + + // Try to recreate task 0 + Assert.throws( + () => svc.registerTask(gSubFolderPath, taskNames[0], gValidTaskXML), + /NS_ERROR_FILE_ALREADY_EXISTS/ + ); + + // Update task 0 + svc.registerTask( + gSubFolderPath, + taskNames[0], + gValidTaskXML, + true /* aUpdateExisting */ + ); + + // Read back XML + Assert.ok(svc.getTaskXML(gSubFolderPath, taskNames[0])); + + // Create remaining tasks + for (const task of taskNames.slice(1)) { + svc.registerTask(gSubFolderPath, task, gValidTaskXML); + } + + // Try to create with invalid XML + Assert.throws( + () => svc.registerTask(gSubFolderPath, randomName(), gInvalidTaskXML), + /NS_ERROR_FAILURE/ + ); + + // Validate XML + Assert.equal(svc.validateTaskDefinition(gValidTaskXML), 0 /* S_OK */); + + // Try to validate invalid XML + Assert.notEqual(svc.validateTaskDefinition(gInvalidTaskXML), 0 /* S_OK */); + + // Test enumeration + { + let foundTasks = svc.getFolderTasks(gSubFolderPath); + foundTasks.sort(); + + let allTasks = taskNames.slice(); + allTasks.sort(); + + Assert.deepEqual(foundTasks, allTasks); + } + + // Try deleting non-empty folder + Assert.throws( + () => svc.deleteFolder(gParentFolderPath, gSubFolderName), + /NS_ERROR_FILE_DIR_NOT_EMPTY/ + ); + + const missingTaskName = randomName(); + + // Try deleting non-existent task + Assert.throws( + () => svc.deleteTask(gSubFolderName, missingTaskName), + /NS_ERROR_FILE_NOT_FOUND/ + ); + + // Try reading non-existent task + Assert.throws( + () => svc.getTaskXML(gSubFolderPath, missingTaskName), + /NS_ERROR_FILE_NOT_FOUND/ + ); + + /***** Cleanup *****/ + // Explicitly call cleanup() to test that it removes the folder without error. + cleanup(); +}); diff --git a/toolkit/components/taskscheduler/tests/xpcshell/xpcshell.ini b/toolkit/components/taskscheduler/tests/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..ee406fe74b --- /dev/null +++ b/toolkit/components/taskscheduler/tests/xpcshell/xpcshell.ini @@ -0,0 +1,5 @@ +[test_WinTaskSchedulerService.js] +run-if = os == "win" # Test of Windows only service +[test_TaskSchedulerWinImpl.js] +run-if = os == "win" # Test of Windows backend +[test_TaskScheduler.js] |