1 /*
2  * Copyright 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 androidx.slice;
18 
19 import static androidx.slice.compat.SliceProviderCompat.PERMS_PREFIX;
20 
21 import static junit.framework.Assert.assertEquals;
22 import static junit.framework.Assert.assertTrue;
23 
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.Mockito.clearInvocations;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.timeout;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import android.content.ContentResolver;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.net.Uri;
36 import android.support.test.InstrumentationRegistry;
37 import android.support.test.filters.SmallTest;
38 import android.support.test.runner.AndroidJUnit4;
39 
40 import androidx.annotation.NonNull;
41 import androidx.core.os.BuildCompat;
42 import androidx.slice.compat.CompatPermissionManager;
43 import androidx.slice.render.SliceRenderActivity;
44 
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.util.Arrays;
50 import java.util.Collection;
51 import java.util.List;
52 import java.util.concurrent.Executor;
53 
54 @RunWith(AndroidJUnit4.class)
55 @SmallTest
56 public class SliceManagerTest {
57 
58     private final Context mContext = InstrumentationRegistry.getContext();
59     private SliceProvider mSliceProvider;
60     private SliceManager mManager;
61 
62     @Before
setup()63     public void setup() {
64         TestSliceProvider.sSliceProviderReceiver = mSliceProvider = mock(SliceProvider.class);
65         mManager = SliceManager.getInstance(mContext);
66     }
67 
68     @Test
testPin()69     public void testPin() {
70         Uri uri = new Uri.Builder()
71                 .scheme(ContentResolver.SCHEME_CONTENT)
72                 .authority(mContext.getPackageName())
73                 .build();
74         try {
75             mManager.pinSlice(uri);
76             verify(mSliceProvider, timeout(2000)).onSlicePinned(eq(uri));
77         } finally {
78             mManager.unpinSlice(uri);
79         }
80     }
81 
82     @Test
testUnpin()83     public void testUnpin() {
84         Uri uri = new Uri.Builder()
85                 .scheme(ContentResolver.SCHEME_CONTENT)
86                 .authority(mContext.getPackageName())
87                 .build();
88         mManager.pinSlice(uri);
89         verify(mSliceProvider, timeout(2000)).onSlicePinned(eq(uri));
90         clearInvocations(mSliceProvider);
91         mManager.unpinSlice(uri);
92         verify(mSliceProvider, timeout(2000)).onSliceUnpinned(eq(uri));
93     }
94 
95     @Test
testPinList()96     public void testPinList() {
97         Uri uri = new Uri.Builder()
98                 .scheme(ContentResolver.SCHEME_CONTENT)
99                 .authority(mContext.getPackageName())
100                 .build();
101         Uri longerUri = uri.buildUpon().appendPath("something").build();
102         try {
103             mManager.pinSlice(uri);
104             mManager.pinSlice(longerUri);
105             verify(mSliceProvider, timeout(2000)).onSlicePinned(eq(longerUri));
106 
107             List<Uri> uris = mManager.getPinnedSlices();
108             assertEquals(2, uris.size());
109             assertTrue(uris.contains(uri));
110             assertTrue(uris.contains(longerUri));
111         } finally {
112             mManager.unpinSlice(uri);
113             mManager.unpinSlice(longerUri);
114         }
115     }
116 
117     @Test
testCallback()118     public void testCallback() {
119         if (BuildCompat.isAtLeastP()) {
120             return;
121         }
122         Uri uri = new Uri.Builder()
123                 .scheme(ContentResolver.SCHEME_CONTENT)
124                 .authority(mContext.getPackageName())
125                 .build();
126         Slice s = new Slice.Builder(uri).build();
127         SliceManager.SliceCallback callback = mock(SliceManager.SliceCallback.class);
128         when(mSliceProvider.onBindSlice(eq(uri))).thenReturn(s);
129         mManager.registerSliceCallback(uri, new Executor() {
130             @Override
131             public void execute(@NonNull Runnable command) {
132                 command.run();
133             }
134         }, callback);
135 
136         mContext.getContentResolver().notifyChange(uri, null);
137 
138         verify(callback, timeout(2000)).onSliceUpdated(any(Slice.class));
139     }
140 
141     @Test
testPinnedSpecs()142     public void testPinnedSpecs() {
143         if (BuildCompat.isAtLeastP()) {
144             return;
145         }
146         Uri uri = new Uri.Builder()
147                 .scheme(ContentResolver.SCHEME_CONTENT)
148                 .authority(mContext.getPackageName())
149                 .build();
150         mManager.pinSlice(uri);
151         verify(mSliceProvider).onSlicePinned(eq(uri));
152 
153         // Disabled while we update APIs.
154         //assertEquals(SliceLiveData.SUPPORTED_SPECS, mManager.getPinnedSpecs(uri));
155     }
156 
157     @Test
testMapIntentToUriStatic()158     public void testMapIntentToUriStatic() {
159         Uri expected = Uri.parse("content://androidx.slice.view.test/render");
160 
161         Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
162 
163         assertEquals(expected, uri);
164     }
165 
166     @Test
testMapIntentToUri()167     public void testMapIntentToUri() {
168         Uri expected = Uri.parse("content://androidx.slice.view.test/render");
169         Intent intent = new Intent("androidx.slice.action.TEST")
170                 .setPackage(mContext.getPackageName());
171 
172         when(mSliceProvider.onMapIntentToUri(eq(intent))).thenReturn(expected);
173         Uri uri = mManager.mapIntentToUri(intent);
174 
175         verify(mSliceProvider).onMapIntentToUri(eq(intent));
176         assertEquals(expected, uri);
177     }
178 
179     @Test
testGetDescendants()180     public void testGetDescendants() {
181         Uri uri = new Uri.Builder()
182                 .scheme(ContentResolver.SCHEME_CONTENT)
183                 .authority(mContext.getPackageName())
184                 .build();
185         Collection<Uri> collection = Arrays.asList(
186                 uri,
187                 uri.buildUpon().appendPath("1").build(),
188                 uri.buildUpon().appendPath("2").build()
189         );
190         when(mSliceProvider.onGetSliceDescendants(any(Uri.class)))
191                 .thenReturn(collection);
192 
193         Collection<Uri> allUris = mManager.getSliceDescendants(uri);
194 
195         assertEquals(allUris, collection);
196         verify(mSliceProvider).onGetSliceDescendants(eq(uri));
197     }
198 
199     public static class TestSliceProvider extends SliceProvider {
200 
201         public static SliceProvider sSliceProviderReceiver;
202 
203         @Override
onCreateSliceProvider()204         public boolean onCreateSliceProvider() {
205             if (sSliceProviderReceiver != null) {
206                 sSliceProviderReceiver.onCreateSliceProvider();
207             }
208             return true;
209         }
210 
211         @Override
onBindSlice(Uri sliceUri)212         public Slice onBindSlice(Uri sliceUri) {
213             if (sSliceProviderReceiver != null) {
214                 return sSliceProviderReceiver.onBindSlice(sliceUri);
215             }
216             return null;
217         }
218 
219         @NonNull
220         @Override
onMapIntentToUri(Intent intent)221         public Uri onMapIntentToUri(Intent intent) {
222             if (sSliceProviderReceiver != null) {
223                 return sSliceProviderReceiver.onMapIntentToUri(intent);
224             }
225             return null;
226         }
227 
228         @Override
onSlicePinned(Uri sliceUri)229         public void onSlicePinned(Uri sliceUri) {
230             if (sSliceProviderReceiver != null) {
231                 sSliceProviderReceiver.onSlicePinned(sliceUri);
232             }
233         }
234 
235         @Override
onSliceUnpinned(Uri sliceUri)236         public void onSliceUnpinned(Uri sliceUri) {
237             if (sSliceProviderReceiver != null) {
238                 sSliceProviderReceiver.onSliceUnpinned(sliceUri);
239             }
240         }
241 
onCreatePermissionManager( String[] autoGrantPermissions)242         protected CompatPermissionManager onCreatePermissionManager(
243                 String[] autoGrantPermissions) {
244             return new CompatPermissionManager(getContext(), PERMS_PREFIX + getClass().getName(),
245                     -1 /* Different uid to run permissions */, autoGrantPermissions);
246         }
247 
248         @Override
onGetSliceDescendants(Uri uri)249         public Collection<Uri> onGetSliceDescendants(Uri uri) {
250             if (sSliceProviderReceiver != null) {
251                 return sSliceProviderReceiver.onGetSliceDescendants(uri);
252             }
253             return super.onGetSliceDescendants(uri);
254         }
255     }
256 }
257