• 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.android.documentsui.services.FileOperationService.OPERATION_COPY;
20 import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE;
21 import static com.android.documentsui.services.FileOperations.createBaseIntent;
22 import static com.android.documentsui.services.FileOperations.createJobId;
23 import static com.google.android.collect.Lists.newArrayList;
24 import static org.junit.Assert.fail;
25 
26 import android.content.Context;
27 import android.content.Intent;
28 import android.net.Uri;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.support.test.InstrumentationRegistry;
32 import android.support.test.filters.MediumTest;
33 import android.test.ServiceTestCase;
34 
35 import com.android.documentsui.R;
36 import com.android.documentsui.base.DocumentInfo;
37 import com.android.documentsui.base.DocumentStack;
38 import com.android.documentsui.base.Features;
39 import com.android.documentsui.clipping.UrisSupplier;
40 import com.android.documentsui.services.FileOperationService.OpType;
41 import com.android.documentsui.testing.DocsProviders;
42 import com.android.documentsui.testing.TestFeatures;
43 import com.android.documentsui.testing.TestHandler;
44 import com.android.documentsui.testing.TestScheduledExecutorService;
45 
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 @MediumTest
50 public class FileOperationServiceTest extends ServiceTestCase<FileOperationService> {
51 
52     private static final Uri SRC_PARENT =
53             Uri.parse("content://com.android.documentsui.testing/parent");
54     private static final DocumentInfo ALPHA_DOC = createDoc("alpha");
55     private static final DocumentInfo BETA_DOC = createDoc("alpha");
56     private static final DocumentInfo GAMMA_DOC = createDoc("gamma");
57     private static final DocumentInfo DELTA_DOC = createDoc("delta");
58 
59     private final List<TestJob> mCopyJobs = new ArrayList<>();
60     private final List<TestJob> mDeleteJobs = new ArrayList<>();
61 
62     private FileOperationService mService;
63     private TestScheduledExecutorService mExecutor;
64     private TestScheduledExecutorService mDeletionExecutor;
65     private TestHandler mHandler;
66     private TestForegroundManager mForegroundManager;
67     private TestNotificationManager mTestNotificationManager;
68 
FileOperationServiceTest()69     public FileOperationServiceTest() {
70         super(FileOperationService.class);
71     }
72 
73     @Override
setUp()74     protected void setUp() throws Exception {
75         super.setUp();
76         setupService();  // must be called first for our test setup to work correctly.
77 
78         mExecutor = new TestScheduledExecutorService();
79         mDeletionExecutor = new TestScheduledExecutorService();
80         mHandler = new TestHandler();
81         mForegroundManager = new TestForegroundManager();
82         mTestNotificationManager = new TestNotificationManager(mForegroundManager);
83         TestFeatures features = new TestFeatures();
84         features.notificationChannel = InstrumentationRegistry.getTargetContext()
85                 .getResources().getBoolean(R.bool.feature_notification_channel);
86 
87         mCopyJobs.clear();
88         mDeleteJobs.clear();
89 
90         // Install test doubles.
91         mService = getService();
92 
93         assertNull(mService.executor);
94         mService.executor = mExecutor;
95 
96         assertNull(mService.deletionExecutor);
97         mService.deletionExecutor = mDeletionExecutor;
98 
99         assertNull(mService.handler);
100         mService.handler = mHandler;
101 
102         assertNull(mService.foregroundManager);
103         mService.foregroundManager = mForegroundManager;
104 
105         assertNull(mService.notificationManager);
106         mService.notificationManager = mTestNotificationManager.createNotificationManager();
107 
108         assertNull(mService.features);
109         mService.features = features;
110     }
111 
112     @Override
tearDown()113     protected void tearDown() {
114         // Release all possibly held wake lock here
115         mExecutor.runAll();
116         mDeletionExecutor.runAll();
117 
118         // There are lots of progress notifications generated in this test case.
119         // Dismiss all of them here.
120         mHandler.dispatchAllMessages();
121     }
122 
testRunsCopyJobs()123     public void testRunsCopyJobs() throws Exception {
124         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
125         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
126 
127         mExecutor.runAll();
128         assertAllCopyJobsStarted();
129     }
130 
testRunsCopyJobs_AfterExceptionInJobCreation()131     public void testRunsCopyJobs_AfterExceptionInJobCreation() throws Exception {
132         try {
133             startService(createCopyIntent(new ArrayList<>(), BETA_DOC));
134             fail("Should have throw exception.");
135         } catch(IllegalArgumentException expected) {
136             // We're sending a naughty empty list that should result in an IllegalArgumentException.
137         }
138         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
139 
140         assertJobsCreated(1);
141 
142         mExecutor.runAll();
143         assertAllCopyJobsStarted();
144     }
145 
testRunsCopyJobs_AfterFailure()146     public void testRunsCopyJobs_AfterFailure() throws Exception {
147         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
148         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
149 
150         mCopyJobs.get(0).fail(ALPHA_DOC);
151 
152         mExecutor.runAll();
153         assertAllCopyJobsStarted();
154     }
155 
testRunsCopyJobs_notRunsDeleteJobs()156     public void testRunsCopyJobs_notRunsDeleteJobs() throws Exception {
157         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
158         startService(createDeleteIntent(newArrayList(GAMMA_DOC)));
159 
160         mExecutor.runAll();
161         assertNoDeleteJobsStarted();
162     }
163 
testRunsDeleteJobs()164     public void testRunsDeleteJobs() throws Exception {
165         startService(createDeleteIntent(newArrayList(ALPHA_DOC)));
166 
167         mDeletionExecutor.runAll();
168         assertAllDeleteJobsStarted();
169     }
170 
testRunsDeleteJobs_NotRunsCopyJobs()171     public void testRunsDeleteJobs_NotRunsCopyJobs() throws Exception {
172         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
173         startService(createDeleteIntent(newArrayList(GAMMA_DOC)));
174 
175         mDeletionExecutor.runAll();
176         assertNoCopyJobsStarted();
177     }
178 
testUpdatesNotification()179     public void testUpdatesNotification() throws Exception {
180         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
181         mExecutor.runAll();
182 
183         // Assert monitoring continues until job is done
184         assertTrue(mHandler.hasScheduledMessage());
185         // Two notifications -- one for setup; one for progress
186         assertEquals(2, mCopyJobs.get(0).getNumOfNotifications());
187     }
188 
testStopsUpdatingNotificationAfterFinished()189     public void testStopsUpdatingNotificationAfterFinished() throws Exception {
190         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
191         mExecutor.runAll();
192 
193         mHandler.dispatchNextMessage();
194         // Assert monitoring stops once job is completed.
195         assertFalse(mHandler.hasScheduledMessage());
196 
197         // Assert no more notification is generated after finish.
198         assertEquals(2, mCopyJobs.get(0).getNumOfNotifications());
199 
200     }
201 
testHoldsWakeLockWhileWorking()202     public void testHoldsWakeLockWhileWorking() throws Exception {
203         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
204 
205         assertTrue(mService.holdsWakeLock());
206     }
207 
testReleasesWakeLock_AfterSuccess()208     public void testReleasesWakeLock_AfterSuccess() throws Exception {
209         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
210 
211         assertTrue(mService.holdsWakeLock());
212         mExecutor.runAll();
213         assertFalse(mService.holdsWakeLock());
214     }
215 
testReleasesWakeLock_AfterFailure()216     public void testReleasesWakeLock_AfterFailure() throws Exception {
217         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
218 
219         assertTrue(mService.holdsWakeLock());
220         mExecutor.runAll();
221         assertFalse(mService.holdsWakeLock());
222     }
223 
testShutdownStopsExecutor_AfterSuccess()224     public void testShutdownStopsExecutor_AfterSuccess() throws Exception {
225         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
226 
227         mExecutor.assertAlive();
228         mDeletionExecutor.assertAlive();
229 
230         mExecutor.runAll();
231         shutdownService();
232 
233         assertExecutorsShutdown();
234     }
235 
testShutdownStopsExecutor_AfterMixedFailures()236     public void testShutdownStopsExecutor_AfterMixedFailures() throws Exception {
237         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
238         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
239 
240         mCopyJobs.get(0).fail(ALPHA_DOC);
241 
242         mExecutor.runAll();
243         shutdownService();
244 
245         assertExecutorsShutdown();
246     }
247 
testShutdownStopsExecutor_AfterTotalFailure()248     public void testShutdownStopsExecutor_AfterTotalFailure() throws Exception {
249         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
250         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
251 
252         mCopyJobs.get(0).fail(ALPHA_DOC);
253         mCopyJobs.get(1).fail(GAMMA_DOC);
254 
255         mExecutor.runAll();
256         shutdownService();
257 
258         assertExecutorsShutdown();
259     }
260 
testRunsInForeground_MultipleJobs()261     public void testRunsInForeground_MultipleJobs() throws Exception {
262         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
263         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
264 
265         mExecutor.run(0);
266         mForegroundManager.assertInForeground();
267 
268         mHandler.dispatchAllMessages();
269         mForegroundManager.assertInForeground();
270     }
271 
testFinishesInBackground_MultipleJobs()272     public void testFinishesInBackground_MultipleJobs() throws Exception {
273         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
274         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
275 
276         mExecutor.run(0);
277         mForegroundManager.assertInForeground();
278 
279         mHandler.dispatchAllMessages();
280         mForegroundManager.assertInForeground();
281 
282         mExecutor.run(0);
283         mHandler.dispatchAllMessages();
284         mForegroundManager.assertInBackground();
285     }
286 
testAllNotificationsDismissedAfterShutdown()287     public void testAllNotificationsDismissedAfterShutdown() throws Exception {
288         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
289         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
290 
291         mExecutor.runAll();
292 
293         mHandler.dispatchAllMessages();
294         mTestNotificationManager.assertNumberOfNotifications(0);
295     }
296 
createCopyIntent(ArrayList<DocumentInfo> files, DocumentInfo dest)297     private Intent createCopyIntent(ArrayList<DocumentInfo> files, DocumentInfo dest)
298             throws Exception {
299         DocumentStack stack = new DocumentStack();
300         stack.push(dest);
301 
302         List<Uri> uris = new ArrayList<>(files.size());
303         for (DocumentInfo file: files) {
304             uris.add(file.derivedUri);
305         }
306 
307         UrisSupplier urisSupplier = DocsProviders.createDocsProvider(uris);
308         TestFileOperation operation = new TestFileOperation(OPERATION_COPY, urisSupplier, stack);
309 
310         return createBaseIntent(getContext(), createJobId(), operation);
311     }
312 
createDeleteIntent(ArrayList<DocumentInfo> files)313     private Intent createDeleteIntent(ArrayList<DocumentInfo> files) {
314         DocumentStack stack = new DocumentStack();
315 
316         List<Uri> uris = new ArrayList<>(files.size());
317         for (DocumentInfo file: files) {
318             uris.add(file.derivedUri);
319         }
320 
321         UrisSupplier urisSupplier = DocsProviders.createDocsProvider(uris);
322         TestFileOperation operation = new TestFileOperation(OPERATION_DELETE, urisSupplier, stack);
323 
324         return createBaseIntent(getContext(), createJobId(), operation);
325     }
326 
createDoc(String name)327     private static DocumentInfo createDoc(String name) {
328         // Doesn't need to be valid content Uri, just some urly looking thing.
329         Uri uri = new Uri.Builder()
330                 .scheme("content")
331                 .authority("com.android.documentsui.testing")
332                 .path(name)
333                 .build();
334 
335         return createDoc(uri);
336     }
337 
assertAllCopyJobsStarted()338     void assertAllCopyJobsStarted() {
339         for (TestJob job : mCopyJobs) {
340             job.assertStarted();
341         }
342     }
343 
assertAllDeleteJobsStarted()344     void assertAllDeleteJobsStarted() {
345         for (TestJob job : mDeleteJobs) {
346             job.assertStarted();
347         }
348     }
349 
assertNoCopyJobsStarted()350     void assertNoCopyJobsStarted() {
351         for (TestJob job : mCopyJobs) {
352             job.assertNotStarted();
353         }
354     }
355 
assertNoDeleteJobsStarted()356     void assertNoDeleteJobsStarted() {
357         for (TestJob job : mDeleteJobs) {
358             job.assertNotStarted();
359         }
360     }
361 
assertJobsCreated(int expected)362     void assertJobsCreated(int expected) {
363         assertEquals(expected, mCopyJobs.size() + mDeleteJobs.size());
364     }
createDoc(Uri destination)365     private static DocumentInfo createDoc(Uri destination) {
366         DocumentInfo destDoc = new DocumentInfo();
367         destDoc.derivedUri = destination;
368         return destDoc;
369     }
370 
assertExecutorsShutdown()371     private void assertExecutorsShutdown() {
372         mExecutor.assertShutdown();
373         mDeletionExecutor.assertShutdown();
374     }
375 
376     private final class TestFileOperation extends FileOperation {
377 
378         private final Runnable mJobRunnable = () -> {
379             // The following statement is executed concurrently to Job.start() in real situation.
380             // Call it in TestJob.start() to mimic this behavior.
381             mHandler.dispatchNextMessage();
382         };
383         private final @OpType int mOpType;
384         private final UrisSupplier mSrcs;
385         private final DocumentStack mDestination;
386 
TestFileOperation( @pType int opType, UrisSupplier srcs, DocumentStack destination)387         private TestFileOperation(
388                 @OpType int opType, UrisSupplier srcs, DocumentStack destination) {
389             super(opType, srcs, destination);
390             mOpType = opType;
391             mSrcs = srcs;
392             mDestination = destination;
393         }
394 
395         @Override
createJob(Context service, Job.Listener listener, String id, Features features)396         public Job createJob(Context service, Job.Listener listener, String id, Features features) {
397             TestJob job = new TestJob(
398                     service, listener, id, mOpType, mDestination, mSrcs, mJobRunnable, features);
399 
400             if (mOpType == OPERATION_COPY) {
401                 mCopyJobs.add(job);
402             }
403 
404             if (mOpType == OPERATION_DELETE) {
405                 mDeleteJobs.add(job);
406             }
407 
408             return job;
409         }
410 
411         /**
412          * CREATOR is required for Parcelables, but we never pass this class via parcel.
413          */
414         public Parcelable.Creator<TestFileOperation> CREATOR =
415                 new Parcelable.Creator<TestFileOperation>() {
416 
417             @Override
418             public TestFileOperation createFromParcel(Parcel source) {
419                 throw new UnsupportedOperationException("Can't create from a parcel.");
420             }
421 
422             @Override
423             public TestFileOperation[] newArray(int size) {
424                 throw new UnsupportedOperationException("Can't create a new array.");
425             }
426         };
427     }
428 }
429