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