1 /*
2  * Copyright (C) 2013 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.roots;
18 
19 import static com.google.common.collect.Lists.newArrayList;
20 import static com.google.common.truth.Truth.assertThat;
21 
22 import android.provider.DocumentsContract;
23 import android.test.AndroidTestCase;
24 import android.test.suitebuilder.annotation.SmallTest;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.android.documentsui.base.Providers;
29 import com.android.documentsui.base.RootInfo;
30 import com.android.documentsui.base.State;
31 import com.android.documentsui.base.UserId;
32 
33 import com.google.common.collect.Lists;
34 import com.google.common.truth.Correspondence;
35 
36 import java.util.List;
37 import java.util.Objects;
38 
39 @SmallTest
40 public class ProvidersAccessTest extends AndroidTestCase {
41 
42     private static final UserId OTHER_USER = UserId.of(UserId.DEFAULT_USER.getIdentifier() + 1);
43     private static final Correspondence<RootInfo, RootInfo> USER_ID_MIME_TYPES_CORRESPONDENCE =
44             Correspondence.from((@Nullable RootInfo actual, @Nullable RootInfo expected) -> {
45                     return actual != null && expected != null
46                             && Objects.equals(actual.userId, expected.userId)
47                             && Objects.equals(actual.derivedMimeTypes, expected.derivedMimeTypes);
48             }, "has same userId and mimeTypes as in");
49 
50     private static RootInfo mNull = buildForMimeTypes((String[]) null);
51     private static RootInfo mEmpty = buildForMimeTypes();
52     private static RootInfo mWild = buildForMimeTypes("*/*");
53     private static RootInfo mImages = buildForMimeTypes("image/*");
54     private static RootInfo mAudio = buildForMimeTypes(
55             "audio/*", "application/ogg", "application/x-flac");
56     private static RootInfo mDocs = buildForMimeTypes(
57             "application/msword", "application/vnd.ms-excel");
58     private static RootInfo mMalformed1 = buildForMimeTypes("meow");
59     private static RootInfo mMalformed2 = buildForMimeTypes("*/meow");
60     private static RootInfo mImagesOtherUser = buildForMimeTypes(OTHER_USER, "image/*");
61 
62     private List<RootInfo> mRoots;
63 
64     private State mState;
65 
66     @Override
setUp()67     protected void setUp() throws Exception {
68         super.setUp();
69 
70         mRoots = Lists.newArrayList(
71                 mNull, mWild, mEmpty, mImages, mAudio, mDocs, mMalformed1, mMalformed2,
72                 mImagesOtherUser);
73 
74         mState = new State();
75         mState.action = State.ACTION_OPEN;
76         mState.localOnly = false;
77     }
78 
testMatchingRoots_Everything()79     public void testMatchingRoots_Everything() throws Exception {
80         mState.acceptMimes = new String[]{"*/*"};
81         assertContainsExactly(
82                 newArrayList(mNull, mWild, mImages, mAudio, mDocs, mMalformed1, mMalformed2),
83                 ProvidersAccess.getMatchingRoots(mRoots, mState));
84     }
85 
testMatchingRoots_PngOrWild()86     public void testMatchingRoots_PngOrWild() throws Exception {
87         mState.acceptMimes = new String[] { "image/png", "*/*" };
88         assertContainsExactly(
89                 newArrayList(mNull, mWild, mImages, mAudio, mDocs, mMalformed1, mMalformed2),
90                 ProvidersAccess.getMatchingRoots(mRoots, mState));
91     }
92 
testMatchingRoots_AudioWild()93     public void testMatchingRoots_AudioWild() throws Exception {
94         mState.acceptMimes = new String[] { "audio/*" };
95         assertContainsExactly(
96                 newArrayList(mNull, mWild, mAudio),
97                 ProvidersAccess.getMatchingRoots(mRoots, mState));
98     }
99 
testMatchingRoots_AudioWildOrImageWild()100     public void testMatchingRoots_AudioWildOrImageWild() throws Exception {
101         mState.acceptMimes = new String[] { "audio/*", "image/*" };
102         assertContainsExactly(
103                 newArrayList(mNull, mWild, mAudio, mImages),
104                 ProvidersAccess.getMatchingRoots(mRoots, mState));
105     }
106 
testMatchingRoots_AudioSpecific()107     public void testMatchingRoots_AudioSpecific() throws Exception {
108         mState.acceptMimes = new String[] { "audio/mpeg" };
109         assertContainsExactly(
110                 newArrayList(mNull, mWild, mAudio),
111                 ProvidersAccess.getMatchingRoots(mRoots, mState));
112     }
113 
testMatchingRoots_Document()114     public void testMatchingRoots_Document() throws Exception {
115         mState.acceptMimes = new String[] { "application/msword" };
116         assertContainsExactly(
117                 newArrayList(mNull, mWild, mDocs),
118                 ProvidersAccess.getMatchingRoots(mRoots, mState));
119     }
120 
testMatchingRoots_Application()121     public void testMatchingRoots_Application() throws Exception {
122         mState.acceptMimes = new String[] { "application/*" };
123         assertContainsExactly(
124                 newArrayList(mNull, mWild, mAudio, mDocs),
125                 ProvidersAccess.getMatchingRoots(mRoots, mState));
126     }
127 
testMatchingRoots_FlacOrPng()128     public void testMatchingRoots_FlacOrPng() throws Exception {
129         mState.acceptMimes = new String[] { "application/x-flac", "image/png" };
130         assertContainsExactly(
131                 newArrayList(mNull, mWild, mAudio, mImages),
132                 ProvidersAccess.getMatchingRoots(mRoots, mState));
133     }
134 
testMatchingRoots_FlacOrPng_crossProfile()135     public void testMatchingRoots_FlacOrPng_crossProfile() throws Exception {
136         mState.supportsCrossProfile = true;
137         mState.acceptMimes = new String[]{"application/x-flac", "image/png"};
138         assertContainsExactly(
139                 newArrayList(mNull, mWild, mAudio, mImages, mImagesOtherUser),
140                 ProvidersAccess.getMatchingRoots(mRoots, mState));
141     }
142 
testDefaultRoot()143     public void testDefaultRoot() {
144         mState.acceptMimes = new String[] { "*/*" };
145         assertNull(ProvidersAccess.getDefaultRoot(mRoots, mState));
146 
147         RootInfo downloads = buildForMimeTypes("*/*");
148         downloads.authority = Providers.AUTHORITY_DOWNLOADS;
149         mRoots.add(downloads);
150 
151         assertEquals(downloads, ProvidersAccess.getDefaultRoot(mRoots, mState));
152     }
153 
testDefaultRoot_openDocumentTree()154     public void testDefaultRoot_openDocumentTree() {
155         RootInfo storage = buildForMimeTypes("*/*");
156         storage.authority = Providers.AUTHORITY_STORAGE;
157         storage.flags = DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD;
158         mRoots.add(storage);
159 
160         mState.action = State.ACTION_OPEN_TREE;
161         mState.acceptMimes = new String[] { "*/*" };
162         assertEquals(storage, ProvidersAccess.getDefaultRoot(mRoots, mState));
163     }
164 
testExcludedAuthorities()165     public void testExcludedAuthorities() throws Exception {
166         final List<RootInfo> roots = newArrayList();
167 
168         // Set up some roots
169         for (int i = 0; i < 5; ++i) {
170             RootInfo root = new RootInfo();
171             root.userId = UserId.DEFAULT_USER;
172             root.authority = "authority" + i;
173             roots.add(root);
174         }
175         // Make some allowed authorities
176         List<RootInfo> allowedRoots = newArrayList(
177             roots.get(0), roots.get(2), roots.get(4));
178         // Set up the excluded authority list
179         for (RootInfo root: roots) {
180             if (!allowedRoots.contains(root)) {
181                 mState.excludedAuthorities.add(root.authority);
182             }
183         }
184         mState.acceptMimes = new String[] { "*/*" };
185 
186         assertContainsExactly(
187             allowedRoots,
188             ProvidersAccess.getMatchingRoots(roots, mState));
189     }
190 
assertContainsExactly(List<RootInfo> expected, List<RootInfo> actual)191     private static void assertContainsExactly(List<RootInfo> expected, List<RootInfo> actual) {
192         assertThat(actual)
193                 .comparingElementsUsing(USER_ID_MIME_TYPES_CORRESPONDENCE)
194                 .containsExactlyElementsIn(expected);
195     }
196 
buildForMimeTypes(String... mimeTypes)197     private static RootInfo buildForMimeTypes(String... mimeTypes) {
198         return buildForMimeTypes(UserId.DEFAULT_USER, mimeTypes);
199     }
200 
buildForMimeTypes(UserId userId, String... mimeTypes)201     private static RootInfo buildForMimeTypes(UserId userId, String... mimeTypes) {
202         final RootInfo root = new RootInfo();
203         root.userId = userId;
204         root.derivedMimeTypes = mimeTypes;
205         return root;
206     }
207 }
208