Previously if there were multiple clients it would trigger an infinite loop as the cache gits dropped after the first bind, and the second client would trigger another load. Instead now cache as long as slices are pinned since thats the intended behavior of caching. Test: make RunSettingsRoboTests Change-Id: I7d29fab87573120b34cd55e1696c4c5b70fc891c Fixes: 78471335
376 lines
14 KiB
Java
376 lines
14 KiB
Java
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
package com.android.settings.slices;
|
|
|
|
import static android.content.ContentResolver.SCHEME_CONTENT;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
|
|
import static org.mockito.Mockito.doReturn;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.spy;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
import android.app.slice.SliceManager;
|
|
import android.content.ContentResolver;
|
|
import android.content.ContentValues;
|
|
import android.content.Context;
|
|
import android.database.sqlite.SQLiteDatabase;
|
|
import android.net.Uri;
|
|
import android.provider.SettingsSlicesContract;
|
|
|
|
import com.android.settings.testutils.DatabaseTestUtils;
|
|
import com.android.settings.testutils.FakeToggleController;
|
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.robolectric.RuntimeEnvironment;
|
|
|
|
import androidx.slice.Slice;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
|
|
/**
|
|
* TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
|
|
*/
|
|
@RunWith(SettingsRobolectricTestRunner.class)
|
|
public class SettingsSliceProviderTest {
|
|
|
|
private final String KEY = "KEY";
|
|
private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
|
|
private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY;
|
|
private final String TITLE = "title";
|
|
private final String SUMMARY = "summary";
|
|
private final String SCREEN_TITLE = "screen title";
|
|
private final String FRAGMENT_NAME = "fragment name";
|
|
private final int ICON = 1234; // I declare a thumb war
|
|
private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
|
|
private final String PREF_CONTROLLER = FakeToggleController.class.getName();
|
|
private Context mContext;
|
|
private SettingsSliceProvider mProvider;
|
|
private SQLiteDatabase mDb;
|
|
private SliceManager mManager;
|
|
|
|
@Before
|
|
public void setUp() {
|
|
mContext = spy(RuntimeEnvironment.application);
|
|
mProvider = spy(new SettingsSliceProvider());
|
|
mProvider.mSliceWeakDataCache = new HashMap<>();
|
|
mProvider.mSliceDataCache = new HashMap<>();
|
|
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
|
|
when(mProvider.getContext()).thenReturn(mContext);
|
|
|
|
mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
|
SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
|
|
mManager = mock(SliceManager.class);
|
|
when(mContext.getSystemService(SliceManager.class)).thenReturn(mManager);
|
|
when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
|
|
}
|
|
|
|
@After
|
|
public void cleanUp() {
|
|
DatabaseTestUtils.clearDb(mContext);
|
|
}
|
|
|
|
@Test
|
|
public void testInitialSliceReturned_emptySlice() {
|
|
insertSpecialCase(KEY);
|
|
Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
|
|
Slice slice = mProvider.onBindSlice(uri);
|
|
|
|
assertThat(slice.getUri()).isEqualTo(uri);
|
|
assertThat(slice.getItems()).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void testLoadSlice_returnsSliceFromAccessor() {
|
|
insertSpecialCase(KEY);
|
|
Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
|
|
|
|
mProvider.loadSlice(uri);
|
|
SliceData data = mProvider.mSliceWeakDataCache.get(uri);
|
|
|
|
assertThat(data.getKey()).isEqualTo(KEY);
|
|
assertThat(data.getTitle()).isEqualTo(TITLE);
|
|
}
|
|
|
|
@Test
|
|
public void testLoadSlice_doesntCacheWithoutPin() {
|
|
insertSpecialCase(KEY);
|
|
Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
|
|
|
|
mProvider.loadSlice(uri);
|
|
SliceData data = mProvider.mSliceDataCache.get(uri);
|
|
|
|
assertThat(data).isNull();
|
|
}
|
|
|
|
@Test
|
|
public void testLoadSlice_cachesWithPin() {
|
|
insertSpecialCase(KEY);
|
|
Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
|
|
when(mManager.getPinnedSlices()).thenReturn(Arrays.asList(uri));
|
|
|
|
mProvider.loadSlice(uri);
|
|
SliceData data = mProvider.mSliceDataCache.get(uri);
|
|
|
|
assertThat(data.getKey()).isEqualTo(KEY);
|
|
assertThat(data.getTitle()).isEqualTo(TITLE);
|
|
}
|
|
|
|
@Test
|
|
public void testLoadSlice_cachedEntryRemovedOnBuild() {
|
|
SliceData data = getDummyData();
|
|
mProvider.mSliceWeakDataCache.put(data.getUri(), data);
|
|
mProvider.onBindSlice(data.getUri());
|
|
insertSpecialCase(data.getKey());
|
|
|
|
SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
|
|
|
|
assertThat(cachedData).isNull();
|
|
}
|
|
|
|
@Test
|
|
public void testLoadSlice_cachedEntryRemovedOnUnpin() {
|
|
SliceData data = getDummyData();
|
|
mProvider.mSliceDataCache.put(data.getUri(), data);
|
|
mProvider.onSliceUnpinned(data.getUri());
|
|
insertSpecialCase(data.getKey());
|
|
|
|
SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
|
|
|
|
assertThat(cachedData).isNull();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_fullActionUri_returnsSelf() {
|
|
final Uri uri = SliceBuilderUtils.getUri(
|
|
SettingsSlicesContract.PATH_SETTING_ACTION + "/key", true);
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactly(uri);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_fullIntentUri_returnsSelf() {
|
|
final Uri uri = SliceBuilderUtils.getUri(
|
|
SettingsSlicesContract.PATH_SETTING_ACTION + "/key", true);
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactly(uri);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_wrongPath_returnsEmpty() {
|
|
final Uri uri = SliceBuilderUtils.getUri("invalid_path", true);
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_invalidPath_returnsEmpty() {
|
|
final String key = "platform_key";
|
|
insertSpecialCase(key, true /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath("invalid")
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_platformSlice_doesNotReturnOEMSlice() {
|
|
insertSpecialCase("oem_key", false /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_oemSlice_doesNotReturnPlatformSlice() {
|
|
insertSpecialCase("platform_key", true /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_oemSlice_returnsOEMUriDescendant() {
|
|
final String key = "oem_key";
|
|
insertSpecialCase(key, false /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.build();
|
|
final Uri expectedUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactly(expectedUri);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_oemSliceNoPath_returnsOEMUriDescendant() {
|
|
final String key = "oem_key";
|
|
insertSpecialCase(key, false /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.build();
|
|
final Uri expectedUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactly(expectedUri);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_platformSlice_returnsPlatformUriDescendant() {
|
|
final String key = "platform_key";
|
|
insertSpecialCase(key, true /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.build();
|
|
final Uri expectedUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactly(expectedUri);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_platformSliceNoPath_returnsPlatformUriDescendant() {
|
|
final String key = "platform_key";
|
|
insertSpecialCase(key, true /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.build();
|
|
final Uri expectedUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(key)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactly(expectedUri);
|
|
}
|
|
|
|
@Test
|
|
public void getDescendantUris_noAuthorityNorPath_returnsAllUris() {
|
|
final String platformKey = "platform_key";
|
|
final String oemKey = "oemKey";
|
|
insertSpecialCase(platformKey, true /* isPlatformSlice */);
|
|
insertSpecialCase(oemKey, false /* isPlatformSlice */);
|
|
final Uri uri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.build();
|
|
final Uri expectedPlatformUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSlicesContract.AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(platformKey)
|
|
.build();
|
|
final Uri expectedOemUri = new Uri.Builder()
|
|
.scheme(SCHEME_CONTENT)
|
|
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
|
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
|
.appendPath(oemKey)
|
|
.build();
|
|
|
|
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(uri);
|
|
|
|
assertThat(descendants).containsExactly(expectedPlatformUri, expectedOemUri);
|
|
}
|
|
|
|
private void insertSpecialCase(String key) {
|
|
insertSpecialCase(key, true);
|
|
}
|
|
|
|
private void insertSpecialCase(String key, boolean isPlatformSlice) {
|
|
ContentValues values = new ContentValues();
|
|
values.put(SlicesDatabaseHelper.IndexColumns.KEY, key);
|
|
values.put(SlicesDatabaseHelper.IndexColumns.TITLE, TITLE);
|
|
values.put(SlicesDatabaseHelper.IndexColumns.SUMMARY, "s");
|
|
values.put(SlicesDatabaseHelper.IndexColumns.SCREENTITLE, "s");
|
|
values.put(SlicesDatabaseHelper.IndexColumns.ICON_RESOURCE, 1234);
|
|
values.put(SlicesDatabaseHelper.IndexColumns.FRAGMENT, "test");
|
|
values.put(SlicesDatabaseHelper.IndexColumns.CONTROLLER, "test");
|
|
values.put(SlicesDatabaseHelper.IndexColumns.PLATFORM_SLICE, isPlatformSlice);
|
|
values.put(SlicesDatabaseHelper.IndexColumns.SLICE_TYPE, SliceData.SliceType.INTENT);
|
|
|
|
mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values);
|
|
}
|
|
|
|
private SliceData getDummyData() {
|
|
return new SliceData.Builder()
|
|
.setKey(KEY)
|
|
.setTitle(TITLE)
|
|
.setSummary(SUMMARY)
|
|
.setScreenTitle(SCREEN_TITLE)
|
|
.setIcon(ICON)
|
|
.setFragmentName(FRAGMENT_NAME)
|
|
.setUri(URI)
|
|
.setPreferenceControllerClassName(PREF_CONTROLLER)
|
|
.build();
|
|
}
|
|
} |