/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 60000; MARIONETTE_HEAD_JS = 'mmdb_head.js'; const DBNAME = "test_mmdb_full_storage:" + newUUID(); function newSavableMessage(aSender, aReceiver) { return { type: "sms", sender: aSender ? aSender : "+0987654321", receiver: aReceiver? aReceiver : "+1234567890", body: "quick fox jump over the lazy dog", deliveryStatusRequested: false, messageClass: "normal", timestamp: Date.now(), iccId: "1029384756" }; } function setStorageFull(aFull) { SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", aFull ? "full" : "free"); } function isFileNoDeviceSpaceError(aErrorResult) { is(aErrorResult, Cr.NS_ERROR_FILE_NO_DEVICE_SPACE, "Database error code"); } function isCallbackStorageFullError(aErrorCode) { is(aErrorCode, Ci.nsIMobileMessageCallback.STORAGE_FULL_ERROR, "nsIMobileMessageCallback error code"); } function testSaveSendingMessage(aMmdb) { log("testSaveSendingMessage()"); setStorageFull(true); return saveSendingMessage(aMmdb, newSavableMessage()) // Resolved/rejected results are both [, ], // and we need only the error code in both cases. .then((aValue) => aValue[0], (aValue) => aValue[0]) .then(isFileNoDeviceSpaceError) .then(() => setStorageFull(false)); } function testSaveReceivedMessage(aMmdb) { log("testSaveReceivedMessage()"); setStorageFull(true); return saveReceivedMessage(aMmdb, newSavableMessage()) // Resolved/rejected results are both [, ], // and we need only the error code in both cases. .then((aValue) => aValue[0], (aValue) => aValue[0]) .then(isFileNoDeviceSpaceError) .then(() => setStorageFull(false)); } function testGetMessageRecordById(aMmdb) { log("testGetMessageRecordById()"); setStorageFull(false); return saveReceivedMessage(aMmdb, newSavableMessage()) // Resolved result is [, ], .then(function(aValue) { let domMessage = aValue[1]; setStorageFull(true); return getMessageRecordById(aMmdb, domMessage.id) .then(() => setStorageFull(false)); }); } function testMarkMessageRead(aMmdb) { log("testMarkMessageRead()"); setStorageFull(false); return saveReceivedMessage(aMmdb, newSavableMessage()) // Resolved/rejected results are both [, ]. .then(function(aValue) { let domMessage = aValue[1]; setStorageFull(true); return markMessageRead(aMmdb, domMessage.id, true) .then(null, (aValue) => aValue) .then(isCallbackStorageFullError) .then(() => setStorageFull(false)); }); } function testDeleteMessage(aMmdb) { log("testDeleteMessage()"); // Note: // Due to thread-based design in MobileMessageDB and transaction-based design // in indexedDB, There are 2 restrictions to delete a message when storage full // in the following condition: // If the deletion won't remove all the messages in a thead and // 1. the last message of the thread or // 2. an unread message // are to be deleted. // // This will trigger an update of the thread's subject, message body, // unread count, etc but update operation is restricted when storage is full. let testAddress = "1111111111"; let savedMsgIds = []; let promises = []; let numOfTestMessages = 5; setStorageFull(false); // Save several unread messages to the same thread then delete them. for (let i = 0; i < numOfTestMessages; i++) { promises.push(saveReceivedMessage(aMmdb, newSavableMessage(testAddress)) .then((aValue) => { savedMsgIds.push(aValue[1].id); })); } return Promise.all(promises) .then(() => setStorageFull(true)) // Failure is expected when deleting the last one. .then(() => deleteMessage(aMmdb, [savedMsgIds[numOfTestMessages - 1]], 1)) .then(null, (aValue) => aValue) .then(isCallbackStorageFullError) // Failure is expected when deleting an unread message. .then(() => deleteMessage(aMmdb, [savedMsgIds[0]], 1)) .then(null, (aValue) => aValue) .then(isCallbackStorageFullError) // Delete all messages in the thread. .then(() => deleteMessage(aMmdb, savedMsgIds, savedMsgIds.length)) .then(() => setStorageFull(false)); } function testSaveSmsSegment(aMmdb) { log("testSaveSmsSegment()"); let smsSegment = { sender: "+0987654321", encoding: 0x00, // GSM 7bit alphabet iccId: "1029384756", segmentRef: 0, segmentSeq: 1, segmentMaxSeq: 3, body: "quick fox jump over the lazy dog" } setStorageFull(true); return saveSmsSegment(aMmdb, smsSegment) // Resolved/rejected results are both [, ], // and we need only the error code in both cases. .then((aValue) => aValue[0], (aValue) => aValue[0]) .then(isFileNoDeviceSpaceError) .then(() => setStorageFull(false)); } function testCreateMessageCursor(aMmdb) { log("testCreateMessageCursor()"); setStorageFull(true); return createMessageCursor(aMmdb) .then(() => setStorageFull(false)); } function testCreateThreadCursor(aMmdb) { log("testCreateThreadCursor()"); setStorageFull(true); return createThreadCursor(aMmdb) .then(() => setStorageFull(false)); } startTestBase(function testCaseMain() { let mmdb = newMobileMessageDB(); return initMobileMessageDB(mmdb, DBNAME, 0) .then(() => testSaveSendingMessage(mmdb)) .then(() => testSaveReceivedMessage(mmdb)) .then(() => testGetMessageRecordById(mmdb)) .then(() => testMarkMessageRead(mmdb)) .then(() => testDeleteMessage(mmdb)) .then(() => testSaveSmsSegment(mmdb)) .then(() => testCreateMessageCursor(mmdb)) .then(() => testCreateThreadCursor(mmdb)) .then(() => closeMobileMessageDB(mmdb)); });