1 /*
2  * Copyright (C) 2018 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.assertEquals;
22 import static junit.framework.Assert.assertTrue;
23 
24 import android.database.Cursor;
25 import android.os.Bundle;
26 import android.provider.DocumentsContract;
27 import android.provider.DocumentsContract.Document;
28 
29 import androidx.test.filters.MediumTest;
30 
31 import com.android.documentsui.base.DocumentInfo;
32 import com.android.documentsui.base.State;
33 import com.android.documentsui.sorting.SortDimension;
34 import com.android.documentsui.sorting.SortModel;
35 import com.android.documentsui.testing.ActivityManagers;
36 import com.android.documentsui.testing.TestEnv;
37 import com.android.documentsui.testing.TestFileTypeLookup;
38 import com.android.documentsui.testing.TestImmediateExecutor;
39 import com.android.documentsui.testing.TestProvidersAccess;
40 import com.android.modules.utils.build.SdkLevel;
41 
42 import com.google.common.collect.Lists;
43 
44 import org.junit.After;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 import org.junit.runners.Parameterized;
49 import org.junit.runners.Parameterized.Parameter;
50 import org.junit.runners.Parameterized.Parameters;
51 
52 @RunWith(Parameterized.class)
53 @MediumTest
54 public class GlobalSearchLoaderTest {
55 
56     private static final String SEARCH_STRING = "freddy";
57     private static int FILE_FLAG = Document.FLAG_SUPPORTS_MOVE | Document.FLAG_SUPPORTS_DELETE
58             | Document.FLAG_SUPPORTS_REMOVE;
59 
60     private TestEnv mEnv;
61     private TestActivity mActivity;
62     private GlobalSearchLoader mLoader;
63     private TestConfigStore mTestConfigStore;
64     private Bundle mQueryArgs = new Bundle();
65 
66     @Parameter(0)
67     public boolean isPrivateSpaceEnabled;
68 
69     /**
70      * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
71      * private space flag enabled and once with it disabled.
72      */
73     @Parameters(name = "privateSpaceEnabled={0}")
data()74     public static Iterable<?> data() {
75         return Lists.newArrayList(true, false);
76     }
77 
78     @Before
setUp()79     public void setUp() {
80         mEnv = TestEnv.create();
81         mActivity = TestActivity.create(mEnv);
82         mActivity.activityManager = ActivityManagers.create(false);
83         mTestConfigStore = new TestConfigStore();
84         mEnv.state.configStore = mTestConfigStore;
85 
86         isPrivateSpaceEnabled &= SdkLevel.isAtLeastS();
87         if (isPrivateSpaceEnabled) {
88             mTestConfigStore.enablePrivateSpaceInPhotoPicker();
89             mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.USER_ID, true);
90         }
91 
92         mEnv.state.action = State.ACTION_BROWSE;
93         mEnv.state.acceptMimes = new String[]{"*/*"};
94 
95         mQueryArgs.putString(DocumentsContract.QUERY_ARG_DISPLAY_NAME, SEARCH_STRING);
96         mLoader = new GlobalSearchLoader(mActivity, mEnv.providers, mEnv.state,
97                 TestImmediateExecutor.createLookup(), new TestFileTypeLookup(), mQueryArgs,
98                 TestProvidersAccess.USER_ID);
99 
100         final DocumentInfo doc = mEnv.model.createFile(SEARCH_STRING + ".jpg", FILE_FLAG);
101         doc.lastModified = System.currentTimeMillis();
102         mEnv.mockProviders.get(TestProvidersAccess.DOWNLOADS.authority)
103                 .setNextChildDocumentsReturns(doc);
104 
105         TestProvidersAccess.DOWNLOADS.flags |= DocumentsContract.Root.FLAG_SUPPORTS_SEARCH;
106     }
107 
108     @After
tearDown()109     public void tearDown() {
110         TestProvidersAccess.DOWNLOADS.flags &= ~DocumentsContract.Root.FLAG_SUPPORTS_SEARCH;
111     }
112 
113     @Test
testNotSearchableRoot_beIgnored()114     public void testNotSearchableRoot_beIgnored() {
115         TestProvidersAccess.PICKLES.flags |= DocumentsContract.Root.FLAG_LOCAL_ONLY;
116         assertTrue(mLoader.shouldIgnoreRoot(TestProvidersAccess.PICKLES));
117         TestProvidersAccess.PICKLES.flags &= ~DocumentsContract.Root.FLAG_LOCAL_ONLY;
118     }
119 
120     @Test
testNotLocalOnlyRoot_beIgnored()121     public void testNotLocalOnlyRoot_beIgnored() {
122         if (isPrivateSpaceEnabled) return;
123         TestProvidersAccess.PICKLES.flags |= DocumentsContract.Root.FLAG_SUPPORTS_SEARCH;
124         assertTrue(mLoader.shouldIgnoreRoot(TestProvidersAccess.PICKLES));
125         TestProvidersAccess.PICKLES.flags &= ~DocumentsContract.Root.FLAG_SUPPORTS_SEARCH;
126     }
127 
128     @Test
testShowAdvance_storageRoot_beIgnored()129     public void testShowAdvance_storageRoot_beIgnored() {
130         TestProvidersAccess.HOME.flags |= DocumentsContract.Root.FLAG_SUPPORTS_SEARCH;
131         assertTrue(mLoader.shouldIgnoreRoot(TestProvidersAccess.HOME));
132         TestProvidersAccess.HOME.flags &= ~(DocumentsContract.Root.FLAG_SUPPORTS_SEARCH);
133     }
134 
135     @Test
testCrossProfileRoot_notInTextSearch_beIgnored()136     public void testCrossProfileRoot_notInTextSearch_beIgnored() {
137         mEnv.state.supportsCrossProfile = true;
138         mQueryArgs.remove(DocumentsContract.QUERY_ARG_DISPLAY_NAME);
139         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.OtherUser.USER_ID;
140         assertThat(mLoader.shouldIgnoreRoot(TestProvidersAccess.DOWNLOADS)).isTrue();
141         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.USER_ID;
142     }
143 
144     @Test
testCrossProfileRoot_inTextSearch_beIncluded()145     public void testCrossProfileRoot_inTextSearch_beIncluded() {
146         mEnv.state.supportsCrossProfile = true;
147         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.OtherUser.USER_ID;
148         assertThat(mLoader.shouldIgnoreRoot(TestProvidersAccess.DOWNLOADS)).isFalse();
149         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.USER_ID;
150     }
151 
152     @Test
testSearchResult_includeDirectory()153     public void testSearchResult_includeDirectory() {
154         final DocumentInfo doc = mEnv.model.createFolder(SEARCH_STRING);
155         doc.lastModified = System.currentTimeMillis();
156 
157         mEnv.mockProviders.get(TestProvidersAccess.DOWNLOADS.authority)
158                 .setNextChildDocumentsReturns(doc);
159 
160         final DirectoryResult result = mLoader.loadInBackground();
161 
162         final Cursor c = result.getCursor();
163         assertEquals(1, c.getCount());
164 
165         c.moveToNext();
166         final String mimeType = c.getString(c.getColumnIndex(Document.COLUMN_MIME_TYPE));
167         assertEquals(Document.MIME_TYPE_DIR, mimeType);
168     }
169 
170     @Test
testShowOrHideHiddenFiles()171     public void testShowOrHideHiddenFiles() {
172         final DocumentInfo doc1 = mEnv.model.createFile(".test" + SEARCH_STRING);
173         final DocumentInfo doc2 = mEnv.model.createFile("test" + SEARCH_STRING);
174         doc1.documentId = ".test";
175         doc2.documentId = "parent_folder/.hidden_folder/test";
176         mEnv.mockProviders.get(TestProvidersAccess.DOWNLOADS.authority)
177                 .setNextChildDocumentsReturns(doc1, doc2);
178 
179         assertEquals(false, mLoader.mState.showHiddenFiles);
180         DirectoryResult result = mLoader.loadInBackground();
181         assertEquals(0, result.getCursor().getCount());
182 
183         mLoader.mState.showHiddenFiles = true;
184         result = mLoader.loadInBackground();
185         assertEquals(2, result.getCursor().getCount());
186     }
187 
188     @Test
testSearchResult_isNotMovable()189     public void testSearchResult_isNotMovable() {
190         final DirectoryResult result = mLoader.loadInBackground();
191 
192         final Cursor c = result.getCursor();
193         assertEquals(1, c.getCount());
194 
195         c.moveToNext();
196         final int flags = c.getInt(c.getColumnIndex(Document.COLUMN_FLAGS));
197         assertEquals(0, flags & Document.FLAG_SUPPORTS_DELETE);
198         assertEquals(0, flags & Document.FLAG_SUPPORTS_REMOVE);
199         assertEquals(0, flags & Document.FLAG_SUPPORTS_MOVE);
200     }
201 
202     @Test
testSearchResult_includeDirectory_excludedOtherUsers()203     public void testSearchResult_includeDirectory_excludedOtherUsers() {
204         mEnv.state.canShareAcrossProfile = false;
205 
206         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.USER_ID;
207         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.OtherUser.USER_ID;
208         TestProvidersAccess.PICKLES.flags |= (DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
209                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
210 
211         final DocumentInfo currentUserDoc = mEnv.model.createFile(
212                 SEARCH_STRING + "_currentUser.pdf");
213         currentUserDoc.lastModified = System.currentTimeMillis();
214         mEnv.mockProviders.get(TestProvidersAccess.DOWNLOADS.authority)
215                 .setNextChildDocumentsReturns(currentUserDoc);
216 
217         final DocumentInfo otherUserDoc = mEnv.model.createFile(SEARCH_STRING + "_otherUser.png");
218         otherUserDoc.lastModified = System.currentTimeMillis();
219         mEnv.mockProviders.get(TestProvidersAccess.PICKLES.authority)
220                 .setNextChildDocumentsReturns(otherUserDoc);
221 
222         final DirectoryResult result = mLoader.loadInBackground();
223         final Cursor c = result.getCursor();
224 
225         assertThat(c.getCount()).isEqualTo(1);
226         c.moveToNext();
227         final String docName = c.getString(c.getColumnIndex(Document.COLUMN_DISPLAY_NAME));
228         assertThat(docName).contains("currentUser");
229 
230         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.USER_ID;
231         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.USER_ID;
232         TestProvidersAccess.PICKLES.flags &= ~DocumentsContract.Root.FLAG_SUPPORTS_SEARCH;
233     }
234 
235     @Test
testSearchResult_includeSearchString()236     public void testSearchResult_includeSearchString() {
237         final DocumentInfo pdfDoc = mEnv.model.createFile(SEARCH_STRING + ".pdf");
238         pdfDoc.lastModified = System.currentTimeMillis();
239 
240         final DocumentInfo apkDoc = mEnv.model.createFile(SEARCH_STRING + ".apk");
241         apkDoc.lastModified = System.currentTimeMillis();
242 
243         final DocumentInfo testApkDoc = mEnv.model.createFile("test.apk");
244         testApkDoc.lastModified = System.currentTimeMillis();
245 
246         mEnv.mockProviders.get(TestProvidersAccess.DOWNLOADS.authority)
247                 .setNextChildDocumentsReturns(pdfDoc, apkDoc, testApkDoc);
248 
249         mEnv.state.sortModel.sortByUser(
250                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
251 
252         final DirectoryResult result = mLoader.loadInBackground();
253         final Cursor c = result.getCursor();
254 
255         assertEquals(2, c.getCount());
256 
257         c.moveToNext();
258         String displayName = c.getString(c.getColumnIndex(Document.COLUMN_DISPLAY_NAME));
259         assertTrue(displayName.contains(SEARCH_STRING));
260 
261         c.moveToNext();
262         displayName = c.getString(c.getColumnIndex(Document.COLUMN_DISPLAY_NAME));
263         assertTrue(displayName.contains(SEARCH_STRING));
264     }
265 
266     @Test
testSearchResult_includeDifferentRoot()267     public void testSearchResult_includeDifferentRoot() {
268         final DocumentInfo pdfDoc = mEnv.model.createFile(SEARCH_STRING + ".pdf");
269         pdfDoc.lastModified = System.currentTimeMillis();
270 
271         final DocumentInfo apkDoc = mEnv.model.createFile(SEARCH_STRING + ".apk");
272         apkDoc.lastModified = System.currentTimeMillis();
273 
274         final DocumentInfo testApkDoc = mEnv.model.createFile("test.apk");
275         testApkDoc.lastModified = System.currentTimeMillis();
276 
277         mEnv.mockProviders.get(TestProvidersAccess.PICKLES.authority)
278                 .setNextChildDocumentsReturns(pdfDoc, apkDoc, testApkDoc);
279 
280         TestProvidersAccess.PICKLES.flags |= (DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
281                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
282 
283         mEnv.state.sortModel.sortByUser(
284                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
285 
286         final DirectoryResult result = mLoader.loadInBackground();
287         final Cursor c = result.getCursor();
288 
289         assertEquals(3, c.getCount());
290 
291         TestProvidersAccess.PICKLES.flags &= ~(DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
292                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
293     }
294 
295     @Test
testSearchResult_includeCurrentUserRootOnly()296     public void testSearchResult_includeCurrentUserRootOnly() {
297         mEnv.state.canShareAcrossProfile = false;
298         mEnv.state.supportsCrossProfile = true;
299 
300         final DocumentInfo pdfDoc = mEnv.model.createFile(SEARCH_STRING + ".pdf");
301         pdfDoc.lastModified = System.currentTimeMillis();
302 
303         final DocumentInfo apkDoc = mEnv.model.createFile(SEARCH_STRING + ".apk");
304         apkDoc.lastModified = System.currentTimeMillis();
305 
306         final DocumentInfo testApkDoc = mEnv.model.createFile("test.apk");
307         testApkDoc.lastModified = System.currentTimeMillis();
308 
309         mEnv.mockProviders.get(TestProvidersAccess.PICKLES.authority)
310                 .setNextChildDocumentsReturns(pdfDoc, apkDoc, testApkDoc);
311         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.OtherUser.USER_ID;
312 
313         TestProvidersAccess.PICKLES.flags |= (DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
314                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
315         mEnv.state.sortModel.sortByUser(
316                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
317 
318         final DirectoryResult result = mLoader.loadInBackground();
319         final Cursor c = result.getCursor();
320 
321         assertEquals(1, c.getCount());
322 
323         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.USER_ID;
324         TestProvidersAccess.PICKLES.flags &= ~(DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
325                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
326     }
327 
328 
329     @Test
testSearchResult_includeBothUsersRoots()330     public void testSearchResult_includeBothUsersRoots() {
331         if (isPrivateSpaceEnabled) {
332             mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.OtherUser.USER_ID, true);
333         } else {
334             mEnv.state.canShareAcrossProfile = true;
335         }
336         mEnv.state.supportsCrossProfile = true;
337 
338         final DocumentInfo pdfDoc = mEnv.model.createFile(SEARCH_STRING + ".pdf");
339         pdfDoc.lastModified = System.currentTimeMillis();
340 
341         final DocumentInfo apkDoc = mEnv.model.createFile(SEARCH_STRING + ".apk");
342         apkDoc.lastModified = System.currentTimeMillis();
343 
344         final DocumentInfo testApkDoc = mEnv.model.createFile("test.apk");
345         testApkDoc.lastModified = System.currentTimeMillis();
346 
347         mEnv.mockProviders.get(TestProvidersAccess.PICKLES.authority)
348                 .setNextChildDocumentsReturns(pdfDoc, apkDoc, testApkDoc);
349         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.OtherUser.USER_ID;
350 
351         TestProvidersAccess.PICKLES.flags |= (DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
352                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
353         mEnv.state.sortModel.sortByUser(
354                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
355 
356         final DirectoryResult result = mLoader.loadInBackground();
357         final Cursor c = result.getCursor();
358 
359         assertEquals(3, c.getCount());
360 
361         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.USER_ID;
362         TestProvidersAccess.PICKLES.flags &= ~(DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
363                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
364     }
365 
366 
367     @Test
testSearchResult_emptyCurrentUsersRoot()368     public void testSearchResult_emptyCurrentUsersRoot() {
369         mEnv.state.canShareAcrossProfile = false;
370         mEnv.state.supportsCrossProfile = true;
371 
372         final DocumentInfo pdfDoc = mEnv.model.createFile(SEARCH_STRING + ".pdf");
373         pdfDoc.lastModified = System.currentTimeMillis();
374 
375         mEnv.mockProviders.get(TestProvidersAccess.PICKLES.authority)
376                 .setNextChildDocumentsReturns(pdfDoc);
377 
378         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.OtherUser.USER_ID;
379         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.OtherUser.USER_ID;
380         TestProvidersAccess.PICKLES.flags |= (DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
381                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
382         mEnv.state.sortModel.sortByUser(
383                 SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_ASCENDING);
384 
385         final DirectoryResult result = mLoader.loadInBackground();
386         assertThat(result.getCursor().getCount()).isEqualTo(0);
387         // We don't expect exception even if all roots are from other users.
388         assertThat(result.exception).isNull();
389 
390 
391         TestProvidersAccess.DOWNLOADS.userId = TestProvidersAccess.USER_ID;
392         TestProvidersAccess.PICKLES.userId = TestProvidersAccess.USER_ID;
393         TestProvidersAccess.PICKLES.flags &= ~(DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
394                 | DocumentsContract.Root.FLAG_LOCAL_ONLY);
395     }
396 }
397