Prevent race conditions after dropping database
Block all loaders from accessing the database before indexing is complete. Bug: 37501479 Test: make RunSettingsRoboTests Change-Id: I2af98dcf6bceacbeffa70bd40879c97bb0cbc828 Merged-In: I02f8423c0ffc27abbb8ceb61a8c47d2f0796d0bb
This commit is contained in:
@@ -79,7 +79,7 @@ public class SettingsActivity extends SettingsDrawerActivity
|
||||
|
||||
private static final String LOG_TAG = "Settings";
|
||||
|
||||
private static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
|
||||
public static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
|
||||
|
||||
// Constants for state save/restore
|
||||
private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
|
||||
|
12
src/com/android/settings/search/IndexingCallback.java
Normal file
12
src/com/android/settings/search/IndexingCallback.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.android.settings.search;
|
||||
|
||||
/**
|
||||
* Callback for Settings search indexing.
|
||||
*/
|
||||
public interface IndexingCallback {
|
||||
|
||||
/**
|
||||
* Called when Indexing is finished.
|
||||
*/
|
||||
void onIndexingFinished();
|
||||
}
|
@@ -42,6 +42,7 @@ import android.util.Xml;
|
||||
import com.android.settings.core.PreferenceController;
|
||||
import com.android.settings.search.IndexDatabaseHelper;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.IndexingCallback;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settings.search.SearchIndexableResources;
|
||||
|
||||
@@ -132,7 +133,8 @@ public class DatabaseIndexingManager {
|
||||
|
||||
private final String mBaseAuthority;
|
||||
|
||||
private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
|
||||
@VisibleForTesting
|
||||
final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false);
|
||||
|
||||
@VisibleForTesting
|
||||
final UpdateData mDataToProcess = new UpdateData();
|
||||
@@ -147,17 +149,13 @@ public class DatabaseIndexingManager {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return mIsAvailable.get();
|
||||
public boolean isIndexingComplete() {
|
||||
return mIsIndexingComplete.get();
|
||||
}
|
||||
|
||||
public void indexDatabase() {
|
||||
AsyncTask.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
performIndexing();
|
||||
}
|
||||
});
|
||||
public void indexDatabase(IndexingCallback callback) {
|
||||
IndexingTask task = new IndexingTask(callback);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,15 +169,12 @@ public class DatabaseIndexingManager {
|
||||
final List<ResolveInfo> list =
|
||||
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
|
||||
|
||||
final String localeStr = Locale.getDefault().toString();
|
||||
final String fingerprint = Build.FINGERPRINT;
|
||||
String localeStr = Locale.getDefault().toString();
|
||||
String fingerprint = Build.FINGERPRINT;
|
||||
final boolean isFullIndex = isFullIndex(localeStr, fingerprint);
|
||||
|
||||
// Drop the database when the locale or build has changed. This eliminates rows which are
|
||||
// dynamically inserted in the old language, or deprecated settings.
|
||||
if (isFullIndex) {
|
||||
final SQLiteDatabase db = getWritableDatabase();
|
||||
IndexDatabaseHelper.getInstance(mContext).reconstruct(db);
|
||||
rebuildDatabase();
|
||||
}
|
||||
|
||||
for (final ResolveInfo info : list) {
|
||||
@@ -217,6 +212,18 @@ public class DatabaseIndexingManager {
|
||||
return !isLocaleIndexed || !isBuildIndexed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstruct the database in the following cases:
|
||||
* - Language has changed
|
||||
* - Build has changed
|
||||
*/
|
||||
private void rebuildDatabase() {
|
||||
// Drop the database when the locale or build has changed. This eliminates rows which are
|
||||
// dynamically inserted in the old language, or deprecated settings.
|
||||
final SQLiteDatabase db = getWritableDatabase();
|
||||
IndexDatabaseHelper.getInstance(mContext).reconstruct(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new data to the database and verifies the correctness of the ENABLED column.
|
||||
* First, the data to be updated and all non-indexable keys are copied locally.
|
||||
@@ -229,7 +236,6 @@ public class DatabaseIndexingManager {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void updateDatabase(boolean needsReindexing, String localeStr) {
|
||||
mIsAvailable.set(false);
|
||||
final UpdateData copy;
|
||||
|
||||
synchronized (mDataToProcess) {
|
||||
@@ -264,8 +270,6 @@ public class DatabaseIndexingManager {
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
mIsAvailable.set(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1223,4 +1227,33 @@ public class DatabaseIndexingManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IndexingTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
@VisibleForTesting
|
||||
IndexingCallback mCallback;
|
||||
|
||||
public IndexingTask(IndexingCallback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
mIsIndexingComplete.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
performIndexing();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
mIsIndexingComplete.set(true);
|
||||
if (mCallback != null) {
|
||||
mCallback.onIndexingFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,6 +21,7 @@ import android.view.Menu;
|
||||
|
||||
import android.view.View;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.search.IndexingCallback;
|
||||
|
||||
/**
|
||||
* FeatureProvider for Settings Search
|
||||
@@ -68,7 +69,12 @@ public interface SearchFeatureProvider {
|
||||
/**
|
||||
* Updates the Settings indexes
|
||||
*/
|
||||
void updateIndex(Context context);
|
||||
void updateIndex(Context context, IndexingCallback callback);
|
||||
|
||||
/**
|
||||
* @returns true when indexing is complete.
|
||||
*/
|
||||
boolean isIndexingComplete(Context context);
|
||||
|
||||
/**
|
||||
* Initializes the feedback button in case it was dismissed.
|
||||
|
@@ -19,6 +19,7 @@ package com.android.settings.search2;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -26,6 +27,9 @@ import android.view.MenuItem;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.PackageManagerWrapperImpl;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.search.IndexingCallback;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* FeatureProvider for the refactored search code.
|
||||
@@ -88,6 +92,11 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
return mDatabaseIndexingManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndexingComplete(Context context) {
|
||||
return getIndexingManager(context).isIndexingComplete();
|
||||
}
|
||||
|
||||
public SiteMapManager getSiteMapManager() {
|
||||
if (mSiteMapManager == null) {
|
||||
mSiteMapManager = new SiteMapManager();
|
||||
@@ -96,9 +105,9 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIndex(Context context) {
|
||||
public void updateIndex(Context context, IndexingCallback callback) {
|
||||
long indexStartTime = System.currentTimeMillis();
|
||||
getIndexingManager(context).indexDatabase();
|
||||
getIndexingManager(context).indexDatabase(callback);
|
||||
Log.d(TAG, "IndexDatabase() took " +
|
||||
(System.currentTimeMillis() - indexStartTime) + " ms");
|
||||
}
|
||||
|
@@ -41,12 +41,23 @@ import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.IndexingCallback;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* This fragment manages the lifecycle of indexing and searching.
|
||||
*
|
||||
* In onCreate, the indexing process is initiated in DatabaseIndexingManager.
|
||||
* While the indexing is happening, loaders are blocked from accessing the database, but the user
|
||||
* is free to start typing their query.
|
||||
*
|
||||
* When the indexing is complete, the fragment gets a callback to initialize the loaders and search
|
||||
* the query if the user has entered text.
|
||||
*/
|
||||
public class SearchFragment extends InstrumentedFragment implements SearchView.OnQueryTextListener,
|
||||
LoaderManager.LoaderCallbacks<List<? extends SearchResult>> {
|
||||
LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {
|
||||
private static final String TAG = "SearchFragment";
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -59,26 +70,30 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
private static final String STATE_RESULT_CLICK_COUNT = "state_result_click_count";
|
||||
|
||||
// Loader IDs
|
||||
private static final int LOADER_ID_DATABASE = 1;
|
||||
private static final int LOADER_ID_INSTALLED_APPS = 2;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_DATABASE = 1;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_INSTALLED_APPS = 2;
|
||||
|
||||
private static final int NUM_QUERY_LOADERS = 2;
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
@VisibleForTesting
|
||||
AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
|
||||
|
||||
// Logging
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
@VisibleForTesting
|
||||
static final String RESULT_CLICK_COUNT = "settings_search_result_click_count";
|
||||
|
||||
@VisibleForTesting
|
||||
String mQuery;
|
||||
|
||||
private boolean mNeverEnteredQuery = true;
|
||||
private boolean mShowingSavedQuery;
|
||||
@VisibleForTesting
|
||||
boolean mShowingSavedQuery;
|
||||
private int mResultClickCount;
|
||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
private SavedQueryController mSavedQueryController;
|
||||
@VisibleForTesting
|
||||
SavedQueryController mSavedQueryController;
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
SearchFeatureProvider mSearchFeatureProvider;
|
||||
@@ -87,7 +102,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
RecyclerView mResultsRecyclerView;
|
||||
private SearchView mSearchView;
|
||||
@VisibleForTesting
|
||||
SearchView mSearchView;
|
||||
private LinearLayout mNoResultsView;
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -116,6 +132,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
mSearchAdapter = new SearchResultsAdapter(this);
|
||||
mSavedQueryController = new SavedQueryController(
|
||||
@@ -127,15 +144,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
mNeverEnteredQuery = savedInstanceState.getBoolean(STATE_NEVER_ENTERED_QUERY);
|
||||
mResultClickCount = savedInstanceState.getInt(STATE_RESULT_CLICK_COUNT);
|
||||
mShowingSavedQuery = savedInstanceState.getBoolean(STATE_SHOWING_SAVED_QUERY);
|
||||
if (mShowingSavedQuery) {
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
} else {
|
||||
loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
|
||||
loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
|
||||
}
|
||||
} else {
|
||||
mShowingSavedQuery = true;
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
}
|
||||
|
||||
final Activity activity = getActivity();
|
||||
@@ -148,7 +158,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
|
||||
// Run the Index update only if we have some space
|
||||
if (!Utils.isLowStorage(activity)) {
|
||||
mSearchFeatureProvider.updateIndex(activity);
|
||||
mSearchFeatureProvider.updateIndex(activity, this /* indexingCallback */);
|
||||
} else {
|
||||
Log.w(TAG, "Cannot update the Indexer as we are running low on storage space!");
|
||||
}
|
||||
@@ -170,12 +180,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (TextUtils.isEmpty(mQuery)) {
|
||||
return;
|
||||
}
|
||||
final String query = mQuery;
|
||||
mQuery = "";
|
||||
onQueryTextChange(query);
|
||||
requery();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,6 +223,11 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
mNeverEnteredQuery = false;
|
||||
mQuery = query;
|
||||
|
||||
// If indexing is not finished, register the query text, but don't search.
|
||||
if (!mSearchFeatureProvider.isIndexingComplete(getActivity())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isEmptyQuery) {
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.destroyLoader(LOADER_ID_DATABASE);
|
||||
@@ -276,6 +286,22 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
public void onLoaderReset(Loader<List<? extends SearchResult>> loader) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when Indexing is completed.
|
||||
*/
|
||||
@Override
|
||||
public void onIndexingFinished() {
|
||||
if (mShowingSavedQuery) {
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
} else {
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
|
||||
loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
|
||||
}
|
||||
|
||||
requery();
|
||||
}
|
||||
|
||||
public void onSearchResultClicked() {
|
||||
mSavedQueryController.saveQuery(mQuery);
|
||||
mResultClickCount++;
|
||||
@@ -309,6 +335,15 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
return mSearchAdapter.getSearchResults();
|
||||
}
|
||||
|
||||
private void requery() {
|
||||
if (TextUtils.isEmpty(mQuery)) {
|
||||
return;
|
||||
}
|
||||
final String query = mQuery;
|
||||
mQuery = "";
|
||||
onQueryTextChange(query);
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
SearchView makeSearchView(ActionBar actionBar, String query) {
|
||||
final SearchView searchView = new SearchView(actionBar.getThemedContext());
|
||||
|
@@ -18,7 +18,6 @@
|
||||
package com.android.settings.search;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.view.Menu;
|
||||
|
||||
import com.android.settings.SettingsRobolectricTestRunner;
|
||||
@@ -35,14 +34,11 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
@@ -73,19 +69,10 @@ public class SearchFeatureProviderImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSiteMapManager_shouldCacheInstanec() {
|
||||
public void getSiteMapManager_shouldCacheInstance() {
|
||||
final SiteMapManager manager1 = mProvider.getSiteMapManager();
|
||||
final SiteMapManager manager2 = mProvider.getSiteMapManager();
|
||||
|
||||
assertThat(manager1).isSameAs(manager2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateIndexNewSearch_UsesDatabaseIndexingManager() {
|
||||
mProvider = spy(new SearchFeatureProviderImpl());
|
||||
when(mProvider.isEnabled(mActivity)).thenReturn(true);
|
||||
|
||||
mProvider.updateIndex(mActivity);
|
||||
verify(mProvider).getIndexingManager(any(Context.class));
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.search.IndexDatabaseHelper;
|
||||
import com.android.settings.search.IndexingCallback;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settings.testutils.DatabaseTestUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils;
|
||||
@@ -47,6 +48,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowContentResolver;
|
||||
@@ -67,6 +69,7 @@ import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyList;
|
||||
import static org.mockito.Matchers.anyMap;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -933,6 +936,32 @@ public class DatabaseIndexingManagerTest {
|
||||
assertThat(cursor.getCount()).isEqualTo(1);
|
||||
assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAsyncTask_onPostExecute_performsCallback() {
|
||||
IndexingCallback callback = mock(IndexingCallback.class);
|
||||
|
||||
DatabaseIndexingManager.IndexingTask task = mManager.new IndexingTask(callback);
|
||||
task.execute();
|
||||
|
||||
Robolectric.flushForegroundThreadScheduler();
|
||||
|
||||
verify(callback).onIndexingFinished();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAsyncTask_onPostExecute_setsIndexingComplete() {
|
||||
SearchFeatureProviderImpl provider = new SearchFeatureProviderImpl();
|
||||
DatabaseIndexingManager manager = spy(provider.getIndexingManager(mContext));
|
||||
DatabaseIndexingManager.IndexingTask task = manager.new IndexingTask(null);
|
||||
doNothing().when(manager).performIndexing();
|
||||
|
||||
task.execute();
|
||||
Robolectric.flushForegroundThreadScheduler();
|
||||
|
||||
assertThat(provider.isIndexingComplete(mContext)).isTrue();
|
||||
}
|
||||
|
||||
// Util functions
|
||||
|
||||
private SearchIndexableRaw getFakeRaw() {
|
||||
|
@@ -26,6 +26,7 @@ import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.search.IndexingCallback;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -42,6 +43,7 @@ import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
@@ -104,10 +106,7 @@ public class SearchFragmentTest {
|
||||
activityController = Robolectric.buildActivity(SearchActivity.class);
|
||||
activityController.setup(bundle);
|
||||
|
||||
verify(mFeatureFactory.searchFeatureProvider)
|
||||
.getDatabaseSearchLoader(any(Context.class), anyString());
|
||||
verify(mFeatureFactory.searchFeatureProvider)
|
||||
.getInstalledAppSearchLoader(any(Context.class), anyString());
|
||||
assertThat(fragment.mQuery).isEqualTo(testQuery);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,6 +120,8 @@ public class SearchFragmentTest {
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content);
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
fragment.mQuery = "";
|
||||
|
||||
@@ -133,8 +134,6 @@ public class SearchFragmentTest {
|
||||
.getDatabaseSearchLoader(any(Context.class), anyString());
|
||||
verify(mFeatureFactory.searchFeatureProvider, never())
|
||||
.getInstalledAppSearchLoader(any(Context.class), anyString());
|
||||
verify(mFeatureFactory.searchFeatureProvider, times(2))
|
||||
.getSavedQueryLoader(any(Context.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,6 +153,8 @@ public class SearchFragmentTest {
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content);
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
fragment.onQueryTextChange(testQuery);
|
||||
activityController.get().onBackPressed();
|
||||
@@ -181,15 +182,16 @@ public class SearchFragmentTest {
|
||||
.thenReturn(mInstalledAppResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
|
||||
ActivityController<SearchActivity> activityController =
|
||||
Robolectric.buildActivity(SearchActivity.class);
|
||||
activityController.setup();
|
||||
|
||||
SearchFragment fragment = spy((SearchFragment) activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content));
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController);
|
||||
fragment.mQuery = "123";
|
||||
|
||||
fragment.onQueryTextChange("");
|
||||
|
||||
verify(mFeatureFactory.searchFeatureProvider, never())
|
||||
@@ -215,9 +217,12 @@ public class SearchFragmentTest {
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content);
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
fragment.onAttach(null);
|
||||
verify(mFeatureFactory.searchFeatureProvider).updateIndex(any(Context.class));
|
||||
verify(mFeatureFactory.searchFeatureProvider).updateIndex(any(Context.class),
|
||||
any(IndexingCallback.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -237,6 +242,8 @@ public class SearchFragmentTest {
|
||||
|
||||
SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content));
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
fragment.onQueryTextChange("non-empty");
|
||||
|
||||
@@ -261,11 +268,11 @@ public class SearchFragmentTest {
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content));
|
||||
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
when(fragment.getLoaderManager()).thenReturn(mock(LoaderManager.class));
|
||||
|
||||
fragment.onQueryTextChange("");
|
||||
|
||||
Robolectric.flushForegroundThreadScheduler();
|
||||
|
||||
verify(mFeatureFactory.searchFeatureProvider).hideFeedbackButton();
|
||||
@@ -281,18 +288,65 @@ public class SearchFragmentTest {
|
||||
.thenReturn(new MockAppLoader(RuntimeEnvironment.application));
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
ActivityController<SearchActivity> activityController =
|
||||
Robolectric.buildActivity(SearchActivity.class);
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content);
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
fragment.onQueryTextChange("non-empty");
|
||||
Robolectric.flushForegroundThreadScheduler();
|
||||
|
||||
verify(mFeatureFactory.searchFeatureProvider).showFeedbackButton(any(SearchFragment.class),
|
||||
any(View.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preIndexingFinished_isIndexingFinishedFlag_isFalse() {
|
||||
ActivityController<SearchActivity> activityController =
|
||||
Robolectric.buildActivity(SearchActivity.class);
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content);
|
||||
|
||||
fragment.onQueryTextChange("non-empty");
|
||||
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
|
||||
.thenReturn(false);
|
||||
}
|
||||
|
||||
Robolectric.flushForegroundThreadScheduler();
|
||||
@Test
|
||||
public void onIndexingFinished_notShowingSavedQuery_initLoaders() {
|
||||
ActivityController<SearchActivity> activityController =
|
||||
Robolectric.buildActivity(SearchActivity.class);
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content));
|
||||
final LoaderManager loaderManager = mock(LoaderManager.class);
|
||||
when(fragment.getLoaderManager()).thenReturn(loaderManager);
|
||||
fragment.mShowingSavedQuery = false;
|
||||
fragment.mQuery = null;
|
||||
|
||||
verify(mFeatureFactory.searchFeatureProvider).showFeedbackButton(any(SearchFragment.class),
|
||||
any(View.class));
|
||||
fragment.onIndexingFinished();
|
||||
|
||||
verify(loaderManager).initLoader(eq(SearchFragment.LOADER_ID_DATABASE),
|
||||
eq(null), any(LoaderManager.LoaderCallbacks.class));
|
||||
verify(loaderManager).initLoader(eq(SearchFragment.LOADER_ID_INSTALLED_APPS),
|
||||
eq(null), any(LoaderManager.LoaderCallbacks.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onIndexingFinished_showingSavedQuery_loadsSavedQueries() {
|
||||
ActivityController<SearchActivity> activityController =
|
||||
Robolectric.buildActivity(SearchActivity.class);
|
||||
activityController.setup();
|
||||
SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
|
||||
.findFragmentById(R.id.main_content));
|
||||
fragment.mShowingSavedQuery = true;
|
||||
ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController);
|
||||
|
||||
fragment.onIndexingFinished();
|
||||
|
||||
verify(fragment.mSavedQueryController).loadSavedQueries();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user