Update DatabaseLoader querying.

Change the way the queries are built and sorted for the
database loader. It now constructs 3 queries that match
different columns. Each query returns their results
and gets sorted, then merged into the master list of
results.

Change-Id: Id643422ddfe26bfec89e04ee9f5832b99148e22a
Test: make RunSettingsRoboTests
Bug: 32904057, 32903623
This commit is contained in:
Matthew Fritze
2016-12-20 15:50:56 -08:00
parent d5e2bfcc7a
commit 7fda314980
7 changed files with 462 additions and 110 deletions

View File

@@ -0,0 +1,246 @@
/*
* Copyright (C) 2017 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.search;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.DatabaseIndexingUtils;
import com.android.settings.search2.DatabaseResultLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.lang.reflect.Field;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DatabaseResultLoaderTest {
private Context mContext;
private DatabaseResultLoader loader;
SQLiteDatabase mDb;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
setUpDb();
}
@After
public void cleanUp() {
Field instance;
Class clazz = IndexDatabaseHelper.class;
try {
instance = clazz.getDeclaredField("sSingleton");
instance.setAccessible(true);
instance.set(null, null);
} catch (Exception e) {
throw new RuntimeException();
}
}
@Test
public void testMatchTitle() {
loader = new DatabaseResultLoader(mContext, "title");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testMatchSummary() {
loader = new DatabaseResultLoader(mContext, "summary");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testMatchKeywords() {
loader = new DatabaseResultLoader(mContext, "keywords");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testMatchEntries() {
loader = new DatabaseResultLoader(mContext, "entries");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testSpecialCaseWord_MatchesNonPrefix() {
insertSpecialCase("Data usage");
loader = new DatabaseResultLoader(mContext, "usage");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseSpace_Matches() {
insertSpecialCase("space");
loader = new DatabaseResultLoader(mContext, " space ");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesWordNoDash() {
insertSpecialCase("wi-fi calling");
loader = new DatabaseResultLoader(mContext, "wifi");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesWordWithDash() {
insertSpecialCase("priorités seulment");
loader = new DatabaseResultLoader(mContext, "priorités");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesWordWithoutDash() {
insertSpecialCase("priorités seulment");
loader = new DatabaseResultLoader(mContext, "priorites");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesEntireQueryWithoutDash() {
insertSpecialCase("wi-fi calling");
loader = new DatabaseResultLoader(mContext, "wifi calling");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
private void insertSpecialCase(String specialCase) {
String normalized = DatabaseIndexingUtils.normalizeHyphen(specialCase);
normalized = DatabaseIndexingUtils.normalizeString(normalized);
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 0);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, specialCase);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, normalized);
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
}
private void setUpDb() {
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 0);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, "alpha_title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "alpha title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "alpha_entries");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "alpha_keywords");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 1);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, "bravo_title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "bravo title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "bravo_entries");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "bravo_keywords");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 2);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, "charlie_title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "charlie title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "charlie_entries");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "charlie_keywords");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
}
}

View File

@@ -40,6 +40,8 @@ import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@@ -48,16 +50,32 @@ public class CursorToSearchResultConverterTest {
private CursorToSearchResultConverter mConverter;
private static final String[] COLUMNS = new String[]{"rank", "title", "summary_on",
"summary off", "entries", "keywords", "class name", "screen title", "icon",
"intent action", "target package", "target class", "enabled", "key",
"payload_type", "payload"};
private static final String[] COLUMNS = new String[]{
IndexColumns.DOCID,
IndexColumns.DATA_TITLE,
IndexColumns.DATA_SUMMARY_ON,
IndexColumns.DATA_SUMMARY_OFF,
IndexColumns.CLASS_NAME,
IndexColumns.SCREEN_TITLE,
IndexColumns.ICON,
IndexColumns.INTENT_ACTION,
IndexColumns.INTENT_TARGET_PACKAGE,
IndexColumns.INTENT_TARGET_CLASS,
IndexColumns.DATA_KEY_REF,
IndexColumns.PAYLOAD_TYPE,
IndexColumns.PAYLOAD
};
private static final String[] TITLES = new String[]{"title1", "title2", "title3"};
private static final String SUMMARY = "SUMMARY";
private static final String ID = "id";
private static final String[] TITLES = {"title1", "title2", "title3"};
private static final String SUMMARY = "summary";
private static final String QUERY = "query";
private final int BASE_RANK = 1;
private static final int EXAMPLES = 3;
private static final Intent mIntent = new Intent("com.android.settings");
private static final int mIcon = R.drawable.ic_search_history;
private List<SearchResult> mResults;
private Drawable mDrawable;
@@ -65,32 +83,33 @@ public class CursorToSearchResultConverterTest {
public void setUp() {
Context context = Robolectric.buildActivity(Activity.class).get();
mDrawable = context.getDrawable(mIcon);
mConverter = new CursorToSearchResultConverter(context);
mResults = new ArrayList<>();
mConverter = new CursorToSearchResultConverter(context, QUERY);
}
@Test
public void testParseNullResults_ReturnsNull() {
List<SearchResult> results = mConverter.convertCursor(null);
List<SearchResult> results = mConverter.convertCursor(null, BASE_RANK);
assertThat(results).isNull();
}
@Test
public void testParseCursor_NotNull() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
assertThat(results).isNotNull();
}
@Test
public void testParseCursor_MatchesRank() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).rank).isEqualTo(i);
assertThat(results.get(i).rank).isEqualTo(BASE_RANK);
}
}
@Test
public void testParseCursor_MatchesTitle() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).title).isEqualTo(TITLES[i]);
}
@@ -98,7 +117,7 @@ public class CursorToSearchResultConverterTest {
@Test
public void testParseCursor_MatchesSummary() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).summary).isEqualTo(SUMMARY);
}
@@ -106,7 +125,7 @@ public class CursorToSearchResultConverterTest {
@Test
public void testParseCursor_MatchesIcon() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) {
Drawable resultDrawable = results.get(i).icon;
assertThat(resultDrawable.toString()).isEqualTo(mDrawable.toString());
@@ -116,7 +135,7 @@ public class CursorToSearchResultConverterTest {
@Test
public void testParseCursor_NoIcon() {
List<SearchResult> results = mConverter.convertCursor(
getDummyCursor(false /* hasIcon */));
getDummyCursor(false /* hasIcon */), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) {
Drawable resultDrawable = results.get(i).icon;
assertThat(resultDrawable).isNull();
@@ -125,7 +144,7 @@ public class CursorToSearchResultConverterTest {
@Test
public void testParseCursor_MatchesPayloadType() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
ResultPayload payload;
for (int i = 0; i < EXAMPLES; i++) {
payload = results.get(i).payload;
@@ -138,24 +157,21 @@ public class CursorToSearchResultConverterTest {
MatrixCursor cursor = new MatrixCursor(COLUMNS);
final String BLANK = "";
cursor.addRow(new Object[]{
0, // rank
TITLES[0],
SUMMARY,
ID, // Doc ID
TITLES[0], // Title
SUMMARY, // Summary on
SUMMARY, // summary off
BLANK, // entries
BLANK, // Keywords
GestureSettings.class.getName(),
BLANK, // screen title
null, // icon
BLANK, // action
null, // target package
BLANK, // target class
BLANK, // enabled
BLANK, // key
BLANK, // Key
0, // Payload Type
null // Payload
});
List<SearchResult> results = mConverter.convertCursor(cursor);
List<SearchResult> results = mConverter.convertCursor(cursor, BASE_RANK);
IntentPayload payload = (IntentPayload) results.get(0).payload;
Intent intent = payload.intent;
assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
@@ -163,7 +179,7 @@ public class CursorToSearchResultConverterTest {
@Test
public void testParseCursor_MatchesIntentPayload() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
IntentPayload payload;
for (int i = 0; i < EXAMPLES; i++) {
payload = (IntentPayload) results.get(i).payload;
@@ -185,24 +201,21 @@ public class CursorToSearchResultConverterTest {
final InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, map);
cursor.addRow(new Object[]{
0, // rank
TITLES[0],
SUMMARY,
ID, // Doc ID
TITLES[0], // Title
SUMMARY, // Summary on
SUMMARY, // summary off
BLANK, // entries
BLANK, // Keywords
GestureSettings.class.getName(),
BLANK, // screen title
null, // icon
BLANK, // action
null, // target package
BLANK, // target class
BLANK, // enabled
BLANK, // key
BLANK, // Key
type, // Payload Type
ResultPayloadUtils.marshall(payload) // Payload
});
List<SearchResult> results = mConverter.convertCursor(cursor);
List<SearchResult> results = mConverter.convertCursor(cursor, BASE_RANK);
InlineSwitchPayload newPayload = (InlineSwitchPayload) results.get(0).payload;
assertThat(newPayload.settingsUri).isEqualTo(uri);
@@ -222,22 +235,17 @@ public class CursorToSearchResultConverterTest {
for (int i = 0; i < EXAMPLES; i++) {
ArrayList<String> item = new ArrayList<>(COLUMNS.length);
item.add(Integer.toString(i));
item.add(TITLES[i]);
item.add(SUMMARY);
item.add(ID + i); // Doc ID
item.add(TITLES[i]); // Title
item.add(SUMMARY); // Summary on
item.add(BLANK); // summary off
item.add(BLANK); // entries
item.add(BLANK); // keywords
item.add(BLANK); // classname
item.add(BLANK); // screen title
item.add(hasIcon ? Integer.toString(mIcon) : null);
item.add(mIntent.getAction());
item.add(hasIcon ? Integer.toString(mIcon) : null); // Icon
item.add(mIntent.getAction()); // Intent action
item.add(BLANK); // target package
item.add(BLANK); // target class
item.add(BLANK); // enabled
item.add(BLANK); // key
// Note there is no user id. This is omitted because it is not being
// queried. Should the queries change, so should this method.
item.add(BLANK); // Key
item.add(Integer.toString(0)); // Payload Type
item.add(null); // Payload

View File

@@ -46,7 +46,6 @@ import static org.mockito.Mockito.when;
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class InstalledAppResultLoaderTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)