Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #286 from asutherland/activesync-flag-snafu

Bug 972514 - [B2G][Email]Flagged Hotmail emails do not display the changed on or off flag indicator when synced from the server to the device using ActiveSync. r=mcav
  • Loading branch information...
commit 8f2996aa17f4795dcff7acb130897bed8dedd3ae 2 parents f6018f1 + f1f50ec
@asutherland asutherland authored
View
14 data/lib/mailapi/activesync/folder.js
@@ -810,16 +810,16 @@ ActiveSyncFolderConn.prototype = {
}
}
- body.bodyReps = [mailRep.makeBodyPart({
- type: bodyType,
- sizeEstimate: bodySize,
- amountDownloaded: 0,
- isDownloaded: false
- })];
-
// If this is an add, then these are new structures so we need to normalize
// them.
if (isAdded) {
+ body.bodyReps = [mailRep.makeBodyPart({
+ type: bodyType,
+ sizeEstimate: bodySize,
+ amountDownloaded: 0,
+ isDownloaded: false
+ })];
+
return {
header: mailRep.makeHeaderInfo(header),
body: mailRep.makeBodyInfo(body)
View
6 data/lib/mailapi/mailapi.js
@@ -2249,8 +2249,10 @@ MailAPI.prototype = {
for (var i = 0; i < msg.sliceUpdates.length; i++) {
var update = msg.sliceUpdates[i];
if (update.type === 'update') {
- // Updates are performed and fire immediately/synchronously
- this._processSliceUpdate(msg, update, slice);
+ // Updates are identified by their index position, so they need to be
+ // processed in the same order we're hearing about them.
+ this._spliceFireFuncs.push(
+ this._processSliceUpdate.bind(this, msg, update.updates, slice));
} else {
// Added items are transformed immediately, but the actual mutation of
// the slice and notifications do not fire until _fireAllSplices().
View
9 data/lib/mailapi/slice_bridge_proxy.js
@@ -62,10 +62,15 @@ SliceBridgeProxy.prototype = {
/**
* Issue an update for existing items.
+ *
+ * @param {Array[]} indexUpdatesRun
+ * Flattened pairs of index and the updated object wire representation.
*/
sendUpdate: function sbp_sendUpdate(indexUpdatesRun) {
- var update = indexUpdatesRun;
- update.type = 'update';
+ var update = {
+ updates: indexUpdatesRun,
+ type: 'update',
+ };
this.addUpdate(update);
},
View
2  package.json
@@ -23,7 +23,7 @@
"url": "https://github.com/mozilla-b2g/gaia-email-libs-and-more/issues"
},
"devDependencies": {
- "mail-fakeservers": "~0.0.13",
+ "mail-fakeservers": "~0.0.15",
"mozilla-download": "~0.4"
}
}
View
4 test/test-files.json
@@ -164,6 +164,10 @@
"variants": ["imap:fake", "imap:real", "activesync:fake", "pop3:fake"]
},
+ "test_sync_server_changes.js": {
+ "variants": ["imap:fake", "imap:real", "activesync:fake"]
+ },
+
"test_autoconfig.js": {
"variants": ["noserver"]
},
View
46 test/unit/resources/th_fake_activesync_server.js
@@ -201,6 +201,52 @@ var TestActiveSyncServerMixins = {
});
},
+ modifyMessagesInFolder: function(serverFolderInfo, messages,
+ addFlags, delFlags) {
+ var changes = {};
+ addFlags = addFlags || [];
+ delFlags = delFlags || [];
+ addFlags.forEach(function(flag) {
+ switch (flag) {
+ case '\\Flagged':
+ changes.flag = true;
+ break;
+ case '\\Seen':
+ changes.read = true;
+ break;
+ default:
+ console.warn('ActiveSync does not grok (add) flag:', flag);
+ break;
+ }
+ });
+ delFlags.forEach(function(flag) {
+ switch (flag) {
+ case '\\Flagged':
+ changes.flag = false;
+ break;
+ case '\\Seen':
+ changes.read = false;
+ break;
+ default:
+ console.warn('ActiveSync does not grok (false) flag:', flag);
+ break;
+ }
+ });
+ var serverIds = messages.map(function(message) {
+ // message is either a MailHeader (where srvid is currently available) or
+ // a knownMessage, in which case the rep is what we generated in
+ // addMessagesToFolder where the good stuff is in id
+ return message._wireRep ? message._wireRep.srvid : message.id;
+ });
+ return this._backdoor({
+ command: 'modifyMessagesInFolder',
+ folderId: serverFolderInfo.id,
+ serverIds: serverIds,
+ changes: changes
+ });
+
+ },
+
deleteMessagesFromFolder: function(serverFolderInfo, messages) {
// The server is our friend and uses the message's message-id header value
// as its serverId.
View
114 test/unit/test_imap_general.js
@@ -13,8 +13,6 @@
* - Resynchronizing an already-synchronized unchanged folder only issues
* the time-range search and flag fetches and returns the same set of
* messages.
- * - Resynchronizing an already-synchronized folder detects new messages,
- * deleted messages, and flag changes.
**/
define(['rdcommon/testcontext', './resources/th_main',
@@ -142,120 +140,8 @@ TD.commonCase('folder sync', function(T) {
{ count: INITIAL_FILL_SIZE, full: 0, flags: 0, deleted: 0 },
{ top: true, bottom: false, grow: false, newCount: null });
testUniverse.do_pretendToBeOffline(false);
- // this used to be [5, 5, 2] like the initial sync. Now it's just a refresh.
- var manipView = testAccount.do_openFolderView(
- 'resyncs', msearchFolder,
- [{ count: 12, full: 0, flags: 12, deleted: 0 }],
- { top: true, bottom: false, grow: false, newCount: 0 });
-
- /**
- * Use our mutation mechanism with speculative application disabled in order
- * to cause some apparent flag changes and deletions to occur.
- */
- T.group('sync detects additions/modifications/deletions');
- T.action('mutate', msearchFolder, function() {
- testAccount.modifyMessageFlagsOnServerButNotLocally(
- manipView, [3], ['\\Seen'], null);
- testAccount.modifyMessageFlagsOnServerButNotLocally(
- manipView, [4], ['\\Flagged'], null);
- // delete 2 from the first interval (of 7), 1 from the second (of 7)
- testAccount.deleteMessagesOnServerButNotLocally(
- manipView, [1, 2, 8]);
- });
- testAccount.do_closeFolderView(manipView);
- // add messages (4, 3) to (5-2=3, 5-1=4) so our fetches become: 7, 7
- // (and we are no longer covering all known messages)
- testAccount.do_addMessagesToFolder(
- msearchFolder,
- { count: 7, age: { days: 2 }, age_incr: { days: 1 } });
- // - open view, checking refresh, and _leave it open_ for the next group
- // the refresh will see everything at once; this used to be: 7/4/3/2,
- // 7/3/4/1. The refresh will fully span all known messages because the
- // 7 new messages are interspersed among the known messages and this is a
- // refresh that does not overflow, not a deepening sync.
- var msearchView = testAccount.do_openFolderView(
- 'msearch', msearchFolder,
- // because the new messages are interleaved rather than at the end, we will
- // end up with more than 12/INITIAL_FILL_SIZE in the second case.
- [{ count: 16, full: 7, flags: 9, deleted: 3 }],
- // these messages are all older than the newest message, none are 'new'
- { top: true, bottom: false, grow: false, newCount: 0 });
-
- /**
- * Perform some manipulations with the view still open, then trigger a refresh
- * and make sure the view updates correctly.
- */
- T.group('sync refresh detects mutations and updates in-place');
- var expectedRefreshChanges = {
- changes: null,
- deletions: null,
- };
- T.action('mutate', msearchFolder, function() {
- expectedRefreshChanges.deletions =
- testAccount.deleteMessagesOnServerButNotLocally(msearchView, [0, 8]);
- var read = testAccount.modifyMessageFlagsOnServerButNotLocally(
- msearchView, [9], ['\\Seen'], null)[0];
- var starred = testAccount.modifyMessageFlagsOnServerButNotLocally(
- msearchView, [10], ['\\Flagged'], null)[0];
- expectedRefreshChanges.changes = [
- [read, 'isRead', true],
- [starred, 'isStarred', true],
- ];
- });
- testAccount.do_refreshFolderView(
- msearchView,
- // Our expectations happen in a single go here because the refresh covers
- // the entire date range in question.
- { count: 14, full: 0, flags: 14, deleted: 2 },
- expectedRefreshChanges,
- { top: true, bottom: false, grow: false, newCount: 0 });
-
- T.group('get the message body for an existing message');
- T.action(eSync, 'request message body from', msearchView, function() {
- // Pick an index that's not the first one of anything...
- var index = 5,
- synMessage = msearchView.testFolder.knownMessages[index];
-
- var bodyPart = synMessage.bodyPart;
- while (!(bodyPart instanceof $msggen.SyntheticPartLeaf))
- bodyPart = bodyPart.parts[0];
-
- eSync.expect_namedValue(
- 'bodyInfo',
- {
- content: bodyPart._contentType === 'text/html' ? bodyPart.body :
- [0x1, bodyPart.body],
- type: bodyPart._contentType === 'text/html' ? 'html' : 'plain',
- length: 1
- });
-
- var header = msearchView.slice.items[index];
- header.getBody({ downloadBodyReps: true }, function(bodyInfo) {
- bodyInfo.onchange = function() {
- eSync.namedValue('bodyInfo', {
- content: bodyInfo.bodyReps[0].content,
- type: bodyInfo.bodyReps[0].type,
- length: bodyInfo.bodyReps.length
- });
- bodyInfo.die();
- };
- });
- });
-
- T.group('fail to get the message body for a deleted message');
- T.action(eSync, 'request deleted message body from',
- msearchFolder.storageActor, function() {
- eSync.expect_namedValue('bodyInfo', null);
- msearchFolder.storageActor.expect_bodyNotFound();
- var deletedHeader = expectedRefreshChanges.deletions[0];
- deletedHeader.getBody(function(bodyInfo) {
- eSync.namedValue('bodyInfo', bodyInfo);
- // it's null so we don't call bodyInfo.die(), but if it wasn't...!
- });
- });
T.group('cleanup');
- testAccount.do_closeFolderView(msearchView);
});
}); // end define
View
88 test/unit/test_sync_server_changes.js
@@ -0,0 +1,88 @@
+/**
+ * Test our sync mechanism's ability to update already-synchronized messages'
+ * states based on changes that happen on the server somehow. (Probably via
+ * a webmail UI or other client.)
+ *
+ * This file is not run for POP3 which of course lacks such niceties.
+ */
+
+define(['rdcommon/testcontext', './resources/th_main',
+ './resources/messageGenerator', 'exports'],
+ function($tc, $th_main, $msggen, exports) {
+
+var TD = exports.TD = $tc.defineTestsFor(
+ { id: 'test_sync_server_changes' }, null, [$th_main.TESTHELPER], ['app']);
+
+TD.commonCase('detect server changes', function(T) {
+ T.group('setup');
+ var testUniverse = T.actor('testUniverse', 'U'),
+ testAccount = T.actor('testAccount', 'A', { universe: testUniverse }),
+ eSync = T.lazyLogger('sync');
+
+ var testFolder = testAccount.do_createTestFolder(
+ 'test_sync_server_changes', // (insert one more than we want to find)
+ { count: 9, age: { days: 1 }, age_incr: { days: 1 }, age_incr_every: 2 });
+ var manipView = testAccount.do_openFolderView(
+ 'syncs', testFolder,
+ [{ count: 9, full: 9, flags: 0, deleted: 0,
+ filterType: 'none' }],
+ { top: true, bottom: true, grow: false, newCount: null },
+ { syncedToDawnOfTime: true });
+
+ T.group('sync detects additions/modifications/deletions with fresh view');
+ // Change existing messages and delete some existing ones (3, so => 6).
+ T.action('mutate', testFolder, function() {
+ testAccount.modifyMessageFlagsOnServerButNotLocally(
+ manipView, [3], ['\\Seen'], null);
+ testAccount.modifyMessageFlagsOnServerButNotLocally(
+ manipView, [4], ['\\Flagged'], null);
+ testAccount.deleteMessagesOnServerButNotLocally(
+ manipView, [1, 2, 8]);
+ });
+ testAccount.do_closeFolderView(manipView);
+
+ // Add some more messages to the folder. ( => 11 )
+ testAccount.do_addMessagesToFolder(
+ testFolder,
+ { count: 5, age: { days: 2 }, age_incr: { days: 1 }, age_incr_every: 2 });
+ // - open view, checking refresh, and _leave it open_ for the next group
+ var checkView = testAccount.do_openFolderView(
+ 'check', testFolder,
+ [{ count: 11, full: 5, flags: 6, changed: 2, deleted: 3 }],
+ // these messages are all older than the newest message, none are 'new'
+ { top: true, bottom: true, grow: false, newCount: 0 });
+
+ /**
+ * Perform some manipulations with the view still open, then trigger a refresh
+ * and make sure the view updates correctly.
+ */
+ T.group('sync refresh detects mutations and updates in-place with open view');
+ var expectedRefreshChanges = {
+ changes: null,
+ deletions: null,
+ };
+ T.action('mutate', testFolder, function() {
+ expectedRefreshChanges.deletions =
+ testAccount.deleteMessagesOnServerButNotLocally(checkView, [0, 8]);
+ var read = testAccount.modifyMessageFlagsOnServerButNotLocally(
+ checkView, [9], ['\\Seen'], null)[0];
+ var starred = testAccount.modifyMessageFlagsOnServerButNotLocally(
+ checkView, [10], ['\\Flagged'], null)[0];
+ expectedRefreshChanges.changes = [
+ [read, 'isRead', true],
+ [starred, 'isStarred', true],
+ ];
+ });
+ testAccount.do_refreshFolderView(
+ checkView,
+ // Our expectations happen in a single go here because the refresh covers
+ // the entire date range in question.
+ { count: 9, full: 0, flags: 9, changed: 2, deleted: 2 },
+ expectedRefreshChanges,
+ { top: true, bottom: true, grow: false, newCount: 0 });
+
+ T.group('cleanup');
+ testAccount.do_closeFolderView(checkView);
+});
+
+}); // end define
Please sign in to comment.
Something went wrong with that request. Please try again.