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