2 * Copyright (C) The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.quicksearchbox;
19 import com.android.quicksearchbox.util.MockExecutor;
20 import com.android.quicksearchbox.util.Util;
22 import android.app.SearchManager;
23 import android.test.AndroidTestCase;
24 import android.test.MoreAsserts;
25 import android.test.suitebuilder.annotation.MediumTest;
26 import android.util.Log;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.List;
35 import java.util.Map.Entry;
37 import junit.framework.Assert;
40 * Abstract base class for tests of {@link ShortcutRepository}
41 * implementations. Most importantly, verifies the
42 * stuff we are doing with sqlite works how we expect it to.
44 * Attempts to test logic independent of the (sql) details of the implementation, so these should
45 * be useful even in the face of a schema change.
48 public class ShortcutRepositoryTest extends AndroidTestCase {
50 private static final String TAG = "ShortcutRepositoryTest";
52 static final long NOW = 1239841162000L; // millis since epoch. some time in 2009
54 static final Source APP_SOURCE = new MockSource("com.example.app/.App");
56 static final Source APP_SOURCE_V2 = new MockSource("com.example.app/.App", 2);
58 static final Source CONTACTS_SOURCE = new MockSource("com.android.contacts/.Contacts");
60 static final Source BOOKMARKS_SOURCE = new MockSource("com.android.browser/.Bookmarks");
62 static final Source HISTORY_SOURCE = new MockSource("com.android.browser/.History");
64 static final Source MUSIC_SOURCE = new MockSource("com.android.music/.Music");
66 static final Source MARKET_SOURCE = new MockSource("com.android.vending/.Market");
68 static final Corpus APP_CORPUS = new MockCorpus(APP_SOURCE);
70 static final Corpus CONTACTS_CORPUS = new MockCorpus(CONTACTS_SOURCE);
72 static final int MAX_SHORTCUTS = 8;
74 protected Config mConfig;
75 protected MockCorpora mCorpora;
76 protected MockExecutor mLogExecutor;
77 protected ShortcutRefresher mRefresher;
79 protected List<Corpus> mAllowedCorpora;
81 protected ShortcutRepositoryImplLog mRepo;
83 protected ListSuggestionCursor mAppSuggestions;
84 protected ListSuggestionCursor mContactSuggestions;
86 protected SuggestionData mApp1;
87 protected SuggestionData mApp2;
88 protected SuggestionData mApp3;
90 protected SuggestionData mContact1;
91 protected SuggestionData mContact2;
93 protected ShortcutRepositoryImplLog createShortcutRepository() {
94 return new ShortcutRepositoryImplLog(getContext(), mConfig, mCorpora,
95 mRefresher, new MockHandler(), mLogExecutor,
96 "test-shortcuts-log.db").disableUpdateDelay();
100 protected void setUp() throws Exception {
103 mConfig = new Config(getContext());
104 mCorpora = new MockCorpora();
105 mCorpora.addCorpus(APP_CORPUS);
106 mCorpora.addCorpus(CONTACTS_CORPUS);
107 mRefresher = new MockShortcutRefresher();
108 mLogExecutor = new MockExecutor();
109 mRepo = createShortcutRepository();
111 mAllowedCorpora = new ArrayList<Corpus>(mCorpora.getAllCorpora());
113 mApp1 = makeApp("app1");
114 mApp2 = makeApp("app2");
115 mApp3 = makeApp("app3");
116 mAppSuggestions = new ListSuggestionCursor("foo", mApp1, mApp2, mApp3);
118 mContact1 = new SuggestionData(CONTACTS_SOURCE)
119 .setText1("Joe Blow")
120 .setIntentAction("view")
121 .setIntentData("contacts/joeblow")
122 .setShortcutId("j-blow");
123 mContact2 = new SuggestionData(CONTACTS_SOURCE)
124 .setText1("Mike Johnston")
125 .setIntentAction("view")
126 .setIntentData("contacts/mikeJ")
127 .setShortcutId("mo-jo");
129 mContactSuggestions = new ListSuggestionCursor("foo", mContact1, mContact2);
132 private SuggestionData makeApp(String name) {
133 return new SuggestionData(APP_SOURCE)
135 .setIntentAction("view")
136 .setIntentData("apps/" + name)
137 .setShortcutId("shorcut_" + name);
140 private SuggestionData makeContact(String name) {
141 return new SuggestionData(CONTACTS_SOURCE)
143 .setIntentAction("view")
144 .setIntentData("contacts/" + name)
145 .setShortcutId("shorcut_" + name);
149 protected void tearDown() throws Exception {
151 mRepo.deleteRepository();
154 public void testHasHistory() {
155 assertFalse(mRepo.hasHistory());
156 reportClickAtTime(mAppSuggestions, 0, NOW);
157 assertTrue(mRepo.hasHistory());
158 mRepo.clearHistory();
159 assertTrue(mRepo.hasHistory());
160 mLogExecutor.runNext();
161 assertFalse(mRepo.hasHistory());
164 public void testNoMatch() {
165 SuggestionData clicked = new SuggestionData(CONTACTS_SOURCE)
166 .setText1("bob smith")
167 .setIntentAction("action")
168 .setIntentData("data");
170 reportClick("bob smith", clicked);
171 assertNoShortcuts("joe");
174 public void testFullPackingUnpacking() {
175 SuggestionData clicked = new SuggestionData(CONTACTS_SOURCE)
176 .setFormat("<i>%s</i>")
178 .setText2("description")
179 .setText2Url("description_url")
180 .setIcon1("android.resource://system/drawable/foo")
181 .setIcon2("content://test/bar")
182 .setIntentAction("action")
183 .setIntentData("data")
184 .setSuggestionQuery("query")
185 .setIntentExtraData("extradata")
186 .setShortcutId("idofshortcut")
187 .setSuggestionLogType("logtype");
188 reportClick("q", clicked);
190 assertShortcuts("q", clicked);
191 assertShortcuts("", clicked);
194 public void testSpinnerWhileRefreshing() {
195 SuggestionData clicked = new SuggestionData(CONTACTS_SOURCE)
197 .setText2("description")
199 .setSuggestionQuery("query")
200 .setIntentExtraData("extradata")
201 .setShortcutId("idofshortcut")
202 .setSpinnerWhileRefreshing(true);
204 reportClick("q", clicked);
206 String spinnerUri = Util.getResourceUri(mContext, R.drawable.search_spinner).toString();
207 SuggestionData expected = new SuggestionData(CONTACTS_SOURCE)
209 .setText2("description")
210 .setIcon2(spinnerUri)
211 .setSuggestionQuery("query")
212 .setIntentExtraData("extradata")
213 .setShortcutId("idofshortcut")
214 .setSpinnerWhileRefreshing(true);
216 assertShortcuts("q", expected);
219 public void testPrefixesMatch() {
220 assertNoShortcuts("bob");
222 SuggestionData clicked = new SuggestionData(CONTACTS_SOURCE)
223 .setText1("bob smith the third")
224 .setIntentAction("action")
225 .setIntentData("intentdata");
227 reportClick("bob smith", clicked);
229 assertShortcuts("bob smith", clicked);
230 assertShortcuts("bob s", clicked);
231 assertShortcuts("b", clicked);
234 public void testMatchesOneAndNotOthers() {
235 SuggestionData bob = new SuggestionData(CONTACTS_SOURCE)
236 .setText1("bob smith the third")
237 .setIntentAction("action")
238 .setIntentData("intentdata/bob");
240 reportClick("bob", bob);
242 SuggestionData george = new SuggestionData(CONTACTS_SOURCE)
243 .setText1("george jones")
244 .setIntentAction("action")
245 .setIntentData("intentdata/george");
246 reportClick("geor", george);
248 assertShortcuts("b for bob", "b", bob);
249 assertShortcuts("g for george", "g", george);
252 public void testDifferentPrefixesMatchSameEntity() {
253 SuggestionData clicked = new SuggestionData(CONTACTS_SOURCE)
254 .setText1("bob smith the third")
255 .setIntentAction("action")
256 .setIntentData("intentdata");
258 reportClick("bob", clicked);
259 reportClick("smith", clicked);
260 assertShortcuts("b", clicked);
261 assertShortcuts("s", clicked);
264 public void testMoreClicksWins() {
265 reportClick("app", mApp1);
266 reportClick("app", mApp2);
267 reportClick("app", mApp1);
269 assertShortcuts("expected app1 to beat app2 since it has more hits",
270 "app", mApp1, mApp2);
272 reportClick("app", mApp2);
273 reportClick("app", mApp2);
275 assertShortcuts("query 'app': expecting app2 to beat app1 since it has more hits",
276 "app", mApp2, mApp1);
277 assertShortcuts("query 'a': expecting app2 to beat app1 since it has more hits",
281 public void testMostRecentClickWins() {
282 // App 1 has 3 clicks
283 reportClick("app", mApp1, NOW - 5);
284 reportClick("app", mApp1, NOW - 5);
285 reportClick("app", mApp1, NOW - 5);
286 // App 2 has 2 clicks
287 reportClick("app", mApp2, NOW - 2);
288 reportClick("app", mApp2, NOW - 2);
289 // App 3 only has 1, but it's most recent
290 reportClick("app", mApp3, NOW - 1);
292 assertShortcuts("expected app3 to beat app1 and app2 because it's clicked last",
293 "app", mApp3, mApp1, mApp2);
295 reportClick("app", mApp2, NOW);
297 assertShortcuts("query 'app': expecting app2 to beat app1 since it's clicked last",
298 "app", mApp2, mApp1, mApp3);
299 assertShortcuts("query 'a': expecting app2 to beat app1 since it's clicked last",
300 "a", mApp2, mApp1, mApp3);
301 assertShortcuts("query '': expecting app2 to beat app1 since it's clicked last",
302 "", mApp2, mApp1, mApp3);
305 public void testMostRecentClickWinsOnEmptyQuery() {
306 reportClick("app", mApp1, NOW - 3);
307 reportClick("app", mApp1, NOW - 2);
308 reportClick("app", mApp2, NOW - 1);
310 assertShortcuts("expected app2 to beat app1 since it's clicked last", "",
314 public void testMostRecentClickWinsEvenWithMoreThanLimitShortcuts() {
315 for (int i = 0; i < MAX_SHORTCUTS; i++) {
316 SuggestionData app = makeApp("TestApp" + i);
317 // Each of these shortcuts has two clicks
318 reportClick("app", app, NOW - 2);
319 reportClick("app", app, NOW - 1);
322 // mApp1 has only one click, but is more recent
323 reportClick("app", mApp1, NOW);
325 assertShortcutAtPosition(
326 "expecting app1 to beat all others since it's clicked last",
331 * similar to {@link #testMoreClicksWins()} but clicks are reported with prefixes of the
332 * original query. we want to make sure a match on query 'a' updates the stats for the
333 * entry it matched against, 'app'.
335 public void testPrefixMatchUpdatesSameEntry() {
336 reportClick("app", mApp1, NOW);
337 reportClick("app", mApp2, NOW);
338 reportClick("app", mApp1, NOW);
340 assertShortcuts("expected app1 to beat app2 since it has more hits",
341 "app", mApp1, mApp2);
344 private static final long DAY_MILLIS = 86400000L; // just ask the google
345 private static final long HOUR_MILLIS = 3600000L;
347 public void testMoreRecentlyClickedWins() {
348 reportClick("app", mApp1, NOW - DAY_MILLIS*2);
349 reportClick("app", mApp2, NOW);
350 reportClick("app", mApp3, NOW - DAY_MILLIS*4);
352 assertShortcuts("expecting more recently clicked app to rank higher",
353 "app", mApp2, mApp1, mApp3);
356 public void testMoreRecentlyClickedWinsSeconds() {
357 reportClick("app", mApp1, NOW - 10000);
358 reportClick("app", mApp2, NOW - 5000);
359 reportClick("app", mApp3, NOW);
361 assertShortcuts("expecting more recently clicked app to rank higher",
362 "app", mApp3, mApp2, mApp1);
365 public void testRecencyOverridesClicks() {
367 // 5 clicks, most recent half way through age limit
368 long halfWindow = mConfig.getMaxStatAgeMillis() / 2;
369 reportClick("app", mApp1, NOW - halfWindow);
370 reportClick("app", mApp1, NOW - halfWindow);
371 reportClick("app", mApp1, NOW - halfWindow);
372 reportClick("app", mApp1, NOW - halfWindow);
373 reportClick("app", mApp1, NOW - halfWindow);
375 // 3 clicks, the most recent very recent
376 reportClick("app", mApp2, NOW - HOUR_MILLIS);
377 reportClick("app", mApp2, NOW - HOUR_MILLIS);
378 reportClick("app", mApp2, NOW - HOUR_MILLIS);
380 assertShortcuts("expecting 3 recent clicks to beat 5 clicks long ago",
381 "app", mApp2, mApp1);
384 public void testEntryOlderThanAgeLimitFiltered() {
385 reportClick("app", mApp1);
387 long pastWindow = mConfig.getMaxStatAgeMillis() + 1000;
388 reportClick("app", mApp2, NOW - pastWindow);
390 assertShortcuts("expecting app2 not clicked on recently enough to be filtered",
394 public void testZeroQueryResults_MoreClicksWins() {
395 reportClick("app", mApp1);
396 reportClick("app", mApp1);
397 reportClick("foo", mApp2);
399 assertShortcuts("", mApp1, mApp2);
401 reportClick("foo", mApp2);
402 reportClick("foo", mApp2);
404 assertShortcuts("", mApp2, mApp1);
407 public void testZeroQueryResults_DifferentQueryhitsCreditSameShortcut() {
408 reportClick("app", mApp1);
409 reportClick("foo", mApp2);
410 reportClick("bar", mApp2);
412 assertShortcuts("hits for 'foo' and 'bar' on app2 should have combined to rank it " +
413 "ahead of app1, which only has one hit.",
416 reportClick("z", mApp1);
417 reportClick("2", mApp1);
419 assertShortcuts("", mApp1, mApp2);
422 public void testZeroQueryResults_zeroQueryHitCounts() {
423 reportClick("app", mApp1);
424 reportClick("", mApp2);
425 reportClick("", mApp2);
427 assertShortcuts("hits for '' on app2 should have combined to rank it " +
428 "ahead of app1, which only has one hit.",
431 reportClick("", mApp1);
432 reportClick("", mApp1);
434 assertShortcuts("zero query hits for app1 should have made it higher than app2.",
437 assertShortcuts("query for 'a' should only match app1.",
441 public void testRefreshShortcut() {
442 final SuggestionData app1 = new SuggestionData(APP_SOURCE)
445 .setText2("cool app")
446 .setShortcutId("app1_id");
448 reportClick("app", app1);
450 final SuggestionData updated = new SuggestionData(APP_SOURCE)
451 .setFormat("format (updated)")
452 .setText1("app1 (updated)")
453 .setText2("cool app")
454 .setShortcutId("app1_id");
456 refreshShortcut(APP_SOURCE, "app1_id", updated);
458 assertShortcuts("expected updated properties in match",
462 public void testRefreshShortcutChangedIntent() {
464 final SuggestionData app1 = new SuggestionData(APP_SOURCE)
465 .setIntentData("data")
468 .setText2("cool app")
469 .setShortcutId("app1_id");
471 reportClick("app", app1);
473 final SuggestionData updated = new SuggestionData(APP_SOURCE)
474 .setIntentData("data-updated")
475 .setFormat("format (updated)")
476 .setText1("app1 (updated)")
477 .setText2("cool app")
478 .setShortcutId("app1_id");
480 refreshShortcut(APP_SOURCE, "app1_id", updated);
482 assertShortcuts("expected updated properties in match",
486 public void testInvalidateShortcut() {
487 final SuggestionData app1 = new SuggestionData(APP_SOURCE)
489 .setText2("cool app")
490 .setShortcutId("app1_id");
492 reportClick("app", app1);
494 invalidateShortcut(APP_SOURCE, "app1_id");
496 assertNoShortcuts("should be no matches since shortcut is invalid.", "app");
499 public void testInvalidateShortcut_sameIdDifferentSources() {
500 final String sameid = "same_id";
501 final SuggestionData app = new SuggestionData(APP_SOURCE)
503 .setText2("cool app")
504 .setShortcutId(sameid);
505 reportClick("app", app);
506 assertShortcuts("app should be there", "", app);
508 final SuggestionData contact = new SuggestionData(CONTACTS_SOURCE)
509 .setText1("joe blow")
510 .setText2("a good pal")
511 .setShortcutId(sameid);
512 reportClick("joe", contact);
513 reportClick("joe", contact);
514 assertShortcuts("app and contact should be there.", "", contact, app);
516 refreshShortcut(APP_SOURCE, sameid, null);
517 assertNoShortcuts("app should not be there.", "app");
518 assertShortcuts("contact with same shortcut id should still be there.",
520 assertShortcuts("contact with same shortcut id should still be there.",
524 public void testNeverMakeShortcut() {
525 final SuggestionData contact = new SuggestionData(CONTACTS_SOURCE)
526 .setText1("unshortcuttable contact")
527 .setText2("you didn't want to call them again anyway")
528 .setShortcutId(SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT);
529 reportClick("unshortcuttable", contact);
530 assertNoShortcuts("never-shortcutted suggestion should not be there.", "unshortcuttable");
533 public void testCountResetAfterShortcutDeleted() {
534 reportClick("app", mApp1);
535 reportClick("app", mApp1);
536 reportClick("app", mApp1);
537 reportClick("app", mApp1);
539 reportClick("app", mApp2);
540 reportClick("app", mApp2);
543 assertShortcuts("app", mApp1, mApp2);
546 invalidateShortcut(APP_SOURCE, mApp1.getShortcutId());
547 reportClick("app", mApp1);
550 assertShortcuts("expecting app1's click count to reset after being invalidated.",
551 "app", mApp2, mApp1);
554 public void testShortcutsAllowedCorpora() {
555 reportClick("a", mApp1);
556 reportClick("a", mContact1);
558 assertShortcuts("only allowed shortcuts should be returned",
559 "a", Arrays.asList(APP_CORPUS), mApp1);
563 // SOURCE RANKING TESTS BELOW
566 public void testSourceRanking_moreClicksWins() {
567 assertCorpusRanking("expected no ranking");
569 int minClicks = mConfig.getMinClicksForSourceRanking();
572 for (int i = 0; i < minClicks + 1; i++) {
573 reportClick("a", mApp1);
575 // fewer clicks on a contact
576 for (int i = 0; i < minClicks; i++) {
577 reportClick("a", mContact1);
580 assertCorpusRanking("expecting apps to rank ahead of contacts (more clicks)",
581 APP_CORPUS, CONTACTS_CORPUS);
583 // more clicks on a contact
584 reportClick("a", mContact1);
585 reportClick("a", mContact1);
587 assertCorpusRanking("expecting contacts to rank ahead of apps (more clicks)",
588 CONTACTS_CORPUS, APP_CORPUS);
591 public void testOldSourceStatsDontCount() {
592 // apps were popular back in the day
593 final long toOld = mConfig.getMaxStatAgeMillis() + 1;
594 int minClicks = mConfig.getMinClicksForSourceRanking();
595 for (int i = 0; i < minClicks; i++) {
596 reportClick("app", mApp1, NOW - toOld);
599 // and contacts is 1/2
600 for (int i = 0; i < minClicks; i++) {
601 reportClick("bob", mContact1, NOW);
604 assertCorpusRanking("old clicks for apps shouldn't count.",
609 public void testSourceRanking_filterSourcesWithInsufficientData() {
610 int minClicks = mConfig.getMinClicksForSourceRanking();
612 for (int i = 0; i < minClicks - 1; i++) {
613 reportClick("app", mApp1);
616 for (int i = 0; i < minClicks; i++) {
617 reportClick("bob", mContact1);
621 "ordering should only include sources with at least " + minClicks + " clicks.",
627 public void testAppUpgradeClearsShortcuts() {
628 reportClick("a", mApp1);
629 reportClick("add", mApp1);
630 reportClick("a", mContact1);
632 assertShortcuts("all shortcuts should be returned",
633 "a", mAllowedCorpora, mApp1, mContact1);
635 // Upgrade an existing corpus
636 MockCorpus upgradedCorpus = new MockCorpus(APP_SOURCE_V2);
637 mCorpora.addCorpus(upgradedCorpus);
639 List<Corpus> newAllowedCorpora = new ArrayList<Corpus>(mCorpora.getAllCorpora());
640 assertShortcuts("app shortcuts should be removed when the source was upgraded",
641 "a", newAllowedCorpora, mContact1);
644 public void testAppUpgradePromotesLowerRanked() {
646 ListSuggestionCursor expected = new ListSuggestionCursor("a");
647 for (int i = 0; i < MAX_SHORTCUTS + 1; i++) {
648 reportClick("app", mApp1, NOW);
652 // Enough contact clicks to make one more shortcut than getMaxShortcutsReturned()
653 for (int i = 0; i < MAX_SHORTCUTS; i++) {
654 SuggestionData contact = makeContact("andy" + i);
655 int numClicks = MAX_SHORTCUTS - i; // use click count to get shortcuts in order
656 for (int j = 0; j < numClicks; j++) {
657 reportClick("and", contact, NOW);
659 expected.add(contact);
662 // Expect the app, and then all but one contact
663 assertShortcuts("app and all but one contact should be returned",
664 "a", mAllowedCorpora, SuggestionCursorUtil.slice(expected, 0, MAX_SHORTCUTS));
666 // Upgrade app corpus
667 MockCorpus upgradedCorpus = new MockCorpus(APP_SOURCE_V2);
668 mCorpora.addCorpus(upgradedCorpus);
670 // Expect all contacts
671 List<Corpus> newAllowedCorpora = new ArrayList<Corpus>(mCorpora.getAllCorpora());
672 assertShortcuts("app shortcuts should be removed when the source was upgraded "
673 + "and a contact should take its place",
674 "a", newAllowedCorpora, SuggestionCursorUtil.slice(expected, 1, MAX_SHORTCUTS));
677 public void testIrrelevantAppUpgrade() {
678 reportClick("a", mApp1);
679 reportClick("add", mApp1);
680 reportClick("a", mContact1);
682 assertShortcuts("all shortcuts should be returned",
683 "a", mAllowedCorpora, mApp1, mContact1);
685 // Fire a corpus set update that affect no shortcuts corpus
686 MockCorpus newCorpus = new MockCorpus(new MockSource("newsource"));
687 mCorpora.addCorpus(newCorpus);
689 assertShortcuts("all shortcuts should be returned",
690 "a", mAllowedCorpora, mApp1, mContact1);
695 protected ListSuggestionCursor makeCursor(String query, SuggestionData... suggestions) {
696 ListSuggestionCursor cursor = new ListSuggestionCursor(query);
697 for (SuggestionData suggestion : suggestions) {
698 cursor.add(suggestion);
703 protected void reportClick(String query, SuggestionData suggestion) {
704 reportClick(new ListSuggestionCursor(query, suggestion), 0);
707 protected void reportClick(String query, SuggestionData suggestion, long now) {
708 reportClickAtTime(new ListSuggestionCursor(query, suggestion), 0, now);
711 protected void reportClick(SuggestionCursor suggestions, int position) {
712 reportClickAtTime(suggestions, position, NOW);
715 protected void reportClickAtTime(SuggestionCursor suggestions, int position, long now) {
716 mRepo.reportClickAtTime(suggestions, position, now);
717 mLogExecutor.runNext();
720 protected void invalidateShortcut(Source source, String shortcutId) {
721 refreshShortcut(source, shortcutId, null);
724 protected void refreshShortcut(Source source, String shortcutId, SuggestionData suggestion) {
725 SuggestionCursor refreshed =
726 suggestion == null ? null : new ListSuggestionCursor(null, suggestion);
727 mRepo.refreshShortcut(source, shortcutId, refreshed);
728 mLogExecutor.runNext();
731 protected void sourceImpressions(Source source, int clicks, int impressions) {
732 if (clicks > impressions) throw new IllegalArgumentException("ya moran!");
734 for (int i = 0; i < impressions; i++, clicks--) {
735 sourceImpression(source, clicks > 0);
740 * Simulate an impression, and optionally a click, on a source.
742 * @param source The name of the source.
743 * @param click Whether to register a click in addition to the impression.
745 protected void sourceImpression(Source source, boolean click) {
746 sourceImpression(source, click, NOW);
750 * Simulate an impression, and optionally a click, on a source.
752 * @param source The name of the source.
753 * @param click Whether to register a click in addition to the impression.
755 protected void sourceImpression(Source source, boolean click, long now) {
756 SuggestionData suggestionClicked = !click ?
758 new SuggestionData(source)
759 .setIntentAction("view")
760 .setIntentData("data/id")
761 .setShortcutId("shortcutid");
763 reportClick("a", suggestionClicked);
766 void assertNoShortcuts(String query) {
767 assertNoShortcuts("", query);
770 void assertNoShortcuts(String message, String query) {
771 SuggestionCursor cursor = mRepo.getShortcutsForQuery(query, mAllowedCorpora, NOW);
773 assertNull(message + ", got shortcuts", cursor);
775 if (cursor != null) cursor.close();
779 void assertShortcuts(String query, SuggestionData... expected) {
780 assertShortcuts("", query, expected);
783 void assertShortcutAtPosition(String message, String query,
784 int position, SuggestionData expected) {
785 SuggestionCursor cursor = mRepo.getShortcutsForQuery(query, mAllowedCorpora, NOW);
787 SuggestionCursor expectedCursor = new ListSuggestionCursor(query, expected);
788 SuggestionCursorUtil.assertSameSuggestion(message, position, expectedCursor, cursor);
790 if (cursor != null) cursor.close();
794 void assertShortcutCount(String message, String query, int expectedCount) {
795 SuggestionCursor cursor = mRepo.getShortcutsForQuery(query, mAllowedCorpora, NOW);
797 assertEquals(message, expectedCount, cursor.getCount());
799 if (cursor != null) cursor.close();
803 void assertShortcuts(String message, String query, Collection<Corpus> allowedCorpora,
804 SuggestionCursor expected) {
805 SuggestionCursor cursor = mRepo.getShortcutsForQuery(query, allowedCorpora, NOW);
807 SuggestionCursorUtil.assertSameSuggestions(message, expected, cursor, true);
809 if (cursor != null) cursor.close();
813 void assertShortcuts(String message, String query, Collection<Corpus> allowedCorpora,
814 SuggestionData... expected) {
815 assertShortcuts(message, query, allowedCorpora, new ListSuggestionCursor(query, expected));
818 void assertShortcuts(String message, String query, SuggestionData... expected) {
819 assertShortcuts(message, query, mAllowedCorpora, expected);
822 void assertCorpusRanking(String message, Corpus... expected) {
823 String[] expectedNames = new String[expected.length];
824 for (int i = 0; i < expected.length; i++) {
825 expectedNames[i] = expected[i].getName();
827 Map<String,Integer> scores = mRepo.getCorpusScores();
828 List<String> observed = sortByValues(scores);
829 // Highest scores should come first
830 Collections.reverse(observed);
831 Log.d(TAG, "scores=" + scores);
832 assertContentsInOrder(message, observed, (Object[]) expectedNames);
835 static <A extends Comparable<A>, B extends Comparable<B>> List<A> sortByValues(Map<A,B> map) {
836 Comparator<Map.Entry<A,B>> comp = new Comparator<Map.Entry<A,B>>() {
837 public int compare(Entry<A, B> object1, Entry<A, B> object2) {
838 int diff = object1.getValue().compareTo(object2.getValue());
842 return object1.getKey().compareTo(object2.getKey());
846 ArrayList<Map.Entry<A,B>> sorted = new ArrayList<Map.Entry<A,B>>(map.size());
847 sorted.addAll(map.entrySet());
848 Collections.sort(sorted, comp);
849 ArrayList<A> out = new ArrayList<A>(sorted.size());
850 for (Map.Entry<A,B> e : sorted) {
856 static void assertContentsInOrder(Iterable<?> actual, Object... expected) {
857 assertContentsInOrder(null, actual, expected);
861 * an implementation of {@link MoreAsserts#assertContentsInOrder(String, Iterable, Object[])}
862 * that isn't busted. a bug has been filed about that, but for now this works.
864 static void assertContentsInOrder(
865 String message, Iterable<?> actual, Object... expected) {
866 ArrayList actualList = new ArrayList();
867 for (Object o : actual) {
870 Assert.assertEquals(message, Arrays.asList(expected), actualList);