blob: 7b7076cd3fdfce1bbbad2a4fff9cbb919063c63a [file] [log] [blame]
/*
* 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 Bundle queryArgs = mSearchViewManager.buildQueryArgs();
assertFalse(queryArgs.isEmpty());
final long weekAgoTime = queryArgs.getLong(QUERY_ARG_LAST_MODIFIED_AFTER);
assertEquals(LocalDate.now().minusDays(7).atStartOfDay(ZoneId.systemDefault())
.toInstant().toEpochMilli(), weekAgoTime);
}
@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;
}
}