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 assertTrue(isDatabaseSecureAgainstBadSelection()); 176 mServer = new MockWebServer(); 177 mServer.play(); 178 } 179 180 @Override tearDown()181 protected void tearDown() throws Exception { 182 cleanUpDownloads(); 183 mServer.shutdown(); 184 mMockitoHelper.tearDown(); 185 super.tearDown(); 186 } 187 startDownload(long id)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 isDatabaseEmpty()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 isDatabaseSecureAgainstBadSelection()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 */ cleanUpDownloads()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 enqueueResponse(MockResponse resp)243 void enqueueResponse(MockResponse resp) { 244 mServer.enqueue(resp); 245 } 246 buildResponse(int status, String body)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 buildResponse(int status, byte[] body)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 buildEmptyResponse(int status)259 MockResponse buildEmptyResponse(int status) { 260 return buildResponse(status, ""); 261 } 262 263 /** 264 * Fetch the last request received by the MockWebServer. 265 */ takeRequest()266 protected RecordedRequest takeRequest() throws InterruptedException { 267 RecordedRequest request = mServer.takeRequest(); 268 assertNotNull("Expected request was not made", request); 269 return request; 270 } 271 getServerUri(String path)272 String getServerUri(String path) throws MalformedURLException, UnknownHostException { 273 return mServer.getUrl(path).toString(); 274 } 275 readStream(InputStream inputStream)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 assertStartsWith(String expectedPrefix, String actual)288 protected void assertStartsWith(String expectedPrefix, String actual) { 289 String regex = "^" + expectedPrefix + ".*"; 290 MoreAsserts.assertMatchesRegex(regex, actual); 291 } 292 } 293