1/*
2 * Copyright (C) 2010 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
17package com.android.email.activity;
18
19import android.content.Context;
20import android.database.AbstractCursor;
21import android.database.Cursor;
22import android.os.Handler;
23import android.test.ProviderTestCase2;
24import android.test.suitebuilder.annotation.SmallTest;
25
26import com.android.email.MessageListContext;
27import com.android.email.provider.EmailProvider;
28import com.android.emailcommon.provider.EmailContent;
29import com.android.emailcommon.utility.DelayedOperations;
30
31import junit.framework.Assert;
32
33@SmallTest
34public class MessageOrderManagerTest extends ProviderTestCase2<EmailProvider> {
35
36    private MyCallback mCallback;
37
38    @Override protected void setUp() throws Exception {
39        super.setUp();
40        mCallback = new MyCallback();
41    }
42
43    public MessageOrderManagerTest() {
44        super(EmailProvider.class, EmailContent.AUTHORITY);
45    }
46
47    private static void assertCanMove(MessageOrderManager mom, boolean newer, boolean older) {
48        Assert.assertEquals(older, mom.canMoveToOlder());
49        Assert.assertEquals(newer, mom.canMoveToNewer());
50    }
51
52    public void testBasic() {
53        MessageOrderManagerForTest mom = new MessageOrderManagerForTest(getContext(), 1, mCallback);
54        mom.assertStartQueryCalledAndReset();
55
56        // moveTo not called, so it returns -1
57        assertEquals(-1, mom.getCurrentMessageId());
58
59        // Task not finished, so all returns false.
60        assertCanMove(mom, false, false);
61        assertFalse(mom.moveToNewer());
62        assertFalse(mom.moveToOlder());
63
64        // Set current message
65        mom.moveTo(54);
66        assertEquals(54, mom.getCurrentMessageId());
67
68        // Task still not finished, so all returns false.
69        assertCanMove(mom, false, false);
70        assertFalse(mom.moveToNewer());
71        assertFalse(mom.moveToOlder());
72
73        // Both callbacks shouldn't have called.
74        mCallback.assertCallbacksCalled(false, false);
75
76        // Cursor not open yet, so these are both 0.
77        assertEquals(0, mom.getCurrentPosition());
78        assertEquals(0, mom.getTotalMessageCount());
79    }
80
81    /**
82     * Test with actual message list.
83     *
84     * In this test, {@link MessageOrderManager#moveTo} is called AFTER the cursor opens.
85     */
86    public void testWithList() {
87        MessageOrderManagerForTest mom = new MessageOrderManagerForTest(getContext(), 1, mCallback);
88        mom.assertStartQueryCalledAndReset();
89
90        // Callback not called yet.
91        mCallback.assertCallbacksCalled(false, false);
92
93        // Inject mock cursor.  (Imitate async query done.)
94        MyCursor cursor = new MyCursor(11, 22, 33, 44); // Newer to older
95        mom.onCursorOpenDone(cursor);
96
97        assertEquals(0, mom.getCurrentPosition());
98        assertEquals(4, mom.getTotalMessageCount());
99
100        // Current message id not set yet, so callback should have called yet.
101        mCallback.assertCallbacksCalled(false, false);
102
103        // Set current message id -- now onMessagesChanged() should get called.
104        mom.moveTo(22);
105        assertEquals(1, mom.getCurrentPosition());
106        mCallback.assertCallbacksCalled(true, false);
107        assertEquals(22, mom.getCurrentMessageId());
108        assertCanMove(mom, true, true);
109
110        // Move to row 1
111        assertTrue(mom.moveToNewer());
112        assertEquals(0, mom.getCurrentPosition());
113        assertEquals(11, mom.getCurrentMessageId());
114        assertCanMove(mom, false, true);
115        mCallback.assertCallbacksCalled(true, false);
116
117        // Try to move to newer, but no newer messages
118        assertFalse(mom.moveToNewer());
119        assertEquals(0, mom.getCurrentPosition());
120        assertEquals(11, mom.getCurrentMessageId()); // Still row 1
121        mCallback.assertCallbacksCalled(false, false);
122
123        // Move to row 2
124        assertTrue(mom.moveToOlder());
125        assertEquals(1, mom.getCurrentPosition());
126        assertEquals(22, mom.getCurrentMessageId());
127        assertCanMove(mom, true, true);
128        mCallback.assertCallbacksCalled(true, false);
129
130        // Move to row 3
131        assertTrue(mom.moveToOlder());
132        assertEquals(2, mom.getCurrentPosition());
133        assertEquals(33, mom.getCurrentMessageId());
134        assertCanMove(mom, true, true);
135        mCallback.assertCallbacksCalled(true, false);
136
137        // Move to row 4
138        assertTrue(mom.moveToOlder());
139        assertEquals(3, mom.getCurrentPosition());
140        assertEquals(44, mom.getCurrentMessageId());
141        assertCanMove(mom, true, false);
142        mCallback.assertCallbacksCalled(true, false);
143
144        // Try to move older, but no Older messages
145        assertFalse(mom.moveToOlder());
146        assertEquals(3, mom.getCurrentPosition());
147        mCallback.assertCallbacksCalled(false, false);
148
149        // Move to row 3
150        assertTrue(mom.moveToNewer());
151        assertEquals(2, mom.getCurrentPosition());
152        assertEquals(33, mom.getCurrentMessageId());
153        assertCanMove(mom, true, true);
154        mCallback.assertCallbacksCalled(true, false);
155    }
156
157    /**
158     * Test with actual message list.
159     *
160     * In this test, {@link MessageOrderManager#moveTo} is called BEFORE the cursor opens.
161     */
162    public void testWithList2() {
163        MessageOrderManagerForTest mom = new MessageOrderManagerForTest(getContext(), 1, mCallback);
164        mom.assertStartQueryCalledAndReset();
165
166        // Callback not called yet.
167        mCallback.assertCallbacksCalled(false, false);
168
169        mom.moveTo(22);
170        mCallback.assertCallbacksCalled(false, false); // Cursor not open, callback not called yet.
171        assertEquals(22, mom.getCurrentMessageId());
172
173        // cursor not open yet
174        assertEquals(0, mom.getCurrentPosition());
175        assertEquals(0, mom.getTotalMessageCount());
176
177        // Inject mock cursor.  (Imitate async query done.)
178        MyCursor cursor = new MyCursor(11, 22, 33, 44); // Newer to older
179        mom.onCursorOpenDone(cursor);
180
181        // As soon as the cursor opens, callback gets called.
182        mCallback.assertCallbacksCalled(true, false);
183        assertEquals(22, mom.getCurrentMessageId());
184
185        assertEquals(1, mom.getCurrentPosition());
186        assertEquals(4, mom.getTotalMessageCount());
187    }
188
189    public void testContentChanged() {
190        MessageOrderManagerForTest mom = new MessageOrderManagerForTest(getContext(), 1, mCallback);
191
192        // Inject mock cursor.  (Imitate async query done.)
193        MyCursor cursor = new MyCursor(11, 22, 33, 44); // Newer to older
194        mom.onCursorOpenDone(cursor);
195
196        // Move to 22
197        mom.moveTo(22);
198        mCallback.assertCallbacksCalled(true, false);
199        assertEquals(22, mom.getCurrentMessageId());
200        assertCanMove(mom, true, true);
201
202        // Delete 33
203        mom.updateMessageList(11, 22, 44);
204
205        mCallback.assertCallbacksCalled(true, false);
206        assertEquals(22, mom.getCurrentMessageId());
207        assertCanMove(mom, true, true);
208
209        // Delete 44
210        mom.updateMessageList(11, 22);
211
212        mCallback.assertCallbacksCalled(true, false);
213        assertEquals(22, mom.getCurrentMessageId());
214        assertCanMove(mom, true, false); // Can't move to older
215
216        // Append 55
217        mom.updateMessageList(11, 22, 55);
218
219        mCallback.assertCallbacksCalled(true, false);
220        assertEquals(22, mom.getCurrentMessageId());
221        assertCanMove(mom, true, true);
222
223        // Delete 11
224        mom.updateMessageList(22, 55);
225
226        mCallback.assertCallbacksCalled(true, false);
227        assertEquals(22, mom.getCurrentMessageId());
228        assertCanMove(mom, false, true);
229
230        // Delete 55
231        mom.updateMessageList(22);
232
233        mCallback.assertCallbacksCalled(true, false);
234        assertEquals(22, mom.getCurrentMessageId());
235        assertCanMove(mom, false, false); // Can't move either way
236
237        // Delete 22 -- no messages left.
238        mom.updateMessageList();
239        mCallback.assertCallbacksCalled(false, true);
240
241        // Test for the case where list is not empty, but the current message is gone.
242        // First, set up a list with 22 as the current message.
243        mom.updateMessageList(11, 22, 33, 44);
244        mom.moveTo(22);
245        assertEquals(22, mom.getCurrentMessageId());
246        mCallback.assertCallbacksCalled(true, false);
247
248        // Then remove the current message.
249        mom.updateMessageList(11, 33, 44);
250        mCallback.assertCallbacksCalled(false, true);
251    }
252
253    /**
254     * Test using the actual {@link MessageOrderManager} rather than
255     * {@link MessageOrderManagerForTest}.
256     */
257    public void testWithActualClass() {
258        // There are not many things we can test synchronously.
259        // Just open & close just to make sure it won't crash.
260        MessageOrderManager mom = new MessageOrderManager(
261                getContext(), MessageListContext.forMailbox(1, 1), new MyCallback());
262        mom.moveTo(123);
263        mom.close();
264    }
265
266    private static class MyCallback implements MessageOrderManager.Callback {
267        public boolean mCalledOnMessageNotFound;
268        public boolean mCalledOnMessagesChanged;
269
270        @Override public void onMessagesChanged() {
271            mCalledOnMessagesChanged = true;
272        }
273
274        @Override public void onMessageNotFound() {
275            mCalledOnMessageNotFound = true;
276        }
277
278        /**
279         * Asserts that the callbacks have/have not been called, and reset the flags.
280         */
281        public void assertCallbacksCalled(boolean messagesChanged, boolean messageNotFound) {
282            assertEquals(messagesChanged, mCalledOnMessagesChanged);
283            assertEquals(messageNotFound, mCalledOnMessageNotFound);
284
285            mCalledOnMessagesChanged = false;
286            mCalledOnMessageNotFound = false;
287        }
288    }
289
290    /**
291     *  "Non" delayed operation -- runs the runnable immediately
292     */
293    private static final class NonDelayedOperations extends DelayedOperations {
294        public NonDelayedOperations() {
295            super(new Handler());
296        }
297
298        @Override
299        public void post(Runnable r) {
300            r.run();
301        }
302    }
303
304    /**
305     * MessageOrderManager for test.  Overrides {@link #startQuery}
306     */
307    private static class MessageOrderManagerForTest extends MessageOrderManager {
308        private Cursor mLastCursor;
309        public boolean mStartQueryCalled;
310
311        public MessageOrderManagerForTest(Context context, long mailboxId, Callback callback) {
312            super(context, MessageListContext.forMailbox(1, mailboxId),
313                    callback, new NonDelayedOperations());
314        }
315
316        @Override void startQuery() {
317            // To make tests synchronous, we replace this method.
318            mStartQueryCalled = true;
319        }
320
321        @Override /* package */ Handler getHandlerForContentObserver() {
322            return null;
323        }
324
325        @Override void onCursorOpenDone(Cursor cursor) {
326            super.onCursorOpenDone(cursor);
327            mLastCursor =  cursor;
328        }
329
330        /**
331         * Utility method to emulate data set changed.
332         */
333        public void updateMessageList(long... idList) {
334            assertNotNull(mLastCursor); // Make sure a cursor is set.
335
336            // Notify dataset change -- it should end up startQuery() gets called.
337            ((MyCursor) mLastCursor).notifyChanged();
338            assertStartQueryCalledAndReset(); // Start
339
340            // Set a new cursor with a new list.
341            onCursorOpenDone(new MyCursor(idList));
342        }
343
344        public void assertStartQueryCalledAndReset() {
345            assertTrue(mStartQueryCalled);
346            mStartQueryCalled = false;
347        }
348    }
349
350    private static class MyCursor extends AbstractCursor {
351        private final long[] mList;
352
353        public MyCursor(long... idList) {
354            mList = (idList == null) ? new long[0] : idList;
355        }
356
357        public void notifyChanged() {
358            onChange(false);
359        }
360
361        @Override public int getColumnCount() {
362            return 1;
363        }
364
365        @Override public int getCount() {
366            return mList.length;
367        }
368
369        @Override public String[] getColumnNames() {
370            return new String[] {EmailContent.RECORD_ID};
371        }
372
373        @Override public long getLong(int columnIndex) {
374            Assert.assertEquals(EmailContent.ID_PROJECTION_COLUMN, columnIndex);
375            return mList[mPos];
376        }
377
378        @Override public double getDouble(int column) {
379            throw new junit.framework.AssertionFailedError();
380        }
381
382        @Override public float getFloat(int column) {
383            throw new junit.framework.AssertionFailedError();
384        }
385
386        @Override public int getInt(int column) {
387            throw new junit.framework.AssertionFailedError();
388        }
389
390        @Override public short getShort(int column) {
391            throw new junit.framework.AssertionFailedError();
392        }
393
394        @Override public String getString(int column) {
395            throw new junit.framework.AssertionFailedError();
396        }
397
398        @Override public boolean isNull(int column) {
399            throw new junit.framework.AssertionFailedError();
400        }
401    }
402}
403