1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package com.android.settings.search;
19
20import static com.google.common.truth.Truth.assertThat;
21import static org.mockito.Matchers.any;
22import static org.mockito.Matchers.anyString;
23import static org.mockito.Mockito.never;
24import static org.mockito.Mockito.times;
25import static org.mockito.Mockito.verify;
26import static org.mockito.Mockito.when;
27
28import android.app.Activity;
29import android.content.Context;
30import android.content.Intent;
31import android.graphics.drawable.Drawable;
32import android.util.Pair;
33import android.view.ViewGroup;
34import android.widget.FrameLayout;
35
36import com.android.settings.R;
37import com.android.settings.TestConfig;
38import com.android.settings.search.SearchResult.Builder;
39import com.android.settings.search.ranking.SearchResultsRankerCallback;
40import com.android.settings.testutils.SettingsRobolectricTestRunner;
41
42import org.junit.Before;
43import org.junit.Test;
44import org.junit.runner.RunWith;
45import org.mockito.ArgumentCaptor;
46import org.mockito.Captor;
47import org.mockito.Mock;
48import org.mockito.MockitoAnnotations;
49import org.robolectric.Robolectric;
50import org.robolectric.annotation.Config;
51import org.robolectric.shadows.ShadowLooper;
52
53import java.util.ArrayList;
54import java.util.Arrays;
55import java.util.HashSet;
56import java.util.List;
57import java.util.Objects;
58import java.util.Set;
59
60@RunWith(SettingsRobolectricTestRunner.class)
61@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
62public class SearchResultsAdapterTest {
63
64    @Mock
65    private SearchFragment mFragment;
66    @Mock
67    private SearchFeatureProvider mSearchFeatureProvider;
68    @Mock
69    private Context mMockContext;
70    @Captor
71    private ArgumentCaptor<Integer> mSearchResultsCountCaptor =
72            ArgumentCaptor.forClass(Integer.class);
73    private SearchResultsAdapter mAdapter;
74    private Context mContext;
75    private String mLoaderClassName;
76
77    private String[] TITLES = {"alpha", "bravo", "charlie", "appAlpha", "appBravo", "appCharlie"};
78
79    @Before
80    public void setUp() {
81        MockitoAnnotations.initMocks(this);
82        mContext = Robolectric.buildActivity(Activity.class).get();
83        mLoaderClassName = DatabaseResultLoader.class.getName();
84        when(mFragment.getContext()).thenReturn(mMockContext);
85        when(mMockContext.getApplicationContext()).thenReturn(mContext);
86        when(mSearchFeatureProvider.smartSearchRankingTimeoutMs(any(Context.class)))
87                .thenReturn(300L);
88        mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider);
89    }
90
91    @Test
92    public void testNoResultsAdded_emptyListReturned() {
93        List<SearchResult> updatedResults = mAdapter.getSearchResults();
94        assertThat(updatedResults).isEmpty();
95    }
96
97    @Test
98    public void testSingleSourceMerge_exactCopyReturned() {
99        Set<SearchResult> intentResults = getIntentSampleResults();
100        mAdapter.initializeSearch("");
101        mAdapter.addSearchResults(intentResults, mLoaderClassName);
102        mAdapter.notifyResultsLoaded();
103
104        List<SearchResult> updatedResults = mAdapter.getSearchResults();
105        assertThat(updatedResults).containsAllIn(intentResults);
106    }
107
108    @Test
109    public void testCreateViewHolder_returnsIntentResult() {
110        ViewGroup group = new FrameLayout(mContext);
111        SearchViewHolder view = mAdapter.onCreateViewHolder(group,
112                ResultPayload.PayloadType.INTENT);
113        assertThat(view).isInstanceOf(IntentSearchViewHolder.class);
114    }
115
116    @Test
117    public void testCreateViewHolder_returnsIntentSwitchResult() {
118        // TODO (b/62807132) test for InlineResult
119        ViewGroup group = new FrameLayout(mContext);
120        SearchViewHolder view = mAdapter.onCreateViewHolder(group,
121                ResultPayload.PayloadType.INLINE_SWITCH);
122        assertThat(view).isInstanceOf(IntentSearchViewHolder.class);
123    }
124
125    @Test
126    public void testEndToEndSearch_properResultsMerged_correctOrder() {
127        mAdapter.initializeSearch("");
128        mAdapter.addSearchResults(new HashSet<>(getDummyAppResults()),
129                InstalledAppResultLoader.class.getName());
130        mAdapter.addSearchResults(new HashSet<>(getDummyDbResults()),
131                DatabaseResultLoader.class.getName());
132        mAdapter.notifyResultsLoaded();
133
134        List<SearchResult> results = mAdapter.getSearchResults();
135        assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
136        assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
137        assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
138        assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
139        assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
140        assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
141        verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
142        assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
143    }
144
145    @Test
146    public void testEndToEndSearch_addResults_resultsAddedInOrder() {
147        List<SearchResult> appResults = getDummyAppResults();
148        List<SearchResult> dbResults = getDummyDbResults();
149        mAdapter.initializeSearch("");
150        // Add two individual items
151        mAdapter.addSearchResults(new HashSet<>(appResults.subList(0, 1)),
152                InstalledAppResultLoader.class.getName());
153        mAdapter.addSearchResults(new HashSet<>(dbResults.subList(0, 1)),
154                DatabaseResultLoader.class.getName());
155        mAdapter.notifyResultsLoaded();
156        // Add super-set of items
157        mAdapter.initializeSearch("");
158        mAdapter.addSearchResults(
159                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
160        mAdapter.addSearchResults(
161                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
162        mAdapter.notifyResultsLoaded();
163
164        List<SearchResult> results = mAdapter.getSearchResults();
165        assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
166        assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
167        assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
168        assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
169        assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
170        assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
171        verify(mFragment, times(2)).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
172        assertThat(mSearchResultsCountCaptor.getAllValues().toArray())
173                .isEqualTo(new Integer[] {2, 6});
174    }
175
176    @Test
177    public void testEndToEndSearch_removeResults_resultsAdded() {
178        List<SearchResult> appResults = getDummyAppResults();
179        List<SearchResult> dbResults = getDummyDbResults();
180        // Add list of items
181        mAdapter.initializeSearch("");
182        mAdapter.addSearchResults(new HashSet<>(appResults),
183                InstalledAppResultLoader.class.getName());
184        mAdapter.addSearchResults(new HashSet<>(dbResults),
185                DatabaseResultLoader.class.getName());
186        mAdapter.notifyResultsLoaded();
187        // Add subset of items
188        mAdapter.initializeSearch("");
189        mAdapter.addSearchResults(new HashSet<>(appResults.subList(0, 1)),
190                InstalledAppResultLoader.class.getName());
191        mAdapter.addSearchResults(new HashSet<>(dbResults.subList(0, 1)),
192                DatabaseResultLoader.class.getName());
193        mAdapter.notifyResultsLoaded();
194
195        List<SearchResult> results = mAdapter.getSearchResults();
196        assertThat(results.get(0).title).isEqualTo(TITLES[0]);
197        assertThat(results.get(1).title).isEqualTo(TITLES[3]);
198        verify(mFragment, times(2)).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
199        assertThat(mSearchResultsCountCaptor.getAllValues().toArray())
200                .isEqualTo(new Integer[] {6, 2});
201    }
202    @Test
203    public void testEndToEndSearch_smartSearchRankingEnabledAndSucceededAfterResultsLoaded() {
204        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
205
206        List<SearchResult> appResults = getDummyAppResults();
207        List<SearchResult> dbResults = getDummyDbResults();
208        mAdapter.initializeSearch("");
209        mAdapter.addSearchResults(
210                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
211        mAdapter.addSearchResults(
212                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
213        mAdapter.notifyResultsLoaded();
214        mAdapter.onRankingScoresAvailable(getDummyRankingScores());
215
216        List<SearchResult> results = mAdapter.getSearchResults();
217        assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
218        assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
219        assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
220        assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
221        assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
222        assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
223        verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
224        assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
225        assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.SUCCEEDED);
226    }
227
228    @Test
229    public void testEndToEndSearch_smartSearchRankingEnabledAndSucceededBeforeResultsLoaded() {
230        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
231
232        List<SearchResult> appResults = getDummyAppResults();
233        List<SearchResult> dbResults = getDummyDbResults();
234        mAdapter.initializeSearch("");
235        mAdapter.onRankingScoresAvailable(getDummyRankingScores());
236        mAdapter.addSearchResults(
237                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
238        mAdapter.addSearchResults(
239                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
240        mAdapter.notifyResultsLoaded();
241
242        List<SearchResult> results = mAdapter.getSearchResults();
243        assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
244        assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
245        assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
246        assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
247        assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
248        assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
249        verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
250        assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
251        assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.SUCCEEDED);
252    }
253
254    @Test
255    public void testEndToEndSearch_smartSearchRankingEnabledAndFailedAfterResultsLoaded() {
256        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
257
258        List<SearchResult> appResults = getDummyAppResults();
259        List<SearchResult> dbResults = getDummyDbResults();
260        mAdapter.initializeSearch("");
261        mAdapter.addSearchResults(
262                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
263        mAdapter.addSearchResults(
264                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
265        mAdapter.notifyResultsLoaded();
266        mAdapter.onRankingFailed();
267
268        List<SearchResult> results = mAdapter.getSearchResults();
269        assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
270        assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
271        assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
272        assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
273        assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
274        assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
275        verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
276        assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
277        assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.FAILED);
278    }
279
280    @Test
281    public void testEndToEndSearch_smartSearchRankingEnabledAndFailedBeforeResultsLoaded() {
282        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
283
284        List<SearchResult> appResults = getDummyAppResults();
285        List<SearchResult> dbResults = getDummyDbResults();
286        mAdapter.initializeSearch("");
287        mAdapter.onRankingFailed();
288        mAdapter.addSearchResults(
289                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
290        mAdapter.addSearchResults(
291                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
292        mAdapter.notifyResultsLoaded();
293
294        List<SearchResult> results = mAdapter.getSearchResults();
295        assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
296        assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
297        assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
298        assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
299        assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
300        assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
301        verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
302        assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
303        assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.FAILED);
304    }
305
306    @Test
307    public void testEndToEndSearch_smartSearchRankingEnabledAndTimedoutAfterResultsLoaded() {
308        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
309
310        List<SearchResult> appResults = getDummyAppResults();
311        List<SearchResult> dbResults = getDummyDbResults();
312        mAdapter.initializeSearch("");
313        mAdapter.addSearchResults(
314                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
315        mAdapter.addSearchResults(
316                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
317        mAdapter.notifyResultsLoaded();
318
319        waitUntilRankingTimesOut();
320
321        List<SearchResult> results = mAdapter.getSearchResults();
322        assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
323        assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
324        assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
325        assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
326        assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
327        assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
328        verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
329        assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
330        assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.TIMED_OUT);
331    }
332
333    @Test
334    public void testEndToEndSearch_smartSearchRankingEnabledAndTimedoutBeforeResultsLoaded() {
335        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
336
337        List<SearchResult> appResults = getDummyAppResults();
338        List<SearchResult> dbResults = getDummyDbResults();
339        mAdapter.initializeSearch("");
340
341        waitUntilRankingTimesOut();
342
343        mAdapter.addSearchResults(
344                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
345        mAdapter.addSearchResults(
346                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
347        mAdapter.notifyResultsLoaded();
348
349        List<SearchResult> results = mAdapter.getSearchResults();
350        assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
351        assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
352        assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
353        assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
354        assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
355        assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
356        verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
357        assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
358        assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.TIMED_OUT);
359    }
360
361    @Test
362    public void testDoSmartRanking_shouldRankAppResultsAfterDbResults() {
363        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
364
365        List<SearchResult> appResults = getDummyAppResults();
366        List<SearchResult> dbResults = getDummyDbResults();
367        mAdapter.initializeSearch("");
368        mAdapter.addSearchResults(
369                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
370        mAdapter.addSearchResults(
371                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
372        mAdapter.notifyResultsLoaded();
373        mAdapter.onRankingScoresAvailable(getDummyRankingScores());
374        List<SearchResult> results = mAdapter.doAsyncRanking();
375        assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
376        assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
377        assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
378        assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
379        assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
380        assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
381    }
382
383    @Test
384    public void testDoSmartRanking_shouldRankResultsWithMissingScoresAfterScoredResults() {
385        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
386
387        List<SearchResult> appResults = getDummyAppResults();
388        List<SearchResult> dbResults = getDummyDbResults();
389        mAdapter.initializeSearch("");
390        mAdapter.addSearchResults(
391                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
392        mAdapter.addSearchResults(
393                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
394        mAdapter.notifyResultsLoaded();
395        List<Pair<String, Float>> rankingScores = getDummyRankingScores();
396        rankingScores.remove(1); // no ranking score for alpha
397        mAdapter.onRankingScoresAvailable(rankingScores);
398        List<SearchResult> results = mAdapter.doAsyncRanking();
399        assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
400        assertThat(results.get(1).title).isEqualTo(TITLES[1]); // bravo
401        assertThat(results.get(2).title).isEqualTo(TITLES[0]); // alpha
402        assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
403        assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
404        assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
405    }
406
407    @Test
408    public void testGetUnsortedLoadedResults () {
409        List<SearchResult> appResults = getDummyAppResults();
410        List<SearchResult> dbResults = getDummyDbResults();
411        mAdapter.initializeSearch("");
412        mAdapter.addSearchResults(
413                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
414        mAdapter.addSearchResults(
415                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
416        Set<CharSequence> expectedDbTitles = new HashSet<>(
417                Arrays.asList("alpha", "bravo", "charlie"));
418        Set<CharSequence> expectedAppTitles = new HashSet<>(
419                Arrays.asList("appAlpha", "appBravo", "appCharlie"));
420        Set<CharSequence> actualDbTitles = new HashSet<>();
421        Set<CharSequence> actualAppTitles = new HashSet<>();
422        for (SearchResult result : mAdapter.getUnsortedLoadedResults(SearchResultsAdapter
423                .DB_RESULTS_LOADER_KEY)) {
424            actualDbTitles.add(result.title);
425        }
426        for (SearchResult result : mAdapter.getUnsortedLoadedResults(SearchResultsAdapter
427                .APP_RESULTS_LOADER_KEY)) {
428            actualAppTitles.add(result.title);
429        }
430        assertThat(actualDbTitles).isEqualTo(expectedDbTitles);
431        assertThat(actualAppTitles).isEqualTo(expectedAppTitles);
432    }
433
434    @Test
435    public void testGetSortedLoadedResults() {
436        List<SearchResult> appResults = getDummyAppResults();
437        List<SearchResult> dbResults = getDummyDbResults();
438        mAdapter.initializeSearch("");
439        mAdapter.addSearchResults(
440                new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
441        mAdapter.addSearchResults(
442                new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
443        List<? extends SearchResult> actualDbResults =
444                mAdapter.getSortedLoadedResults(SearchResultsAdapter.DB_RESULTS_LOADER_KEY);
445        List<? extends SearchResult> actualAppResults =
446                mAdapter.getSortedLoadedResults(SearchResultsAdapter.APP_RESULTS_LOADER_KEY);
447        assertThat(actualDbResults.get(0).title).isEqualTo(TITLES[0]); // charlie
448        assertThat(actualDbResults.get(1).title).isEqualTo(TITLES[1]); // bravo
449        assertThat(actualDbResults.get(2).title).isEqualTo(TITLES[2]); // alpha
450        assertThat(actualAppResults.get(0).title).isEqualTo(TITLES[3]); // appAlpha
451        assertThat(actualAppResults.get(1).title).isEqualTo(TITLES[4]); // appBravo
452        assertThat(actualAppResults.get(2).title).isEqualTo(TITLES[5]); // appCharlie
453    }
454
455    @Test
456    public void testInitializeSearch_shouldNotRunSmartRankingIfDisabled() {
457        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(false);
458        mAdapter.initializeSearch("");
459        mAdapter.notifyResultsLoaded();
460        verify(mSearchFeatureProvider, never()).querySearchResults(
461                any(Context.class), anyString(), any(SearchResultsRankerCallback.class));
462        assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.DISABLED);
463    }
464
465    @Test
466    public void testInitialSearch_shouldRunSmartRankingIfEnabled() {
467        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
468        mAdapter.initializeSearch("");
469        mAdapter.notifyResultsLoaded();
470        verify(mSearchFeatureProvider, times(1)).querySearchResults(
471                any(Context.class), anyString(), any(SearchResultsRankerCallback.class));
472        assertThat(mAdapter.getAsyncRankingState())
473                .isEqualTo(SearchResultsAdapter.PENDING_RESULTS);
474    }
475
476    @Test
477    public void testGetRankingScoreByStableId() {
478        when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
479
480        List<SearchResult> appResults = getDummyAppResults();
481        List<SearchResult> dbResults = getDummyDbResults();
482        mAdapter.initializeSearch("");
483        mAdapter.onRankingScoresAvailable(getDummyRankingScores());
484        assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(0).stableId))
485                .isWithin(1e-10f).of(0.8f);
486        assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(1).stableId))
487                .isWithin(1e-10f).of(0.2f);
488        assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(2).stableId))
489                .isWithin(1e-10f).of(0.9f);
490        assertThat(mAdapter.getRankingScoreByStableId(appResults.get(0).stableId))
491                .isEqualTo(-Float.MAX_VALUE);
492        assertThat(mAdapter.getRankingScoreByStableId(appResults.get(1).stableId))
493                .isEqualTo(-Float.MAX_VALUE);
494        assertThat(mAdapter.getRankingScoreByStableId(appResults.get(2).stableId))
495                .isEqualTo(-Float.MAX_VALUE);
496    }
497
498    private void waitUntilRankingTimesOut() {
499        while (mAdapter.getHandler().hasMessages(mAdapter.MSG_RANKING_TIMED_OUT)) {
500            try {
501                ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
502                Thread.sleep(100);
503            } catch (InterruptedException e) {
504                // Do nothing
505            }
506        }
507    }
508
509    private List<SearchResult> getDummyDbResults() {
510        List<SearchResult> results = new ArrayList<>();
511        ResultPayload payload = new ResultPayload(new Intent());
512        SearchResult.Builder builder = new SearchResult.Builder();
513        builder.setPayload(payload)
514                .setTitle(TITLES[0])
515                .setRank(1)
516                .setStableId(Objects.hash(TITLES[0], "db"));
517        results.add(builder.build());
518
519        builder.setTitle(TITLES[1])
520                .setRank(3)
521                .setStableId(Objects.hash(TITLES[1], "db"));
522        results.add(builder.build());
523
524        builder.setTitle(TITLES[2])
525                .setRank(6)
526                .setStableId(Objects.hash(TITLES[2], "db"));
527        results.add(builder.build());
528
529        return results;
530    }
531
532    private List<SearchResult> getDummyAppResults() {
533        List<SearchResult> results = new ArrayList<>();
534        ResultPayload payload = new ResultPayload(new Intent());
535        AppSearchResult.Builder builder = new AppSearchResult.Builder();
536        builder.setPayload(payload)
537                .setTitle(TITLES[3])
538                .setRank(1)
539                .setStableId(Objects.hash(TITLES[3], "app"));
540        results.add(builder.build());
541
542        builder.setTitle(TITLES[4])
543                .setRank(2)
544                .setStableId(Objects.hash(TITLES[4], "app"));
545        results.add(builder.build());
546
547        builder.setTitle(TITLES[5])
548                .setRank(4)
549                .setStableId(Objects.hash(TITLES[5], "app"));
550        results.add(builder.build());
551
552        return results;
553    }
554
555    private Set<SearchResult> getIntentSampleResults() {
556        Set<SearchResult> sampleResults = new HashSet<>();
557        ArrayList<String> breadcrumbs = new ArrayList<>();
558        final Drawable icon = mContext.getDrawable(R.drawable.ic_search_24dp);
559        final ResultPayload payload = new ResultPayload(null);
560        final SearchResult.Builder builder = new Builder();
561        builder.setTitle("title")
562                .setSummary("summary")
563                .setRank(1)
564                .addBreadcrumbs(breadcrumbs)
565                .setIcon(icon)
566                .setPayload(payload)
567                .setStableId(Objects.hash("title", "summary", 1));
568        sampleResults.add(builder.build());
569
570        builder.setRank(2)
571                .setStableId(Objects.hash("title", "summary", 2));
572        sampleResults.add(builder.build());
573
574        builder.setRank(3)
575                .setStableId(Objects.hash("title", "summary", 3));
576        sampleResults.add(builder.build());
577        return sampleResults;
578    }
579
580    private List<Pair<String, Float>> getDummyRankingScores() {
581        List<SearchResult> results = getDummyDbResults();
582        List<Pair<String, Float>> scores = new ArrayList<>();
583        scores.add(new Pair<>(Long.toString(results.get(2).stableId), 0.9f)); // charlie
584        scores.add(new Pair<>(Long.toString(results.get(0).stableId), 0.8f)); // alpha
585        scores.add(new Pair<>(Long.toString(results.get(1).stableId), 0.2f)); // bravo
586        return scores;
587    }
588}
589