diff options
Diffstat (limited to 'tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java')
-rw-r--r-- | tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java | 542 |
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(); + } + } + } +} + |