Test + utilize change notification in DownloadProvider
Steve Howard [Sat, 24 Jul 2010 01:30:16 +0000 (18:30 -0700)]
Now that I've learned how ContentResolver.notifyChange() works, add a
test to explicitly check it's being called in DownloadProvider, and
take advantage of it in waitForDownloadToStop to get rid of the ugly
old polling mechanism.

Change-Id: Iba71bdf1b0d31454cc4e186ceb4fdd9fdb5faad5

tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java
tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java

index 7af98c1..cb4ad8c 100644 (file)
@@ -20,7 +20,9 @@ import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.database.Cursor;
+import android.net.Uri;
 import android.provider.Downloads;
 import android.test.MoreAsserts;
 import android.test.RenamingDelegatingContext;
@@ -54,7 +56,7 @@ public abstract class AbstractDownloadManagerFunctionalTest extends
     protected static final int HTTP_NOT_FOUND = 404;
     protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
     protected MockWebServer mServer;
-    protected MockContentResolver mResolver;
+    protected MockContentResolverWithNotify mResolver;
     protected TestContext mTestContext;
     protected FakeSystemFacade mSystemFacade;
 
@@ -63,6 +65,21 @@ public abstract class AbstractDownloadManagerFunctionalTest extends
         public boolean isComplete(int status);
     }
 
+    static class MockContentResolverWithNotify extends MockContentResolver {
+        public boolean mNotifyWasCalled = false;
+
+        public synchronized void resetNotified() {
+            mNotifyWasCalled = false;
+        }
+
+        @Override
+        public synchronized void notifyChange(Uri uri, ContentObserver observer,
+                boolean syncToNetwork) {
+            mNotifyWasCalled = true;
+            notifyAll();
+        }
+    }
+
     /**
      * Context passed to the provider and the service.  Allows most methods to pass through to the
      * real Context (this is a LargeTest), with a few exceptions, including renaming file operations
@@ -179,7 +196,7 @@ public abstract class AbstractDownloadManagerFunctionalTest extends
         DownloadProvider provider = new DownloadProvider();
         provider.mSystemFacade = mSystemFacade;
         provider.attachInfo(mTestContext, null);
-        mResolver = new MockContentResolver();
+        mResolver = new MockContentResolverWithNotify();
         mResolver.addProvider(PROVIDER_AUTHORITY, provider);
     }
 
@@ -257,25 +274,43 @@ public abstract class AbstractDownloadManagerFunctionalTest extends
      */
     protected void waitForDownloadToStop(StatusReader reader, int expectedStatus)
             throws Exception {
-        // TODO(showard): find a better way to accomplish this
         long startTimeMillis = System.currentTimeMillis();
+        long endTimeMillis = startTimeMillis + REQUEST_TIMEOUT_MILLIS;
         int status = reader.getStatus();
         while (status != expectedStatus) {
             if (reader.isComplete(status)) {
                 fail("Download completed with unexpected status: " + status);
             }
-            if (System.currentTimeMillis() > startTimeMillis + REQUEST_TIMEOUT_MILLIS) {
+            waitForChange(endTimeMillis);
+            if (startTimeMillis > endTimeMillis) {
                 fail("Download timed out with status " + status);
             }
-            Thread.sleep(100);
             mServer.checkForExceptions();
             status = reader.getStatus();
         }
 
-        long delta = System.currentTimeMillis() - startTimeMillis;
+        long delta = startTimeMillis - startTimeMillis;
         Log.d(LOG_TAG, "Status " + status + " reached after " + delta + "ms");
     }
 
+    /**
+     * Wait until mResolver gets notifyChange() called, or endTimeMillis is reached.
+     */
+    private void waitForChange(long endTimeMillis) {
+        synchronized(mResolver) {
+            long now = System.currentTimeMillis();
+            while (!mResolver.mNotifyWasCalled && now < endTimeMillis) {
+                try {
+                    mResolver.wait(endTimeMillis - now);
+                } catch (InterruptedException exc) {
+                    // no problem
+                }
+                now = System.currentTimeMillis();
+            }
+            mResolver.resetNotified();
+        }
+    }
+
     protected String readStream(InputStream inputStream) throws IOException {
         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
         try {
index 7982ef4..3d32ae3 100644 (file)
@@ -472,6 +472,19 @@ public class PublicApiFunctionalTest extends AbstractDownloadManagerFunctionalTe
         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
     }
 
+    public void testContentObserver() throws Exception {
+        enqueueEmptyResponse(HTTP_OK);
+        enqueueRequest(getRequest());
+        mResolver.resetNotified();
+        startService(null);
+        synchronized(mResolver) {
+            if (!mResolver.mNotifyWasCalled) {
+                mResolver.wait(2000);
+            }
+        }
+        assertTrue(mResolver.mNotifyWasCalled);
+    }
+
     private void runSimpleFailureTest(int expectedErrorCode) throws Exception {
         Download download = enqueueRequest(getRequest());
         download.runUntilStatus(DownloadManager.STATUS_FAILED);