Home | History | Annotate | Download | only in downloads
      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 
     17 package com.android.providers.downloads;
     18 
     19 import static org.mockito.Mockito.mock;
     20 import static org.mockito.Mockito.when;
     21 
     22 import android.app.DownloadManager;
     23 import android.app.NotificationManager;
     24 import android.app.job.JobParameters;
     25 import android.app.job.JobScheduler;
     26 import android.content.ContentResolver;
     27 import android.content.Context;
     28 import android.content.pm.ProviderInfo;
     29 import android.database.ContentObserver;
     30 import android.database.Cursor;
     31 import android.net.Uri;
     32 import android.provider.Downloads;
     33 import android.test.MoreAsserts;
     34 import android.test.RenamingDelegatingContext;
     35 import android.test.ServiceTestCase;
     36 import android.test.mock.MockContentResolver;
     37 import android.util.Log;
     38 
     39 import com.google.mockwebserver.MockResponse;
     40 import com.google.mockwebserver.MockWebServer;
     41 import com.google.mockwebserver.RecordedRequest;
     42 import com.google.mockwebserver.SocketPolicy;
     43 
     44 import java.io.BufferedReader;
     45 import java.io.File;
     46 import java.io.IOException;
     47 import java.io.InputStream;
     48 import java.io.InputStreamReader;
     49 import java.net.MalformedURLException;
     50 import java.net.UnknownHostException;
     51 
     52 public abstract class AbstractDownloadProviderFunctionalTest extends
     53         ServiceTestCase<DownloadJobService> {
     54 
     55     protected static final String LOG_TAG = "DownloadProviderFunctionalTest";
     56     private static final String PROVIDER_AUTHORITY = "downloads";
     57     protected static final long RETRY_DELAY_MILLIS = 61 * 1000;
     58 
     59     protected static final String
     60             FILE_CONTENT = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
     61 
     62     private final MockitoHelper mMockitoHelper = new MockitoHelper();
     63 
     64     protected MockWebServer mServer;
     65     protected MockContentResolverWithNotify mResolver;
     66     protected TestContext mTestContext;
     67     protected FakeSystemFacade mSystemFacade;
     68     protected static String STRING_1K;
     69     static {
     70         StringBuilder buff = new StringBuilder();
     71         for (int i = 0; i < 1024; i++) {
     72             buff.append("a" + i % 26);
     73         }
     74         STRING_1K = buff.toString();
     75     }
     76 
     77     static class MockContentResolverWithNotify extends MockContentResolver {
     78         public boolean mNotifyWasCalled = false;
     79 
     80         public MockContentResolverWithNotify(Context context) {
     81             super(context);
     82         }
     83 
     84         public synchronized void resetNotified() {
     85             mNotifyWasCalled = false;
     86         }
     87 
     88         @Override
     89         public synchronized void notifyChange(
     90                 Uri uri, ContentObserver observer, boolean syncToNetwork) {
     91             mNotifyWasCalled = true;
     92         }
     93     }
     94 
     95     /**
     96      * Context passed to the provider and the service.  Allows most methods to pass through to the
     97      * real Context (this is a LargeTest), with a few exceptions, including renaming file operations
     98      * to avoid file and DB conflicts (via RenamingDelegatingContext).
     99      */
    100     static class TestContext extends RenamingDelegatingContext {
    101         private static final String FILENAME_PREFIX = "test.";
    102 
    103         private final ContentResolver mResolver;
    104         private final NotificationManager mNotifManager;
    105         private final DownloadManager mDownloadManager;
    106         private final JobScheduler mJobScheduler;
    107 
    108         public TestContext(Context realContext) {
    109             super(realContext, FILENAME_PREFIX);
    110             mResolver = new MockContentResolverWithNotify(this);
    111             mNotifManager = mock(NotificationManager.class);
    112             mDownloadManager = mock(DownloadManager.class);
    113             mJobScheduler = mock(JobScheduler.class);
    114         }
    115 
    116         /**
    117          * Direct DownloadService to our test instance of DownloadProvider.
    118          */
    119         @Override
    120         public ContentResolver getContentResolver() {
    121             return mResolver;
    122         }
    123 
    124         /**
    125          * Stub some system services, allow access to others, and block the rest.
    126          */
    127         @Override
    128         public Object getSystemService(String name) {
    129             if (Context.NOTIFICATION_SERVICE.equals(name)) {
    130                 return mNotifManager;
    131             } else if (Context.DOWNLOAD_SERVICE.equals(name)) {
    132                 return mDownloadManager;
    133             } else if (Context.JOB_SCHEDULER_SERVICE.equals(name)) {
    134                 return mJobScheduler;
    135             }
    136 
    137             return super.getSystemService(name);
    138         }
    139     }
    140 
    141     public AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade) {
    142         super(DownloadJobService.class);
    143         mSystemFacade = systemFacade;
    144     }
    145 
    146     @Override
    147     protected void setUp() throws Exception {
    148         super.setUp();
    149         mMockitoHelper.setUp(getClass());
    150 
    151         // Since we're testing a system app, AppDataDirGuesser doesn't find our
    152         // cache dir, so set it explicitly.
    153         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
    154 
    155         final Context realContext = getContext();
    156 
    157         mTestContext = new TestContext(realContext);
    158         mResolver = (MockContentResolverWithNotify) mTestContext.getContentResolver();
    159 
    160         final DownloadProvider provider = new DownloadProvider();
    161         provider.mSystemFacade = mSystemFacade;
    162 
    163         ProviderInfo info = new ProviderInfo();
    164         info.authority = "downloads";
    165         provider.attachInfo(mTestContext, info);
    166 
    167         mResolver.addProvider(PROVIDER_AUTHORITY, provider);
    168 
    169         setContext(mTestContext);
    170         setupService();
    171         Helpers.setSystemFacade(mSystemFacade);
    172 
    173         mSystemFacade.setUp();
    174         assertTrue(isDatabaseEmpty()); // ensure we're not messing with real data
    175         assertTrue(isDatabaseSecureAgainstBadSelection());
    176         mServer = new MockWebServer();
    177         mServer.play();
    178     }
    179 
    180     @Override
    181     protected void tearDown() throws Exception {
    182         cleanUpDownloads();
    183         mServer.shutdown();
    184         mMockitoHelper.tearDown();
    185         super.tearDown();
    186     }
    187 
    188     protected void startDownload(long id) {
    189         final JobParameters params = mock(JobParameters.class);
    190         when(params.getJobId()).thenReturn((int) id);
    191         getService().onStartJob(params);
    192     }
    193 
    194     private boolean isDatabaseEmpty() {
    195         Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
    196                 null, null, null, null);
    197         try {
    198             return cursor.getCount() == 0;
    199         } finally {
    200             cursor.close();
    201         }
    202     }
    203 
    204     private boolean isDatabaseSecureAgainstBadSelection() {
    205         Cursor cursor = null;
    206         try {
    207             cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, null,
    208                     "('1'='1'))) ORDER BY lastmod DESC--", null, null);
    209         }
    210         catch (Exception e) {
    211             return true;
    212         } finally {
    213             if (cursor != null) {
    214                 cursor.close();
    215             }
    216         }
    217 
    218         return false;
    219     }
    220 
    221     /**
    222      * Remove any downloaded files and delete any lingering downloads.
    223      */
    224     void cleanUpDownloads() {
    225         if (mResolver == null) {
    226             return;
    227         }
    228         String[] columns = new String[] {Downloads.Impl._DATA};
    229         Cursor cursor = mResolver.query(Downloads.Impl.CONTENT_URI, columns, null, null, null);
    230         try {
    231             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
    232                 String filePath = cursor.getString(0);
    233                 if (filePath == null) continue;
    234                 Log.d(LOG_TAG, "Deleting " + filePath);
    235                 new File(filePath).delete();
    236             }
    237         } finally {
    238             cursor.close();
    239         }
    240         mResolver.delete(Downloads.Impl.CONTENT_URI, null, null);
    241     }
    242 
    243     void enqueueResponse(MockResponse resp) {
    244         mServer.enqueue(resp);
    245     }
    246 
    247     MockResponse buildResponse(int status, String body) {
    248         return new MockResponse().setResponseCode(status).setBody(body)
    249                 .setHeader("Content-type", "text/plain")
    250                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
    251     }
    252 
    253     MockResponse buildResponse(int status, byte[] body) {
    254         return new MockResponse().setResponseCode(status).setBody(body)
    255                 .setHeader("Content-type", "text/plain")
    256                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
    257     }
    258 
    259     MockResponse buildEmptyResponse(int status) {
    260         return buildResponse(status, "");
    261     }
    262 
    263     /**
    264      * Fetch the last request received by the MockWebServer.
    265      */
    266     protected RecordedRequest takeRequest() throws InterruptedException {
    267         RecordedRequest request = mServer.takeRequest();
    268         assertNotNull("Expected request was not made", request);
    269         return request;
    270     }
    271 
    272     String getServerUri(String path) throws MalformedURLException, UnknownHostException {
    273         return mServer.getUrl(path).toString();
    274     }
    275 
    276     protected String readStream(InputStream inputStream) throws IOException {
    277         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    278         try {
    279             char[] buffer = new char[1024];
    280             int length = reader.read(buffer);
    281             assertTrue("Failed to read anything from input stream", length > -1);
    282             return String.valueOf(buffer, 0, length);
    283         } finally {
    284             reader.close();
    285         }
    286     }
    287 
    288     protected void assertStartsWith(String expectedPrefix, String actual) {
    289         String regex = "^" + expectedPrefix + ".*";
    290         MoreAsserts.assertMatchesRegex(regex, actual);
    291     }
    292 }
    293