summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/test/unit/test_imapHdrChunking.js
blob: 8fc23e85021135c9c503886e79fb263b741a2cfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* 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/. */

/*
 * Tests imap msg header download chunking
 */

var { MailServices } = ChromeUtils.import(
  "resource:///modules/MailServices.jsm"
);
var { MessageGenerator, MessageScenarioFactory } = ChromeUtils.import(
  "resource://testing-common/mailnews/MessageGenerator.jsm"
);
var { PromiseTestUtils } = ChromeUtils.import(
  "resource://testing-common/mailnews/PromiseTestUtils.jsm"
);
var { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

/**
 * Keep it so that OVERALL_MESSAGES % CHUNKING_SIZE !== 0.
 * With a modulo operator for CHUNKING_SIZE and a prime number for
 *  OVERALL_MESSAGES this should prove that there have been a
 *  chunking process without being depended on the first chunk.
 */
const CHUNKING_SIZE = 3;
const OVERALL_MESSAGES = 137;

// Dummy message window so we can say the inbox is open in a window.
var dummyMsgWindow = Cc["@mozilla.org/messenger/msgwindow;1"].createInstance(
  Ci.nsIMsgWindow
);

function FolderIntPropertyChangedListener() {
  this._promise = new Promise(resolve => {
    this._resolve = resolve;
  });
  this._gotNewMailBiff = false;
}

FolderIntPropertyChangedListener.prototype = {
  onFolderIntPropertyChanged(aItem, aProperty, aOldValue, aNewValue) {
    if (
      aProperty == "BiffState" &&
      aNewValue == Ci.nsIMsgFolder.nsMsgBiffState_NewMail
    ) {
      this._gotNewMailBiff = true;
      this._resolve();
    }
  },
  get promise() {
    return this._promise;
  },
  get gotNewMailBiff() {
    return this._gotNewMailBiff;
  },
};

var gFolderListener = new FolderIntPropertyChangedListener();
/** Used to store a listener between tasks for inspecting chunking behaviour. */
var gListener = new PromiseTestUtils.PromiseUrlListener();

add_setup(async function () {
  Assert.equal(
    OVERALL_MESSAGES % CHUNKING_SIZE !== 0,
    true,
    "const sanity check"
  );
  setupIMAPPump();
  // We need to register the dummyMsgWindow so that we'll think the
  //  Inbox is open in a folder and fetch headers in chunks.
  dummyMsgWindow.openFolder = IMAPPump.inbox;
  MailServices.mailSession.AddMsgWindow(dummyMsgWindow);
  MailServices.mailSession.AddFolderListener(
    gFolderListener,
    Ci.nsIFolderListener.intPropertyChanged
  );

  // Set chunk size to CHUNKING_SIZE, so we'll have to chain several requests to get
  //  OVERALL_MESSAGES headers.
  Services.prefs.setIntPref("mail.imap.hdr_chunk_size", CHUNKING_SIZE);
  // Turn off offline sync to avoid complications in verifying that we can
  //  run a url after the first header chunk.
  Services.prefs.setBoolPref(
    "mail.server.server1.autosync_offline_stores",
    false
  );
});

// Upload messages to the imap fake server Inbox.
add_task(async function uploadImapMessages() {
  // make OVERALL_MESSAGES messages
  let messageGenerator = new MessageGenerator();
  let scenarioFactory = new MessageScenarioFactory(messageGenerator);

  // build up a list of messages
  let messages = [];
  messages = messages.concat(scenarioFactory.directReply(OVERALL_MESSAGES));

  // Add OVERALL_MESSAGES messages with uids 1,2,3...,OVERALL_MESSAGES.
  let imapInbox = IMAPPump.daemon.getMailbox("INBOX");
  // Create the ImapMessages and store them on the mailbox.
  messages.forEach(function (message) {
    let dataUri = Services.io.newURI(
      "data:text/plain;base64," + btoa(message.toMessageString())
    );
    imapInbox.addMessage(
      new ImapMessage(dataUri.spec, imapInbox.uidnext++, [])
    );
  });
  // Do not wait for the listener to finish.
  // We want to observe the message batches in the update process.
  // updateFolderWithListener with null for nsIMsgWindow makes biff notify.
  IMAPPump.inbox.updateFolderWithListener(null, gListener);
});

add_task(async function testMessageFetched() {
  // If we're really chunking, then the message fetch should have started before
  // we finished the updateFolder URL.
  await TestUtils.waitForCondition(() => {
    return gFolderListener.gotNewMailBiff === true;
  });
  Assert.ok(gFolderListener.gotNewMailBiff);

  // We do not check for the first chunk as this is unreliable without explicit
  //  listeners/events.
  // Instead we are checking if there's no rest of the division with
  //  CHUNKING_SIZE while the chunking process is ongoing.
  // It's important that the chunking is intact and as well not failing
  //  randomly in the test infrastructure.
  // See at the CHUNKING_SIZE and OVERALL_MESSAGES declarations.
  //
  // HINT:
  // If this causes future problems because stuff getting faster,
  //  try to increase the overall message count.
  await TestUtils.waitForCondition(() => {
    let messagesDBFolder = IMAPPump.inbox.msgDatabase.dBFolderInfo.numMessages;
    if (messagesDBFolder !== 0) {
      Assert.equal(
        messagesDBFolder % CHUNKING_SIZE,
        0,
        `${messagesDBFolder} messages in folder should be of chunk size ${CHUNKING_SIZE}`
      ); // This is the primary test.
      return true;
    } else if (messagesDBFolder === OVERALL_MESSAGES) {
      throw new Error(
        `Batching failed in sizes of ${CHUNKING_SIZE} found instead ${OVERALL_MESSAGES} immediately`
      );
    }
    return false; // Rerun waitForCondition.
  }, 50);
});

add_task(async function testHdrsDownloaded() {
  await gListener.promise; // Now we wait for the finished update of the Folder.
  // Make sure that we got all OVERALL_MESSAGES headers.
  Assert.equal(
    IMAPPump.inbox.msgDatabase.dBFolderInfo.numMessages,
    OVERALL_MESSAGES
  );
});

// Cleanup
add_task(async function endTest() {
  teardownIMAPPump();
});