Move notification tests to LittleMock.
Jeff Sharkey [Wed, 25 Jul 2012 00:42:25 +0000 (17:42 -0700)]
Directly mock NotificationManager instead of using SystemFacade.

Change-Id: If932d26e23816e8674469c275a828701cce5fc2d

src/com/android/providers/downloads/DownloadNotification.java
src/com/android/providers/downloads/DownloadReceiver.java
src/com/android/providers/downloads/DownloadService.java
src/com/android/providers/downloads/RealSystemFacade.java
src/com/android/providers/downloads/SystemFacade.java
tests/Android.mk
tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
tests/src/com/android/providers/downloads/FakeSystemFacade.java
tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java

index bbd39f6..f5778e7 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.providers.downloads;
 
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ContentUris;
 import android.content.Context;
@@ -38,9 +39,9 @@ import java.util.HashMap;
  */
 class DownloadNotification {
 
-    Context mContext;
-    HashMap <String, NotificationItem> mNotifications;
-    private SystemFacade mSystemFacade;
+    private Context mContext;
+    private NotificationManager mNotifManager;
+    private HashMap<String, NotificationItem> mNotifications;
 
     /** Time when each {@link DownloadInfo#mId} was first shown. */
     private SparseLongArray mFirstShown = new SparseLongArray();
@@ -102,7 +103,8 @@ class DownloadNotification {
      */
     DownloadNotification(Context ctx, SystemFacade systemFacade) {
         mContext = ctx;
-        mSystemFacade = systemFacade;
+        mNotifManager = (NotificationManager) mContext.getSystemService(
+                Context.NOTIFICATION_SERVICE);
         mNotifications = new HashMap<String, NotificationItem>();
     }
 
@@ -207,8 +209,7 @@ class DownloadNotification {
 
             builder.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0));
 
-            mSystemFacade.postNotification(item.mId, builder.getNotification());
-
+            mNotifManager.notify(item.mId, builder.build());
         }
     }
 
@@ -262,7 +263,7 @@ class DownloadNotification {
         intent.setData(contentUri);
         builder.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0));
 
-        mSystemFacade.postNotification(id, builder.getNotification());
+        mNotifManager.notify((int) id, builder.build());
     }
 
     private boolean isActiveAndVisible(DownloadInfo download) {
index 26ad992..81ff404 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.providers.downloads;
 
 import android.app.DownloadManager;
+import android.app.NotificationManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ContentUris;
@@ -120,7 +121,9 @@ public class DownloadReceiver extends BroadcastReceiver {
      * @param cursor Cursor for reading the download's fields
      */
     private void hideNotification(Context context, Uri uri, Cursor cursor) {
-        mSystemFacade.cancelNotification(ContentUris.parseId(uri));
+        final NotificationManager notifManager = (NotificationManager) context.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+        notifManager.cancel((int) ContentUris.parseId(uri));
 
         int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS);
         int status = cursor.getInt(statusColumn);
index 55efefc..7030dea 100644 (file)
@@ -19,6 +19,7 @@ package com.android.providers.downloads;
 import static com.android.providers.downloads.Constants.TAG;
 
 import android.app.AlarmManager;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.ComponentName;
@@ -65,6 +66,7 @@ public class DownloadService extends Service {
 
     /** Class to handle Notification Manager updates */
     private DownloadNotification mNotifier;
+    private NotificationManager mNotifManager;
 
     /**
      * The Service's view of the list of downloads, mapping download IDs to the corresponding info
@@ -221,7 +223,9 @@ public class DownloadService extends Service {
         mMediaScannerConnection = new MediaScannerConnection();
 
         mNotifier = new DownloadNotification(this, mSystemFacade);
-        mSystemFacade.cancelAllNotifications();
+        mNotifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotifManager.cancelAll();
+
         mStorageManager = StorageManager.getInstance(getApplicationContext());
         updateFromProvider();
     }
@@ -464,7 +468,7 @@ public class DownloadService extends Service {
                 !Downloads.Impl.isStatusCompleted(oldStatus)
                 && Downloads.Impl.isStatusCompleted(info.mStatus);
         if (lostVisibility || justCompleted) {
-            mSystemFacade.cancelNotification(info.mId);
+            mNotifManager.cancel((int) info.mId);
         }
 
         info.startIfReady(now, mStorageManager);
@@ -487,7 +491,7 @@ public class DownloadService extends Service {
             }
             new File(info.mFileName).delete();
         }
-        mSystemFacade.cancelNotification(info.mId);
+        mNotifManager.cancel((int) info.mId);
         mDownloads.remove(info.mId);
     }
 
index 6580f90..228c716 100644 (file)
@@ -1,26 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.providers.downloads;
 
 import android.app.DownloadManager;
-import android.app.Notification;
-import android.app.NotificationManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
 class RealSystemFacade implements SystemFacade {
     private Context mContext;
-    private NotificationManager mNotificationManager;
 
     public RealSystemFacade(Context context) {
         mContext = context;
-        mNotificationManager = (NotificationManager)
-                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
     }
 
     public long currentTimeMillis() {
@@ -85,26 +94,6 @@ class RealSystemFacade implements SystemFacade {
     }
 
     @Override
-    public void postNotification(long id, Notification notification) {
-        /**
-         * TODO: The system notification manager takes ints, not longs, as IDs, but the download
-         * manager uses IDs take straight from the database, which are longs.  This will have to be
-         * dealt with at some point.
-         */
-        mNotificationManager.notify((int) id, notification);
-    }
-
-    @Override
-    public void cancelNotification(long id) {
-        mNotificationManager.cancel((int) id);
-    }
-
-    @Override
-    public void cancelAllNotifications() {
-        mNotificationManager.cancelAll();
-    }
-
-    @Override
     public void startThread(Thread thread) {
         thread.start();
     }
index d143935..fda97e0 100644 (file)
@@ -1,12 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package com.android.providers.downloads;
 
-import android.app.Notification;
 import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.NetworkInfo;
 
-
 interface SystemFacade {
     /**
      * @see System#currentTimeMillis()
@@ -50,21 +63,6 @@ interface SystemFacade {
     public boolean userOwnsPackage(int uid, String pckg) throws NameNotFoundException;
 
     /**
-     * Post a system notification to the NotificationManager.
-     */
-    public void postNotification(long id, Notification notification);
-
-    /**
-     * Cancel a system notification.
-     */
-    public void cancelNotification(long id);
-
-    /**
-     * Cancel all system notifications.
-     */
-    public void cancelAllNotifications();
-
-    /**
      * Start a thread.
      */
     public void startThread(Thread thread);
index aaf32ba..ff3e1d4 100644 (file)
@@ -8,7 +8,7 @@ LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_INSTRUMENTATION_FOR := DownloadProvider
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockwebserver
+LOCAL_STATIC_JAVA_LIBRARIES := mockwebserver littlemock dexmaker
 LOCAL_PACKAGE_NAME := DownloadProviderTests
 LOCAL_CERTIFICATE := media
 
index 1912e84..a65693f 100644 (file)
@@ -16,6 +16,9 @@
 
 package com.android.providers.downloads;
 
+import static com.google.testing.littlemock.LittleMock.mock;
+
+import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -42,9 +45,6 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.MalformedURLException;
 import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
 
 public abstract class AbstractDownloadProviderFunctionalTest extends
         ServiceTestCase<DownloadService> {
@@ -94,19 +94,14 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
     static class TestContext extends RenamingDelegatingContext {
         private static final String FILENAME_PREFIX = "test.";
 
-        private Context mRealContext;
-        private Set<String> mAllowedSystemServices;
         private ContentResolver mResolver;
+        private final NotificationManager mNotifManager;
 
         boolean mHasServiceBeenStarted = false;
 
         public TestContext(Context realContext) {
             super(realContext, FILENAME_PREFIX);
-            mRealContext = realContext;
-            mAllowedSystemServices = new HashSet<String>(Arrays.asList(new String[] {
-                    Context.NOTIFICATION_SERVICE,
-                    Context.POWER_SERVICE,
-            }));
+            mNotifManager = mock(NotificationManager.class);
         }
 
         public void setResolver(ContentResolver resolver) {
@@ -118,7 +113,6 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
          */
         @Override
         public ContentResolver getContentResolver() {
-            assert mResolver != null;
             return mResolver;
         }
 
@@ -127,9 +121,10 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
          */
         @Override
         public Object getSystemService(String name) {
-            if (mAllowedSystemServices.contains(name)) {
-                return mRealContext.getSystemService(name);
+            if (Context.NOTIFICATION_SERVICE.equals(name)) {
+                return mNotifManager;
             }
+
             return super.getSystemService(name);
         }
 
@@ -155,10 +150,13 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
     protected void setUp() throws Exception {
         super.setUp();
 
-        Context realContext = getContext();
+        // Since we're testing a system app, AppDataDirGuesser doesn't find our
+        // cache dir, so set it explicitly.
+        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+
+        final Context realContext = getContext();
         mTestContext = new TestContext(realContext);
         setupProviderAndResolver();
-
         mTestContext.setResolver(mResolver);
         setContext(mTestContext);
         setupService();
index c184de8..6898efd 100644 (file)
@@ -1,17 +1,13 @@
 package com.android.providers.downloads;
 
-import android.app.Notification;
 import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
-import android.test.AssertionFailedError;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Queue;
 
 public class FakeSystemFacade implements SystemFacade {
@@ -22,8 +18,6 @@ public class FakeSystemFacade implements SystemFacade {
     Long mMaxBytesOverMobile = null;
     Long mRecommendedMaxBytesOverMobile = null;
     List<Intent> mBroadcastsSent = new ArrayList<Intent>();
-    Map<Long,Notification> mActiveNotifications = new HashMap<Long,Notification>();
-    List<Notification> mCanceledNotifications = new ArrayList<Notification>();
     Queue<Thread> mStartedThreads = new LinkedList<Thread>();
     private boolean returnActualTime = false;
 
@@ -73,29 +67,6 @@ public class FakeSystemFacade implements SystemFacade {
         return true;
     }
 
-    @Override
-    public void postNotification(long id, Notification notification) {
-        if (notification == null) {
-            throw new AssertionFailedError("Posting null notification");
-        }
-        mActiveNotifications.put(id, notification);
-    }
-
-    @Override
-    public void cancelNotification(long id) {
-        Notification notification = mActiveNotifications.remove(id);
-        if (notification != null) {
-            mCanceledNotifications.add(notification);
-        }
-    }
-
-    @Override
-    public void cancelAllNotifications() {
-        for (long id : mActiveNotifications.keySet()) {
-            cancelNotification(id);
-        }
-    }
-
     public boolean startThreadsWithoutWaiting = false;
     public void setStartThreadsWithoutWaiting(boolean flag) {
         this.startThreadsWithoutWaiting = flag;
index 2f5282a..34a69df 100644 (file)
 
 package com.android.providers.downloads;
 
+import static com.google.testing.littlemock.LittleMock.anyInt;
+import static com.google.testing.littlemock.LittleMock.atLeastOnce;
+import static com.google.testing.littlemock.LittleMock.isA;
+import static com.google.testing.littlemock.LittleMock.never;
+import static com.google.testing.littlemock.LittleMock.times;
+import static com.google.testing.littlemock.LittleMock.verify;
+
 import android.app.DownloadManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
 import android.net.ConnectivityManager;
@@ -24,7 +34,6 @@ import android.net.Uri;
 import android.os.Environment;
 import android.provider.Downloads;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
 
 import com.google.mockwebserver.MockResponse;
 import com.google.mockwebserver.RecordedRequest;
@@ -37,12 +46,14 @@ import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.util.List;
 
+
 @LargeTest
 public class PublicApiFunctionalTest extends AbstractPublicApiTest {
     private static final String REDIRECTED_PATH = "/other_path";
     private static final String ETAG = "my_etag";
 
     protected File mTestDirectory;
+    private NotificationManager mNotifManager;
 
     public PublicApiFunctionalTest() {
         super(new FakeSystemFacade());
@@ -52,6 +63,9 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
     protected void setUp() throws Exception {
         super.setUp();
 
+        mNotifManager = (NotificationManager) getContext()
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
         mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator
                                   + "download_manager_functional_test");
         if (mTestDirectory.exists()) {
@@ -501,24 +515,42 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
         assertTrue(mResolver.mNotifyWasCalled);
     }
 
-    public void testNotifications() throws Exception {
-        enqueueResponse(buildEmptyResponse(HTTP_OK));
+    public void testNotificationNever() throws Exception {
         enqueueResponse(buildEmptyResponse(HTTP_OK));
 
-        Download download = enqueueRequest(getRequest().setShowRunningNotification(false));
+        final Download download = enqueueRequest(
+                getRequest().setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN));
         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
-        assertEquals(0, mSystemFacade.mActiveNotifications.size());
-        assertEquals(0, mSystemFacade.mCanceledNotifications.size());
+        runService();
+
+        verify(mNotifManager, never()).notify(anyInt(), isA(Notification.class));
+        // TODO: verify that it never cancels
+    }
+
+    public void testNotificationVisible() throws Exception {
+        enqueueResponse(buildEmptyResponse(HTTP_OK));
 
-        download = enqueueRequest(getRequest()); // notifications by default
+        // only shows in-progress notifications
+        final Download download = enqueueRequest(getRequest());
         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
-        assertEquals(1, mSystemFacade.mActiveNotifications.size());
+        runService();
+
+        // TODO: verify different notif types with tags
+        verify(mNotifManager, atLeastOnce()).notify(anyInt(), isA(Notification.class));
+        verify(mNotifManager, times(1)).cancel(anyInt());
+    }
 
-        // The notification doesn't actually get canceled until the UpdateThread runs again, which
-        // gets triggered by the DownloadThread updating the status in the provider.
+    public void testNotificationVisibleComplete() throws Exception {
+        enqueueResponse(buildEmptyResponse(HTTP_OK));
+
+        final Download download = enqueueRequest(getRequest().setNotificationVisibility(
+                DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED));
+        download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
         runService();
-        assertEquals(0, mSystemFacade.mActiveNotifications.size());
-        assertEquals(1, mSystemFacade.mCanceledNotifications.size());
+
+        // TODO: verify different notif types with tags
+        verify(mNotifManager, atLeastOnce()).notify(anyInt(), isA(Notification.class));
+        verify(mNotifManager, times(1)).cancel(anyInt());
     }
 
     public void testRetryAfter() throws Exception {