• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.messaging.datamodel.action;
18 
19 import android.content.ContentProvider;
20 import android.content.pm.ProviderInfo;
21 import android.database.Cursor;
22 import android.net.Uri;
23 import android.test.suitebuilder.annotation.SmallTest;
24 import android.text.TextUtils;
25 
26 import com.android.messaging.BugleTestCase;
27 import com.android.messaging.FakeContext;
28 import com.android.messaging.FakeFactory;
29 import com.android.messaging.datamodel.BugleDatabaseOperations;
30 import com.android.messaging.datamodel.DataModel;
31 import com.android.messaging.datamodel.DatabaseHelper;
32 import com.android.messaging.datamodel.DatabaseHelper.ConversationColumns;
33 import com.android.messaging.datamodel.DatabaseWrapper;
34 import com.android.messaging.datamodel.FakeDataModel;
35 import com.android.messaging.datamodel.MediaScratchFileProvider;
36 import com.android.messaging.datamodel.MessagingContentProvider;
37 import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService;
38 import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService.StubActionServiceCallLog;
39 import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
40 import com.android.messaging.datamodel.action.ReadDraftDataAction.ReadDraftDataActionListener;
41 import com.android.messaging.datamodel.data.MessageData;
42 import com.android.messaging.datamodel.data.MessagePartData;
43 import com.android.messaging.datamodel.data.ParticipantData;
44 import com.android.messaging.util.ContentType;
45 
46 import org.mockito.Mock;
47 
48 import java.io.FileNotFoundException;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.OutputStream;
52 import java.util.ArrayList;
53 
54 @SmallTest
55 public class ReadWriteDraftMessageActionTest extends BugleTestCase {
56 
57     @Mock ReadDraftDataActionListener mockListener;
58 
59     // TODO: Add test cases
60     //  1. Make sure drafts can include attachments and multiple parts
61     //  2. Make sure attachments get cleaned up appropriately
62     //  3. Make sure messageId and partIds not reused (currently each draft is a new message).
testWriteDraft()63     public void testWriteDraft() {
64         final String draftMessage = "draftMessage";
65         final long threadId = 1234567;
66         final boolean senderBlocked = false;
67         final String participantNumber = "5551234567";
68 
69         final DatabaseWrapper db = DataModel.get().getDatabase();
70 
71         final String conversationId = getOrCreateConversation(db, participantNumber, threadId,
72                 senderBlocked);
73         final String selfId = getOrCreateSelfId(db);
74 
75         // Should clear/stub DB
76         final ArrayList<StubActionServiceCallLog> calls = mService.getCalls();
77 
78         final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId,
79                 draftMessage);
80 
81         WriteDraftMessageAction.writeDraftMessage(conversationId, message);
82 
83         assertEquals("Failed to start service once for action", calls.size(), 1);
84         assertTrue("Action not SaveDraftMessageAction",
85                 calls.get(0).action instanceof WriteDraftMessageAction);
86 
87         final Action save = calls.get(0).action;
88 
89         final Object result = save.executeAction();
90 
91         assertTrue("Expect row number string as result", result instanceof String);
92         final String messageId = (String) result;
93 
94         // Should check DB
95         final MessageData actual = BugleDatabaseOperations.readMessage(db, messageId);
96         assertNotNull("Database missing draft", actual);
97         assertEquals("Draft text changed", draftMessage, actual.getMessageText());
98     }
99 
getOrCreateSelfId(final DatabaseWrapper db)100     private static String getOrCreateSelfId(final DatabaseWrapper db) {
101         db.beginTransaction();
102         final String selfId = BugleDatabaseOperations.getOrCreateParticipantInTransaction(db,
103                 ParticipantData.getSelfParticipant(ParticipantData.DEFAULT_SELF_SUB_ID));
104         db.setTransactionSuccessful();
105         db.endTransaction();
106         return selfId;
107     }
108 
getOrCreateConversation(final DatabaseWrapper db, final String participantNumber, final long threadId, final boolean senderBlocked)109     private static String getOrCreateConversation(final DatabaseWrapper db,
110             final String participantNumber, final long threadId, final boolean senderBlocked) {
111         final ArrayList<ParticipantData> participants =
112                 new ArrayList<ParticipantData>();
113         participants.add(ParticipantData.getFromRawPhoneBySystemLocale(participantNumber));
114 
115         final String conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId,
116                 senderBlocked, participants, false, false, null);
117         assertNotNull("No conversation", conversationId);
118         return conversationId;
119     }
120 
testReadDraft()121     public void testReadDraft() {
122         final Object data = "data";
123         final String draftMessage = "draftMessage";
124         final long threadId = 1234567;
125         final boolean senderBlocked = false;
126         final String participantNumber = "5552345678";
127 
128         final DatabaseWrapper db = DataModel.get().getDatabase();
129 
130         final String conversationId = getOrCreateConversation(db, participantNumber, threadId,
131                 senderBlocked);
132         final String selfId = getOrCreateSelfId(db);
133 
134         // Should clear/stub DB
135         final ArrayList<StubActionServiceCallLog> calls = mService.getCalls();
136 
137         final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId,
138                 draftMessage);
139 
140         BugleDatabaseOperations.updateDraftMessageData(db, conversationId, message,
141                 BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT);
142 
143         final ActionMonitor monitor =
144                 ReadDraftDataAction.readDraftData(conversationId, null, data, mockListener);
145 
146         assertEquals("Unexpected number of calls to service", 1, calls.size());
147         assertTrue("Action not of type ReadDraftMessageAction",
148                 calls.get(0).action instanceof ReadDraftDataAction);
149 
150         final Action read = calls.get(0).action;
151 
152         final Object result = read.executeAction();
153 
154         assertTrue(result instanceof ReadDraftDataAction.DraftData);
155         final ReadDraftDataAction.DraftData draft = (ReadDraftDataAction.DraftData) result;
156 
157         assertEquals("Draft message text differs", draftMessage, draft.message.getMessageText());
158         assertEquals("Draft self differs", selfId, draft.message.getSelfId());
159         assertEquals("Draft conversation differs", conversationId,
160                 draft.conversation.getConversationId());
161     }
162 
testReadDraftForNewConversation()163     public void testReadDraftForNewConversation() {
164         final Object data = "data";
165         long threadId = 1234567;
166         final boolean senderBlocked = false;
167         long phoneNumber = 5557654567L;
168         final String notConversationId = "ThisIsNotValidConversationId";
169 
170         final DatabaseWrapper db = DataModel.get().getDatabase();
171         final String selfId = getOrCreateSelfId(db);
172 
173         // Unless set a new conversation should have a null draft message
174         final MessageData blank = BugleDatabaseOperations.readDraftMessageData(db,
175                 notConversationId, selfId);
176         assertNull(blank);
177 
178         String conversationId = null;
179         do {
180             conversationId = BugleDatabaseOperations.getExistingConversation(db,
181                     threadId, senderBlocked);
182             threadId++;
183             phoneNumber++;
184         }
185         while(!TextUtils.isEmpty(conversationId));
186 
187         final ArrayList<ParticipantData> participants =
188                 new ArrayList<ParticipantData>();
189         participants.add(ParticipantData.getFromRawPhoneBySystemLocale(Long.toString(phoneNumber)));
190 
191         conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId,
192                 senderBlocked, participants, false, false, null);
193         assertNotNull("No conversation", conversationId);
194 
195         final MessageData actual = BugleDatabaseOperations.readDraftMessageData(db, conversationId,
196                 selfId);
197         assertNull(actual);
198 
199         // Should clear/stub DB
200         final ArrayList<StubActionServiceCallLog> calls = mService.getCalls();
201 
202         final ActionMonitor monitor =
203                 ReadDraftDataAction.readDraftData(conversationId, null, data, mockListener);
204 
205         assertEquals("Unexpected number of calls to service", 1, calls.size());
206         assertTrue("Action not of type ReadDraftMessageAction",
207                 calls.get(0).action instanceof ReadDraftDataAction);
208 
209         final Action read = calls.get(0).action;
210 
211         final Object result = read.executeAction();
212 
213         assertTrue(result instanceof ReadDraftDataAction.DraftData);
214         final ReadDraftDataAction.DraftData draft = (ReadDraftDataAction.DraftData) result;
215 
216         assertEquals("Draft message text differs", "", draft.message.getMessageText());
217         assertEquals("Draft self differs", selfId, draft.message.getSelfId());
218         assertEquals("Draft conversation differs", conversationId,
219                 draft.conversation.getConversationId());
220     }
221 
testWriteAndReadDraft()222     public void testWriteAndReadDraft() {
223         final Object data = "data";
224         final String draftMessage = "draftMessage";
225 
226         final DatabaseWrapper db = DataModel.get().getDatabase();
227         final Cursor conversations = db.query(DatabaseHelper.CONVERSATIONS_TABLE,
228                 new String[] { ConversationColumns._ID, ConversationColumns.CURRENT_SELF_ID }, null,
229                 null, null /* groupBy */, null /* having */, null /* orderBy */);
230 
231         if (conversations.moveToFirst()) {
232             final String conversationId = conversations.getString(0);
233             final String selfId = getOrCreateSelfId(db);
234 
235             // Should clear/stub DB
236             final ArrayList<StubActionServiceCallLog> calls = mService.getCalls();
237 
238             final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId,
239                     draftMessage);
240 
241             WriteDraftMessageAction.writeDraftMessage(conversationId, message);
242 
243             assertEquals("Failed to start service once for action", calls.size(), 1);
244             assertTrue("Action not SaveDraftMessageAction",
245                     calls.get(0).action instanceof WriteDraftMessageAction);
246 
247             final Action save = calls.get(0).action;
248 
249             Object result = save.executeAction();
250 
251             assertTrue("Expect row number string as result", result instanceof String);
252 
253             // Should check DB
254 
255             final ActionMonitor monitor =
256                     ReadDraftDataAction.readDraftData(conversationId, null, data,
257                             mockListener);
258 
259             assertEquals("Expect two calls queued", 2, calls.size());
260             assertTrue("Expect action", calls.get(1).action instanceof ReadDraftDataAction);
261 
262             final Action read = calls.get(1).action;
263 
264             result = read.executeAction();
265 
266             assertTrue(result instanceof ReadDraftDataAction.DraftData);
267             final ReadDraftDataAction.DraftData draft = (ReadDraftDataAction.DraftData) result;
268 
269             assertEquals("Draft message text differs", draftMessage, draft.message.getMessageText());
270             // The conversation's self id is used as the draft's self id.
271             assertEquals("Draft self differs", conversations.getString(1),
272                     draft.message.getSelfId());
273             assertEquals("Draft conversation differs", conversationId,
274                     draft.conversation.getConversationId());
275         } else {
276             fail("No conversations in database");
277         }
278     }
279 
testUpdateDraft()280     public void testUpdateDraft() {
281         final String initialMessage = "initialMessage";
282         final String draftMessage = "draftMessage";
283         final long threadId = 1234567;
284         final boolean senderBlocked = false;
285         final String participantNumber = "5553456789";
286 
287         final DatabaseWrapper db = DataModel.get().getDatabase();
288 
289         final String conversationId = getOrCreateConversation(db, participantNumber, threadId,
290                 senderBlocked);
291         final String selfId = getOrCreateSelfId(db);
292 
293         final ArrayList<StubActionServiceCallLog> calls = mService.getCalls();
294 
295         // Insert initial message
296         MessageData initial = MessageData.createDraftSmsMessage(conversationId, selfId,
297                 initialMessage);
298 
299         BugleDatabaseOperations.updateDraftMessageData(db, conversationId, initial,
300                 BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT);
301 
302         initial = BugleDatabaseOperations.readDraftMessageData(db,
303                 conversationId, selfId);
304         assertEquals("Initial text mismatch", initialMessage, initial.getMessageText());
305 
306         // Now update the draft
307         final MessageData message = MessageData.createDraftSmsMessage(conversationId, selfId,
308                 draftMessage);
309         WriteDraftMessageAction.writeDraftMessage(conversationId, message);
310 
311         assertEquals("Failed to start service once for action", calls.size(), 1);
312         assertTrue("Action not SaveDraftMessageAction",
313                 calls.get(0).action instanceof WriteDraftMessageAction);
314 
315         final Action save = calls.get(0).action;
316 
317         final Object result = save.executeAction();
318 
319         assertTrue("Expect row number string as result", result instanceof String);
320 
321         // Check DB
322         final MessageData actual =  BugleDatabaseOperations.readDraftMessageData(db,
323                 conversationId, selfId);
324         assertNotNull("Database missing draft", actual);
325         assertEquals("Draft text mismatch", draftMessage, actual.getMessageText());
326         assertNull("Draft messageId should be null", actual.getMessageId());
327     }
328 
testBugleDatabaseDraftOperations()329     public void testBugleDatabaseDraftOperations() {
330         final String initialMessage = "initialMessage";
331         final String draftMessage = "draftMessage";
332         final long threadId = 1234599;
333         final boolean senderBlocked = false;
334         final String participantNumber = "5553456798";
335         final String subject = "subject here";
336 
337         final DatabaseWrapper db = DataModel.get().getDatabase();
338 
339         final String conversationId = getOrCreateConversation(db, participantNumber, threadId,
340                 senderBlocked);
341         final String selfId = getOrCreateSelfId(db);
342 
343         final String text = "This is some text";
344         final Uri mOutputUri = MediaScratchFileProvider.buildMediaScratchSpaceUri("txt");
345         OutputStream outputStream = null;
346         try {
347             outputStream = mContext.getContentResolver().openOutputStream(mOutputUri);
348             outputStream.write(text.getBytes());
349         } catch (final FileNotFoundException e) {
350             fail("Cannot open output file");
351         } catch (final IOException e) {
352             fail("Cannot write output file");
353         }
354 
355         final MessageData initial =
356                 MessageData.createDraftMmsMessage(conversationId, selfId, initialMessage, subject);
357         initial.addPart(MessagePartData.createMediaMessagePart(ContentType.MULTIPART_MIXED,
358                 mOutputUri, 0, 0));
359 
360         final String initialMessageId = BugleDatabaseOperations.updateDraftMessageData(db,
361                 conversationId, initial, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT);
362         assertNotNull(initialMessageId);
363 
364         final MessageData initialDraft = BugleDatabaseOperations.readMessage(db, initialMessageId);
365         assertNotNull(initialDraft);
366         int cnt = 0;
367         for(final MessagePartData part : initialDraft.getParts()) {
368             if (part.isAttachment()) {
369                 assertEquals(part.getContentUri(), mOutputUri);
370             } else {
371                 assertEquals(part.getText(), initialMessage);
372             }
373             cnt++;
374         }
375         assertEquals("Wrong number of parts", 2, cnt);
376 
377         InputStream inputStream = null;
378         try {
379             inputStream = mContext.getContentResolver().openInputStream(mOutputUri);
380             final byte[] buffer = new byte[256];
381             final int read = inputStream.read(buffer);
382             assertEquals(read, text.getBytes().length);
383         } catch (final FileNotFoundException e) {
384             fail("Cannot open input file");
385         } catch (final IOException e) {
386             fail("Cannot read input file");
387         }
388 
389         final String moreText = "This is some more text";
390         final Uri mAnotherUri = MediaScratchFileProvider.buildMediaScratchSpaceUri("txt");
391         outputStream = null;
392         try {
393             outputStream = mContext.getContentResolver().openOutputStream(mAnotherUri);
394             outputStream.write(moreText.getBytes());
395         } catch (final FileNotFoundException e) {
396             fail("Cannot open output file");
397         } catch (final IOException e) {
398             fail("Cannot write output file");
399         }
400 
401         final MessageData another =
402                 MessageData.createDraftMmsMessage(conversationId, selfId, draftMessage, subject);
403         another.addPart(MessagePartData.createMediaMessagePart(ContentType.MMS_MULTIPART_MIXED,
404                 mAnotherUri, 0, 0));
405 
406         final String anotherMessageId = BugleDatabaseOperations.updateDraftMessageData(db,
407                 conversationId, another, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT);
408         assertNotNull(anotherMessageId);
409 
410         final MessageData anotherDraft = BugleDatabaseOperations.readMessage(db, anotherMessageId);
411         assertNotNull(anotherDraft);
412         cnt = 0;
413         for(final MessagePartData part : anotherDraft.getParts()) {
414             if (part.isAttachment()) {
415                 assertEquals(part.getContentUri(), mAnotherUri);
416             } else {
417                 assertEquals(part.getText(), draftMessage);
418             }
419             cnt++;
420         }
421         assertEquals("Wrong number of parts", 2, cnt);
422 
423         inputStream = null;
424         try {
425             inputStream = mContext.getContentResolver().openInputStream(mOutputUri);
426             assertNull("Original draft content should have been deleted", inputStream);
427         } catch (final FileNotFoundException e) {
428         }
429         inputStream = null;
430         try {
431             inputStream = mContext.getContentResolver().openInputStream(mAnotherUri);
432             final byte[] buffer = new byte[256];
433             final int read = inputStream.read(buffer);
434             assertEquals(read, moreText.getBytes().length);
435         } catch (final FileNotFoundException e) {
436             fail("Cannot open input file");
437         } catch (final IOException e) {
438             fail("Cannot read input file");
439         }
440 
441         final MessageData last = null;
442         final String lastPartId = BugleDatabaseOperations.updateDraftMessageData(db,
443                 conversationId, last, BugleDatabaseOperations.UPDATE_MODE_ADD_DRAFT);
444         assertNull(lastPartId);
445 
446         inputStream = null;
447         try {
448             inputStream = mContext.getContentResolver().openInputStream(mAnotherUri);
449             assertNull("Original draft content should have been deleted", inputStream);
450         } catch (final FileNotFoundException e) {
451         }
452 
453     }
454 
455     private StubActionService mService;
456 
457     @Override
setUp()458     public void setUp() throws Exception {
459         super.setUp();
460 
461         final FakeContext context = new FakeContext(getTestContext());
462 
463         final ContentProvider bugleProvider = new MessagingContentProvider();
464         final ProviderInfo bugleProviderInfo = new ProviderInfo();
465         bugleProviderInfo.authority = MessagingContentProvider.AUTHORITY;
466         bugleProvider.attachInfo(mContext, bugleProviderInfo);
467         context.addContentProvider(MessagingContentProvider.AUTHORITY, bugleProvider);
468         final ContentProvider mediaProvider = new MediaScratchFileProvider();
469         final ProviderInfo mediaProviderInfo = new ProviderInfo();
470         mediaProviderInfo.authority = MediaScratchFileProvider.AUTHORITY;
471         mediaProvider.attachInfo(mContext, mediaProviderInfo);
472         context.addContentProvider(MediaScratchFileProvider.AUTHORITY, mediaProvider);
473 
474         mService = new StubActionService();
475         final FakeDataModel fakeDataModel = new FakeDataModel(context)
476                 .withActionService(mService)
477                 .withConnectivityUtil(new StubConnectivityUtil(context));
478         FakeFactory.registerWithFakeContext(getTestContext(), context)
479                 .withDataModel(fakeDataModel);
480 
481     }
482 }
483