blob: b3a6ca9389289556726d305471ceee54848af361 [file] [log] [blame]
/*
* Copyright (C) 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.globalsearch;
import junit.framework.TestCase;
import junit.framework.Assert;
import android.content.ComponentName;
import android.test.MoreAsserts;
import com.google.android.collect.Sets;
import com.google.android.collect.Lists;
import static com.android.globalsearch.SourceSuggestionBacker.SourceStat;
import java.util.List;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Arrays;
/**
* Tests {@link SourceSuggestionBacker}
*
* see the {@link #setUp()} method for the default setup of the backer that is used by most
* test cases.
*/
public class SourceSuggestionBackerTest extends TestCase
implements SourceSuggestionBacker.MoreExpanderFactory,
SourceSuggestionBacker.CorpusResultFactory {
private ComponentName mName1;
private ComponentName mName2;
private ComponentName mName3;
private TestSuggestionSource mSource1;
private TestSuggestionSource mSource2;
private TestSuggestionSource mSource3;
private TestBacker mBacker;
private static final int MAX_PROMOTED_SHOWING = 6;
private static final long NOW = 700L;
private static final long DEADLINE = 2000L;
private SuggestionData mShortcut1;
private SuggestionData mGoToWebsite;
private SuggestionData mSearchTheWeb;
private SuggestionData mMoreNotExpanded;
private SuggestionData mMoreExpanded;
private static final String SOURCE1_LABEL = "source1 label";
private static final String SOURCE2_LABEL = "source2 label";
private static final String SOURCE3_LABEL = "source3 label";
@Override
protected void setUp() throws Exception {
super.setUp();
mName1 = new ComponentName(
"com.android.globalsearch", "com.android.globalsearch.One");
mName2 = new ComponentName(
"com.android.globalsearch", "com.android.globalsearch.Two");
mName3 = new ComponentName(
"com.android.globalsearch", "com.android.globalsearch.Three");
mSource1 = new TestSuggestionSource.Builder()
.setComponent(mName1)
.setLabel(SOURCE1_LABEL)
.create();
mSource2 = new TestSuggestionSource.Builder()
.setComponent(mName2)
.setLabel(SOURCE2_LABEL)
.create();
mSource3 = new TestSuggestionSource.Builder()
.setComponent(mName3)
.setLabel(SOURCE3_LABEL)
.create();
mShortcut1 = new SuggestionData.Builder(mName1)
.title("shortcut")
.description("description")
.shortcutId("shortcutid")
.intentAction("action.VIEW")
.intentData("shorctut1")
.build();
// Normally we pass null in for this value; we only use this value to
// explicitly test the "go to website" functionality.
mGoToWebsite = new SuggestionData.Builder(mName1)
.title("go to website")
.description("google.com")
.build();
mSearchTheWeb = new SuggestionData.Builder(mName1)
.title("search the web for 'yo'")
.description("description")
.build();
mBacker = new TestBacker(
"query",
Lists.newArrayList(mShortcut1),
Lists.<SuggestionSource>newArrayList(mSource1, mSource2, mSource3),
Sets.newHashSet(mName1, mName2), // promoted sources
mSource1, // source1 is the web souce
Lists.<SuggestionResult>newArrayList(),
null, // no "go to website" suggestion
mSearchTheWeb, // the "search the web" suggestion
MAX_PROMOTED_SHOWING,
DEADLINE,
this, /** more factory points to {@link #getMoreEntry} */
this); /** corpus factory points to {@link #getCorpusEntry} */
mMoreNotExpanded = new SuggestionData.Builder(mName1)
.title("more unexpanded")
.build();
mMoreExpanded = new SuggestionData.Builder(mName1)
.title("more expanded")
.build();
mBacker.setNow(NOW);
}
/** {@inheritDoc} */
public SuggestionData getMoreEntry(
boolean expanded,
List<SourceSuggestionBacker.SourceStat> sourceStats) {
return expanded ? mMoreExpanded : mMoreNotExpanded;
}
/** {@inheritDoc} */
public SuggestionData getCorpusEntry(
String query, SourceSuggestionBacker.SourceStat sourceStat) {
return makeCorpusEntry(
sourceStat.getLabel(),
sourceStat.getResponseStatus(),
sourceStat.getNumResults());
}
public void testNoResultsReported() {
assertContentsInOrder(
"should only be shortcuts before deadline.",
getSnapshotFromBacker(false),
mShortcut1);
assertContentsInOrder(
"should only be shortcuts before deadline.",
getSnapshotFromBacker(true),
mShortcut1);
mBacker.setNow(NOW + DEADLINE);
assertContentsInOrder(
"after deadline should see 'search the web' and 'more' entry.",
getSnapshotFromBacker(false),
mShortcut1,
mSearchTheWeb,
mMoreNotExpanded);
assertContentsInOrder(
"after deadline (expanded) should see 'search the web' and 'more' entries.",
getSnapshotFromBacker(true),
mShortcut1,
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE1_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0),
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0),
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0));
}
public void testSomeResultsReported() {
// source 1 reports back 4 results
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3)
)));
assertContentsInOrder(
"before deadline, should show shortcuts and chunks of promoted sources.",
getSnapshotFromBacker(false),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1));
mBacker.setNow(NOW + DEADLINE);
// source 3 is in progress
mBacker.reportSourceStarted(mName3);
assertContentsInOrder(
"after deadline(expanded), should show shortcuts, chunks of promoted sources, " +
"rest of promoted slots filled, and more.",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3),
mSearchTheWeb,
mMoreExpanded,
// no "more" result for source 1 since we've displayed all of its entries now
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0),
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_IN_PROGRESS, 0));
}
public void testPromotedSourceRespondsAfterDeadline() {
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1))));
mBacker.setNow(NOW + DEADLINE);
assertContentsInOrder(
"after deadline pending promoted source should be under 'more'.",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
mSearchTheWeb,
mMoreExpanded,
// source 2 hasn't responded yet
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0),
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1))));
assertContentsInOrder(
"after deadline late reporting promoted source should be under 'more'.",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_FINISHED, 2),
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0));
}
public void testZeroReportingSources() {
assertFalse("reporting zero results before ever being shown should not require updating.",
mBacker.addSourceResults(new SuggestionResult(mSource1)));
assertContentsInOrder(
"zero reporting before deadline.",
getSnapshotFromBacker(true),
mShortcut1);
mBacker.setNow(NOW + DEADLINE);
assertContentsInOrder(
"zero reporting before deadline, viewing after.",
getSnapshotFromBacker(true),
mShortcut1,
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0),
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0));
// source 2 reports after deadline
assertTrue("reporting zero results after being shown should require updating.",
mBacker.addSourceResults(new SuggestionResult(mSource2)));
assertContentsInOrder(
"zero reporting after deadline.",
getSnapshotFromBacker(true),
mShortcut1,
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_FINISHED, 0),
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0));
mBacker.addSourceResults(
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1))));
assertContentsInOrder(
"last source reported after deadline.",
getSnapshotFromBacker(true),
mShortcut1,
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_FINISHED, 0),
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_FINISHED, 2));
}
public void testSourceReportsAfterDeadlineWithResults() {
mBacker = new TestBacker(
"query",
Lists.<SuggestionData>newArrayList(), // no shortcuts
Lists.<SuggestionSource>newArrayList(mSource1, mSource2, mSource3),
Sets.newHashSet(mName1, mName2, mName3), // all 3 are promoted sources
mSource1,
Lists.<SuggestionResult>newArrayList(),
null,
mSearchTheWeb,
MAX_PROMOTED_SHOWING,
DEADLINE,
this,
this);
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2)
)));
mBacker.setNow(NOW + DEADLINE);
assertContentsInOrder(
"after deadline passes, we mix in the remaining promoted slots among the " +
"promoted sources that have responded.",
getSnapshotFromBacker(true),
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName1, 2), // remaining space (now that deadline has passed)
makeSourceResult(mName2, 2),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0)
);
mBacker.addSourceResults(
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1),
makeSourceResult(mName3, 2)
)));
assertContentsInOrder(
"promoted source reported after deadline, results should remain stable (even " +
"though this is not the optimal order if we had known the third promoted " +
"source was going to miss the deadline).",
getSnapshotFromBacker(true),
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName2, 2),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_FINISHED, 3)
);
}
public void testFillSpaceLargerThanChunkSizeAfterDeadline() {
mBacker = new TestBacker(
"query",
Lists.<SuggestionData>newArrayList(), // no shortcuts
Lists.<SuggestionSource>newArrayList(mSource1, mSource2, mSource3),
Sets.newHashSet(mName1, mName2, mName3), // all 3 are promoted sources
mSource1,
Lists.<SuggestionResult>newArrayList(),
null,
mSearchTheWeb,
MAX_PROMOTED_SHOWING,
DEADLINE,
this,
this);
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3),
makeSourceResult(mName1, 4),
makeSourceResult(mName1, 5)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0)
)));
mBacker.setNow(NOW + DEADLINE);
mBacker.addSourceResults(
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1),
makeSourceResult(mName3, 2),
makeSourceResult(mName3, 3),
makeSourceResult(mName3, 4),
makeSourceResult(mName3, 5)
)));
assertContentsInOrder(
"after deadline has passed, promoted sources that have reported should fill in " +
"the remaining slots",
getSnapshotFromBacker(true),
// chunk 1
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
// chunk 2
makeSourceResult(mName2, 0),
// remaining space
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3),
makeSourceResult(mName1, 4),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE1_LABEL, SourceStat.RESPONSE_FINISHED, 1), // 1 remaining result
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_FINISHED, 6)); // reported after deadline
}
public void testAllResultsReported() {
// each one reports 4 results
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2),
makeSourceResult(mName2, 3)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1),
makeSourceResult(mName3, 2),
makeSourceResult(mName3, 3)
)));
assertContentsInOrder(
"all responded.",
getSnapshotFromBacker(false),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName1, 2), // remaining slots (source 3 is not promoted)
mSearchTheWeb,
mMoreNotExpanded);
assertContentsInOrder(
"all responded (expanded).",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName1, 2),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE1_LABEL, SourceStat.RESPONSE_FINISHED, 1), // 1 remaining
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_FINISHED, 2), // 2 remaining
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_FINISHED, 4));
}
public void testNoWebSource() {
mBacker = new TestBacker(
"query",
Lists.newArrayList(mShortcut1),
Lists.<SuggestionSource>newArrayList(mSource1, mSource2, mSource3),
Sets.newHashSet(mName1, mName2), // promoted sources
null, // NO WEB SOURCE
Lists.<SuggestionResult>newArrayList(),
null, // no "go to website" suggestion
mSearchTheWeb, // the "search the web" suggestion
MAX_PROMOTED_SHOWING,
DEADLINE,
this, /** more factory points to {@link #getMoreEntry} */
this); /** corpus factory points to {@link #getCorpusEntry} */
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3)
)));
assertContentsInOrder(
"expecting business as usual even though there is no web source enabled.",
getSnapshotFromBacker(false),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1));
}
public void testDuplicatesOfShortcut() {
// four results from source 1, the first of which is a dupe of the shortcut
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
mShortcut1,
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3)
)));
// before deadline, not all have reported, so just one chunk from source 1
assertContentsInOrder(
"expecting duplicate to be removed.",
getSnapshotFromBacker(false),
mShortcut1,
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2));
}
public void testDuplicatesBetweenSources() {
// two different suggestions for facebook with the same intent action and intent data
final SuggestionData facebook1 = new SuggestionData.Builder(mName1)
.title("facebook")
.intentAction("action.VIEW")
.intentData("http://www.facebook.com")
.build();
final SuggestionData facebook2 = new SuggestionData.Builder(mName2)
.title("facebook home")
.intentAction("action.VIEW")
.intentData("http://www.facebook.com")
.build();
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
facebook1
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
facebook2
)));
assertContentsInOrder(
"expecting duplicate to be removed.",
getSnapshotFromBacker(false),
mShortcut1,
facebook1,
mSearchTheWeb,
mMoreNotExpanded);
}
/**
* When there are duplicates removed, we still want to fill the valuable slots with more
* information.
*/
public void testDuplicatesRemovedSlotsStillFilled() {
// two different suggestions for facebook with the same intent action and intent data
final SuggestionData facebook1 = new SuggestionData.Builder(mName1)
.title("facebook")
.intentAction("action.VIEW")
.intentData("http://www.facebook.com")
.build();
final SuggestionData facebook2 = new SuggestionData.Builder(mName2)
.title("facebook home")
.intentAction("action.VIEW")
.intentData("http://www.facebook.com")
.build();
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
facebook1,
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
facebook2,
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2),
makeSourceResult(mName2, 3)
)));
assertContentsInOrder(
"after duplicate is removed, expecting all 6 promoted slots to still be filled.",
getSnapshotFromBacker(false),
mShortcut1,
// chunk 1
facebook1,
makeSourceResult(mName1, 1),
// chunk 2
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2),
// remainder
makeSourceResult(mName1, 2),
mSearchTheWeb,
mMoreNotExpanded);
}
public void testShortcutsOnly() {
mBacker = new TestBacker(
"query",
Lists.newArrayList(mShortcut1),
Lists.<SuggestionSource>newArrayList(), // no sources
Sets.<ComponentName>newHashSet(),
mSource1,
Lists.<SuggestionResult>newArrayList(),
null,
mSearchTheWeb,
MAX_PROMOTED_SHOWING,
DEADLINE,
this,
this);
mBacker.setNow(NOW);
assertContentsInOrder(
"shortcuts only.",
getSnapshotFromBacker(false),
mShortcut1);
assertContentsInOrder(
"shortcuts only.",
getSnapshotFromBacker(true),
mShortcut1);
}
public void testAllSourcesPromotedResponded_resultsFitInPromotedSlots() {
mBacker = new TestBacker(
"query",
Lists.newArrayList(mShortcut1),
Lists.<SuggestionSource>newArrayList(mSource1, mSource2),
Sets.<ComponentName>newHashSet(mName1, mName2), // every source is promoted
mSource1,
Lists.<SuggestionResult>newArrayList(),
null,
mSearchTheWeb,
MAX_PROMOTED_SHOWING,
DEADLINE,
this,
this);
mBacker.setNow(NOW);
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1))));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1))));
assertContentsInOrder(
"should not show 'more' entries if all results fit, and there are no unpromoted " +
"sources.",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
mSearchTheWeb);
}
public void testCachedSourceResults() {
final ArrayList<SuggestionResult> cached = Lists.newArrayList(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1))),
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1))),
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1))));
mBacker = new TestBacker(
"query",
Lists.newArrayList(mShortcut1),
Lists.<SuggestionSource>newArrayList(mSource1, mSource2, mSource3),
Sets.<ComponentName>newHashSet(mName1, mName2), // promoted sources
mSource1,
cached,
null,
mSearchTheWeb,
MAX_PROMOTED_SHOWING,
DEADLINE,
this,
this);
assertContentsInOrder(
"three cached sources, 2 promoted, one not.",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_FINISHED, 2));
}
public void testGoToWebsiteSuggestion() {
// Recreate the backer, this time with the "go to website" suggestion passed in.
// Then check that the backer correctly shows the suggestion even when there are
// lots of other results.
mBacker = new TestBacker(
"query",
Lists.newArrayList(mShortcut1),
Lists.<SuggestionSource>newArrayList(mSource1, mSource2, mSource3),
Sets.newHashSet(mName1, mName2), // promoted sources
mSource1,
Lists.<SuggestionResult>newArrayList(),
mGoToWebsite,
mSearchTheWeb,
MAX_PROMOTED_SHOWING,
DEADLINE,
this,
this);
// each one reports 4 results
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2),
makeSourceResult(mName2, 3)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1),
makeSourceResult(mName3, 2),
makeSourceResult(mName3, 3)
)));
assertContentsInOrder(
"first suggestion should be go to website suggestion",
getSnapshotFromBacker(false),
mGoToWebsite,
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName1, 2), // remaining slots (source 3 is not promoted)
mSearchTheWeb,
mMoreNotExpanded);
}
public void testPinToBottomSuggestion() {
// each one reports 4 results; source 1 reports a pin-to-bottom suggestion last
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3),
makePinToBottomSourceResult(mName1, 4)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2),
makeSourceResult(mName2, 3)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1),
makeSourceResult(mName3, 2),
makeSourceResult(mName3, 3)
)));
assertContentsInOrder(
"pin to bottom non-expanded.",
getSnapshotFromBacker(false),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName1, 2), // remaining slots (source 3 is not promoted)
mSearchTheWeb,
makePinToBottomSourceResult(mName1, 4),
mMoreNotExpanded);
assertContentsInOrder(
"pin to bottom expanded.",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName1, 2),
mSearchTheWeb,
makePinToBottomSourceResult(mName1, 4),
mMoreExpanded,
makeCorpusEntry(SOURCE1_LABEL, SourceStat.RESPONSE_FINISHED, 1), // 1 remaining
makeCorpusEntry(SOURCE2_LABEL, SourceStat.RESPONSE_FINISHED, 2), // 2 remaining
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_FINISHED, 4));
}
public void testPinToBottomNotShownIfSourceNotInPromotedList() {
// a couple source return before deadline
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2),
makeSourceResult(mName2, 3)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource3, Lists.newArrayList(
makeSourceResult(mName3, 0),
makeSourceResult(mName3, 1),
makeSourceResult(mName3, 2),
makeSourceResult(mName3, 3)
)));
// another returns after the deadline with a pin to bottom
mBacker.setNow(NOW + DEADLINE);
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName1, 2),
makeSourceResult(mName1, 3),
makePinToBottomSourceResult(mName1, 4)
)));
assertContentsInOrder(
"pin to bottom shouldn't be shown if its source responded after deadline.",
getSnapshotFromBacker(false),
mShortcut1,
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
makeSourceResult(mName2, 2),
makeSourceResult(mName2, 3),
mSearchTheWeb,
mMoreNotExpanded);
}
/**
* If non promoted sources report zero results before the user has expanded "more", then
* we don't bother showing "source X reported 0 results" upon expansion.
*
* We also remove the "more results" once we know there are no sources under "more results"
* to show.
*/
public void testNonPromotedSourcesWithZeroResults_reportedBeforeViewed() {
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1)
)));
assertContentsInOrder(
"non promoted source with zero results before viewed.",
getSnapshotFromBacker(false),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
mSearchTheWeb,
mMoreNotExpanded);
// non-promoted source 3
mBacker.addSourceResults(new SuggestionResult(mSource3));
assertContentsInOrder(
"non promoted source with zero results after expansion.",
getSnapshotFromBacker(true), // EXPANDED
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
mSearchTheWeb); // no "more" results entry at all
}
/**
* Similar to {@link #testNonPromotedSourcesWithZeroResults_reportedBeforeViewed()}, but in
* this case, the user has expanded the "more results" and thus viewed the entries; we can't
* remove them at this point and will continue to show them, even though they returned
* zero results.
*/
public void testNonPromotedSourcesWithZeroResults_reportedAfterViewed() {
mBacker.addSourceResults(
new SuggestionResult(mSource1, Lists.newArrayList(
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1)
)));
mBacker.addSourceResults(
new SuggestionResult(mSource2, Lists.newArrayList(
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1)
)));
assertContentsInOrder(
"non promoted source with zero results viewed before report.",
getSnapshotFromBacker(true),
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_NOT_STARTED, 0));
// non-promoted source 3
mBacker.addSourceResults(new SuggestionResult(mSource3));
assertContentsInOrder(
"non promoted source with zero results pinned after being viewed.",
getSnapshotFromBacker(true), // EXPANDED
mShortcut1,
makeSourceResult(mName1, 0),
makeSourceResult(mName1, 1),
makeSourceResult(mName2, 0),
makeSourceResult(mName2, 1),
mSearchTheWeb,
mMoreExpanded,
makeCorpusEntry(SOURCE3_LABEL, SourceStat.RESPONSE_FINISHED, 0));
}
List<SuggestionData> getSnapshotFromBacker(boolean expandAdditional) {
final ArrayList<SuggestionData> list = Lists.newArrayList();
mBacker.snapshotSuggestions(list, expandAdditional);
return list;
}
private SuggestionData makeCorpusEntry(
String label, int responseStatus, int numResultsUndisplayed) {
final SuggestionData.Builder builder = new SuggestionData.Builder(mName1);
builder.title("more_" + label + " " + getResponseStatusString(responseStatus)
+ ", numleft: " + numResultsUndisplayed);
return builder
.build();
}
private String getResponseStatusString(int responseStatus) {
switch (responseStatus) {
case SourceStat.RESPONSE_NOT_STARTED:
return "RESPONSE_NOT_STARTED";
case SourceStat.RESPONSE_IN_PROGRESS:
return "RESPONSE_IN_PROGRESS";
case SourceStat.RESPONSE_FINISHED:
return "RESPONSE_FINISHED";
default:
throw new IllegalArgumentException("unknown status " + responseStatus);
}
}
private SuggestionData makeSourceResult(ComponentName name, int index) {
return new SuggestionData.Builder(name)
.title(name.getClassName() + " " + index)
.intentAction(name.getClassName())
.intentData("" + index)
.build();
}
private SuggestionData makePinToBottomSourceResult(ComponentName name, int index) {
return new SuggestionData.Builder(name)
.title(name.getClassName() + " manage search history " + index)
.intentAction(name.getClassName())
.intentData("" + index)
.pinToBottom(true)
.build();
}
/**
* Allows setting what "now" is for testing
*/
private static class TestBacker extends SourceSuggestionBacker {
long now = 0L;
public TestBacker(
String query,
List<SuggestionData> shortcuts,
List<SuggestionSource> sources,
HashSet<ComponentName> promotedSources,
SuggestionSource selectedWebSearchSource,
List<SuggestionResult> cachedResults,
SuggestionData goToWebsite,
SuggestionData searchTheWeb,
int maxPromotedSlots,
long deadline,
MoreExpanderFactory moreFactory,
CorpusResultFactory corpusFactory) {
super(query, shortcuts, sources, promotedSources, selectedWebSearchSource, cachedResults,
goToWebsite, searchTheWeb, maxPromotedSlots, deadline, moreFactory,
corpusFactory);
}
public long getNow() {
return now;
}
public void setNow(long now) {
this.now = now;
}
}
static void assertContentsInOrder(Iterable<?> actual, Object... expected) {
assertContentsInOrder(null, actual, expected);
}
/**
* an implementation of {@link MoreAsserts#assertContentsInOrder(String, Iterable, Object[])}
* with some additional information placed in the assert message in the error case to make it
* easier to see where the mismatch is.
*/
static void assertContentsInOrder(
String message, Iterable<?> actual, Object... expected) {
ArrayList actualList = new ArrayList();
for (Object o : actual) {
actualList.add(o);
}
StringBuilder sb = new StringBuilder();
if (message != null) sb.append(message);
final List<Object> expectedList = Arrays.asList(expected);
if (expectedList.size() != actualList.size()) {
sb.append("\nsize mismatch (expected: ").append(expectedList.size())
.append(" actual: ").append(actualList.size()).append('.');
}
for (int i = 0; i < Math.min(expectedList.size(), actualList.size()); i++) {
final Object expectedItem = expectedList.get(i);
final Object actualItem = actualList.get(i);
if (!expectedItem.equals(actualItem)) {
sb.append("\n").append("at index ").append(i)
.append(" expected: ").append(expectedItem)
.append("\n").append("actual: ").append(actualItem);
}
}
Assert.assertEquals(sb.toString(), expectedList, actualList);
}
}