summaryrefslogtreecommitdiffstats
path: root/comm/calendar/test/unit/test_alarmservice.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/calendar/test/unit/test_alarmservice.js
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/calendar/test/unit/test_alarmservice.js')
-rw-r--r--comm/calendar/test/unit/test_alarmservice.js606
1 files changed, 606 insertions, 0 deletions
diff --git a/comm/calendar/test/unit/test_alarmservice.js b/comm/calendar/test/unit/test_alarmservice.js
new file mode 100644
index 0000000000..df61dc029b
--- /dev/null
+++ b/comm/calendar/test/unit/test_alarmservice.js
@@ -0,0 +1,606 @@
+/* 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 { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");
+var { TestUtils } = ChromeUtils.importESModule("resource://testing-common/TestUtils.sys.mjs");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ CalAlarm: "resource:///modules/CalAlarm.jsm",
+ CalEvent: "resource:///modules/CalEvent.jsm",
+ CalRecurrenceInfo: "resource:///modules/CalRecurrenceInfo.jsm",
+});
+
+var EXPECT_NONE = 0;
+var EXPECT_FIRED = 1;
+var EXPECT_TIMER = 2;
+
+function do_check_xor(a, b, aMessage) {
+ return ok((a && !b) || (!a && b), aMessage);
+}
+
+var alarmObserver = {
+ QueryInterface: ChromeUtils.generateQI(["calIAlarmServiceObserver"]),
+
+ service: null,
+ firedMap: {},
+ expectedMap: {},
+ pendingOps: {},
+
+ onAlarm(aItem, aAlarm) {
+ this.firedMap[aItem.hashId] = this.firedMap[aItem.hashId] || {};
+ this.firedMap[aItem.hashId][aAlarm.icalString] = true;
+ },
+
+ onNotification(item) {},
+
+ onRemoveAlarmsByItem(aItem) {
+ if (aItem.hashId in this.firedMap) {
+ delete this.firedMap[aItem.hashId];
+ }
+ },
+
+ onRemoveAlarmsByCalendar() {},
+
+ onAlarmsLoaded(aCalendar) {
+ this.checkLoadStatus();
+ if (aCalendar.id in this.pendingOps) {
+ this.pendingOps[aCalendar.id].call();
+ }
+ },
+
+ async doOnAlarmsLoaded(aCalendar) {
+ this.checkLoadStatus();
+ if (
+ aCalendar.id in this.service.mLoadedCalendars &&
+ this.service.mLoadedCalendars[aCalendar.id]
+ ) {
+ // the calendar's alarms have already been loaded
+ } else {
+ await new Promise(resolve => {
+ // the calendar hasn't been fully loaded yet, set as a pending operation
+ this.pendingOps[aCalendar.id] = resolve;
+ });
+ }
+ },
+
+ getTimer(aCalendarId, aItemId, aAlarmStr) {
+ return aCalendarId in this.service.mTimerMap &&
+ aItemId in this.service.mTimerMap[aCalendarId] &&
+ aAlarmStr in this.service.mTimerMap[aCalendarId][aItemId]
+ ? this.service.mTimerMap[aCalendarId][aItemId][aAlarmStr]
+ : null;
+ },
+
+ expectResult(aCalendar, aItem, aAlarm, aExpected) {
+ let expectedAndTitle = {
+ expected: aExpected,
+ title: aItem.title,
+ };
+ this.expectedMap[aCalendar.id] = this.expectedMap[aCalendar.id] || {};
+ this.expectedMap[aCalendar.id][aItem.hashId] =
+ this.expectedMap[aCalendar.id][aItem.hashId] || {};
+ this.expectedMap[aCalendar.id][aItem.hashId][aAlarm.icalString] = expectedAndTitle;
+ },
+
+ expectOccurrences(aCalendar, aItem, aAlarm, aExpectedArray) {
+ // we need to be earlier than the first occurrence
+ let date = aItem.startDate.clone();
+ date.second -= 1;
+
+ for (let expected of aExpectedArray) {
+ let occ = aItem.recurrenceInfo.getNextOccurrence(date);
+ occ.QueryInterface(Ci.calIEvent);
+ date = occ.startDate;
+ this.expectResult(aCalendar, occ, aAlarm, expected);
+ }
+ },
+
+ checkExpected(aMessage) {
+ for (let calId in this.expectedMap) {
+ for (let id in this.expectedMap[calId]) {
+ for (let icalString in this.expectedMap[calId][id]) {
+ let expectedAndTitle = this.expectedMap[calId][id][icalString];
+ // if no explicit message has been passed, take the item title
+ let message = typeof aMessage == "string" ? aMessage : expectedAndTitle.title;
+ // only alarms expected as fired should exist in our fired alarm map
+ do_check_xor(
+ expectedAndTitle.expected != EXPECT_FIRED,
+ id in this.firedMap && icalString in this.firedMap[id],
+ message + "; check fired"
+ );
+ // only alarms expected as timers should exist in the service's timer map
+ do_check_xor(
+ expectedAndTitle.expected != EXPECT_TIMER,
+ !!this.getTimer(calId, id, icalString),
+ message + "; check timer"
+ );
+ }
+ }
+ }
+ },
+
+ checkLoadStatus() {
+ for (let calId in this.service.mLoadedCalendars) {
+ if (!this.service.mLoadedCalendars[calId]) {
+ // at least one calendar hasn't finished loading alarms
+ ok(this.service.isLoading);
+ return;
+ }
+ }
+ ok(!this.service.isLoading);
+ },
+
+ clear() {
+ this.firedMap = {};
+ this.pendingOps = {};
+ this.expectedMap = {};
+ },
+};
+
+add_setup(async function () {
+ do_get_profile();
+ await new Promise(resolve =>
+ do_calendar_startup(() => {
+ alarmObserver.service = Cc["@mozilla.org/calendar/alarm-service;1"].getService(
+ Ci.calIAlarmService
+ ).wrappedJSObject;
+ ok(!alarmObserver.service.mStarted);
+ alarmObserver.service.startup(null);
+ ok(alarmObserver.service.mStarted);
+
+ // we need to replace the existing observers with our observer
+ for (let obs of alarmObserver.service.mObservers.values()) {
+ alarmObserver.service.removeObserver(obs);
+ }
+ alarmObserver.service.addObserver(alarmObserver);
+ resolve();
+ })
+ );
+});
+
+function createAlarmFromDuration(aOffset) {
+ let alarm = new CalAlarm();
+
+ alarm.related = Ci.calIAlarm.ALARM_RELATED_START;
+ alarm.offset = cal.createDuration(aOffset);
+
+ return alarm;
+}
+
+function createEventWithAlarm(aCalendar, aStart, aEnd, aOffset, aRRule) {
+ let alarm = null;
+ let item = new CalEvent();
+
+ item.id = cal.getUUID();
+ item.calendar = aCalendar;
+ item.startDate = aStart || cal.dtz.now();
+ item.endDate = aEnd || cal.dtz.now();
+ if (aOffset) {
+ alarm = createAlarmFromDuration(aOffset);
+ item.addAlarm(alarm);
+ }
+ if (aRRule) {
+ item.recurrenceInfo = new CalRecurrenceInfo(item);
+ item.recurrenceInfo.appendRecurrenceItem(cal.createRecurrenceRule(aRRule));
+ }
+ return [item, alarm];
+}
+
+async function addTestItems(aCalendar) {
+ let item, alarm;
+
+ // alarm on an item starting more than a month in the past should not fire
+ let date = cal.dtz.now();
+ date.day -= 32;
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "P7D");
+ item.title = "addTestItems Test 1";
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_NONE);
+ await aCalendar.addItem(item);
+
+ // alarm 15 minutes ago should fire
+ date = cal.dtz.now();
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "-PT15M");
+ item.title = "addTestItems Test 2";
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_FIRED);
+ await aCalendar.addItem(item);
+
+ // alarm within 6 hours should have a timer set
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "PT1H");
+ item.title = "addTestItems Test 3";
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_TIMER);
+ await aCalendar.addItem(item);
+
+ // alarm more than 6 hours in the future should not have a timer set
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "PT7H");
+ item.title = "addTestItems Test 4";
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_NONE);
+ await aCalendar.addItem(item);
+
+ // test multiple alarms on an item
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date);
+ item.title = "addTestItems Test 5";
+ const firedOffsets = [
+ ["-PT1H", EXPECT_FIRED],
+ ["-PT15M", EXPECT_FIRED],
+ ["PT1H", EXPECT_TIMER],
+ ["PT7H", EXPECT_NONE],
+ ["P7D", EXPECT_NONE],
+ ];
+
+ firedOffsets.forEach(([offset, expected]) => {
+ alarm = createAlarmFromDuration(offset);
+ item.addAlarm(alarm);
+ alarmObserver.expectResult(aCalendar, item, alarm, expected);
+ });
+ await aCalendar.addItem(item);
+
+ // Bug 1344068 - Alarm with lastAck on exception, should take parent lastAck.
+ // Alarm 15 minutes ago should fire.
+ date = cal.dtz.now();
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "-PT15M", "RRULE:FREQ=DAILY;COUNT=1");
+ item.title = "addTestItems Test 6";
+
+ // Parent item is acknowledged before alarm, so it should fire.
+ let lastAck = item.startDate.clone();
+ lastAck.hour -= 1;
+ item.alarmLastAck = lastAck;
+
+ // Occurrence is acknowledged after alarm (start date), so if the alarm
+ // service wrongly uses the exception occurrence then we catch it.
+ let occ = item.recurrenceInfo.getOccurrenceFor(item.startDate);
+ occ.alarmLastAck = item.startDate.clone();
+ item.recurrenceInfo.modifyException(occ, true);
+
+ alarmObserver.expectOccurrences(aCalendar, item, alarm, [EXPECT_FIRED]);
+ await aCalendar.addItem(item);
+
+ // daily repeating event starting almost 2 full days ago. The alarms on the first 2 occurrences
+ // should fire, and a timer should be set for the next occurrence only
+ date = cal.dtz.now();
+ date.hour -= 47;
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "-PT15M", "RRULE:FREQ=DAILY");
+ item.title = "addTestItems Test 7";
+ alarmObserver.expectOccurrences(aCalendar, item, alarm, [
+ EXPECT_FIRED,
+ EXPECT_FIRED,
+ EXPECT_TIMER,
+ EXPECT_NONE,
+ EXPECT_NONE,
+ ]);
+ await aCalendar.addItem(item);
+
+ // monthly repeating event starting 2 months and a day ago. The alarms on the first 2 occurrences
+ // should be ignored, the alarm on the next occurrence only should fire.
+ // Missing recurrences of the event in particular days of the year generate exceptions to the
+ // regular sequence of alarms.
+ date = cal.dtz.now();
+ let statusAlarmSequences = {
+ reg: [EXPECT_NONE, EXPECT_NONE, EXPECT_FIRED, EXPECT_NONE, EXPECT_NONE],
+ excep1: [EXPECT_NONE, EXPECT_FIRED, EXPECT_NONE, EXPECT_NONE, EXPECT_NONE],
+ excep2: [EXPECT_NONE, EXPECT_NONE, EXPECT_NONE, EXPECT_NONE, EXPECT_NONE],
+ };
+ let expected = [];
+ if (date.day == 1) {
+ // Exceptions for missing occurrences on months with 30 days when the event starts on 31st.
+ let sequence = [
+ "excep1",
+ "reg",
+ "excep2",
+ "excep1",
+ "reg",
+ "excep1",
+ "reg",
+ "excep1",
+ "reg",
+ "excep2",
+ "excep1",
+ "reg",
+ ][date.month];
+ expected = statusAlarmSequences[sequence];
+ } else if (date.day == 30 && (date.month == 2 || date.month == 3)) {
+ // Exceptions for missing occurrences or different start date caused by February.
+ let leapYear = date.endOfYear.yearday == 366;
+ expected = leapYear ? statusAlarmSequences.reg : statusAlarmSequences.excep1;
+ } else if (date.day == 31 && date.month == 2) {
+ // Exceptions for missing occurrences caused by February.
+ expected = statusAlarmSequences.excep1;
+ } else {
+ // Regular sequence of alarms expected for all the others days.
+ expected = statusAlarmSequences.reg;
+ }
+ date.month -= 2;
+ date.day -= 1;
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "-PT15M", "RRULE:FREQ=MONTHLY");
+ item.title = "addTestItems Test 8";
+ alarmObserver.expectOccurrences(aCalendar, item, alarm, expected);
+ await aCalendar.addItem(item);
+}
+
+async function doModifyItemTest(aCalendar) {
+ let item, alarm;
+
+ // begin with item starting before the alarm date range
+ let date = cal.dtz.now();
+ date.day -= 32;
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "PT0S");
+ await aCalendar.addItem(item);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_NONE);
+ alarmObserver.checkExpected("doModifyItemTest Test 1");
+
+ // move event into the fired range
+ let oldItem = item.clone();
+ date.day += 31;
+ item.startDate = date.clone();
+ item.generation++;
+ await aCalendar.modifyItem(item, oldItem);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_FIRED);
+ alarmObserver.checkExpected("doModifyItemTest Test 2");
+
+ // move event into the timer range
+ oldItem = item.clone();
+ date.hour += 25;
+ item.startDate = date.clone();
+ item.generation++;
+ await aCalendar.modifyItem(item, oldItem);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_TIMER);
+ alarmObserver.checkExpected("doModifyItemTest Test 3");
+
+ // move event past the timer range
+ oldItem = item.clone();
+ date.hour += 6;
+ item.startDate = date.clone();
+ item.generation++;
+ await aCalendar.modifyItem(item, oldItem);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_NONE);
+ alarmObserver.checkExpected("doModifyItemTest Test 4");
+
+ // re-move the event in the timer range and verify that the timer
+ // doesn't change when the timezone changes to floating (bug 1300493).
+ oldItem = item.clone();
+ date.hour -= 6;
+ item.startDate = date.clone();
+ item.generation++;
+ await aCalendar.modifyItem(item, oldItem);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_TIMER);
+ alarmObserver.checkExpected("doModifyItemTest Test 5");
+ let oldTimer = alarmObserver.getTimer(aCalendar.id, item.hashId, alarm.icalString);
+ oldItem = item.clone();
+ // change the timezone to floating
+ item.startDate.timezone = cal.dtz.floating;
+ item.generation++;
+ await aCalendar.modifyItem(item, oldItem);
+ // the alarm must still be timer and with the same value (apart from milliseconds)
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_TIMER);
+ alarmObserver.checkExpected("doModifyItemTest Test 5, floating timezone");
+ let newTimer = alarmObserver.getTimer(aCalendar.id, item.hashId, alarm.icalString);
+ ok(
+ newTimer.delay - oldTimer.delay <= 1000,
+ "doModifyItemTest Test 5, floating timezone; check timer value"
+ );
+}
+
+async function doDeleteItemTest(aCalendar) {
+ alarmObserver.clear();
+ let item, alarm;
+ let item2, alarm2;
+
+ // create a fired alarm and a timer
+ let date = cal.dtz.now();
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "-PT5M");
+ [item2, alarm2] = createEventWithAlarm(aCalendar, date, date, "PT1H");
+ item.title = "doDeleteItemTest item Test 1";
+ item2.title = "doDeleteItemTest item2 Test 1";
+ await aCalendar.addItem(item);
+ await aCalendar.addItem(item2);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_FIRED);
+ alarmObserver.expectResult(aCalendar, item2, alarm2, EXPECT_TIMER);
+ alarmObserver.checkExpected();
+
+ // item deletion should clear the fired alarm and timer
+ await aCalendar.deleteItem(item);
+ await aCalendar.deleteItem(item2);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_NONE);
+ alarmObserver.expectResult(aCalendar, item2, alarm2, EXPECT_NONE);
+ alarmObserver.checkExpected("doDeleteItemTest, cleared fired alarm and timer");
+}
+
+async function doAcknowledgeTest(aCalendar) {
+ alarmObserver.clear();
+ let item, alarm;
+ let item2, alarm2;
+
+ // create the fired alarms
+ let date = cal.dtz.now();
+ [item, alarm] = createEventWithAlarm(aCalendar, date, date, "-PT5M");
+ [item2, alarm2] = createEventWithAlarm(aCalendar, date, date, "-PT5M");
+ item.title = "doAcknowledgeTest item Test 1";
+ item2.title = "doAcknowledgeTest item2 Test 1";
+ await aCalendar.addItem(item);
+ await aCalendar.addItem(item2);
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_FIRED);
+ alarmObserver.expectResult(aCalendar, item2, alarm2, EXPECT_FIRED);
+ alarmObserver.checkExpected();
+
+ // test snooze alarm
+ alarmObserver.service.snoozeAlarm(item, alarm, cal.createDuration("PT1H"));
+ alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_TIMER);
+ alarmObserver.checkExpected("doAcknowledgeTest, test snooze alarm");
+
+ // the snoozed alarm timer delay should be close to an hour
+ let tmr = alarmObserver.getTimer(aCalendar.id, item.hashId, alarm.icalString);
+ ok(
+ Math.abs(tmr.delay - 3600000) <= 1000,
+ "doAcknowledgeTest, snoozed alarm timer delay close to an hour"
+ );
+
+ // test dismiss alarm
+ alarmObserver.service.dismissAlarm(item2, alarm2);
+ alarmObserver.expectResult(aCalendar, item2, alarm2, EXPECT_NONE);
+ alarmObserver.checkExpected("doAcknowledgeTest, test dismiss alarm");
+}
+
+async function doRunTest(aOnCalendarCreated) {
+ alarmObserver.clear();
+
+ let memory = cal.manager.createCalendar("memory", Services.io.newURI("moz-memory-calendar://"));
+ memory.id = cal.getUUID();
+
+ if (aOnCalendarCreated) {
+ await aOnCalendarCreated(memory);
+ }
+
+ cal.manager.registerCalendar(memory);
+ await alarmObserver.doOnAlarmsLoaded(memory);
+ return memory;
+}
+
+/**
+ * Test the initial alarm loading of a calendar with existing data.
+ */
+add_task(async function test_loadCalendar() {
+ await doRunTest(async memory => addTestItems(memory));
+ alarmObserver.checkExpected();
+});
+
+/**
+ * Test adding alarm data to a calendar already registered.
+ */
+add_task(async function test_addItems() {
+ let memory = await doRunTest();
+ await addTestItems(memory);
+ alarmObserver.checkExpected();
+});
+
+/**
+ * Test response to modification of alarm data.
+ */
+add_task(async function test_modifyItems() {
+ let memory = await doRunTest();
+ await doModifyItemTest(memory);
+ await doDeleteItemTest(memory);
+ await doAcknowledgeTest(memory);
+});
+
+/**
+ * Test an array of timers has expected delay values.
+ *
+ * @param {nsITimer[]} timers - An array of nsITimer.
+ * @param {number[]} expected - Expected delays in seconds.
+ */
+function matchTimers(timers, expected) {
+ let delays = timers.map(timer => timer.delay / 1000);
+ let matched = true;
+ for (let i = 0; i < delays.length; i++) {
+ if (Math.abs(delays[i] - expected[i]) > 2) {
+ matched = false;
+
+ break;
+ }
+ }
+ ok(matched, `Delays=${delays} should match Expected=${expected}`);
+}
+
+/**
+ * Test notification timers are set up correctly when add/modify/remove a
+ * calendar item.
+ */
+add_task(async function test_notificationTimers() {
+ let memory = await doRunTest();
+ // Add an item.
+ let date = cal.dtz.now();
+ date.hour += 1;
+ let item, oldItem;
+ [item] = createEventWithAlarm(memory, date, date, null);
+ await memory.addItem(item);
+ equal(
+ alarmObserver.service.mNotificationTimerMap[item.calendar.id],
+ undefined,
+ "should have no notification timer"
+ );
+
+ // Set the pref to have one notifiaction.
+ Services.prefs.setCharPref("calendar.notifications.times", "-PT1H");
+ oldItem = item.clone();
+ date.hour += 1;
+ item.startDate = date.clone();
+ item.generation++;
+ await memory.modifyItem(item, oldItem);
+ // Should have one notification timer
+ matchTimers(alarmObserver.service.mNotificationTimerMap[item.calendar.id][item.hashId], [3600]);
+
+ // Set the pref to have three notifiactions.
+ Services.prefs.setCharPref("calendar.notifications.times", "END:PT2M,PT0M,END:-PT30M,-PT5M");
+ oldItem = item.clone();
+ date.hour -= 1;
+ item.startDate = date.clone();
+ date.hour += 1;
+ item.endDate = date.clone();
+ item.generation++;
+ await memory.modifyItem(item, oldItem);
+ // Should have four notification timers.
+ matchTimers(alarmObserver.service.mNotificationTimerMap[item.calendar.id][item.hashId], [
+ 3300, // 55 minutes
+ 3600, // 60 minutes
+ 5400, // 90 minutes, which is 30 minutes before the end (END:-PT30M)
+ 7320, // 122 minutes, which is 2 minutes after the end (END:PT2M)
+ ]);
+
+ alarmObserver.service.removeFiredNotificationTimer(item);
+ // Should have three notification timers.
+ matchTimers(
+ alarmObserver.service.mNotificationTimerMap[item.calendar.id][item.hashId],
+ [3600, 5400, 7320]
+ );
+
+ await memory.deleteItem(item);
+ equal(
+ alarmObserver.service.mNotificationTimerMap[item.calendar.id],
+ undefined,
+ "notification timers should be removed"
+ );
+
+ Services.prefs.clearUserPref("calendar.notifications.times");
+});
+
+/**
+ * Test notification timers are set up correctly according to the calendar level
+ * notifications.times config.
+ */
+add_task(async function test_calendarLevelNotificationTimers() {
+ let loaded = false;
+ let item;
+ let memory = await doRunTest();
+
+ if (!loaded) {
+ loaded = true;
+ // Set the global pref to have one notifiaction.
+ Services.prefs.setCharPref("calendar.notifications.times", "-PT1H");
+
+ // Add an item.
+ let date = cal.dtz.now();
+ date.hour += 2;
+ [item] = createEventWithAlarm(memory, date, date, null);
+ await memory.addItem(item);
+
+ // Should have one notification timer.
+ matchTimers(alarmObserver.service.mNotificationTimerMap[item.calendar.id][item.hashId], [3600]);
+ // Set the calendar level pref to have two notification timers.
+ memory.setProperty("notifications.times", "-PT5M,PT0M");
+ }
+
+ await TestUtils.waitForCondition(
+ () => alarmObserver.service.mNotificationTimerMap[item.calendar.id]?.[item.hashId].length == 2
+ );
+ // Should have two notification timers
+ matchTimers(alarmObserver.service.mNotificationTimerMap[item.calendar.id][item.hashId], [
+ 6900, // 105 minutes
+ 7200, // 120 minutes
+ ]);
+
+ Services.prefs.clearUserPref("calendar.notifications.times");
+});
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("calendar.notifications.times");
+});