diff options
Diffstat (limited to 'comm/calendar/test/unit/head.js')
-rw-r--r-- | comm/calendar/test/unit/head.js | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/comm/calendar/test/unit/head.js b/comm/calendar/test/unit/head.js new file mode 100644 index 0000000000..2b7b9c99fa --- /dev/null +++ b/comm/calendar/test/unit/head.js @@ -0,0 +1,337 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var { AppConstants } = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs"); +var { FileUtils } = ChromeUtils.importESModule("resource://gre/modules/FileUtils.sys.mjs"); + +var { updateAppInfo } = ChromeUtils.importESModule("resource://testing-common/AppInfo.sys.mjs"); + +ChromeUtils.defineModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); + +updateAppInfo(); + +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); + +function createDate(aYear, aMonth, aDay, aHasTime, aHour, aMinute, aSecond, aTimezone) { + let date = Cc["@mozilla.org/calendar/datetime;1"].createInstance(Ci.calIDateTime); + date.resetTo( + aYear, + aMonth, + aDay, + aHour || 0, + aMinute || 0, + aSecond || 0, + aTimezone || cal.dtz.UTC + ); + date.isDate = !aHasTime; + return date; +} + +function createEventFromIcalString(icalString) { + if (/^BEGIN:VCALENDAR/.test(icalString)) { + let parser = Cc["@mozilla.org/calendar/ics-parser;1"].createInstance(Ci.calIIcsParser); + parser.parseString(icalString); + let items = parser.getItems(); + cal.ASSERT(items.length == 1); + return items[0].QueryInterface(Ci.calIEvent); + } + let event = Cc["@mozilla.org/calendar/event;1"].createInstance(Ci.calIEvent); + event.icalString = icalString; + return event; +} + +function createTodoFromIcalString(icalString) { + let todo = Cc["@mozilla.org/calendar/todo;1"].createInstance(Ci.calITodo); + todo.icalString = icalString; + return todo; +} + +function getMemoryCal() { + return Cc["@mozilla.org/calendar/calendar;1?type=memory"].createInstance( + Ci.calISyncWriteCalendar + ); +} + +function getStorageCal() { + // Whenever we get the storage calendar we need to request a profile, + // otherwise the cleanup functions will not run + do_get_profile(); + + // create URI + let db = Services.dirsvc.get("TmpD", Ci.nsIFile); + db.append("test_storage.sqlite"); + let uri = Services.io.newFileURI(db); + + // Make sure timezone service is initialized + Cc["@mozilla.org/calendar/timezone-service;1"].getService(Ci.calIStartupService).startup(null); + + // create storage calendar + let stor = Cc["@mozilla.org/calendar/calendar;1?type=storage"].createInstance( + Ci.calISyncWriteCalendar + ); + stor.uri = uri; + stor.id = cal.getUUID(); + return stor; +} + +/** + * Return an item property as string. + * + * @param aItem + * @param string aProp possible item properties: start, end, duration, + * generation, title, + * id, calendar, creationDate, lastModifiedTime, + * stampTime, priority, privacy, status, + * alarmLastAck, recurrenceStartDate + * and any property that can be obtained using getProperty() + */ +function getProps(aItem, aProp) { + let value = null; + switch (aProp) { + case "start": + value = aItem.startDate || aItem.entryDate || null; + break; + case "end": + value = aItem.endDate || aItem.dueDate || null; + break; + case "duration": + value = aItem.duration || null; + break; + case "generation": + value = aItem.generation; + break; + case "title": + value = aItem.title; + break; + case "id": + value = aItem.id; + break; + case "calendar": + value = aItem.calendar.id; + break; + case "creationDate": + value = aItem.creationDate; + break; + case "lastModifiedTime": + value = aItem.lastModifiedTime; + break; + case "stampTime": + value = aItem.stampTime; + break; + case "priority": + value = aItem.priority; + break; + case "privacy": + value = aItem.privacy; + break; + case "status": + value = aItem.status; + break; + case "alarmLastAck": + value = aItem.alarmLastAck; + break; + case "recurrenceStartDate": + value = aItem.recurrenceStartDate; + break; + default: + value = aItem.getProperty(aProp); + } + if (value) { + return value.toString(); + } + return null; +} + +function compareItemsSpecific(aLeftItem, aRightItem, aPropArray) { + if (!aPropArray) { + // left out: "id", "calendar", "lastModifiedTime", "generation", + // "stampTime" as these are expected to change + aPropArray = [ + "start", + "end", + "duration", + "title", + "priority", + "privacy", + "creationDate", + "status", + "alarmLastAck", + "recurrenceStartDate", + ]; + } + if (aLeftItem instanceof Ci.calIEvent) { + aLeftItem.QueryInterface(Ci.calIEvent); + } else if (aLeftItem instanceof Ci.calITodo) { + aLeftItem.QueryInterface(Ci.calITodo); + } + for (let i = 0; i < aPropArray.length; i++) { + equal(getProps(aLeftItem, aPropArray[i]), getProps(aRightItem, aPropArray[i])); + } +} + +/** + * Unfold ics lines by removing any \r\n or \n followed by a linear whitespace + * (space or htab). + * + * @param aLine The line to unfold + * @returns The unfolded line + */ +function ics_unfoldline(aLine) { + return aLine.replace(/\r?\n[ \t]/g, ""); +} + +/** + * Dedent the template string tagged with this function to make indented data + * easier to read. Usage: + * + * let data = dedent` + * This is indented data it will be unindented so that the first line has + * no leading spaces and the second is indented by two spaces. + * `; + * + * @param strings The string fragments from the template string + * @param ...values The interpolated values + * @returns The interpolated, dedented string + */ +function dedent(strings, ...values) { + let parts = []; + // Perform variable interpolation + let minIndent = Infinity; + for (let [i, string] of strings.entries()) { + let innerparts = string.split("\n"); + if (i == 0) { + innerparts.shift(); + } + if (i == strings.length - 1) { + innerparts.pop(); + } + for (let [j, ip] of innerparts.entries()) { + let match = ip.match(/^(\s*)\S*/); + if (j != 0) { + minIndent = Math.min(minIndent, match[1].length); + } + } + parts.push(innerparts); + } + + return parts + .map((part, i) => { + return ( + part + .map((line, j) => { + return j == 0 && i > 0 ? line : line.substr(minIndent); + }) + .join("\n") + (i < values.length ? values[i] : "") + ); + }) + .join(""); +} + +/** + * Read a JSON file and return the JS object + */ +function readJSONFile(aFile) { + let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); + try { + stream.init(aFile, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0); + let bytes = NetUtil.readInputStream(stream, stream.available()); + let data = JSON.parse(new TextDecoder().decode(bytes)); + return data; + } catch (ex) { + dump("readJSONFile: Error reading JSON file: " + ex); + } finally { + stream.close(); + } + return false; +} + +function do_load_timezoneservice(callback) { + do_test_pending(); + cal.timezoneService.startup({ + onResult() { + do_test_finished(); + callback(); + }, + }); +} + +function do_load_calmgr(callback) { + do_test_pending(); + cal.manager.startup({ + onResult() { + do_test_finished(); + callback(); + }, + }); +} + +function do_calendar_startup(callback) { + let obs = { + observe() { + Services.obs.removeObserver(this, "calendar-startup-done"); + do_test_finished(); + executeSoon(callback); + }, + }; + + let startupService = Cc["@mozilla.org/calendar/startup-service;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + + if (startupService.started) { + callback(); + } else { + do_test_pending(); + Services.obs.addObserver(obs, "calendar-startup-done"); + if (this._profileInitialized) { + Services.obs.notifyObservers(null, "profile-after-change", "xpcshell-do-get-profile"); + } else { + do_get_profile(true); + } + } +} + +/** + * Monkey patch the function with the name x on obj and overwrite it with func. + * The first parameter of this function is the original function that can be + * called at any time. + * + * @param obj The object the function is on. + * @param name The string name of the function. + * @param func The function to monkey patch with. + */ +function monkeyPatch(obj, x, func) { + let old = obj[x]; + obj[x] = function () { + let parent = old.bind(obj); + let args = Array.from(arguments); + args.unshift(parent); + try { + return func.apply(obj, args); + } catch (e) { + console.error(e); + throw e; + } + }; +} + +/** + * Asserts the properties of an actual extract parser result to what was + * expected. + * + * @param {object} actual - Mostly the actual output of parse(). + * @param {object} expected - The expected output. + * @param {string} level - The variable name to refer to report on. + */ +function compareExtractResults(actual, expected, level = "") { + for (let [key, value] of Object.entries(expected)) { + let qualifiedKey = [level, Array.isArray(expected) ? `[${key}]` : `.${key}`].join(""); + if (value && typeof value == "object") { + Assert.ok(actual[key], `${qualifiedKey} is not null`); + compareExtractResults(actual[key], value, qualifiedKey); + continue; + } + Assert.equal(actual[key], value, `${qualifiedKey} has value "${value}"`); + } +} |