1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.content.cts; 18 19 20 import android.content.ContentQueryMap; 21 import android.content.ContentResolver; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.database.Cursor; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.os.Looper; 28 import android.platform.test.annotations.AppModeSdkSandbox; 29 import android.test.InstrumentationTestCase; 30 import android.test.UiThreadTest; 31 32 import java.util.Map; 33 import java.util.Observable; 34 import java.util.Observer; 35 36 /** 37 * Test {@link ContentQueryMap}. 38 */ 39 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") 40 public class ContentQueryMapTest extends InstrumentationTestCase { 41 private static final int TEST_TIME_OUT = 5000; 42 43 private static final String NAME0 = "name0"; 44 private static final String VALUE0 = "value0"; 45 private static final String NAME1 = "name1"; 46 private static final String VALUE1 = "value1"; 47 private static final String NAME2 = "name2"; 48 private static final String VALUE2 = "value2"; 49 private static final String NAME3 = "name3"; 50 private static final String VALUE3 = "value3"; 51 52 private static final String[] PROJECTIONS = new String[] { 53 DummyProvider.NAME, DummyProvider.VALUE}; 54 55 private static final int ORIGINAL_ROW_COUNT = 2; 56 private ContentResolver mResolver; 57 private Cursor mCursor; 58 private ContentQueryMap mContentQueryMap; 59 private Context mContext; 60 61 @Override setUp()62 protected void setUp() throws Exception { 63 super.setUp(); 64 mContext = getInstrumentation().getTargetContext(); 65 mResolver = mContext.getContentResolver(); 66 67 ContentValues values0 = new ContentValues(); 68 values0.put(DummyProvider.NAME, NAME0); 69 values0.put(DummyProvider.VALUE, VALUE0); 70 mResolver.insert(DummyProvider.CONTENT_URI, values0); 71 72 ContentValues values1 = new ContentValues(); 73 values1.put(DummyProvider.NAME, NAME1); 74 values1.put(DummyProvider.VALUE, VALUE1); 75 mResolver.insert(DummyProvider.CONTENT_URI, values1); 76 77 mCursor = mResolver.query(DummyProvider.CONTENT_URI, PROJECTIONS, null, null, null); 78 assertNotNull(mCursor); 79 } 80 81 @Override tearDown()82 protected void tearDown() throws Exception { 83 if (mContentQueryMap != null) { 84 mContentQueryMap.close(); 85 mContentQueryMap = null; 86 } 87 if (mCursor != null) { 88 mCursor.close(); 89 mCursor = null; 90 } 91 92 mResolver.delete(DummyProvider.CONTENT_URI, null, null); 93 94 super.tearDown(); 95 } 96 97 @UiThreadTest testConstructor()98 public void testConstructor() { 99 new ContentQueryMap(mCursor, DummyProvider.NAME, true, null); 100 101 new ContentQueryMap(mCursor, DummyProvider.VALUE, false, new Handler()); 102 103 try { 104 new ContentQueryMap(mCursor, null, false, new Handler()); 105 fail("Should throw NullPointerException if param columnNameOfKey is null"); 106 } catch (NullPointerException e) { 107 } 108 109 try { 110 new ContentQueryMap(null, DummyProvider.NAME, false, new Handler()); 111 fail("Should throw NullPointerException if param cursor is null"); 112 } catch (NullPointerException e) { 113 } 114 } 115 116 @UiThreadTest testGetRows()117 public void testGetRows() { 118 // handler can be null 119 mContentQueryMap = new ContentQueryMap(mCursor, DummyProvider.NAME, true, null); 120 Map<String, ContentValues> rows = mContentQueryMap.getRows(); 121 assertEquals(ORIGINAL_ROW_COUNT, rows.size()); 122 assertTrue(rows.containsKey(NAME0)); 123 assertEquals(VALUE0, rows.get(NAME0).getAsString(DummyProvider.VALUE)); 124 assertTrue(rows.containsKey(NAME1)); 125 assertEquals(VALUE1, rows.get(NAME1).getAsString(DummyProvider.VALUE)); 126 mContentQueryMap.close(); 127 128 // the mCursor has been close 129 mContentQueryMap = new ContentQueryMap(mCursor, DummyProvider.NAME, false, new Handler()); 130 rows = mContentQueryMap.getRows(); 131 assertFalse(rows.containsKey(NAME0)); 132 mContentQueryMap.requery(); 133 rows = mContentQueryMap.getRows(); 134 assertFalse(rows.containsKey(NAME0)); 135 } 136 testRequery()137 public void testRequery() { 138 // Disable the keepUpdated to make sure requery() will not be called 139 // from somewhere else 140 mContentQueryMap = new ContentQueryMap(mCursor, DummyProvider.NAME, false, null); 141 ContentValues contentValues = mContentQueryMap.getValues(NAME0); 142 assertNotNull(contentValues); 143 assertEquals(VALUE0, contentValues.getAsString(DummyProvider.VALUE)); 144 145 contentValues = mContentQueryMap.getValues(NAME1); 146 assertNotNull(contentValues); 147 assertEquals(VALUE1, contentValues.getAsString(DummyProvider.VALUE)); 148 149 contentValues = mContentQueryMap.getValues(NAME2); 150 assertNull(contentValues); 151 152 // update NAME0 and VALUE0 153 ContentValues values = new ContentValues(); 154 values.put(DummyProvider.NAME, NAME2); 155 values.put(DummyProvider.VALUE, VALUE2); 156 mResolver.update(DummyProvider.CONTENT_URI, values, 157 DummyProvider.NAME + " = '" + NAME0 + "'", null); 158 mContentQueryMap.requery(); 159 160 contentValues = mContentQueryMap.getValues(NAME0); 161 assertNull(contentValues); 162 163 contentValues = mContentQueryMap.getValues(NAME1); 164 assertNotNull(contentValues); 165 assertEquals(VALUE1, contentValues.getAsString(DummyProvider.VALUE)); 166 167 contentValues = mContentQueryMap.getValues(NAME2); 168 assertNotNull(contentValues); 169 assertEquals(VALUE2, contentValues.getAsString(DummyProvider.VALUE)); 170 } 171 testSetKeepUpdated()172 public void testSetKeepUpdated() throws InterruptedException { 173 MockObserver observer = new MockObserver(); 174 175 // keepUpdated is false 176 mContentQueryMap = new ContentQueryMap(mCursor, DummyProvider.NAME, false, null); 177 mContentQueryMap.addObserver(observer); 178 assertFalse(observer.hadUpdated(0)); 179 180 ContentValues contentValues = mContentQueryMap.getValues(NAME0); 181 assertNotNull(contentValues); 182 assertEquals(VALUE0, contentValues.getAsString(DummyProvider.VALUE)); 183 contentValues = mContentQueryMap.getValues(NAME2); 184 assertNull(contentValues); 185 186 // update NAME0 and VALUE0 187 ContentValues values = new ContentValues(); 188 values.put(DummyProvider.NAME, NAME2); 189 values.put(DummyProvider.VALUE, VALUE2); 190 mResolver.update(DummyProvider.CONTENT_URI, values, 191 DummyProvider.NAME + " = '" + NAME0 + "'", null); 192 193 // values have not been updated 194 assertFalse(observer.hadUpdated(0)); 195 contentValues = mContentQueryMap.getValues(NAME0); 196 assertNotNull(contentValues); 197 assertEquals(VALUE0, contentValues.getAsString(DummyProvider.VALUE)); 198 contentValues = mContentQueryMap.getValues(NAME2); 199 assertNull(contentValues); 200 201 // have to update manually 202 mContentQueryMap.requery(); 203 assertTrue(observer.hadUpdated(0)); 204 assertSame(mContentQueryMap, observer.getObservable()); 205 206 contentValues = mContentQueryMap.getValues(NAME0); 207 assertNull(contentValues); 208 contentValues = mContentQueryMap.getValues(NAME2); 209 assertNotNull(contentValues); 210 assertEquals(VALUE2, contentValues.getAsString(DummyProvider.VALUE)); 211 212 observer.reset(); 213 contentValues = mContentQueryMap.getValues(NAME3); 214 assertNull(contentValues); 215 new Thread(new Runnable() { 216 public void run() { 217 Looper.prepare(); 218 mContentQueryMap.setKeepUpdated(true); 219 synchronized (ContentQueryMapTest.this) { 220 //listener is ready, release the sender thread 221 ContentQueryMapTest.this.notify(); 222 } 223 Looper.loop(); 224 } 225 }).start(); 226 synchronized (this) { 227 wait(TEST_TIME_OUT); 228 }//wait the listener to be ready before launching onChange event 229 230 // insert NAME3 and VALUE3 231 values = new ContentValues(); 232 values.put(DummyProvider.NAME, NAME3); 233 values.put(DummyProvider.VALUE, VALUE3); 234 mResolver.insert(DummyProvider.CONTENT_URI, values); 235 236 // should be updated automatically 237 assertTrue(observer.hadUpdated(TEST_TIME_OUT)); 238 assertSame(mContentQueryMap, observer.getObservable()); 239 contentValues = mContentQueryMap.getValues(NAME3); 240 assertNotNull(contentValues); 241 assertEquals(VALUE3, contentValues.getAsString(DummyProvider.VALUE)); 242 243 observer.reset(); 244 new Thread(new Runnable() { 245 public void run() { 246 Looper.prepare(); 247 mContentQueryMap.setKeepUpdated(false); 248 synchronized (ContentQueryMapTest.this) { 249 //listener is ready, release the sender thread 250 ContentQueryMapTest.this.notify(); 251 } 252 Looper.loop(); 253 } 254 }).start(); 255 synchronized (this) { 256 wait(TEST_TIME_OUT); 257 }//wait the listener to be ready before launching onChange event 258 // update NAME3 and VALUE3 259 values = new ContentValues(); 260 values.put(DummyProvider.NAME, NAME0); 261 values.put(DummyProvider.VALUE, VALUE0); 262 mResolver.update(DummyProvider.CONTENT_URI, values, 263 DummyProvider.NAME + " = '" + NAME3 + "'", null); 264 265 // values have not been updated 266 assertFalse(observer.hadUpdated(TEST_TIME_OUT)); 267 contentValues = mContentQueryMap.getValues(NAME3); 268 assertNotNull(contentValues); 269 assertEquals(VALUE3, contentValues.getAsString(DummyProvider.VALUE)); 270 } 271 testSetKeepUpdatedWithHandler()272 public void testSetKeepUpdatedWithHandler() throws InterruptedException { 273 MockObserver observer = new MockObserver(); 274 HandlerThread thread = new HandlerThread("testSetKeepUpdatedWithHandler"); 275 thread.start(); 276 Handler handler = new Handler(thread.getLooper()); 277 // keepUpdated is false 278 mContentQueryMap = new ContentQueryMap(mCursor, DummyProvider.NAME, false, handler); 279 mContentQueryMap.addObserver(observer); 280 assertFalse(observer.hadUpdated(0)); 281 282 ContentValues contentValues = mContentQueryMap.getValues(NAME0); 283 assertNotNull(contentValues); 284 assertEquals(VALUE0, contentValues.getAsString(DummyProvider.VALUE)); 285 contentValues = mContentQueryMap.getValues(NAME2); 286 assertNull(contentValues); 287 288 // update NAME0 and VALUE0 289 ContentValues values = new ContentValues(); 290 values.put(DummyProvider.NAME, NAME2); 291 values.put(DummyProvider.VALUE, VALUE2); 292 mResolver.update(DummyProvider.CONTENT_URI, values, 293 DummyProvider.NAME + " = '" + NAME0 + "'", null); 294 295 // values have not been updated 296 assertFalse(observer.hadUpdated(TEST_TIME_OUT)); 297 contentValues = mContentQueryMap.getValues(NAME0); 298 assertNotNull(contentValues); 299 assertEquals(VALUE0, contentValues.getAsString(DummyProvider.VALUE)); 300 contentValues = mContentQueryMap.getValues(NAME2); 301 assertNull(contentValues); 302 303 // have to update manually 304 mContentQueryMap.requery(); 305 assertTrue(observer.hadUpdated(0)); 306 assertSame(mContentQueryMap, observer.getObservable()); 307 308 contentValues = mContentQueryMap.getValues(NAME0); 309 assertNull(contentValues); 310 contentValues = mContentQueryMap.getValues(NAME2); 311 assertNotNull(contentValues); 312 assertEquals(VALUE2, contentValues.getAsString(DummyProvider.VALUE)); 313 314 observer.reset(); 315 contentValues = mContentQueryMap.getValues(NAME3); 316 assertNull(contentValues); 317 mContentQueryMap.setKeepUpdated(true); 318 319 // insert NAME3 and VALUE3 320 values = new ContentValues(); 321 values.put(DummyProvider.NAME, NAME3); 322 values.put(DummyProvider.VALUE, VALUE3); 323 mResolver.insert(DummyProvider.CONTENT_URI, values); 324 325 // should be updated automatically 326 assertTrue(observer.hadUpdated(TEST_TIME_OUT)); 327 assertSame(mContentQueryMap, observer.getObservable()); 328 contentValues = mContentQueryMap.getValues(NAME3); 329 assertNotNull(contentValues); 330 assertEquals(VALUE3, contentValues.getAsString(DummyProvider.VALUE)); 331 332 observer.reset(); 333 mContentQueryMap.setKeepUpdated(false); 334 // update NAME3 and VALUE3 335 values = new ContentValues(); 336 values.put(DummyProvider.NAME, NAME0); 337 values.put(DummyProvider.VALUE, VALUE0); 338 mResolver.update(DummyProvider.CONTENT_URI, values, 339 DummyProvider.NAME + " = '" + NAME3 + "'", null); 340 341 // values have not been updated 342 assertFalse(observer.hadUpdated(TEST_TIME_OUT)); 343 contentValues = mContentQueryMap.getValues(NAME3); 344 assertNotNull(contentValues); 345 assertEquals(VALUE3, contentValues.getAsString(DummyProvider.VALUE)); 346 } 347 testGetValuesBoundary()348 public void testGetValuesBoundary() { 349 mContentQueryMap = new ContentQueryMap(mCursor, DummyProvider.NAME, false, null); 350 assertNull(mContentQueryMap.getValues(null)); 351 assertNull(mContentQueryMap.getValues("")); 352 } 353 354 private static final class MockObserver implements Observer { 355 private boolean mHadUpdated = false; 356 private Observable mObservable; 357 reset()358 public void reset() { 359 mHadUpdated = false; 360 mObservable = null; 361 } 362 update(Observable observable, Object data)363 public synchronized void update(Observable observable, Object data) { 364 mObservable = observable; 365 mHadUpdated = true; 366 notify(); 367 } 368 getObservable()369 public Observable getObservable() { 370 return mObservable; 371 } 372 hadUpdated(long timeout)373 public synchronized boolean hadUpdated(long timeout) throws InterruptedException { 374 // do not wait if timeout is 0 375 if (timeout > 0 && !mHadUpdated) { 376 wait(timeout); 377 } 378 return mHadUpdated; 379 } 380 } 381 } 382