• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.documentsui.services;
18 
19 import static com.google.common.collect.Lists.newArrayList;
20 
21 import static org.junit.Assert.assertNotEquals;
22 
23 import android.app.Notification;
24 import android.net.Uri;
25 import android.provider.DocumentsContract;
26 import android.text.format.DateUtils;
27 
28 import androidx.test.filters.MediumTest;
29 
30 import com.android.documentsui.R;
31 import com.android.documentsui.base.DocumentInfo;
32 import com.android.documentsui.services.FileOperationService.OpType;
33 
34 import java.text.NumberFormat;
35 import java.util.List;
36 import java.util.stream.IntStream;
37 
38 @MediumTest
39 public abstract class AbstractCopyJobTest<T extends CopyJob> extends AbstractJobTest<T> {
40 
41     private final @OpType int mOpType;
42 
AbstractCopyJobTest(@pType int opType)43     AbstractCopyJobTest(@OpType int opType) {
44         mOpType = opType;
45     }
46 
getVerb()47     private String getVerb() {
48         switch(mOpType) {
49             case FileOperationService.OPERATION_COPY:
50             case FileOperationService.OPERATION_EXTRACT:
51                 return "Copying";
52             case FileOperationService.OPERATION_COMPRESS:
53                 return "Zipping";
54             case FileOperationService.OPERATION_MOVE:
55                 return "Moving";
56             case FileOperationService.OPERATION_DELETE:
57                 // DeleteJob does not inherit from CopyJob
58             case FileOperationService.OPERATION_UNKNOWN:
59             default:
60                 return "";
61         }
62     }
63 
runCopyFilesTest()64     public void runCopyFilesTest() throws Exception {
65         Uri testFile1 = mDocs.createDocument(mSrcRoot, "text/plain", "test1.txt");
66         mDocs.writeDocument(testFile1, HAM_BYTES);
67 
68         Uri testFile2 = mDocs.createDocument(mSrcRoot, "text/plain", "test2.txt");
69         mDocs.writeDocument(testFile2, FRUITY_BYTES);
70 
71         CopyJob job = createJob(newArrayList(testFile1, testFile2));
72         JobProgress progress = job.getJobProgress();
73         assertEquals(Job.STATE_CREATED, progress.state);
74 
75         job.run();
76         mJobListener.waitForFinished();
77 
78         mDocs.assertChildCount(mDestRoot, 2);
79         mDocs.assertHasFile(mDestRoot, "test1.txt");
80         mDocs.assertHasFile(mDestRoot, "test2.txt");
81         mDocs.assertFileContents(mDestRoot.documentId, "test1.txt", HAM_BYTES);
82         mDocs.assertFileContents(mDestRoot.documentId, "test2.txt", FRUITY_BYTES);
83 
84         progress = job.getJobProgress();
85         assertEquals(Job.STATE_COMPLETED, progress.state);
86         assertFalse(progress.hasFailures);
87         assertEquals(getVerb() + " 2 files to " + mDestRoot.title, progress.msg);
88         assertEquals(HAM_BYTES.length + FRUITY_BYTES.length, progress.currentBytes);
89         assertEquals(HAM_BYTES.length + FRUITY_BYTES.length, progress.requiredBytes);
90     }
91 
runCopyVirtualTypedFileTest()92     public void runCopyVirtualTypedFileTest() throws Exception {
93         Uri testFile = mDocs.createVirtualFile(
94                 mSrcRoot, "/virtual.sth", "virtual/mime-type",
95                 FRUITY_BYTES, "application/pdf", "text/html");
96 
97         CopyJob job = createJob(newArrayList(testFile));
98         job.run();
99         waitForJobFinished();
100 
101         mDocs.assertChildCount(mDestRoot, 1);
102         mDocs.assertHasFile(mDestRoot, "virtual.sth.pdf");  // copy should convert file to PDF.
103         mDocs.assertFileContents(mDestRoot.documentId, "virtual.sth.pdf", FRUITY_BYTES);
104 
105         JobProgress progress = job.getJobProgress();
106         assertEquals(Job.STATE_COMPLETED, progress.state);
107         assertFalse(progress.hasFailures);
108         assertEquals("Copying virtual.sth to " + mDestRoot.title, progress.msg);
109         assertEquals(FRUITY_BYTES.length, progress.currentBytes);
110         assertEquals(FRUITY_BYTES.length, progress.requiredBytes);
111     }
112 
runCopyVirtualNonTypedFileTest()113     public void runCopyVirtualNonTypedFileTest() throws Exception {
114         Uri testFile = mDocs.createVirtualFile(
115                 mSrcRoot, "/virtual.sth", "virtual/mime-type",
116                 FRUITY_BYTES);
117 
118         CopyJob job = createJob(newArrayList(testFile));
119         job.run();
120         waitForJobFinished();
121 
122         mJobListener.assertFailed();
123         mJobListener.assertFilesFailed(newArrayList("virtual.sth"));
124 
125         mDocs.assertChildCount(mDestRoot, 0);
126 
127         JobProgress progress = job.getJobProgress();
128         assertEquals(Job.STATE_COMPLETED, progress.state);
129         assertTrue(progress.hasFailures);
130         assertEquals(getVerb() + " virtual.sth to " + mDestRoot.title, progress.msg);
131         assertEquals(0, progress.currentBytes);
132         assertEquals(FRUITY_BYTES.length, progress.requiredBytes);
133     }
134 
runCopyEmptyDirTest()135     public void runCopyEmptyDirTest() throws Exception {
136         Uri testDir = mDocs.createFolder(mSrcRoot, "emptyDir");
137 
138         CopyJob job = createJob(newArrayList(testDir));
139         job.run();
140         waitForJobFinished();
141 
142         Notification progressNotification = job.getProgressNotification();
143         String copyPercentage = progressNotification.extras.getString(Notification.EXTRA_SUB_TEXT);
144 
145         // the percentage representation should not be NaN.
146         assertNotEquals(copyPercentage.equals(NumberFormat.getPercentInstance().format(Double.NaN)),
147                 "Percentage representation should not be NaN.");
148 
149         mDocs.assertChildCount(mDestRoot, 1);
150         mDocs.assertHasDirectory(mDestRoot, "emptyDir");
151 
152         JobProgress progress = job.getJobProgress();
153         assertEquals(Job.STATE_COMPLETED, progress.state);
154         assertFalse(progress.hasFailures);
155         assertEquals(getVerb() + " emptyDir to " + mDestRoot.title, progress.msg);
156         assertEquals(-1, progress.currentBytes);
157         assertEquals(-1, progress.requiredBytes);
158     }
159 
runCopyDirRecursivelyTest()160     public void runCopyDirRecursivelyTest() throws Exception {
161 
162         Uri testDir1 = mDocs.createFolder(mSrcRoot, "dir1");
163         mDocs.createDocument(testDir1, "text/plain", "test1.txt");
164 
165         Uri testDir2 = mDocs.createFolder(testDir1, "dir2");
166         mDocs.createDocument(testDir2, "text/plain", "test2.txt");
167 
168         createJob(newArrayList(testDir1)).run();
169         waitForJobFinished();
170 
171         DocumentInfo dir1Copy = mDocs.findDocument(mDestRoot.documentId, "dir1");
172 
173         mDocs.assertChildCount(dir1Copy.derivedUri, 2);
174         mDocs.assertHasDirectory(dir1Copy.derivedUri, "dir2");
175         mDocs.assertHasFile(dir1Copy.derivedUri, "test1.txt");
176 
177         DocumentInfo dir2Copy = mDocs.findDocument(dir1Copy.documentId, "dir2");
178         mDocs.assertChildCount(dir2Copy.derivedUri, 1);
179         mDocs.assertHasFile(dir2Copy.derivedUri, "test2.txt");
180     }
181 
runNoCopyDirToSelfTest()182     public void runNoCopyDirToSelfTest() throws Exception {
183         Uri testDir = mDocs.createFolder(mSrcRoot, "someDir");
184 
185         createJob(mOpType,
186                 newArrayList(testDir),
187                 DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId),
188                 testDir).run();
189 
190         waitForJobFinished();
191         mJobListener.assertFailed();
192         mJobListener.assertFilesFailed(newArrayList("someDir"));
193 
194         mDocs.assertChildCount(mDestRoot, 0);
195     }
196 
runNoCopyDirToDescendentTest()197     public void runNoCopyDirToDescendentTest() throws Exception {
198         Uri testDir = mDocs.createFolder(mSrcRoot, "someDir");
199         Uri destDir = mDocs.createFolder(testDir, "theDescendent");
200 
201         createJob(mOpType,
202                 newArrayList(testDir),
203                 DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId),
204                 destDir).run();
205 
206         waitForJobFinished();
207         mJobListener.assertFailed();
208         mJobListener.assertFilesFailed(newArrayList("someDir"));
209 
210         mDocs.assertChildCount(mDestRoot, 0);
211     }
212 
runCopyFileWithReadErrorsTest()213     public void runCopyFileWithReadErrorsTest() throws Exception {
214         Uri testFile = mDocs.createDocument(mSrcRoot, "text/plain", "test1.txt");
215         mDocs.writeDocument(testFile, HAM_BYTES);
216 
217         String testId = DocumentsContract.getDocumentId(testFile);
218         mDocs.simulateReadErrorsForFile(testId, null);
219 
220         createJob(newArrayList(testFile)).run();
221 
222         waitForJobFinished();
223         mJobListener.assertFailed();
224         mJobListener.assertFilesFailed(newArrayList("test1.txt"));
225 
226         mDocs.assertChildCount(mDestRoot, 0);
227     }
228 
runCopyProgressForFileCountTest()229     public void runCopyProgressForFileCountTest() throws Exception {
230         // Init FileCountProgressTracker with 10 docs required to copy.
231         TestCopyJobProcessTracker<CopyJob.FileCountProgressTracker> tracker =
232                 new TestCopyJobProcessTracker(CopyJob.FileCountProgressTracker.class, 10,
233                         createJob(newArrayList(mDocs.createFolder(mSrcRoot, "tempDir"))),
234                         (completed) -> NumberFormat.getPercentInstance().format(completed),
235                         (time) -> mContext.getString(R.string.copy_remaining,
236                                 DateUtils.formatDuration((Long) time)));
237 
238         // Assert init progress is 0 & default remaining time is -1.
239         tracker.getProcessTracker().start();
240         tracker.assertProgressTrackStarted();
241         tracker.assertStartedProgressEquals(0);
242         tracker.assertStartedRemainingTimeEquals(-1);
243 
244         // Progress 20%: 2 docs processed after 1 sec, no remaining time since first sample.
245         IntStream.range(0, 2).forEach(__ -> tracker.getProcessTracker().onDocumentCompleted());
246         tracker.updateProgressAndRemainingTime(1000);
247         tracker.assertProgressEquals(0.2);
248         tracker.assertNoRemainingTime();
249 
250         // Progress 40%: 4 docs processed after 2 secs, expect remaining time is 3 secs.
251         IntStream.range(2, 4).forEach(__ -> tracker.getProcessTracker().onDocumentCompleted());
252         tracker.updateProgressAndRemainingTime(2000);
253         tracker.assertProgressEquals(0.4);
254         tracker.assertReminingTimeEquals(3000L);
255 
256         // progress 100%: 10 doc processed after 5 secs, expect no remaining time shown.
257         IntStream.range(4, 10).forEach(__ -> tracker.getProcessTracker().onDocumentCompleted());
258         tracker.updateProgressAndRemainingTime(5000);
259         tracker.assertProgressEquals(1.0);
260         tracker.assertNoRemainingTime();
261     }
262 
runCopyProgressForByteCountTest()263     public void runCopyProgressForByteCountTest() throws Exception {
264         // Init ByteCountProgressTracker with 100 KBytes required to copy.
265         TestCopyJobProcessTracker<CopyJob.ByteCountProgressTracker> tracker =
266                 new TestCopyJobProcessTracker(CopyJob.ByteCountProgressTracker.class, 100000,
267                         createJob(newArrayList(mDocs.createFolder(mSrcRoot, "tempDir"))),
268                         (completed) -> NumberFormat.getPercentInstance().format(completed),
269                         (time) -> mContext.getString(R.string.copy_remaining,
270                                 DateUtils.formatDuration((Long) time)));
271 
272         // Assert init progress is 0 & default remaining time is -1.
273         tracker.getProcessTracker().start();
274         tracker.assertProgressTrackStarted();
275         tracker.assertStartedProgressEquals(0);
276         tracker.assertStartedRemainingTimeEquals(-1);
277 
278         // Progress 25%: 25 KBytes processed after 1 sec, no remaining time since first sample.
279         tracker.getProcessTracker().onBytesCopied(25000);
280         tracker.updateProgressAndRemainingTime(1000);
281         tracker.assertProgressEquals(0.25);
282         tracker.assertNoRemainingTime();
283 
284         // Progress 50%: 50 KBytes processed after 2 secs, expect remaining time is 2 secs.
285         tracker.getProcessTracker().onBytesCopied(25000);
286         tracker.updateProgressAndRemainingTime(2000);
287         tracker.assertProgressEquals(0.5);
288         tracker.assertReminingTimeEquals(2000L);
289 
290         // Progress 100%: 100 KBytes processed after 4 secs, expect no remaining time shown.
291         tracker.getProcessTracker().onBytesCopied(50000);
292         tracker.updateProgressAndRemainingTime(4000);
293         tracker.assertProgressEquals(1.0);
294         tracker.assertNoRemainingTime();
295     }
296 
waitForJobFinished()297     void waitForJobFinished() throws Exception {
298         mJobListener.waitForFinished();
299         mDocs.waitForWrite();
300     }
301 
302     /**
303      * Creates a job with a stack consisting to the default source and destination.
304      * TODO: Clean up, as mDestRoot.documentInfo may not really be the parent of
305      * srcs.
306      */
createJob(List<Uri> srcs)307     final T createJob(List<Uri> srcs) throws Exception {
308         Uri srcParent = DocumentsContract.buildDocumentUri(AUTHORITY, mSrcRoot.documentId);
309         return createJob(srcs, srcParent);
310     }
311 
createJob(List<Uri> srcs, Uri srcParent)312     final T createJob(List<Uri> srcs, Uri srcParent) throws Exception {
313         Uri destination = DocumentsContract.buildDocumentUri(AUTHORITY, mDestRoot.documentId);
314         return createJob(mOpType, srcs, srcParent, destination);
315     }
316 }
317