Bug 1274728 - Exceptions to canceled events break synchronization. r=MakeMyDay
authorPhilipp Kewisch <mozilla@kewis.ch>
Fri, 10 Jun 2016 11:23:19 +0200
changeset 25293 543fdbd8c369c0ec83dc1cc09378a5047ce419c1
parent 25292 0e32f6823f815089c7708dad7b0415f94c67a2c0
child 25294 231f12348d254f51c8373b9ed1f0790673b32e3d
push id1725
push userclokep@gmail.com
push dateMon, 19 Sep 2016 17:35:08 +0000
treeherdercomm-beta@6ead1abf3817 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMakeMyDay
bugs1274728
Bug 1274728 - Exceptions to canceled events break synchronization. r=MakeMyDay Thanks to Jonathan Chen for the analysis and initial patch MozReview-Commit-ID: 4tMciHjyTWV
calendar/providers/gdata/modules/gdataUtils.jsm
calendar/test/unit/test_gdata_provider.js
--- a/calendar/providers/gdata/modules/gdataUtils.jsm
+++ b/calendar/providers/gdata/modules/gdataUtils.jsm
@@ -1098,16 +1098,23 @@ ItemSaver.prototype = {
      * calendar.
      *
      * @param exc       The exception to process.
      * @param item      The item the exception belongs to.
      * @return          A promise resolved when the item is added to the
      *                    calendar.
      */
     processException: function(exc, item) {
+        if (item.status == "CANCELLED") {
+            // Cancelled master items don't have the full amount of
+            // information, specifically no recurrence info. Since they are
+            // cancelled anyway, we can just ignore processing this exception.
+            return Promise.resolve();
+        }
+
         exc.parentItem = item;
         if (exc.status == "CANCELLED") {
             // Canceled means the occurrence is an EXDATE.
             item.recurrenceInfo.removeOccurrenceAt(exc.recurrenceId);
         } else {
             // Not canceled means the occurrence was modified.
             item.recurrenceInfo.modifyException(exc, true);
         }
--- a/calendar/test/unit/test_gdata_provider.js
+++ b/calendar/test/unit/test_gdata_provider.js
@@ -171,34 +171,53 @@ GDataServer.prototype = {
             }
         };
     },
 
     waitForLoad: function(aCalendar) {
         return new Promise(function(resolve, reject) {
             let observer = cal.createAdapter(Components.interfaces.calIObserver, {
                 onLoad: function() {
+                    let uncached = aCalendar.wrappedJSObject.mUncachedCalendar.wrappedJSObject;
                     aCalendar.removeObserver(observer);
-                    resolve(aCalendar);
+
+                    if (Components.isSuccessCode(uncached._lastStatus)) {
+                        resolve(aCalendar);
+                    } else {
+                        reject(uncached._lastMessage);
+                    }
                 }
             });
             aCalendar.addObserver(observer);
         });
     },
 
     getClient: function() {
         let uri = "googleapi://xpcshell/" +
                   "?testport=" + this.server.identity.primaryPort +
                   (this.calendarId ? "&calendar=" + encodeURIComponent(this.calendarId) : "") +
                   (this.tasksId ? "&tasks=" + encodeURIComponent(this.tasksId) : "");
         let calmgr = cal.getCalendarManager();
         let client = calmgr.createCalendar("gdata", Services.io.newURI(uri, null, null));
+        let uclient = client.wrappedJSObject;
         client.name = "xpcshell";
+
+        // Make sure we catch the last error message in case sync fails
+        monkeyPatch(uclient, "replayChangesOn", function(protofunc, aListener) {
+            protofunc({
+              onResult: function(op, detail) {
+                uclient._lastStatus = op.status;
+                uclient._lastMessage = detail;
+                aListener.onResult(op, detail);
+              }
+           });
+        });
+
         calmgr.registerCalendar(client);
-        client.wrappedJSObject.mThrottleLimits = {};
+        uclient.mThrottleLimits = {};
         MockConflictPrompt.register();
 
         let cachedCalendar = calmgr.getCalendarById(client.id);
         return this.waitForLoad(cachedCalendar);
     },
 
     router: function(nextHandler, request, response) {
         try {
@@ -1143,16 +1162,43 @@ add_task(function* test_recurring_except
     equal(items.length, 1);
 
     exIds = items[0].recurrenceInfo.getExceptionIds({});
     equal(exIds.length, 0);
 
     gServer.resetClient(client);
 });
 
+add_task(function* test_recurring_cancelled_exception() {
+    gServer.syncs = [{
+        token: "1",
+        events: [{
+            "kind": "calendar#event",
+            "etag": "\"1\"",
+            "id": "go6ijb0b46hlpbu4eeu92njevo",
+            "status": "cancelled",
+        },{
+            "kind": "calendar#event",
+            "etag": "\"2\"",
+            "id": "go6ijb0b46hlpbu4eeu92njevo_20060617T160000Z",
+            "status": "cancelled",
+            "recurringEventId": "go6ijb0b46hlpbu4eeu92njevo",
+            "originalStartTime": { "dateTime": "2006-06-17T18:00:00+02:00" }
+        }]
+    }];
+
+    let client = yield gServer.getClient();
+    let pclient = cal.async.promisifyCalendar(client.wrappedJSObject);
+
+    let items = yield pclient.getAllItems();
+    equal(items.length, 0);
+
+    gServer.resetClient(client);
+});
+
 add_task(function* test_import_invitation() {
     Preferences.set("calendar.google.enableAttendees", true);
     let client = yield gServer.getClient();
     let pclient = cal.async.promisifyCalendar(client.wrappedJSObject);
     let event = cal.createEvent([
         "BEGIN:VEVENT",
         "UID:xpcshell-import",
         "CREATED:20060608T210452Z",