blob: 99a8a2ee4773cde0213fd5e4fc3324ab35e2fb33 [file] [log] [blame]
/*
* Copyright (C) 2010 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.providers.applications;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Applications;
import android.test.ProviderTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import java.util.concurrent.FutureTask;
/**
* Instrumentation test for the ApplicationsProvider.
*
* The tests use an IsolatedContext, and are not affected by the real list of
* applications on the device. The ApplicationsProvider's persistent database
* is also created in an isolated context so it doesn't interfere with the
* database of the actual ApplicationsProvider installed on the device.
*/
@SmallTest
public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProviderForTesting> {
private ApplicationsProviderForTesting mProvider;
private MockActivityManager mMockActivityManager;
public ApplicationsProviderTest() {
super(ApplicationsProviderForTesting.class, Applications.AUTHORITY);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mProvider = getProvider();
mMockActivityManager = new MockActivityManager();
}
/**
* Ensures that the ApplicationsProvider is in a ready-to-test state.
*/
private void initProvider(ApplicationsProviderForTesting provider) throws Exception {
// Decouple the provider from Android's real list of applications.
MockPackageManager mockPackageManager = new MockPackageManager();
addDefaultTestPackages(mockPackageManager);
initProvider(provider, mockPackageManager);
}
private void initProvider(ApplicationsProviderForTesting provider,
MockPackageManager mockPackageManager) throws Exception {
provider.setMockPackageManager(mockPackageManager);
provider.setMockActivityManager(mMockActivityManager);
assertTrue(provider.dispatchNextMessage());
}
/**
* Register a few default applications with the ApplicationsProvider that
* tests can query.
*/
private void addDefaultTestPackages(MockPackageManager mockPackageManager) {
mockPackageManager.addPackage(
"Email", new ComponentName("com.android.email", "com.android.email.MainView"));
mockPackageManager.addPackage(
"Ebay", new ComponentName("com.android.ebay", "com.android.ebay.Shopping"));
mockPackageManager.addPackage(
"Fakeapp", new ComponentName("com.android.fakeapp", "com.android.fakeapp.FakeView"));
// Apps that can be used to test ordering.
mockPackageManager.addPackage("AlphabeticA", new ComponentName("a", "a.AView"));
mockPackageManager.addPackage("AlphabeticB", new ComponentName("b", "b.BView"));
mockPackageManager.addPackage("AlphabeticC", new ComponentName("c", "c.CView"));
mockPackageManager.addPackage("AlphabeticD", new ComponentName("d", "d.DView"));
mockPackageManager.addPackage("AlphabeticD2", new ComponentName("d", "d.DView2"));
}
public void testSearch_singleResult() throws Exception {
initProvider(mProvider);
testSearch("ema", "Email");
}
public void testSearch_multipleResults() throws Exception {
initProvider(mProvider);
testSearch("e", "Ebay", "Email");
}
public void testSearch_noResults() throws Exception {
initProvider(mProvider);
testSearch("nosuchapp");
}
public void testSearch_orderingIsAlphabeticByDefault() throws Exception {
initProvider(mProvider);
testSearch("alphabetic", "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD",
"AlphabeticD2");
}
public void testSearch_emptySearchQueryReturnsEverything() throws Exception {
initProvider(mProvider);
testSearch("",
"AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD", "AlphabeticD2",
"Ebay", "Email", "Fakeapp");
}
public void testSearch_appsAreRankedByUsageTimeOnStartup() throws Exception {
mMockActivityManager.addLastResumeTime("d", "d.DView", 3);
mMockActivityManager.addLastResumeTime("b", "b.BView", 1);
// Missing usage time for "a".
mMockActivityManager.addLastResumeTime("c", "c.CView", 0);
// Launch count database is populated on startup.
mProvider.setHasGlobalSearchPermission(true);
initProvider(mProvider);
// New ranking: D, B, A, C (first by launch count, then
// - if the launch counts of two apps are equal - alphabetically)
testSearch("alphabetic", "AlphabeticD", "AlphabeticB", "AlphabeticA", "AlphabeticC",
"AlphabeticD2");
}
public void testSearch_appsAreRankedByResumeTimeAfterUpdate() throws Exception {
initProvider(mProvider);
mProvider.setHasGlobalSearchPermission(true);
mMockActivityManager.addLastResumeTime("d", "d.DView", 3);
mMockActivityManager.addLastResumeTime("b", "b.BView", 1);
// Missing launch count for "a".
mMockActivityManager.addLastResumeTime("c", "c.CView", 0);
// Fetch new data from usage stat provider (in the real instance this
// is triggered by a zero-query from global search).
mProvider.updateUsageStats();
// New ranking: D, B, A, C (first by launch count, then
// - if the launch counts of two apps are equal - alphabetically)
testSearch("alphabetic", "AlphabeticD", "AlphabeticB", "AlphabeticA", "AlphabeticC",
"AlphabeticD2");
}
public void testSearch_noLastAccessTimesWithoutPermission() throws Exception {
initProvider(mProvider);
mProvider.setHasGlobalSearchPermission(false);
mMockActivityManager.addLastResumeTime("d", "d.DView", 1);
mMockActivityManager.addLastResumeTime("b", "b.BView", 2);
mMockActivityManager.addLastResumeTime("c", "c.CView", 3);
assertNull(getGlobalSearchCursor("", false));
Cursor cursor = getGlobalSearchCursor("alphabetic", false);
assertEquals(-1, cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT));
}
public void testSearch_lastAccessTimes() throws Exception {
initProvider(mProvider);
mProvider.setHasGlobalSearchPermission(true);
mMockActivityManager.addLastResumeTime("d", "d.DView", 1);
mMockActivityManager.addLastResumeTime("b", "b.BView", 2);
mMockActivityManager.addLastResumeTime("c", "c.CView", 3);
testLastAccessTimes("", true, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0);
testLastAccessTimes("alphabetic", true, 3, 2, 1, 0, 0);
// Update the last resume time of "c".
mMockActivityManager.addLastResumeTime("c", "c.CView", 5);
// Without a refresh, we should see the same results as before.
testLastAccessTimes("alphabetic", false, 3, 2, 1, 0, 0);
// If we refresh, we should see the change.
testLastAccessTimes("alphabetic", true, 5, 2, 1, 0, 0);
}
/**
* The ApplicationsProvider must only rank by launch count if the caller
* is a privileged application - ordering apps by launch count when asked
* by a regular application would leak information about user behavior.
*/
public void testSearch_notAllowedToRankByLaunchCount() throws Exception {
initProvider(mProvider);
// Simulate non-privileged calling application.
mProvider.setHasGlobalSearchPermission(false);
mMockActivityManager.addLastResumeTime("d", "d.DView", 3);
mMockActivityManager.addLastResumeTime("b", "b.BView", 1);
mMockActivityManager.addLastResumeTime("a", "a.AView", 0);
mMockActivityManager.addLastResumeTime("c", "c.CView", 0);
// Fetch new data from launch count provider.
mProvider.updateUsageStats();
// Launch count information mustn't be leaked - ranking is still
// alphabetic.
testSearch("alphabetic", "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD",
"AlphabeticD2");
}
public void testSearch_disabledPackage() throws Exception {
MockPackageManager mockPackageManager = new MockPackageManager();
mockPackageManager.addPackage("DisabledPackageApp1",
new ComponentName("dp", "dp.DisView1"));
mockPackageManager.addPackage("DisabledPackageApp2",
new ComponentName("dp", "dp.DisView2"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
initProvider(mProvider, mockPackageManager);
mProvider.setHasGlobalSearchPermission(true);
testSearch("dis");
}
public void testSearch_disabledComponent() throws Exception {
MockPackageManager mockPackageManager = new MockPackageManager();
mockPackageManager.addPackage("DisabledApp1", new ComponentName("da", "da.DaView1"),
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
mockPackageManager.addPackage("DisabledApp2", new ComponentName("da", "da.DaView2"),
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
initProvider(mProvider, mockPackageManager);
mProvider.setHasGlobalSearchPermission(true);
testSearch("dis", "DisabledApp2");
}
private void testSearch(String searchQuery, String... expectedResultsInOrder) {
Cursor cursor = Applications.search(getMockContentResolver(), searchQuery);
assertNotNull(cursor);
assertFalse(cursor.isClosed());
verifySearchResults(cursor, expectedResultsInOrder);
cursor.close();
}
private void verifySearchResults(Cursor cursor, String... expectedResultsInOrder) {
int expectedResultCount = expectedResultsInOrder.length;
assertEquals("Wrong number of app search results.",
expectedResultCount, cursor.getCount());
if (expectedResultCount > 0) {
cursor.moveToFirst();
int nameIndex = cursor.getColumnIndex(ApplicationsProvider.NAME);
// Verify that the actual results match the expected ones.
for (int i = 0; i < cursor.getCount(); i++) {
assertEquals("Wrong search result at position " + i,
expectedResultsInOrder[i], cursor.getString(nameIndex));
cursor.moveToNext();
}
}
}
private Cursor getGlobalSearchCursor(String searchQuery, boolean refresh) {
Uri.Builder uriBuilder = Applications.CONTENT_URI.buildUpon();
uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath(searchQuery);
if (refresh) {
uriBuilder.appendQueryParameter(ApplicationsProvider.REFRESH_STATS, "");
}
return getMockContentResolver().query(uriBuilder.build(), null, null, null, null);
}
private void testLastAccessTimes(String searchQuery, boolean refresh,
int... expectedLastAccessTimesInOrder) {
Cursor cursor = getGlobalSearchCursor(searchQuery, refresh);
assertNotNull(cursor);
assertFalse(cursor.isClosed());
verifyLastAccessTimes(cursor, expectedLastAccessTimesInOrder);
cursor.close();
}
private void verifyLastAccessTimes(Cursor cursor, int... expectedLastAccessTimesInOrder) {
cursor.moveToFirst();
int lastAccessTimeIndex = cursor.getColumnIndex(
SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT);
// Verify that the actual results match the expected ones.
for (int i = 0; i < cursor.getCount(); i++) {
assertEquals("Wrong last-access time at position " + i,
expectedLastAccessTimesInOrder[i], cursor.getInt(lastAccessTimeIndex));
cursor.moveToNext();
}
}
}