492 lines
18 KiB
Java
492 lines
18 KiB
Java
/*
|
|
* Copyright (C) 2016 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.documentsui.queries;
|
|
|
|
import static android.provider.DocumentsContract.QUERY_ARG_DISPLAY_NAME;
|
|
import static android.provider.DocumentsContract.QUERY_ARG_FILE_SIZE_OVER;
|
|
import static android.provider.DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER;
|
|
import static android.provider.DocumentsContract.QUERY_ARG_MIME_TYPES;
|
|
import static android.provider.DocumentsContract.Root.FLAG_SUPPORTS_SEARCH;
|
|
|
|
import static com.android.documentsui.base.State.ACTION_GET_CONTENT;
|
|
|
|
import static junit.framework.Assert.assertEquals;
|
|
import static junit.framework.Assert.assertFalse;
|
|
import static junit.framework.Assert.assertTrue;
|
|
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.spy;
|
|
import static org.mockito.Mockito.times;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
import android.content.Intent;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.provider.DocumentsContract;
|
|
import android.text.TextUtils;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
|
|
import androidx.annotation.Nullable;
|
|
import androidx.test.filters.SmallTest;
|
|
import androidx.test.runner.AndroidJUnit4;
|
|
|
|
import com.android.documentsui.MetricConsts;
|
|
import com.android.documentsui.R;
|
|
import com.android.documentsui.base.DocumentInfo;
|
|
import com.android.documentsui.base.DocumentStack;
|
|
import com.android.documentsui.base.EventHandler;
|
|
import com.android.documentsui.base.RootInfo;
|
|
import com.android.documentsui.queries.SearchViewManager.SearchManagerListener;
|
|
import com.android.documentsui.testing.TestEventHandler;
|
|
import com.android.documentsui.testing.TestHandler;
|
|
import com.android.documentsui.testing.TestMenu;
|
|
import com.android.documentsui.testing.TestMenuItem;
|
|
import com.android.documentsui.testing.TestTimer;
|
|
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
|
|
import java.time.LocalDate;
|
|
import java.time.ZoneId;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.Timer;
|
|
import java.util.TimerTask;
|
|
|
|
@RunWith(AndroidJUnit4.class)
|
|
@SmallTest
|
|
public final class SearchViewManagerTest {
|
|
|
|
private TestEventHandler<String> mTestEventHandler;
|
|
private TestTimer mTestTimer;
|
|
private TestHandler mTestHandler;
|
|
private TestMenu mTestMenu;
|
|
private TestMenuItem mSearchMenuItem;
|
|
private TestableSearchViewManager mSearchViewManager;
|
|
private SearchChipViewManager mSearchChipViewManager;
|
|
|
|
private boolean mListenerOnSearchChangedCalled;
|
|
|
|
@Before
|
|
public void setUp() {
|
|
mTestEventHandler = new TestEventHandler<>();
|
|
mTestTimer = new TestTimer();
|
|
mTestHandler = new TestHandler();
|
|
|
|
final SearchManagerListener searchListener = new SearchManagerListener() {
|
|
@Override
|
|
public void onSearchChanged(@Nullable String query) {
|
|
mListenerOnSearchChangedCalled = true;
|
|
}
|
|
|
|
@Override
|
|
public void onSearchFinished() {
|
|
}
|
|
|
|
@Override
|
|
public void onSearchViewChanged(boolean opened) {
|
|
}
|
|
|
|
@Override
|
|
public void onSearchChipStateChanged(View v) {
|
|
}
|
|
|
|
@Override
|
|
public void onSearchViewFocusChanged(boolean hasFocus) {
|
|
}
|
|
|
|
@Override
|
|
public void onSearchViewClearClicked() {
|
|
}
|
|
};
|
|
|
|
ViewGroup chipGroup = mock(ViewGroup.class);
|
|
mSearchChipViewManager = spy(new SearchChipViewManager(chipGroup));
|
|
mSearchViewManager = new TestableSearchViewManager(searchListener, mTestEventHandler,
|
|
mSearchChipViewManager, null /* savedState */, mTestTimer, mTestHandler);
|
|
|
|
mTestMenu = TestMenu.create();
|
|
mSearchMenuItem = mTestMenu.findItem(R.id.option_menu_search);
|
|
mSearchViewManager.install(mTestMenu, true, false);
|
|
}
|
|
|
|
private static class TestableSearchViewManager extends SearchViewManager {
|
|
|
|
private String mHistoryRecorded;
|
|
private boolean mIsHistoryRecorded;
|
|
|
|
public TestableSearchViewManager(
|
|
SearchManagerListener listener,
|
|
EventHandler<String> commandProcessor,
|
|
SearchChipViewManager chipViewManager,
|
|
@Nullable Bundle savedState,
|
|
Timer timer,
|
|
Handler handler) {
|
|
super(listener, commandProcessor, chipViewManager, savedState, timer, handler);
|
|
}
|
|
|
|
@Override
|
|
public TimerTask createSearchTask(String newText) {
|
|
TimerTask task = super.createSearchTask(newText);
|
|
TestTimer.Task testTask = new TestTimer.Task(task);
|
|
return testTask;
|
|
}
|
|
|
|
@Override
|
|
protected void recordHistoryInternal() {
|
|
mHistoryRecorded = getCurrentSearch();
|
|
mIsHistoryRecorded = true;
|
|
}
|
|
|
|
public String getRecordedHistory() {
|
|
return mHistoryRecorded;
|
|
}
|
|
|
|
public boolean isHistoryRecorded() {
|
|
return mIsHistoryRecorded;
|
|
}
|
|
}
|
|
|
|
private void fastForwardTo(long timeMs) {
|
|
mTestTimer.fastForwardTo(timeMs);
|
|
mTestHandler.dispatchAllMessages();
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testParseQueryContent_ActionIsNotMatched_NotParseQueryContent() {
|
|
final String queryString = "query";
|
|
Intent intent = new Intent();
|
|
intent.putExtra(Intent.EXTRA_CONTENT_QUERY, queryString);
|
|
|
|
mSearchViewManager.parseQueryContentFromIntent(intent, -1);
|
|
assertTrue(mSearchViewManager.getQueryContentFromIntent() == null);
|
|
}
|
|
|
|
@Test
|
|
public void testParseQueryContent_queryContentIsMatched() {
|
|
final String queryString = "query";
|
|
Intent intent = new Intent();
|
|
intent.putExtra(Intent.EXTRA_CONTENT_QUERY, queryString);
|
|
|
|
mSearchViewManager.parseQueryContentFromIntent(intent, ACTION_GET_CONTENT);
|
|
assertEquals(queryString, mSearchViewManager.getQueryContentFromIntent());
|
|
}
|
|
|
|
@Test
|
|
public void testIsExpanded_ExpandsOnClick() {
|
|
mSearchViewManager.onClick(null);
|
|
assertTrue(mSearchViewManager.isExpanded());
|
|
}
|
|
|
|
@Test
|
|
public void testIsExpanded_CollapsesOnMenuItemActionCollapse() {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onMenuItemActionCollapse(null);
|
|
assertFalse(mSearchViewManager.isExpanded());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_TrueHasCheckedChip() throws Exception {
|
|
mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
|
|
assertTrue(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_FalseOnClick() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
assertFalse(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_TrueOnQueryTextSubmit() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextSubmit("query");
|
|
assertTrue(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_FalseImmediatelyAfterOnQueryTextChange() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
assertFalse(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_TrueAfterOnQueryTextChangeAndWait() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
assertTrue(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_FalseWhenSecondOnQueryTextChangeResetsTimer() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS - 1);
|
|
mSearchViewManager.onQueryTextChange("qu");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
assertFalse(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_TrueAfterSecondOnQueryTextChangeResetsTimer() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS - 1);
|
|
mSearchViewManager.onQueryTextChange("qu");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS * 2);
|
|
assertTrue(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testIsSearching_FalseIfSearchCanceled() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
mSearchViewManager.cancelSearch();
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
assertFalse(mSearchViewManager.isSearching());
|
|
}
|
|
|
|
@Test
|
|
public void testOnSearchChanged_CalledAfterOnQueryTextSubmit() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextSubmit("q");
|
|
assertTrue(mListenerOnSearchChangedCalled);
|
|
}
|
|
|
|
@Test
|
|
public void testOnSearchChanged_NotCalledImmediatelyAfterOnQueryTextChanged() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
assertFalse(mListenerOnSearchChangedCalled);
|
|
}
|
|
|
|
@Test
|
|
public void testOnSearchChanged_CalledAfterOnQueryTextChangedAndWait() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
assertTrue(mListenerOnSearchChangedCalled);
|
|
}
|
|
|
|
@Test
|
|
public void testOnSearchChanged_CalledOnlyOnceAfterOnQueryTextSubmit() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
mSearchViewManager.onQueryTextSubmit("q");
|
|
|
|
// Clear the flag to check if it gets set again.
|
|
mListenerOnSearchChangedCalled = false;
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
assertFalse(mListenerOnSearchChangedCalled);
|
|
}
|
|
|
|
@Test
|
|
public void testOnSearchChanged_NotCalledForOnQueryTextSubmitIfSearchAlreadyFinished()
|
|
throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
// Clear the flag to check if it gets set again.
|
|
mListenerOnSearchChangedCalled = false;
|
|
mSearchViewManager.onQueryTextSubmit("q");
|
|
assertFalse(mListenerOnSearchChangedCalled);
|
|
}
|
|
|
|
@Test
|
|
public void testHistoryRecorded_recordOnQueryTextSubmit() {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextSubmit("q");
|
|
|
|
assertEquals(mSearchViewManager.getCurrentSearch(),
|
|
mSearchViewManager.getRecordedHistory());
|
|
}
|
|
|
|
@Test
|
|
public void testHistoryRecorded_skipWhenNoSearchString() {
|
|
mSearchViewManager.recordHistory();
|
|
|
|
assertFalse(mSearchViewManager.isHistoryRecorded());
|
|
}
|
|
|
|
@Test
|
|
public void testCheckedChipItems_IsEmptyIfSearchCanceled() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
|
|
mSearchViewManager.cancelSearch();
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
assertTrue(!mSearchChipViewManager.hasCheckedItems());
|
|
}
|
|
|
|
@Test
|
|
public void testBuildQueryArgs_hasSearchString() throws Exception {
|
|
final String query = "q";
|
|
mSearchViewManager.onClick(null);
|
|
mSearchViewManager.onQueryTextChange("q");
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
|
|
final Bundle queryArgs = mSearchViewManager.buildQueryArgs();
|
|
assertFalse(queryArgs.isEmpty());
|
|
|
|
final String queryString = queryArgs.getString(DocumentsContract.QUERY_ARG_DISPLAY_NAME);
|
|
assertEquals(query, queryString);
|
|
}
|
|
|
|
@Test
|
|
public void testBuildQueryArgs_emptySearchString_expandedSearchWithChips_hasEmptyButNotMissingSearchString()
|
|
throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
|
|
final String queryString =
|
|
mSearchViewManager.buildQueryArgs()
|
|
.getString(DocumentsContract.QUERY_ARG_DISPLAY_NAME);
|
|
assertEquals("", queryString);
|
|
}
|
|
|
|
@Test
|
|
public void testBuildQueryArgs_emptySearchString_withChipsWithoutExpandedSearch_hasNoSearchString()
|
|
throws Exception {
|
|
mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
|
|
assertFalse(mSearchViewManager.buildQueryArgs().containsKey(QUERY_ARG_DISPLAY_NAME));
|
|
}
|
|
|
|
@Test
|
|
public void testBuildQueryArgs_emptySearchString_expandedSearchWithNoChips_hasNoSearchString()
|
|
throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
fastForwardTo(SearchViewManager.SEARCH_DELAY_MS);
|
|
|
|
assertFalse(mSearchViewManager.buildQueryArgs().containsKey(QUERY_ARG_DISPLAY_NAME));
|
|
}
|
|
|
|
@Test
|
|
public void testBuildQueryArgs_hasMimeType() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
|
|
|
|
final Bundle queryArgs = mSearchViewManager.buildQueryArgs();
|
|
assertFalse(queryArgs.isEmpty());
|
|
|
|
final String[] mimeTypes = queryArgs.getStringArray(QUERY_ARG_MIME_TYPES);
|
|
assertTrue(mimeTypes.length > 0);
|
|
assertEquals("image/*", mimeTypes[0]);
|
|
}
|
|
|
|
@Test
|
|
public void testBuildQueryArgs_hasLargeFilesSize() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
|
|
|
|
final Bundle queryArgs = mSearchViewManager.buildQueryArgs();
|
|
assertFalse(queryArgs.isEmpty());
|
|
|
|
final long largeFilesSize = queryArgs.getLong(QUERY_ARG_FILE_SIZE_OVER);
|
|
assertEquals(10000000L, largeFilesSize);
|
|
}
|
|
|
|
@Test
|
|
public void testBuildQueryArgs_hasWeekAgoTime() throws Exception {
|
|
mSearchViewManager.onClick(null);
|
|
mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
|
|
|
|
final long startTime = LocalDate.now().minusDays(7).atStartOfDay(ZoneId.systemDefault())
|
|
.toInstant().toEpochMilli();
|
|
|
|
final Bundle queryArgs = mSearchViewManager.buildQueryArgs();
|
|
assertFalse(queryArgs.isEmpty());
|
|
|
|
final long endTime = LocalDate.now().minusDays(7).atStartOfDay(ZoneId.systemDefault())
|
|
.toInstant().toEpochMilli();
|
|
final long weekAgoTime = queryArgs.getLong(QUERY_ARG_LAST_MODIFIED_AFTER);
|
|
assertTrue(weekAgoTime == endTime || weekAgoTime == startTime);
|
|
}
|
|
|
|
@Test
|
|
public void testSupportsMimeTypesSearch_showChips() throws Exception {
|
|
RootInfo root = spy(new RootInfo());
|
|
when(root.isRecents()).thenReturn(false);
|
|
root.flags = FLAG_SUPPORTS_SEARCH;
|
|
root.queryArgs = QUERY_ARG_MIME_TYPES;
|
|
DocumentStack stack = new DocumentStack(root, new DocumentInfo());
|
|
|
|
mSearchViewManager.showMenu(stack);
|
|
|
|
verify(mSearchChipViewManager, times(1)).setChipsRowVisible(true);
|
|
}
|
|
|
|
@Test
|
|
public void testNotSupportsMimeTypesSearch_notShowChips() throws Exception {
|
|
RootInfo root = spy(new RootInfo());
|
|
when(root.isRecents()).thenReturn(false);
|
|
root.flags = FLAG_SUPPORTS_SEARCH;
|
|
root.queryArgs = TextUtils.join("\n",
|
|
new String[]{QUERY_ARG_DISPLAY_NAME, QUERY_ARG_FILE_SIZE_OVER,
|
|
QUERY_ARG_LAST_MODIFIED_AFTER});
|
|
DocumentStack stack = new DocumentStack(root, new DocumentInfo());
|
|
|
|
mSearchViewManager.showMenu(stack);
|
|
|
|
verify(mSearchChipViewManager, times(1)).setChipsRowVisible(false);
|
|
}
|
|
|
|
@Test
|
|
public void testSupportsSearch_showMenu() throws Exception {
|
|
RootInfo root = spy(new RootInfo());
|
|
when(root.isRecents()).thenReturn(false);
|
|
root.flags = FLAG_SUPPORTS_SEARCH;
|
|
DocumentStack stack = new DocumentStack(root, new DocumentInfo());
|
|
|
|
mSearchViewManager.showMenu(stack);
|
|
|
|
assertTrue(mSearchMenuItem.isVisible());
|
|
}
|
|
|
|
@Test
|
|
public void testNotSupportsSearch_notShowMenuAndChips() throws Exception {
|
|
RootInfo root = spy(new RootInfo());
|
|
when(root.isRecents()).thenReturn(false);
|
|
root.queryArgs = QUERY_ARG_MIME_TYPES;
|
|
DocumentStack stack = new DocumentStack(root, new DocumentInfo());
|
|
|
|
mSearchViewManager.install(mTestMenu, true, false);
|
|
mSearchViewManager.showMenu(stack);
|
|
|
|
assertFalse(mSearchMenuItem.isVisible());
|
|
verify(mSearchChipViewManager, times(1)).setChipsRowVisible(false);
|
|
}
|
|
|
|
private static Set<SearchChipData> getFakeSearchChipDataList() {
|
|
final Set<SearchChipData> chipDataList = new HashSet<>();
|
|
chipDataList.add(new SearchChipData(MetricConsts.TYPE_CHIP_IMAGES,
|
|
0 /* titleRes */, new String[]{"image/*"}));
|
|
chipDataList.add(new SearchChipData(MetricConsts.TYPE_CHIP_LARGE_FILES,
|
|
0 /* titleRes */, new String[]{""}));
|
|
chipDataList.add(new SearchChipData(MetricConsts.TYPE_CHIP_FROM_THIS_WEEK,
|
|
0 /* titleRes */, new String[]{""}));
|
|
return chipDataList;
|
|
}
|
|
}
|