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