• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static junit.framework.Assert.assertTrue;
22 import static junit.framework.Assert.fail;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.mockito.Mockito.when;
26 
27 import android.content.Intent;
28 import android.net.Uri;
29 import android.os.Parcelable;
30 import android.provider.DocumentsContract;
31 import android.provider.DocumentsContract.Path;
32 
33 import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails;
34 import androidx.test.filters.MediumTest;
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import com.android.documentsui.base.DocumentStack;
38 import com.android.documentsui.base.EventListener;
39 import com.android.documentsui.base.RootInfo;
40 import com.android.documentsui.base.Shared;
41 import com.android.documentsui.base.State;
42 import com.android.documentsui.files.LauncherActivity;
43 import com.android.documentsui.sorting.SortDimension;
44 import com.android.documentsui.sorting.SortModel;
45 import com.android.documentsui.testing.DocumentStackAsserts;
46 import com.android.documentsui.testing.Roots;
47 import com.android.documentsui.testing.TestEnv;
48 import com.android.documentsui.testing.TestEventHandler;
49 import com.android.documentsui.testing.TestProvidersAccess;
50 import com.android.documentsui.testing.UserManagers;
51 
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 
56 import java.util.Arrays;
57 import java.util.concurrent.CountDownLatch;
58 import java.util.concurrent.TimeUnit;
59 
60 /**
61  * A unit test *for* AbstractActionHandler, not an abstract test baseclass.
62  */
63 @RunWith(AndroidJUnit4.class)
64 @MediumTest
65 public class AbstractActionHandlerTest {
66 
67     private TestActivity mActivity;
68     private TestEnv mEnv;
69     private AbstractActionHandler<TestActivity> mHandler;
70 
71     @Before
setUp()72     public void setUp() {
73         mEnv = TestEnv.create();
74         mActivity = TestActivity.create(mEnv);
75         mActivity.userManager = UserManagers.create();
76         mHandler = new AbstractActionHandler<TestActivity>(
77                 mActivity,
78                 mEnv.state,
79                 mEnv.providers,
80                 mEnv.docs,
81                 mEnv.searchViewManager,
82                 mEnv::lookupExecutor,
83                 mEnv.injector) {
84 
85             @Override
86             public void openRoot(RootInfo root) {
87                 throw new UnsupportedOperationException();
88             }
89 
90             @Override
91             public boolean openItem(
92                     ItemDetails<String> doc, @ViewType int type, @ViewType int fallback) {
93                 throw new UnsupportedOperationException();
94             }
95 
96             @Override
97             public void initLocation(Intent intent) {
98                 throw new UnsupportedOperationException();
99             }
100 
101             @Override
102             protected void launchToDefaultLocation() {
103                 throw new UnsupportedOperationException();
104             }
105         };
106     }
107 
108     @Test
testOpenNewWindow()109     public void testOpenNewWindow() {
110         DocumentStack path = new DocumentStack(Roots.create("123"));
111         mHandler.openInNewWindow(path);
112 
113         Intent expected = LauncherActivity.createLaunchIntent(mActivity);
114         expected.putExtra(Shared.EXTRA_STACK, (Parcelable) path);
115         Intent actual = mActivity.startActivity.getLastValue();
116         assertEquals(expected.toString(), actual.toString());
117     }
118 
119     @Test
testOpensContainerDocuments_OpenFolderInSearch_JumpsToNewLocation()120     public void testOpensContainerDocuments_OpenFolderInSearch_JumpsToNewLocation()
121             throws Exception {
122         if (!mEnv.features.isLaunchToDocumentEnabled()) {
123             return;
124         }
125 
126         mEnv.populateStack();
127 
128         mEnv.searchViewManager.isSearching = true;
129         mEnv.docs.nextIsDocumentsUri = true;
130         mEnv.docs.nextPath = new Path(
131                 TestProvidersAccess.HOME.rootId,
132                 Arrays.asList(TestEnv.FOLDER_1.documentId, TestEnv.FOLDER_2.documentId));
133         mEnv.docs.nextDocuments = Arrays.asList(TestEnv.FOLDER_1, TestEnv.FOLDER_2);
134 
135         mHandler.openContainerDocument(TestEnv.FOLDER_2);
136 
137         mEnv.beforeAsserts();
138 
139         assertEquals(mEnv.docs.nextPath.getPath().size(), mEnv.state.stack.size());
140         assertEquals(TestEnv.FOLDER_2, mEnv.state.stack.pop());
141         assertEquals(TestEnv.FOLDER_1, mEnv.state.stack.pop());
142     }
143 
144 
145     @Test
testOpensContainerDocuments_ClickFolderInSearch_PushToRootDoc_NoFindPathSupport()146     public void testOpensContainerDocuments_ClickFolderInSearch_PushToRootDoc_NoFindPathSupport()
147             throws Exception {
148         mEnv.populateStack();
149 
150         mEnv.searchViewManager.isSearching = true;
151         mEnv.docs.nextIsDocumentsUri = true;
152         mEnv.docs.nextDocuments = Arrays.asList(TestEnv.FOLDER_1, TestEnv.FOLDER_2);
153 
154         mHandler.openContainerDocument(TestEnv.FOLDER_2);
155 
156         mEnv.beforeAsserts();
157 
158         assertEquals(2, mEnv.state.stack.size());
159         assertEquals(TestEnv.FOLDER_2, mEnv.state.stack.pop());
160         assertEquals(TestEnv.FOLDER_0, mEnv.state.stack.pop());
161     }
162 
163     @Test
testOpensContainerDocuments_ClickArchiveInSearch_opensArchiveInArchiveProvider()164     public void testOpensContainerDocuments_ClickArchiveInSearch_opensArchiveInArchiveProvider()
165             throws Exception {
166         if (!mEnv.features.isLaunchToDocumentEnabled()) {
167             return;
168         }
169 
170         mEnv.populateStack();
171 
172         mEnv.searchViewManager.isSearching = true;
173         mEnv.docs.nextIsDocumentsUri = true;
174         mEnv.docs.nextPath = new Path(
175                 TestProvidersAccess.HOME.rootId,
176                 Arrays.asList(TestEnv.FOLDER_1.documentId, TestEnv.FOLDER_2.documentId,
177                         TestEnv.FILE_ARCHIVE.documentId));
178         mEnv.docs.nextDocuments = Arrays.asList(
179                 TestEnv.FOLDER_1, TestEnv.FOLDER_2, TestEnv.FILE_ARCHIVE);
180         mEnv.docs.nextDocument = TestEnv.FILE_IN_ARCHIVE;
181 
182         mHandler.openContainerDocument(TestEnv.FILE_ARCHIVE);
183 
184         mEnv.beforeAsserts();
185 
186         assertEquals(mEnv.docs.nextPath.getPath().size(), mEnv.state.stack.size());
187         assertEquals(TestEnv.FILE_IN_ARCHIVE, mEnv.state.stack.pop());
188         assertEquals(TestEnv.FOLDER_2, mEnv.state.stack.pop());
189         assertEquals(TestEnv.FOLDER_1, mEnv.state.stack.pop());
190     }
191 
192     @Test
testOpensDocument_ExceptionIfAlreadyInStack()193     public void testOpensDocument_ExceptionIfAlreadyInStack() throws Exception {
194         mEnv.populateStack();
195         try {
196             mEnv.state.stack.push(TestEnv.FOLDER_0);
197             fail("Should have thrown IllegalArgumentException.");
198         } catch (IllegalArgumentException expected) {
199         }
200     }
201 
202     @Test
testLaunchToDocuments()203     public void testLaunchToDocuments() throws Exception {
204         if (!mEnv.features.isLaunchToDocumentEnabled()) {
205             return;
206         }
207 
208         mEnv.docs.nextIsDocumentsUri = true;
209         mEnv.docs.nextPath = new Path(
210                 TestProvidersAccess.HOME.rootId,
211                 Arrays.asList(
212                         TestEnv.FOLDER_0.documentId,
213                         TestEnv.FOLDER_1.documentId,
214                         TestEnv.FILE_GIF.documentId));
215         mEnv.docs.nextDocuments =
216                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1, TestEnv.FILE_GIF);
217 
218         mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
219         assertTrue(mHandler.launchToDocument(TestEnv.FILE_GIF.derivedUri));
220 
221         mEnv.beforeAsserts();
222 
223         DocumentStackAsserts.assertEqualsTo(mEnv.state.stack, TestProvidersAccess.HOME,
224                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1));
225         mActivity.refreshCurrentRootAndDirectory.assertCalled();
226     }
227 
228     @Test
testLaunchToDocuments_convertsTreeUriToDocumentUri()229     public void testLaunchToDocuments_convertsTreeUriToDocumentUri() throws Exception {
230         if (!mEnv.features.isLaunchToDocumentEnabled()) {
231             return;
232         }
233 
234         mEnv.docs.nextIsDocumentsUri = true;
235         mEnv.docs.nextPath = new Path(
236                 TestProvidersAccess.HOME.rootId,
237                 Arrays.asList(
238                         TestEnv.FOLDER_0.documentId,
239                         TestEnv.FOLDER_1.documentId,
240                         TestEnv.FILE_GIF.documentId));
241         mEnv.docs.nextDocuments =
242                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1, TestEnv.FILE_GIF);
243 
244         final Uri treeBaseUri = DocumentsContract.buildTreeDocumentUri(
245                 TestProvidersAccess.HOME.authority, TestEnv.FOLDER_0.documentId);
246         final Uri treeDocUri = DocumentsContract.buildDocumentUriUsingTree(
247                 treeBaseUri, TestEnv.FILE_GIF.documentId);
248         assertTrue(mHandler.launchToDocument(treeDocUri));
249 
250         mEnv.beforeAsserts();
251 
252         DocumentStackAsserts.assertEqualsTo(mEnv.state.stack, TestProvidersAccess.HOME,
253                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1));
254         mEnv.docs.lastUri.assertLastArgument(TestEnv.FILE_GIF.derivedUri);
255         mActivity.refreshCurrentRootAndDirectory.assertCalled();
256     }
257 
258     @Test
testLoadChildrenDocuments()259     public void testLoadChildrenDocuments() throws Exception {
260         mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
261         mEnv.state.stack.push(TestEnv.FOLDER_0);
262 
263         mEnv.state.sortModel.sortByUser(
264                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
265 
266         mEnv.mockProviders.get(TestProvidersAccess.HOME.authority)
267                 .setNextChildDocumentsReturns(TestEnv.FILE_APK, TestEnv.FILE_GIF);
268 
269         mHandler.loadDocumentsForCurrentStack();
270         CountDownLatch latch = new CountDownLatch(1);
271         mEnv.model.addUpdateListener(event -> latch.countDown());
272         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
273 
274         latch.await(1, TimeUnit.SECONDS);
275         assertEquals(2, mEnv.model.getItemCount());
276         String[] modelIds = mEnv.model.getModelIds();
277         assertEquals(TestEnv.FILE_APK, mEnv.model.getDocument(modelIds[0]));
278         assertEquals(TestEnv.FILE_GIF, mEnv.model.getDocument(modelIds[1]));
279     }
280 
281     @Test
testCrossProfileDocuments_success()282     public void testCrossProfileDocuments_success() throws Exception {
283         mEnv.state.action = State.ACTION_GET_CONTENT;
284         mEnv.state.canShareAcrossProfile = true;
285         mEnv.state.stack.changeRoot(TestProvidersAccess.OtherUser.HOME);
286         mEnv.state.stack.push(TestEnv.OtherUser.FOLDER_0);
287 
288         mEnv.state.sortModel.sortByUser(
289                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
290 
291         // Currently mock provider does not have cross profile concept, this will always return
292         // the supplied docs without checking for the user. But this should not be a problem for
293         // this test case.
294         mEnv.mockProviders.get(TestProvidersAccess.OtherUser.HOME.authority)
295                 .setNextChildDocumentsReturns(TestEnv.OtherUser.FILE_PNG);
296 
297         mHandler.loadDocumentsForCurrentStack();
298         CountDownLatch latch = new CountDownLatch(1);
299         mEnv.model.addUpdateListener(event -> latch.countDown());
300         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
301 
302         latch.await(1, TimeUnit.SECONDS);
303         assertEquals(1, mEnv.model.getItemCount());
304         String[] modelIds = mEnv.model.getModelIds();
305         assertEquals(TestEnv.OtherUser.FILE_PNG, mEnv.model.getDocument(modelIds[0]));
306     }
307 
308     @Test
testLoadCrossProfileDoc_failsWithQuietModeException()309     public void testLoadCrossProfileDoc_failsWithQuietModeException() throws Exception {
310         mEnv.state.action = State.ACTION_GET_CONTENT;
311         mEnv.state.canShareAcrossProfile = true;
312         mEnv.state.stack.changeRoot(TestProvidersAccess.OtherUser.HOME);
313         mEnv.state.stack.push(TestEnv.OtherUser.FOLDER_0);
314         // Turn off the other user.
315         when(mActivity.userManager.isQuietModeEnabled(TestProvidersAccess.OtherUser.USER_HANDLE))
316                 .thenReturn(true);
317 
318         TestEventHandler<Model.Update> listener = new TestEventHandler<>();
319         mEnv.model.addUpdateListener(listener::accept);
320 
321         mHandler.loadDocumentsForCurrentStack();
322         CountDownLatch latch = new CountDownLatch(1);
323         mEnv.model.addUpdateListener(event -> latch.countDown());
324         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
325 
326         latch.await(1, TimeUnit.SECONDS);
327         assertThat(listener.getLastValue().getException())
328                 .isInstanceOf(CrossProfileQuietModeException.class);
329     }
330 
331     @Test
testLoadCrossProfileDoc_failsWithNoPermissionException()332     public void testLoadCrossProfileDoc_failsWithNoPermissionException() throws Exception {
333         mEnv.state.action = State.ACTION_GET_CONTENT;
334         mEnv.state.stack.changeRoot(TestProvidersAccess.OtherUser.HOME);
335         mEnv.state.stack.push(TestEnv.OtherUser.FOLDER_0);
336         // Disallow sharing across profile
337         mEnv.state.canShareAcrossProfile = false;
338 
339         TestEventHandler<Model.Update> listener = new TestEventHandler<>();
340         mEnv.model.addUpdateListener(listener::accept);
341 
342         mHandler.loadDocumentsForCurrentStack();
343         CountDownLatch latch = new CountDownLatch(1);
344         mEnv.model.addUpdateListener(event -> latch.countDown());
345         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
346 
347         latch.await(1, TimeUnit.SECONDS);
348         assertThat(listener.getLastValue().getException())
349                 .isInstanceOf(CrossProfileNoPermissionException.class);
350     }
351 
352     @Test
testLoadCrossProfileDoc_bothError_showNoPermissionException()353     public void testLoadCrossProfileDoc_bothError_showNoPermissionException() throws Exception {
354         mEnv.state.action = State.ACTION_GET_CONTENT;
355         mEnv.state.stack.changeRoot(TestProvidersAccess.OtherUser.HOME);
356         mEnv.state.stack.push(TestEnv.OtherUser.FOLDER_0);
357         // Disallow sharing
358         mEnv.state.canShareAcrossProfile = false;
359         // Turn off the other user.
360         when(mActivity.userManager.isQuietModeEnabled(TestProvidersAccess.OtherUser.USER_HANDLE))
361                 .thenReturn(true);
362 
363         TestEventHandler<Model.Update> listener = new TestEventHandler<>();
364         mEnv.model.addUpdateListener(listener::accept);
365 
366         mHandler.loadDocumentsForCurrentStack();
367         CountDownLatch latch = new CountDownLatch(1);
368         mEnv.model.addUpdateListener(event -> latch.countDown());
369         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
370 
371         latch.await(1, TimeUnit.SECONDS);
372         assertThat(listener.getLastValue().getException())
373                 .isInstanceOf(CrossProfileNoPermissionException.class);
374     }
375 
376     @Test
testCrossProfileDocuments_reloadSuccessAfterCrossProfileError()377     public void testCrossProfileDocuments_reloadSuccessAfterCrossProfileError() throws Exception {
378         mEnv.state.action = State.ACTION_GET_CONTENT;
379         mEnv.state.stack.changeRoot(TestProvidersAccess.OtherUser.HOME);
380         mEnv.state.stack.push(TestEnv.OtherUser.FOLDER_0);
381 
382         mEnv.state.sortModel.sortByUser(
383                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
384 
385         // Currently mock provider does not have cross profile concept, this will always return
386         // the supplied docs without checking for the user. But this should not be a problem for
387         // this test case.
388         mEnv.mockProviders.get(TestProvidersAccess.OtherUser.HOME.authority)
389                 .setNextChildDocumentsReturns(TestEnv.OtherUser.FILE_PNG);
390 
391         // Disallow sharing across profile
392         mEnv.state.canShareAcrossProfile = false;
393 
394         TestEventHandler<Model.Update> listener = new TestEventHandler<>();
395         mEnv.model.addUpdateListener(listener::accept);
396 
397         mHandler.loadDocumentsForCurrentStack();
398         CountDownLatch latch1 = new CountDownLatch(1);
399         EventListener<Model.Update> updateEventListener1 = update -> latch1.countDown();
400         mEnv.model.addUpdateListener(updateEventListener1);
401         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
402         latch1.await(1, TimeUnit.SECONDS);
403         assertThat(listener.getLastValue().getException())
404                 .isInstanceOf(CrossProfileNoPermissionException.class);
405 
406         // Allow sharing across profile.
407         mEnv.state.canShareAcrossProfile = true;
408 
409         CountDownLatch latch2 = new CountDownLatch(1);
410         mEnv.model.addUpdateListener(update -> latch2.countDown());
411         mHandler.loadDocumentsForCurrentStack();
412         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
413 
414         latch2.await(1, TimeUnit.SECONDS);
415         assertEquals(1, mEnv.model.getItemCount());
416         String[] modelIds = mEnv.model.getModelIds();
417         assertEquals(TestEnv.OtherUser.FILE_PNG, mEnv.model.getDocument(modelIds[0]));
418     }
419 
420     @Test
testLoadChildrenDocuments_failsWithNonRecentsAndEmptyStack()421     public void testLoadChildrenDocuments_failsWithNonRecentsAndEmptyStack() throws Exception {
422         mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
423 
424         mEnv.mockProviders.get(TestProvidersAccess.HOME.authority)
425                 .setNextChildDocumentsReturns(TestEnv.FILE_APK, TestEnv.FILE_GIF);
426 
427         TestEventHandler<Model.Update> listener = new TestEventHandler<>();
428         mEnv.model.addUpdateListener(listener::accept);
429 
430         mHandler.loadDocumentsForCurrentStack();
431         CountDownLatch latch = new CountDownLatch(1);
432         mEnv.model.addUpdateListener(event -> latch.countDown());
433         mActivity.supportLoaderManager.runAsyncTaskLoader(AbstractActionHandler.LOADER_ID);
434 
435         latch.await(1, TimeUnit.SECONDS);
436         assertTrue(listener.getLastValue().hasException());
437     }
438 
439     @Test
testPreviewItem_throwException()440     public void testPreviewItem_throwException() throws Exception {
441         try {
442             mHandler.previewItem(null);
443             fail("Should have thrown UnsupportedOperationException.");
444         } catch (UnsupportedOperationException expected) {
445         }
446     }
447 }
448