summaryrefslogtreecommitdiffstats
path: root/toolkit/components/taskscheduler/tests/xpcshell
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/taskscheduler/tests/xpcshell')
-rw-r--r--toolkit/components/taskscheduler/tests/xpcshell/test_TaskScheduler.js40
-rw-r--r--toolkit/components/taskscheduler/tests/xpcshell/test_TaskSchedulerWinImpl.js205
-rw-r--r--toolkit/components/taskscheduler/tests/xpcshell/test_WinTaskSchedulerService.js191
-rw-r--r--toolkit/components/taskscheduler/tests/xpcshell/xpcshell.ini5
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]