summaryrefslogtreecommitdiffstats
path: root/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTestBase.java
blob: e6b33414e0080ad56732ede04702095dbc8c0831 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.download;

import android.app.DownloadManager;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;

import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.SuppressFBWarnings;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.test.ChromeActivityTestCaseBase;
import org.chromium.content.browser.DownloadController;
import org.chromium.content.browser.DownloadInfo;
import org.chromium.content.browser.test.util.ApplicationUtils;
import org.chromium.content.browser.test.util.CallbackHelper;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Base case for tests that need to download a file.
 *
 * This has to be a base class because some classes (like BrowserEvent) are exposed only
 * to children of ChromeActivityTestCaseBase. It is a very broken approach to sharing
 * but the only other option is to refactor the ChromeActivityTestCaseBase implementation
 * and all of our test cases.
 *
 */
public abstract class DownloadTestBase extends ChromeActivityTestCaseBase<ChromeActivity> {
    private static final String TAG = "DownloadTestBase";
    private static final File DOWNLOAD_DIRECTORY =
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
    public static final long UPDATE_DELAY_MILLIS = 1000;

    public DownloadTestBase() {
        super(ChromeActivity.class);
    }

    /**
     * Check the download exists in DownloadManager by matching the local file
     * path.
     *
     * @param fileName Expected file name. Path is built by appending filename to
     * the system downloads path.
     *
     * @param expectedContents Expected contents of the file, or null if the contents should not be
     * checked.
     */
    public boolean hasDownload(String fileName, String expectedContents) throws IOException {
        File downloadedFile = new File(DOWNLOAD_DIRECTORY, fileName);
        if (!downloadedFile.exists()) {
            Log.d(TAG, "The file " + fileName + " does not exist");
            return false;
        }

        String fullPath = downloadedFile.getAbsolutePath();

        DownloadManager manager =
                (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
        Cursor cursor = manager.query(new DownloadManager.Query());

        cursor.moveToFirst();
        boolean result = false;
        while (!cursor.isAfterLast()) {
            if (fullPath.equals(getPathFromCursor(cursor))) {
                if (expectedContents != null) {
                    FileInputStream stream = new FileInputStream(new File(fullPath));
                    byte[] data = new byte[expectedContents.getBytes().length];
                    try {
                        assertEquals(stream.read(data), data.length);
                        String contents = new String(data);
                        assertEquals(expectedContents, contents);
                    } finally {
                        stream.close();
                    }
                }
                result = true;
                break;
            }
            cursor.moveToNext();
        }
        cursor.close();
        return result;
    }

    /**
     * Check the last download matches the given name and exists in DownloadManager.
     */
    public void checkLastDownload(String fileName) throws IOException {
        String lastDownload = getLastDownloadFile();
        assertTrue(isSameDownloadFile(fileName, lastDownload));
        assertTrue(hasDownload(lastDownload, null));
    }

    public EnqueueHttpGetDownloadCallbackHelper getHttpGetDownloadCallbackHelper() {
        return mEnqueueHttpGetDownloadCallbackHelper;
    }

    /**
     * Delete all download entries in DownloadManager and delete the corresponding files.
     */
    @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
    private void cleanUpAllDownloads() {
        DownloadManager manager =
                (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
        Cursor cursor = manager.query(new DownloadManager.Query());
        int idColumnIndex = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID);
        cursor.moveToFirst();
        while (!cursor.isAfterLast()) {
            long id = cursor.getLong(idColumnIndex);
            String fileName = getPathFromCursor(cursor);
            manager.remove(id);

            if (fileName != null) {  // Somehow fileName can be null for some entries.
                // manager.remove does not remove downloaded file.
                File localFile = new File(fileName);
                if (localFile.exists()) {
                    localFile.delete();
                }
            }

            cursor.moveToNext();
        }
        cursor.close();
    }

    /**
     * Retrieve the path of the downloaded file from a DownloadManager cursor.
     */
    private String getPathFromCursor(Cursor cursor) {
        int columnId = cursor.getColumnIndex("local_filename");
        return cursor.getString(columnId);
    }

    private String getPathForDownload(long downloadId) {
        DownloadManager manager =
                (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(downloadId);
        Cursor cursor = null;
        try {
            cursor = manager.query(query);
            if (!cursor.moveToFirst()) {
                return null;
            }
            return getPathFromCursor(cursor);
        } finally {
            if (cursor != null) cursor.close();
        }
    }

    protected static class EnqueueHttpGetDownloadCallbackHelper extends CallbackHelper {
        private DownloadInfo mDownloadInfo;

        public void notifyCalled(DownloadInfo downloadInfo, boolean notifyCompleted) {
            mDownloadInfo = downloadInfo;
            super.notifyCalled();
        }

        public DownloadInfo getDownloadInfo() {
            return mDownloadInfo;
        }
    }

    private final EnqueueHttpGetDownloadCallbackHelper mEnqueueHttpGetDownloadCallbackHelper =
            new EnqueueHttpGetDownloadCallbackHelper();
    private String mLastDownloadFilePath;
    private final CallbackHelper mHttpDownloadFinished = new CallbackHelper();
    private DownloadManagerService mSavedDownloadManagerService;

    protected String getLastDownloadFile() {
        return new File(mLastDownloadFilePath).getName();
    }

    // The Android DownloadManager sometimes appends a number to a file name when it downloads it
    // ex: google.png becomes google-23.png
    // This happens even when there is no other prior download with that name, it could be a bug.
    // TODO(jcivelli): investigate if we can isolate that behavior and file a bug to Android.
    protected boolean isSameDownloadFile(String originalName, String downloadName) {
        String fileName = originalName;
        String extension = "";
        int dotIndex = originalName.lastIndexOf('.');
        if (dotIndex != -1 && dotIndex < originalName.length()) {
            fileName = originalName.substring(0, dotIndex);
            extension = originalName.substring(dotIndex);  // We include the '.'
        }
        return downloadName.startsWith(fileName) && downloadName.endsWith(extension);
    }

    public int getChromeDownloadCallCount() {
        return mHttpDownloadFinished.getCallCount();
    }

    public boolean waitForChromeDownloadToFinish(int callCount) throws InterruptedException {
        boolean eventReceived = true;
        try {
            mHttpDownloadFinished.waitForCallback(callCount, 1, 5, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            eventReceived = false;
        }
        return eventReceived;
    }

    private class TestDownloadManagerService extends DownloadManagerService {
        public TestDownloadManagerService(Context context, DownloadNotifier downloadNotifier,
                Handler handler, long updateDelayInMillis) {
            super(context, downloadNotifier, handler, updateDelayInMillis);
        }

        @Override
        public void broadcastDownloadSuccessful(DownloadInfo downloadInfo) {
            super.broadcastDownloadSuccessful(downloadInfo);
            mLastDownloadFilePath = downloadInfo.getFilePath();
            mHttpDownloadFinished.notifyCalled();
        }

        @Override
        public void enqueueDownloadManagerRequest(
                final DownloadItem item, boolean notifyCompleted) {
            // Intentionally do not call super, since DownloadManager does not work in test
            // environment.
            mEnqueueHttpGetDownloadCallbackHelper.notifyCalled(
                    item.getDownloadInfo(), notifyCompleted);
        }
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        cleanUpAllDownloads();

        try {
            ApplicationUtils.waitForLibraryDependencies(getInstrumentation());
        } catch (InterruptedException e) {
            fail("Library dependencies were never initialized.");
        }
        final Context context = getInstrumentation().getTargetContext().getApplicationContext();

        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
            @Override
            public void run() {
                mSavedDownloadManagerService = DownloadManagerService.setDownloadManagerService(
                        new TestDownloadManagerService(context, new SystemDownloadNotifier(context),
                                new Handler(), UPDATE_DELAY_MILLIS));
                DownloadController.setDownloadNotificationService(
                        DownloadManagerService.getDownloadManagerService(context));
            }
        });
    }

    @Override
    protected void tearDown() throws Exception {
        cleanUpAllDownloads();
        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
            @Override
            public void run() {
                DownloadManagerService.setDownloadManagerService(mSavedDownloadManagerService);
                DownloadController.setDownloadNotificationService(mSavedDownloadManagerService);
            }
        });
        super.tearDown();
    }
}