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