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