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