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 
MockContentResolverWithNotify(Context context)80         public MockContentResolverWithNotify(Context context) {
81             super(context);
82         }
83 
resetNotified()84         public synchronized void resetNotified() {
85             mNotifyWasCalled = false;
86         }
87 
88         @Override
notifyChange( Uri uri, ContentObserver observer, boolean syncToNetwork)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 
TestContext(Context realContext)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
getContentResolver()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
getSystemService(String name)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 
AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade)141     public AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade) {
142         super(DownloadJobService.class);
143         mSystemFacade = systemFacade;
144     }
145 
146     @Override
setUp()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         mServer = new MockWebServer();
176         mServer.play();
177     }
178 
179     @Override
tearDown()180     protected void tearDown() throws Exception {
181         cleanUpDownloads();
182         mServer.shutdown();
183         mMockitoHelper.tearDown();
184         super.tearDown();
185     }
186 
startDownload(long id)187     protected void startDownload(long id) {
188         final JobParameters params = mock(JobParameters.class);
189         when(params.getJobId()).thenReturn((int) id);
190         getService().onStartJob(params);
191     }
192 
isDatabaseEmpty()193     private boolean isDatabaseEmpty() {
194         Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
195                 null, null, null, null);
196         try {
197             return cursor.getCount() == 0;
198         } finally {
199             cursor.close();
200         }
201     }
202 
203     /**
204      * Remove any downloaded files and delete any lingering downloads.
205      */
cleanUpDownloads()206     void cleanUpDownloads() {
207         if (mResolver == null) {
208             return;
209         }
210         String[] columns = new String[] {Downloads.Impl._DATA};
211         Cursor cursor = mResolver.query(Downloads.Impl.CONTENT_URI, columns, null, null, null);
212         try {
213             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
214                 String filePath = cursor.getString(0);
215                 if (filePath == null) continue;
216                 Log.d(LOG_TAG, "Deleting " + filePath);
217                 new File(filePath).delete();
218             }
219         } finally {
220             cursor.close();
221         }
222         mResolver.delete(Downloads.Impl.CONTENT_URI, null, null);
223     }
224 
enqueueResponse(MockResponse resp)225     void enqueueResponse(MockResponse resp) {
226         mServer.enqueue(resp);
227     }
228 
buildResponse(int status, String body)229     MockResponse buildResponse(int status, String body) {
230         return new MockResponse().setResponseCode(status).setBody(body)
231                 .setHeader("Content-type", "text/plain")
232                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
233     }
234 
buildResponse(int status, byte[] body)235     MockResponse buildResponse(int status, byte[] body) {
236         return new MockResponse().setResponseCode(status).setBody(body)
237                 .setHeader("Content-type", "text/plain")
238                 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
239     }
240 
buildEmptyResponse(int status)241     MockResponse buildEmptyResponse(int status) {
242         return buildResponse(status, "");
243     }
244 
245     /**
246      * Fetch the last request received by the MockWebServer.
247      */
takeRequest()248     protected RecordedRequest takeRequest() throws InterruptedException {
249         RecordedRequest request = mServer.takeRequest();
250         assertNotNull("Expected request was not made", request);
251         return request;
252     }
253 
getServerUri(String path)254     String getServerUri(String path) throws MalformedURLException, UnknownHostException {
255         return mServer.getUrl(path).toString();
256     }
257 
readStream(InputStream inputStream)258     protected String readStream(InputStream inputStream) throws IOException {
259         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
260         try {
261             char[] buffer = new char[1024];
262             int length = reader.read(buffer);
263             assertTrue("Failed to read anything from input stream", length > -1);
264             return String.valueOf(buffer, 0, length);
265         } finally {
266             reader.close();
267         }
268     }
269 
assertStartsWith(String expectedPrefix, String actual)270     protected void assertStartsWith(String expectedPrefix, String actual) {
271         String regex = "^" + expectedPrefix + ".*";
272         MoreAsserts.assertMatchesRegex(regex, actual);
273     }
274 }
275