| /* |
| * 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 android.app.SearchManager; |
| import android.content.ComponentName; |
| import android.test.AndroidTestCase; |
| import android.test.MoreAsserts; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| |
| import junit.framework.Assert; |
| import com.google.android.collect.Lists; |
| |
| /** |
| * Abstract base class for tests of {@link com.android.globalsearch.ShortcutRepository} |
| * implementations. Most importantly, verifies the |
| * stuff we are doing with sqlite works how we expect it to. |
| * |
| * Attempts to test logic independent of the (sql) details of the implementation, so these should |
| * be useful even in the face of a schema change. |
| */ |
| public class ShortcutRepositoryTest extends AndroidTestCase { |
| |
| static final long NOW = 1239841162000L; // millis since epoch. some time in 2009 |
| |
| static final ComponentName APP_COMPONENT = |
| new ComponentName("com.example.app","com.example.app.App"); |
| |
| static final ComponentName CONTACTS_COMPONENT = |
| new ComponentName("com.android.contacts","com.android.contacts.Contacts"); |
| |
| static final ComponentName BOOKMARKS_COMPONENT = |
| new ComponentName("com.android.browser","com.android.browser.Bookmarks"); |
| |
| static final ComponentName HISTORY_COMPONENT = |
| new ComponentName("com.android.browser","com.android.browser.History"); |
| |
| static final ComponentName MUSIC_COMPONENT = |
| new ComponentName("com.android.music","com.android.music.Music"); |
| |
| static final ComponentName MARKET_COMPONENT = |
| new ComponentName("com.android.vending","com.android.vending.Market"); |
| |
| |
| protected ShortcutRepositoryImplLog mRepo; |
| protected SuggestionData mApp1; |
| protected SuggestionData mApp2; |
| protected SuggestionData mContact1; |
| protected SuggestionData mContact2; |
| |
| protected ShortcutRepositoryImplLog createShortcutRepository() { |
| return new ShortcutRepositoryImplLog(getContext(), "test-shortcuts-log.db"); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| |
| mRepo = createShortcutRepository(); |
| |
| mApp1 = makeApp("app1"); |
| mApp2 = makeApp("app2"); |
| |
| mContact1 = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("Joe Blow") |
| .intentAction("view") |
| .intentData("contacts/joeblow") |
| .shortcutId("j-blow") |
| .build(); |
| mContact2 = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("Mike Johnston") |
| .intentAction("view") |
| .intentData("contacts/mikeJ") |
| .shortcutId("mo-jo") |
| .build(); |
| } |
| |
| private SuggestionData makeApp(String name) { |
| return new SuggestionData.Builder(APP_COMPONENT) |
| .title(name) |
| .intentAction("view") |
| .intentData("apps/" + name) |
| .shortcutId("shorcut_" + name) |
| .build(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| super.tearDown(); |
| mRepo.deleteRepository(); |
| } |
| |
| public void testHasHistory() { |
| assertFalse(mRepo.hasHistory()); |
| SessionStats stats = new SessionStats("foo", mApp1); |
| mRepo.reportStats(stats); |
| assertTrue(mRepo.hasHistory()); |
| mRepo.clearHistory(); |
| assertFalse(mRepo.hasHistory()); |
| } |
| |
| public void testNoMatch() { |
| SuggestionData clicked = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("bob smith") |
| .intentAction("action") |
| .intentData("data") |
| .build(); |
| SessionStats stats = new SessionStats( |
| "bob smith", |
| clicked); |
| |
| mRepo.reportStats(stats); |
| MoreAsserts.assertEmpty(mRepo.getShortcutsForQuery("joe", NOW)); |
| } |
| |
| public void testFullPackingUnpacking() { |
| SuggestionData clicked = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .format("<i>%s</i>") |
| .title("title") |
| .description("description") |
| .icon1("icon1") |
| .icon2("icon2") |
| .intentAction("action") |
| .intentData("data") |
| .intentQuery("query") |
| .intentExtraData("extradata") |
| .intentComponentName("componentname") |
| .shortcutId("idofshortcut") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("q", clicked), NOW); |
| |
| assertContentsInOrder(mRepo.getShortcutsForQuery("q", NOW), clicked); |
| assertContentsInOrder(mRepo.getShortcutsForQuery("", NOW), clicked); |
| } |
| |
| public void testSpinnerWhileRefreshing() { |
| SuggestionData clicked = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .format("<i>%s</i>") |
| .title("title") |
| .description("description") |
| .icon1("icon1") |
| .icon2("icon2") |
| .intentAction("action") |
| .intentData("data") |
| .intentQuery("query") |
| .intentExtraData("extradata") |
| .intentComponentName("componentname") |
| .shortcutId("idofshortcut") |
| .spinnerWhileRefreshing(true) |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("q", clicked), NOW); |
| |
| SuggestionData expected = clicked.buildUpon() |
| .icon2(String.valueOf(com.android.internal.R.drawable.search_spinner)) |
| .build(); |
| |
| assertContentsInOrder(mRepo.getShortcutsForQuery("q", NOW), expected); |
| } |
| |
| public void testPrefixesMatch() { |
| MoreAsserts.assertEmpty(mRepo.getShortcutsForQuery("bob", NOW)); |
| |
| SuggestionData clicked = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("bob smith the third") |
| .intentAction("action") |
| .intentData("intentdata") |
| .build(); |
| SessionStats stats = new SessionStats("bob smith", clicked); |
| |
| mRepo.reportStats(stats, NOW); |
| |
| assertContentsInOrder("bob smith", |
| mRepo.getShortcutsForQuery("bob smith", NOW), |
| clicked); |
| assertContentsInOrder("bob s", |
| mRepo.getShortcutsForQuery("bob s", NOW), |
| clicked); |
| assertContentsInOrder("b", |
| mRepo.getShortcutsForQuery("b", NOW), |
| clicked); |
| } |
| |
| public void testMatchesOneAndNotOthers() { |
| SuggestionData bob = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("bob smith the third") |
| .intentAction("action") |
| .intentData("intentdata/bob") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("bob", bob), NOW); |
| |
| SuggestionData george = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("george jones") |
| .intentAction("action") |
| .intentData("intentdata/george") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("geor", george), NOW); |
| |
| assertContentsInOrder("b for bob", |
| mRepo.getShortcutsForQuery("b", NOW), |
| bob); |
| assertContentsInOrder("g for george", |
| mRepo.getShortcutsForQuery("g", NOW), |
| george); |
| } |
| |
| public void testDifferentPrefixesMatchSameEntity() { |
| SuggestionData clicked = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("bob smith the third") |
| .intentAction("action") |
| .intentData("intentdata") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("bob", clicked)); |
| mRepo.reportStats(new SessionStats("smith", clicked)); |
| assertContentsInOrder("b", |
| mRepo.getShortcutsForQuery("b", NOW), |
| clicked); |
| assertContentsInOrder("s", |
| mRepo.getShortcutsForQuery("s", NOW), |
| clicked); |
| } |
| |
| public void testMoreClicksWins() { |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| |
| |
| assertContentsInOrder("expected app1 to beat app2 since it has more hits", |
| mRepo.getShortcutsForQuery("app", NOW), |
| mApp1, mApp2); |
| |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW); |
| |
| assertContentsInOrder( |
| "query 'app': expecting app2 to beat app1 since it has more hits", |
| mRepo.getShortcutsForQuery("app", NOW), |
| mApp2, mApp1); |
| assertContentsInOrder( |
| "query 'a': expecting app2 to beat app1 since it has more hits", |
| mRepo.getShortcutsForQuery("app", NOW), |
| mApp2, mApp1); |
| } |
| |
| /** |
| * similar to {@link #testMoreClicksWins()} but clicks are reported with prefixes of the |
| * original query. we want to make sure a match on query 'a' updates the stats for the |
| * entry it matched against, 'app'. |
| */ |
| public void testPrefixMatchUpdatesSameEntry() { |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| |
| assertContentsInOrder("expected app1 to beat app2 since it has more hits", |
| mRepo.getShortcutsForQuery("app", NOW), |
| mApp1, mApp2); |
| } |
| |
| private static final long DAY_MILLIS = 86400000L; // just ask the google |
| private static final long HOUR_MILLIS = 3600000L; |
| |
| public void testMoreRecentlyClickedWins() { |
| SuggestionData app3 = new SuggestionData.Builder(APP_COMPONENT) |
| .title("third application") |
| .intentAction("action/app3") |
| .intentData("intentdata") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW - DAY_MILLIS*2); |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("app", app3), NOW - DAY_MILLIS*4); |
| |
| assertContentsInOrder("expecting more recently clicked app to rank higher", |
| mRepo.getShortcutsForQuery("app", NOW), |
| mApp2, mApp1, app3); |
| } |
| |
| public void testRecencyOverridesClicks() { |
| |
| // 5 clicks, most recent half way through age limit |
| long halfWindow = ShortcutRepository.MAX_STAT_AGE_MILLIS / 2; |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW - halfWindow); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW - halfWindow); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW - halfWindow); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW - halfWindow); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW - halfWindow); |
| |
| // 3 clicks, the most recent very recent |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW - HOUR_MILLIS); |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW - HOUR_MILLIS); |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW - HOUR_MILLIS); |
| |
| assertContentsInOrder("expecting 3 recent clicks to beat 5 clicks long ago", |
| mRepo.getShortcutsForQuery("app", NOW), |
| mApp2, mApp1); |
| } |
| |
| public void testEntryOlderThanAgeLimitFiltered() { |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| |
| long pastWindow = ShortcutRepository.MAX_STAT_AGE_MILLIS + 1000; |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW - pastWindow); |
| |
| assertContentsInOrder("expecting app2 not clicked on recently enough to be filtered", |
| mRepo.getShortcutsForQuery("app", NOW), |
| mApp1); |
| } |
| |
| public void testZeroQueryResults_MoreClicksWins() { |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("foo", mApp2), NOW); |
| |
| assertContentsInOrder( |
| mRepo.getShortcutsForQuery("", NOW), |
| mApp1, mApp2); |
| |
| mRepo.reportStats(new SessionStats("foo", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("foo", mApp2), NOW); |
| |
| assertContentsInOrder( |
| mRepo.getShortcutsForQuery("", NOW), |
| mApp2, mApp1); |
| } |
| |
| public void testZeroQueryResults_DifferentQueryhitsCreditSameShortcut() { |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("foo", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("bar", mApp2), NOW); |
| |
| assertContentsInOrder("hits for 'foo' and 'bar' on app2 should have combined to rank it " + |
| "ahead of app1, which only has one hit.", |
| mRepo.getShortcutsForQuery("", NOW), |
| mApp2, mApp1); |
| |
| mRepo.reportStats(new SessionStats("z", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("w", mApp1), NOW); |
| |
| assertContentsInOrder( |
| mRepo.getShortcutsForQuery("", NOW), |
| mApp1, mApp2); |
| } |
| |
| public void testZeroQueryResults_zeroQueryHitCounts() { |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("", mApp2), NOW); |
| |
| assertContentsInOrder("hits for '' on app2 should have combined to rank it " + |
| "ahead of app1, which only has one hit.", |
| mRepo.getShortcutsForQuery("", NOW), |
| mApp2, mApp1); |
| |
| mRepo.reportStats(new SessionStats("", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("", mApp1), NOW); |
| |
| assertContentsInOrder("zero query hits for app1 should have made it higher than app2.", |
| mRepo.getShortcutsForQuery("", NOW), |
| mApp1, mApp2); |
| |
| assertContentsInOrder("query for 'a' should only match app1.", |
| mRepo.getShortcutsForQuery("a", NOW), |
| mApp1); |
| } |
| |
| public void testRefreshShortcut() { |
| |
| final SuggestionData app1 = new SuggestionData.Builder(APP_COMPONENT) |
| .format("format") |
| .title("app1") |
| .description("cool app") |
| .shortcutId("app1_id") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("app", app1)); |
| |
| final SuggestionData updated = app1.buildUpon() |
| .format("format (updated)") |
| .title("app1 (updated)") |
| .build(); |
| |
| mRepo.refreshShortcut(APP_COMPONENT, "app1_id", updated); |
| |
| assertContentsInOrder("expected updated properties in match", |
| mRepo.getShortcutsForQuery("app", NOW), |
| updated); |
| } |
| |
| public void testRefreshShortcutChangedIntent() { |
| |
| final SuggestionData app1 = new SuggestionData.Builder(APP_COMPONENT) |
| .intentData("data") |
| .format("format") |
| .title("app1") |
| .description("cool app") |
| .shortcutId("app1_id") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("app", app1)); |
| |
| final SuggestionData updated = app1.buildUpon() |
| .intentData("data-updated") |
| .format("format (updated)") |
| .title("app1 (updated)") |
| .build(); |
| |
| mRepo.refreshShortcut(APP_COMPONENT, "app1_id", updated); |
| |
| assertContentsInOrder("expected updated properties in match", |
| mRepo.getShortcutsForQuery("app", NOW), |
| updated); |
| } |
| |
| public void testInvalidateShortcut() { |
| final SuggestionData app1 = new SuggestionData.Builder(APP_COMPONENT) |
| .title("app1") |
| .description("cool app") |
| .shortcutId("app1_id") |
| .build(); |
| |
| mRepo.reportStats(new SessionStats("app", app1)); |
| |
| // passing null should remove the shortcut |
| mRepo.refreshShortcut(APP_COMPONENT, "app1_id", null); |
| |
| MoreAsserts.assertEmpty("should be no matches since shortcut is invalid.", |
| mRepo.getShortcutsForQuery("app", NOW)); |
| } |
| |
| public void testInvalidateShortcut_sameIdDifferentSources() { |
| final String sameid = "same_id"; |
| final SuggestionData app = new SuggestionData.Builder(APP_COMPONENT) |
| .title("app1") |
| .description("cool app") |
| .shortcutId(sameid) |
| .build(); |
| mRepo.reportStats(new SessionStats("app", app), NOW); |
| |
| final SuggestionData contact = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("joe blow") |
| .description("a good pal") |
| .shortcutId(sameid) |
| .build(); |
| mRepo.reportStats(new SessionStats("joe", contact), NOW); |
| |
| mRepo.refreshShortcut(APP_COMPONENT, sameid, null); |
| MoreAsserts.assertEmpty("app should not be there.", |
| mRepo.getShortcutsForQuery("app", NOW)); |
| assertContentsInOrder("contact with same shortcut id should still be there.", |
| mRepo.getShortcutsForQuery("joe", NOW), contact); |
| } |
| |
| public void testNeverMakeShortcut() { |
| final SuggestionData contact = new SuggestionData.Builder(CONTACTS_COMPONENT) |
| .title("unshortcuttable contact") |
| .description("you didn't want to call them again anyway") |
| .shortcutId(SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT) |
| .build(); |
| mRepo.reportStats(new SessionStats("unshortcuttable", contact), NOW); |
| MoreAsserts.assertEmpty("never-shortcutted suggestion should not be there.", |
| mRepo.getShortcutsForQuery("unshortcuttable", NOW)); |
| } |
| |
| public void testCountResetAfterShortcutDeleted() { |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW); |
| mRepo.reportStats(new SessionStats("app", mApp2), NOW); |
| |
| // app1 wins 4 - 2 |
| assertContentsInOrder(mRepo.getShortcutsForQuery("app", NOW), mApp1, mApp2); |
| |
| // reset to 1 |
| mRepo.refreshShortcut(APP_COMPONENT, mApp1.getShortcutId(), null); |
| mRepo.reportStats(new SessionStats("app", mApp1), NOW); |
| |
| // app2 wins 2 - 1 |
| assertContentsInOrder("expecting app1's click count to reset after being invalidated.", |
| mRepo.getShortcutsForQuery("app", NOW), mApp2, mApp1); |
| } |
| |
| public void testShortcutsLimitedCount() { |
| |
| for (int i = 1; i <= 20; i++) { |
| mRepo.reportStats(new SessionStats("a", makeApp("app" + i)), NOW); |
| } |
| |
| assertEquals("number of shortcuts should be limited.", |
| 12, mRepo.getShortcutsForQuery("", NOW).size()); |
| |
| } |
| |
| |
| |
| // |
| // SOURCE RANKING TESTS BELOW |
| // |
| |
| public void testSourceRanking_moreClicksWins() { |
| |
| // click on an app, impression for both apps and contacts |
| mRepo.reportStats( |
| new SessionStats("a", |
| mApp1, |
| Lists.newArrayList(APP_COMPONENT, CONTACTS_COMPONENT)), NOW); |
| |
| assertContentsInOrder("expecting apps to rank ahead of contacts (more clicks)", |
| mRepo.getSourceRanking(0, 0), |
| APP_COMPONENT, CONTACTS_COMPONENT); |
| |
| // 2 clicks on a contact, impression for both apps and contacts |
| mRepo.reportStats( |
| new SessionStats("a", |
| mContact1, |
| Lists.newArrayList(APP_COMPONENT, CONTACTS_COMPONENT)), NOW); |
| mRepo.reportStats( |
| new SessionStats("a", |
| mContact1, |
| Lists.newArrayList(APP_COMPONENT, CONTACTS_COMPONENT)), NOW); |
| |
| assertContentsInOrder("expecting contacts to rank ahead of apps (more clicks)", |
| mRepo.getSourceRanking(0, 0), |
| CONTACTS_COMPONENT, APP_COMPONENT); |
| } |
| |
| public void testSourceRanking_higherCTRWins() { |
| |
| // app: 1 click in 2 impressions |
| sourceImpression(APP_COMPONENT, true); |
| sourceImpression(APP_COMPONENT, false); |
| |
| // contacts: 2 clicks in 5 impressions |
| sourceImpression(CONTACTS_COMPONENT, true); |
| sourceImpression(CONTACTS_COMPONENT, true); |
| sourceImpression(CONTACTS_COMPONENT, false); |
| sourceImpression(CONTACTS_COMPONENT, false); |
| sourceImpression(CONTACTS_COMPONENT, false); |
| |
| assertContentsInOrder( |
| "apps (1 click / 2 impressions) should beat contacts (2 clicks / 5 impressions)", |
| mRepo.getSourceRanking(0, 0), |
| APP_COMPONENT, CONTACTS_COMPONENT); |
| |
| // contacts: up to 4 clicks in 7 impressions |
| sourceImpression(CONTACTS_COMPONENT, true); |
| sourceImpression(CONTACTS_COMPONENT, true); |
| |
| assertContentsInOrder( |
| "contacts (4 click / 7 impressions) should beat apps (1 clicks / 2 impressions)", |
| mRepo.getSourceRanking(0, 0), |
| CONTACTS_COMPONENT, APP_COMPONENT); |
| } |
| |
| public void testOldSourceStatsDontCount() { |
| // apps were popular back in the day |
| final long toOld = ShortcutRepository.MAX_SOURCE_EVENT_AGE_MILLIS + 1; |
| sourceImpression(APP_COMPONENT, true, NOW - toOld); |
| sourceImpression(APP_COMPONENT, true, NOW - toOld); |
| sourceImpression(APP_COMPONENT, true, NOW - toOld); |
| sourceImpression(APP_COMPONENT, true, NOW - toOld); |
| |
| // more recently apps has bombed |
| sourceImpression(APP_COMPONENT, false, NOW); |
| sourceImpression(APP_COMPONENT, false, NOW); |
| |
| // and apps is 1/2 |
| sourceImpression(CONTACTS_COMPONENT, true, NOW); |
| sourceImpression(CONTACTS_COMPONENT, false, NOW); |
| |
| assertContentsInOrder( |
| "old clicks for apps shouldn't count.", |
| mRepo.getSourceRanking(0, 0), |
| CONTACTS_COMPONENT, APP_COMPONENT); |
| } |
| |
| |
| public void testSourceRanking_filterSourcesWithInsufficientData() { |
| sourceImpressions(APP_COMPONENT, 1, 5); |
| sourceImpressions(CONTACTS_COMPONENT, 1, 2); |
| sourceImpressions(BOOKMARKS_COMPONENT, 9, 10); |
| sourceImpressions(HISTORY_COMPONENT, 4, 4); |
| sourceImpressions(MUSIC_COMPONENT, 0, 1); |
| sourceImpressions(MARKET_COMPONENT, 4, 8); |
| |
| assertContentsInOrder( |
| "ordering should only include sources with at least 5 impressions.", |
| mRepo.getSourceRanking(5, 0), |
| BOOKMARKS_COMPONENT, MARKET_COMPONENT, APP_COMPONENT); |
| |
| assertContentsInOrder( |
| "ordering should only include sources with at least 2 clicks.", |
| mRepo.getSourceRanking(0, 2), |
| HISTORY_COMPONENT, BOOKMARKS_COMPONENT, MARKET_COMPONENT); |
| |
| assertContentsInOrder( |
| "ordering should only include sources with at least 5 impressions and 3 clicks.", |
| mRepo.getSourceRanking(5, 3), |
| BOOKMARKS_COMPONENT, MARKET_COMPONENT); |
| } |
| |
| protected void sourceImpressions(ComponentName source, int clicks, int impressions) { |
| if (clicks > impressions) throw new IllegalArgumentException("ya moran!"); |
| |
| for (int i = 0; i < impressions; i++, clicks--) { |
| sourceImpression(source, clicks > 0); |
| } |
| } |
| |
| /** |
| * Simulate an impression, and optionally a click, on a source. |
| * |
| * @param source The name of the source. |
| * @param click Whether to register a click in addition to the impression. |
| */ |
| protected void sourceImpression(ComponentName source, boolean click) { |
| sourceImpression(source, click, NOW); |
| } |
| |
| /** |
| * Simulate an impression, and optionally a click, on a source. |
| * |
| * @param source The name of the source. |
| * @param click Whether to register a click in addition to the impression. |
| */ |
| protected void sourceImpression(ComponentName source, boolean click, long now) { |
| SuggestionData suggestionClicked = !click ? |
| null : |
| new SuggestionData.Builder(source) |
| .intentAction("view") |
| .intentData("data/id") |
| .shortcutId("shortcutid") |
| .build(); |
| |
| mRepo.reportStats( |
| new SessionStats("a", |
| suggestionClicked, |
| Lists.newArrayList(source)), now); |
| } |
| |
| |
| static void assertContentsInOrder(Iterable<?> actual, Object... expected) { |
| assertContentsInOrder(null, actual, expected); |
| } |
| |
| /** |
| * an implementation of {@link MoreAsserts#assertContentsInOrder(String, Iterable, Object[])} |
| * that isn't busted. a bug has been filed about that, but for now this works. |
| */ |
| static void assertContentsInOrder( |
| String message, Iterable<?> actual, Object... expected) { |
| ArrayList actualList = new ArrayList(); |
| for (Object o : actual) { |
| actualList.add(o); |
| } |
| Assert.assertEquals(message, Arrays.asList(expected), actualList); |
| } |
| } |