summaryrefslogtreecommitdiffstats
path: root/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java')
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java542
1 files changed, 542 insertions, 0 deletions
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java
new file mode 100644
index 0000000..09e3b02
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2006 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.unit_tests;
+
+import com.android.unit_tests.activity.LocalActivity;
+
+import android.app.Activity;
+import android.app.ISearchManager;
+import android.app.SearchManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.ServiceManager;
+import android.server.search.SearchableInfo;
+import android.server.search.SearchableInfo.ActionKeyInfo;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.MoreAsserts;
+import android.test.mock.MockContext;
+import android.test.mock.MockPackageManager;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.AndroidRuntimeException;
+import android.view.KeyEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * To launch this test from the command line:
+ *
+ * adb shell am instrument -w \
+ * -e class com.android.unit_tests.SearchManagerTest \
+ * com.android.unit_tests/android.test.InstrumentationTestRunner
+ */
+public class SearchManagerTest extends ActivityInstrumentationTestCase<LocalActivity> {
+
+ // If non-zero, enable a set of tests that start and stop the search manager.
+ // This is currently disabled because it's causing an unwanted jump from the unit test
+ // activity into the contacts activity. We'll put this back after we disable that jump.
+ private static final int TEST_SEARCH_START = 0;
+
+ /*
+ * Bug list of test ideas.
+ *
+ * testSearchManagerInterfaceAvailable()
+ * Exercise the interface obtained
+ *
+ * testSearchManagerAvailable()
+ * Exercise the interface obtained
+ *
+ * testSearchManagerInvocations()
+ * FIX - make it work again
+ * stress test with a very long string
+ *
+ * SearchableInfo tests
+ * Mock the context so I can provide very specific input data
+ * Confirm OK with "zero" searchables
+ * Confirm "good" metadata read properly
+ * Confirm "bad" metadata skipped properly
+ * Confirm ordering of searchables
+ * Confirm "good" actionkeys
+ * confirm "bad" actionkeys are rejected
+ * confirm XML ordering enforced (will fail today - bug in SearchableInfo)
+ * findActionKey works
+ * getIcon works
+ *
+ * SearchManager tests
+ * confirm proper identification of "default" activity based on policy, not hardcoded contacts
+ *
+ * SearchBar tests
+ * Maybe have to do with framework / unittest runner - need instrumented activity?
+ * How can we unit test the suggestions content providers?
+ * Should I write unit tests for any of them?
+ * Test scenarios:
+ * type-BACK (cancel)
+ * type-GO (send)
+ * type-navigate-click (suggestion)
+ * type-action
+ * type-navigate-action (suggestion)
+ */
+
+ /**
+ * Local copy of activity context
+ */
+ Context mContext;
+
+ public SearchManagerTest() {
+ super("com.android.unit_tests", LocalActivity.class);
+ }
+
+ /**
+ * Setup any common data for the upcoming tests.
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ Activity testActivity = getActivity();
+ mContext = (Context)testActivity;
+ }
+
+ /**
+ * The goal of this test is to confirm that we can obtain
+ * a search manager interface.
+ */
+ @MediumTest
+ public void testSearchManagerInterfaceAvailable() {
+ ISearchManager searchManager1 = ISearchManager.Stub.asInterface(
+ ServiceManager.getService(Context.SEARCH_SERVICE));
+ assertNotNull(searchManager1);
+ }
+
+ /**
+ * The goal of this test is to confirm that we can *only* obtain a search manager
+ * interface from an Activity context.
+ */
+ @MediumTest
+ public void testSearchManagerContextRestrictions() {
+ SearchManager searchManager1 = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager1);
+
+ Context applicationContext = mContext.getApplicationContext();
+ // this should fail, because you can't get a SearchManager from a non-Activity context
+ try {
+ applicationContext.getSystemService(Context.SEARCH_SERVICE);
+ assertFalse("Shouldn't retrieve SearchManager from a non-Activity context", true);
+ } catch (AndroidRuntimeException e) {
+ // happy here - we should catch this.
+ }
+ }
+
+ /**
+ * The goal of this test is to confirm that we can obtain
+ * a search manager at any time, and that for any given context,
+ * it is a singleton.
+ */
+ @LargeTest
+ public void testSearchManagerAvailable() {
+ SearchManager searchManager1 = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager1);
+ SearchManager searchManager2 = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager2);
+ assertSame( searchManager1, searchManager2 );
+ }
+
+ /**
+ * The goal of this test is to confirm that we can start and then
+ * stop a simple search.
+ */
+
+ @MediumTest
+ public void testSearchManagerInvocations() {
+ SearchManager searchManager = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager);
+
+ // TODO: make a real component name, or remove this need
+ final ComponentName cn = new ComponentName("", "");
+
+ if (TEST_SEARCH_START != 0) {
+ // These tests should simply run to completion w/o exceptions
+ searchManager.startSearch(null, false, cn, null, false);
+ searchManager.stopSearch();
+
+ searchManager.startSearch("", false, cn, null, false);
+ searchManager.stopSearch();
+
+ searchManager.startSearch("test search string", false, cn, null, false);
+ searchManager.stopSearch();
+
+ searchManager.startSearch("test search string", true, cn, null, false);
+ searchManager.stopSearch();
+ }
+ }
+
+ /**
+ * The goal of this test is to confirm proper operation of the
+ * SearchableInfo helper class.
+ *
+ * TODO: The metadata source needs to be mocked out because adding
+ * searchability metadata via this test is causing it to leak into the
+ * real system. So for now I'm just going to test for existence of the
+ * GoogleSearch app (which is searchable).
+ */
+ @LargeTest
+ public void testSearchableGoogleSearch() {
+ // test basic array & hashmap
+ SearchableInfo.buildSearchableList(mContext);
+
+ // test linkage from another activity
+ // TODO inject this via mocking into the package manager.
+ // TODO for now, just check for searchable GoogleSearch app (this isn't really a unit test)
+ ComponentName thisActivity = new ComponentName(
+ "com.android.googlesearch",
+ "com.android.googlesearch.GoogleSearch");
+
+ SearchableInfo si = SearchableInfo.getSearchableInfo(mContext, thisActivity);
+ assertNotNull(si);
+ assertTrue(si.mSearchable);
+ assertEquals(thisActivity, si.mSearchActivity);
+
+ Context appContext = si.getActivityContext(mContext);
+ assertNotNull(appContext);
+ MoreAsserts.assertNotEqual(appContext, mContext);
+ assertEquals("Google Search", appContext.getString(si.getHintId()));
+ assertEquals("Google", appContext.getString(si.getLabelId()));
+ }
+
+ /**
+ * Test that non-searchable activities return no searchable info (this would typically
+ * trigger the use of the default searchable e.g. contacts)
+ */
+ @LargeTest
+ public void testNonSearchable() {
+ // test basic array & hashmap
+ SearchableInfo.buildSearchableList(mContext);
+
+ // confirm that we return null for non-searchy activities
+ ComponentName nonActivity = new ComponentName(
+ "com.android.unit_tests",
+ "com.android.unit_tests.NO_SEARCH_ACTIVITY");
+ SearchableInfo si = SearchableInfo.getSearchableInfo(mContext, nonActivity);
+ assertNull(si);
+ }
+
+ /**
+ * This is an attempt to run the searchable info list with a mocked context. Here are some
+ * things I'd like to test.
+ *
+ * Confirm OK with "zero" searchables
+ * Confirm "good" metadata read properly
+ * Confirm "bad" metadata skipped properly
+ * Confirm ordering of searchables
+ * Confirm "good" actionkeys
+ * confirm "bad" actionkeys are rejected
+ * confirm XML ordering enforced (will fail today - bug in SearchableInfo)
+ * findActionKey works
+ * getIcon works
+
+ */
+ @LargeTest
+ public void testSearchableMocked() {
+ MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
+ MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+ ArrayList<SearchableInfo> searchables;
+ int count;
+
+ // build item list with real-world source data
+ mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
+ SearchableInfo.buildSearchableList(mockContext);
+ // tests with "real" searchables (deprecate, this should be a unit test)
+ searchables = SearchableInfo.getSearchablesList();
+ count = searchables.size();
+ assertTrue(count >= 1); // this isn't really a unit test
+ checkSearchables(searchables);
+
+ // build item list with mocked search data
+ // this round of tests confirms good operations with "zero" searchables found
+ // This should return either a null pointer or an empty list
+ mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
+ SearchableInfo.buildSearchableList(mockContext);
+ searchables = SearchableInfo.getSearchablesList();
+ if (searchables != null) {
+ count = searchables.size();
+ assertTrue(count == 0);
+ }
+ }
+
+ /**
+ * Generic health checker for an array of searchables.
+ *
+ * This is designed to pass for any semi-legal searchable, without knowing much about
+ * the format of the underlying data. It's fairly easy for a non-compliant application
+ * to provide meta-data that will pass here (e.g. a non-existent suggestions authority).
+ *
+ * @param searchables The list of searchables to examine.
+ */
+ private void checkSearchables(ArrayList<SearchableInfo> searchablesList) {
+ assertNotNull(searchablesList);
+ int count = searchablesList.size();
+ for (int ii = 0; ii < count; ii++) {
+ SearchableInfo si = searchablesList.get(ii);
+ assertNotNull(si);
+ assertTrue(si.mSearchable);
+ assertTrue(si.getLabelId() != 0); // This must be a useable string
+ assertNotEmpty(si.mSearchActivity.getClassName());
+ assertNotEmpty(si.mSearchActivity.getPackageName());
+ if (si.getSuggestAuthority() != null) {
+ // The suggestion fields are largely optional, so we'll just confirm basic health
+ assertNotEmpty(si.getSuggestAuthority());
+ assertNullOrNotEmpty(si.getSuggestPath());
+ assertNullOrNotEmpty(si.getSuggestSelection());
+ assertNullOrNotEmpty(si.getSuggestIntentAction());
+ assertNullOrNotEmpty(si.getSuggestIntentData());
+ }
+ /* Add a way to get the entire action key list, then explicitly test its elements */
+ /* For now, test the most common action key (CALL) */
+ ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL);
+ if (ai != null) {
+ assertEquals(ai.mKeyCode, KeyEvent.KEYCODE_CALL);
+ // one of these three fields must be non-null & non-empty
+ boolean m1 = (ai.mQueryActionMsg != null) && (ai.mQueryActionMsg.length() > 0);
+ boolean m2 = (ai.mSuggestActionMsg != null) && (ai.mSuggestActionMsg.length() > 0);
+ boolean m3 = (ai.mSuggestActionMsgColumn != null) &&
+ (ai.mSuggestActionMsgColumn.length() > 0);
+ assertTrue(m1 || m2 || m3);
+ }
+
+ /*
+ * Find ways to test these:
+ *
+ * private int mSearchMode
+ * private Drawable mIcon
+ */
+
+ /*
+ * Explicitly not tested here:
+ *
+ * Can be null, so not much to see:
+ * public String mSearchHint
+ * private String mZeroQueryBanner
+ *
+ * To be deprecated/removed, so don't bother:
+ * public boolean mFilterMode
+ * public boolean mQuickStart
+ * private boolean mIconResized
+ * private int mIconResizeWidth
+ * private int mIconResizeHeight
+ *
+ * All of these are "internal" working variables, not part of any contract
+ * private ActivityInfo mActivityInfo
+ * private Rect mTempRect
+ * private String mSuggestProviderPackage
+ * private String mCacheActivityContext
+ */
+ }
+ }
+
+ /**
+ * Combo assert for "string not null and not empty"
+ */
+ private void assertNotEmpty(final String s) {
+ assertNotNull(s);
+ MoreAsserts.assertNotEqual(s, "");
+ }
+
+ /**
+ * Combo assert for "string null or (not null and not empty)"
+ */
+ private void assertNullOrNotEmpty(final String s) {
+ if (s != null) {
+ MoreAsserts.assertNotEqual(s, "");
+ }
+ }
+
+ /**
+ * This is a mock for context. Used to perform a true unit test on SearchableInfo.
+ *
+ */
+ private class MyMockContext extends MockContext {
+
+ protected Context mRealContext;
+ protected PackageManager mPackageManager;
+
+ /**
+ * Constructor.
+ *
+ * @param realContext Please pass in a real context for some pass-throughs to function.
+ */
+ MyMockContext(Context realContext, PackageManager packageManager) {
+ mRealContext = realContext;
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Resources. Pass through for now.
+ */
+ @Override
+ public Resources getResources() {
+ return mRealContext.getResources();
+ }
+
+ /**
+ * Package manager. Pass through for now.
+ */
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ /**
+ * Package manager. Pass through for now.
+ */
+ @Override
+ public Context createPackageContext(String packageName, int flags)
+ throws PackageManager.NameNotFoundException {
+ return mRealContext.createPackageContext(packageName, flags);
+ }
+ }
+
+/**
+ * This is a mock for package manager. Used to perform a true unit test on SearchableInfo.
+ *
+ */
+ private class MyMockPackageManager extends MockPackageManager {
+
+ public final static int SEARCHABLES_PASSTHROUGH = 0;
+ public final static int SEARCHABLES_MOCK_ZERO = 1;
+ public final static int SEARCHABLES_MOCK_ONEGOOD = 2;
+ public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3;
+
+ protected PackageManager mRealPackageManager;
+ protected int mSearchablesMode;
+
+ public MyMockPackageManager(PackageManager realPM) {
+ mRealPackageManager = realPM;
+ mSearchablesMode = SEARCHABLES_PASSTHROUGH;
+ }
+
+ /**
+ * Set the mode for various tests.
+ */
+ public void setSearchablesMode(int newMode) {
+ switch (newMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ case SEARCHABLES_MOCK_ZERO:
+ mSearchablesMode = newMode;
+ break;
+
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Find activities that support a given intent.
+ *
+ * Retrieve all activities that can be performed for the given intent.
+ *
+ * @param intent The desired intent as per resolveActivity().
+ * @param flags Additional option flags. The most important is
+ * MATCH_DEFAULT_ONLY, to limit the resolution to only
+ * those activities that support the CATEGORY_DEFAULT.
+ *
+ * @return A List<ResolveInfo> containing one entry for each matching
+ * Activity. These are ordered from best to worst match -- that
+ * is, the first item in the list is what is returned by
+ * resolveActivity(). If there are no matching activities, an empty
+ * list is returned.
+ */
+ @Override
+ public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+ assertNotNull(intent);
+ assertEquals(intent.getAction(), Intent.ACTION_SEARCH);
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.queryIntentActivities(intent, flags);
+ case SEARCHABLES_MOCK_ZERO:
+ return null;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Retrieve an XML file from a package. This is a low-level API used to
+ * retrieve XML meta data.
+ *
+ * @param packageName The name of the package that this xml is coming from.
+ * Can not be null.
+ * @param resid The resource identifier of the desired xml. Can not be 0.
+ * @param appInfo Overall information about <var>packageName</var>. This
+ * may be null, in which case the application information will be retrieved
+ * for you if needed; if you already have this information around, it can
+ * be much more efficient to supply it here.
+ *
+ * @return Returns an XmlPullParser allowing you to parse out the XML
+ * data. Returns null if the xml resource could not be found for any
+ * reason.
+ */
+ @Override
+ public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) {
+ assertNotNull(packageName);
+ MoreAsserts.assertNotEqual(packageName, "");
+ MoreAsserts.assertNotEqual(resid, 0);
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.getXml(packageName, resid, appInfo);
+ case SEARCHABLES_MOCK_ZERO:
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Find a single content provider by its base path name.
+ *
+ * @param name The name of the provider to find.
+ * @param flags Additional option flags. Currently should always be 0.
+ *
+ * @return ContentProviderInfo Information about the provider, if found,
+ * else null.
+ */
+ @Override
+ public ProviderInfo resolveContentProvider(String name, int flags) {
+ assertNotNull(name);
+ MoreAsserts.assertNotEqual(name, "");
+ assertEquals(flags, 0);
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.resolveContentProvider(name, flags);
+ case SEARCHABLES_MOCK_ZERO:
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+}
+