/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.messaging.datamodel.action; import android.content.ContentProvider; import android.content.pm.ProviderInfo; import android.database.Cursor; import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; import com.android.messaging.BugleTestCase; import com.android.messaging.FakeContext; import com.android.messaging.FakeFactory; import com.android.messaging.datamodel.BugleDatabaseOperations; import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DatabaseHelper; import com.android.messaging.datamodel.DatabaseHelper.ConversationColumns; import com.android.messaging.datamodel.DatabaseWrapper; import com.android.messaging.datamodel.FakeDataModel; import com.android.messaging.datamodel.MediaScratchFileProvider; import com.android.messaging.datamodel.MessagingContentProvider; import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService; import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService.StubActionServiceCallLog; import com.android.messaging.datamodel.action.ReadDraftDataAction.ReadDraftDataActionListener; import com.android.messaging.datamodel.data.MessageData; import com.android.messaging.datamodel.data.MessagePartData; import com.android.messaging.datamodel.data.ParticipantData; import com.android.messaging.util.ContentType; import org.mockito.Mock; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; @SmallTest public class ReadWriteDraftMessageActionTest extends BugleTestCase { @Mock ReadDraftDataActionListener mockListener; // TODO: Add test cases // 1. Make sure drafts can include attachments and multiple parts // 2. Make sure attachments get cleaned up appropriately // 3. Make sure messageId and partIds not reused (currently each draft is a new message). public void testWriteDraft() { final String draftMessage = "draftMessage"; final long threadId = 1234567; final boolean senderBlocked = false; final String participantNumber = "5551234567"; final DatabaseWrapper db = DataModel.get().getDatabase(); final String conversationId = getOrCreateConversation(db, participantNumber, threadId, senderBlocked); final String selfId = getOrCreateSelfId(db); // Should clear/stub DB final ArrayList calls = mService.getCalls(); final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId, draftMessage); WriteDraftMessageAction.writeDraftMessage(conversationId, message); assertEquals("Failed to start service once for action", calls.size(), 1); assertTrue("Action not SaveDraftMessageAction", calls.get(0).action instanceof WriteDraftMessageAction); final Action save = calls.get(0).action; final Object result = save.executeAction(); assertTrue("Expect row number string as result", result instanceof String); final String messageId = (String) result; // Should check DB final MessageData actual = BugleDatabaseOperations.readMessage(db, messageId); assertNotNull("Database missing draft", actual); assertEquals("Draft text changed", draftMessage, actual.getMessageText()); } private static String getOrCreateSelfId(final DatabaseWrapper db) { db.beginTransaction(); final String selfId = BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, ParticipantData.getSelfParticipant(ParticipantData.DEFAULT_SELF_SUB_ID)); db.setTransactionSuccessful(); db.endTransaction(); return selfId; } private static String getOrCreateConversation(final DatabaseWrapper db, final String participantNumber, final long threadId, final boolean senderBlocked) { final ArrayList participants = new ArrayList(); participants.add(ParticipantData.getFromRawPhoneBySystemLocale(participantNumber)); final String conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId, senderBlocked, participants, false, false, null); assertNotNull("No conversation", conversationId); return conversationId; } public void testReadDraft() { final Object data = "data"; final String draftMessage = "draftMessage"; final long threadId = 1234567; final boolean senderBlocked = false; final String participantNumber = "5552345678"; final DatabaseWrapper db = DataModel.get().getDatabase(); final String conversationId = getOrCreateConversation(db, participantNumber, threadId, senderBlocked); final String selfId = getOrCreateSelfId(db); // Should clear/stub DB final ArrayList calls = mService.getCalls(); final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId, draftMessage); BugleDatabaseOperations.updateDraftMessageData(db, conversationId, message, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT); final ActionMonitor monitor = ReadDraftDataAction.readDraftData(conversationId, null, data, mockListener); assertEquals("Unexpected number of calls to service", 1, calls.size()); assertTrue("Action not of type ReadDraftMessageAction", calls.get(0).action instanceof ReadDraftDataAction); final Action read = calls.get(0).action; final Object result = read.executeAction(); assertTrue(result instanceof ReadDraftDataAction.DraftData); final ReadDraftDataAction.DraftData draft = (ReadDraftDataAction.DraftData) result; assertEquals("Draft message text differs", draftMessage, draft.message.getMessageText()); assertEquals("Draft self differs", selfId, draft.message.getSelfId()); assertEquals("Draft conversation differs", conversationId, draft.conversation.getConversationId()); } public void testReadDraftForNewConversation() { final Object data = "data"; long threadId = 1234567; final boolean senderBlocked = false; long phoneNumber = 5557654567L; final String notConversationId = "ThisIsNotValidConversationId"; final DatabaseWrapper db = DataModel.get().getDatabase(); final String selfId = getOrCreateSelfId(db); // Unless set a new conversation should have a null draft message final MessageData blank = BugleDatabaseOperations.readDraftMessageData(db, notConversationId, selfId); assertNull(blank); String conversationId = null; do { conversationId = BugleDatabaseOperations.getExistingConversation(db, threadId, senderBlocked); threadId++; phoneNumber++; } while(!TextUtils.isEmpty(conversationId)); final ArrayList participants = new ArrayList(); participants.add(ParticipantData.getFromRawPhoneBySystemLocale(Long.toString(phoneNumber))); conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId, senderBlocked, participants, false, false, null); assertNotNull("No conversation", conversationId); final MessageData actual = BugleDatabaseOperations.readDraftMessageData(db, conversationId, selfId); assertNull(actual); // Should clear/stub DB final ArrayList calls = mService.getCalls(); final ActionMonitor monitor = ReadDraftDataAction.readDraftData(conversationId, null, data, mockListener); assertEquals("Unexpected number of calls to service", 1, calls.size()); assertTrue("Action not of type ReadDraftMessageAction", calls.get(0).action instanceof ReadDraftDataAction); final Action read = calls.get(0).action; final Object result = read.executeAction(); assertTrue(result instanceof ReadDraftDataAction.DraftData); final ReadDraftDataAction.DraftData draft = (ReadDraftDataAction.DraftData) result; assertEquals("Draft message text differs", "", draft.message.getMessageText()); assertEquals("Draft self differs", selfId, draft.message.getSelfId()); assertEquals("Draft conversation differs", conversationId, draft.conversation.getConversationId()); } public void testWriteAndReadDraft() { final Object data = "data"; final String draftMessage = "draftMessage"; final DatabaseWrapper db = DataModel.get().getDatabase(); final Cursor conversations = db.query(DatabaseHelper.CONVERSATIONS_TABLE, new String[] { ConversationColumns._ID, ConversationColumns.CURRENT_SELF_ID }, null, null, null /* groupBy */, null /* having */, null /* orderBy */); if (conversations.moveToFirst()) { final String conversationId = conversations.getString(0); final String selfId = getOrCreateSelfId(db); // Should clear/stub DB final ArrayList calls = mService.getCalls(); final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId, draftMessage); WriteDraftMessageAction.writeDraftMessage(conversationId, message); assertEquals("Failed to start service once for action", calls.size(), 1); assertTrue("Action not SaveDraftMessageAction", calls.get(0).action instanceof WriteDraftMessageAction); final Action save = calls.get(0).action; Object result = save.executeAction(); assertTrue("Expect row number string as result", result instanceof String); // Should check DB final ActionMonitor monitor = ReadDraftDataAction.readDraftData(conversationId, null, data, mockListener); assertEquals("Expect two calls queued", 2, calls.size()); assertTrue("Expect action", calls.get(1).action instanceof ReadDraftDataAction); final Action read = calls.get(1).action; result = read.executeAction(); assertTrue(result instanceof ReadDraftDataAction.DraftData); final ReadDraftDataAction.DraftData draft = (ReadDraftDataAction.DraftData) result; assertEquals("Draft message text differs", draftMessage, draft.message.getMessageText()); // The conversation's self id is used as the draft's self id. assertEquals("Draft self differs", conversations.getString(1), draft.message.getSelfId()); assertEquals("Draft conversation differs", conversationId, draft.conversation.getConversationId()); } else { fail("No conversations in database"); } } public void testUpdateDraft() { final String initialMessage = "initialMessage"; final String draftMessage = "draftMessage"; final long threadId = 1234567; final boolean senderBlocked = false; final String participantNumber = "5553456789"; final DatabaseWrapper db = DataModel.get().getDatabase(); final String conversationId = getOrCreateConversation(db, participantNumber, threadId, senderBlocked); final String selfId = getOrCreateSelfId(db); final ArrayList calls = mService.getCalls(); // Insert initial message MessageData initial = MessageData.createDraftSmsMessage(conversationId, selfId, initialMessage); BugleDatabaseOperations.updateDraftMessageData(db, conversationId, initial, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT); initial = BugleDatabaseOperations.readDraftMessageData(db, conversationId, selfId); assertEquals("Initial text mismatch", initialMessage, initial.getMessageText()); // Now update the draft final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId, draftMessage); WriteDraftMessageAction.writeDraftMessage(conversationId, message); assertEquals("Failed to start service once for action", calls.size(), 1); assertTrue("Action not SaveDraftMessageAction", calls.get(0).action instanceof WriteDraftMessageAction); final Action save = calls.get(0).action; final Object result = save.executeAction(); assertTrue("Expect row number string as result", result instanceof String); // Check DB final MessageData actual = BugleDatabaseOperations.readDraftMessageData(db, conversationId, selfId); assertNotNull("Database missing draft", actual); assertEquals("Draft text mismatch", draftMessage, actual.getMessageText()); assertNull("Draft messageId should be null", actual.getMessageId()); } public void testBugleDatabaseDraftOperations() { final String initialMessage = "initialMessage"; final String draftMessage = "draftMessage"; final long threadId = 1234599; final boolean senderBlocked = false; final String participantNumber = "5553456798"; final String subject = "subject here"; final DatabaseWrapper db = DataModel.get().getDatabase(); final String conversationId = getOrCreateConversation(db, participantNumber, threadId, senderBlocked); final String selfId = getOrCreateSelfId(db); final String text = "This is some text"; final Uri mOutputUri = MediaScratchFileProvider.buildMediaScratchSpaceUri("txt"); OutputStream outputStream = null; try { outputStream = mContext.getContentResolver().openOutputStream(mOutputUri); outputStream.write(text.getBytes()); } catch (final FileNotFoundException e) { fail("Cannot open output file"); } catch (final IOException e) { fail("Cannot write output file"); } final MessageData initial = MessageData.createDraftMmsMessage(conversationId, selfId, initialMessage, subject); initial.addPart(MessagePartData.createMediaMessagePart(ContentType.MULTIPART_MIXED, mOutputUri, 0, 0)); final String initialMessageId = BugleDatabaseOperations.updateDraftMessageData(db, conversationId, initial, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT); assertNotNull(initialMessageId); final MessageData initialDraft = BugleDatabaseOperations.readMessage(db, initialMessageId); assertNotNull(initialDraft); int cnt = 0; for(final MessagePartData part : initialDraft.getParts()) { if (part.isAttachment()) { assertEquals(part.getContentUri(), mOutputUri); } else { assertEquals(part.getText(), initialMessage); } cnt++; } assertEquals("Wrong number of parts", 2, cnt); InputStream inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(mOutputUri); final byte[] buffer = new byte[256]; final int read = inputStream.read(buffer); assertEquals(read, text.getBytes().length); } catch (final FileNotFoundException e) { fail("Cannot open input file"); } catch (final IOException e) { fail("Cannot read input file"); } final String moreText = "This is some more text"; final Uri mAnotherUri = MediaScratchFileProvider.buildMediaScratchSpaceUri("txt"); outputStream = null; try { outputStream = mContext.getContentResolver().openOutputStream(mAnotherUri); outputStream.write(moreText.getBytes()); } catch (final FileNotFoundException e) { fail("Cannot open output file"); } catch (final IOException e) { fail("Cannot write output file"); } final MessageData another = MessageData.createDraftMmsMessage(conversationId, selfId, draftMessage, subject); another.addPart(MessagePartData.createMediaMessagePart(ContentType.MMS_MULTIPART_MIXED, mAnotherUri, 0, 0)); final String anotherMessageId = BugleDatabaseOperations.updateDraftMessageData(db, conversationId, another, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT); assertNotNull(anotherMessageId); final MessageData anotherDraft = BugleDatabaseOperations.readMessage(db, anotherMessageId); assertNotNull(anotherDraft); cnt = 0; for(final MessagePartData part : anotherDraft.getParts()) { if (part.isAttachment()) { assertEquals(part.getContentUri(), mAnotherUri); } else { assertEquals(part.getText(), draftMessage); } cnt++; } assertEquals("Wrong number of parts", 2, cnt); inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(mOutputUri); assertNull("Original draft content should have been deleted", inputStream); } catch (final FileNotFoundException e) { } inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(mAnotherUri); final byte[] buffer = new byte[256]; final int read = inputStream.read(buffer); assertEquals(read, moreText.getBytes().length); } catch (final FileNotFoundException e) { fail("Cannot open input file"); } catch (final IOException e) { fail("Cannot read input file"); } final MessageData last = null; final String lastPartId = BugleDatabaseOperations.updateDraftMessageData(db, conversationId, last, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT); assertNull(lastPartId); inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(mAnotherUri); assertNull("Original draft content should have been deleted", inputStream); } catch (final FileNotFoundException e) { } } private StubActionService mService; @Override public void setUp() throws Exception { super.setUp(); final FakeContext context = new FakeContext(getTestContext()); final ContentProvider bugleProvider = new MessagingContentProvider(); final ProviderInfo bugleProviderInfo = new ProviderInfo(); bugleProviderInfo.authority = MessagingContentProvider.AUTHORITY; bugleProvider.attachInfo(mContext, bugleProviderInfo); context.addContentProvider(MessagingContentProvider.AUTHORITY, bugleProvider); final ContentProvider mediaProvider = new MediaScratchFileProvider(); final ProviderInfo mediaProviderInfo = new ProviderInfo(); mediaProviderInfo.authority = MediaScratchFileProvider.AUTHORITY; mediaProvider.attachInfo(mContext, mediaProviderInfo); context.addContentProvider(MediaScratchFileProvider.AUTHORITY, mediaProvider); mService = new StubActionService(); final FakeDataModel fakeDataModel = new FakeDataModel(context) .withActionService(mService); FakeFactory.registerWithFakeContext(getTestContext(), context) .withDataModel(fakeDataModel); } }