1fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng/* 2fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Copyright (C) 2011 The Android Open Source Project 3fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * 4fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License"); 5fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * you may not use this file except in compliance with the License. 6fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * You may obtain a copy of the License at 7fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * 8fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * http://www.apache.org/licenses/LICENSE-2.0 9fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * 10fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Unless required by applicable law or agreed to in writing, software 11fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS, 12fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * See the License for the specific language governing permissions and 14fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * limitations under the License. 15fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng */ 16fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 170a49afa2ad697307cc04ef4cb86570574fa720f2Gary Maipackage com.android.contacts.test; 18fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 19fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport android.app.Activity; 20fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport android.app.Instrumentation; 21fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport android.content.Context; 22fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport android.os.PowerManager; 23fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport android.view.View; 24fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport android.view.ViewGroup; 25fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport android.widget.TextView; 26fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 27fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport com.google.common.base.Preconditions; 28fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 29fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport junit.framework.Assert; 30fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 31fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport java.util.ArrayList; 32fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport java.util.List; 33fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport java.util.concurrent.Callable; 34fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport java.util.concurrent.ExecutionException; 35fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport java.util.concurrent.FutureTask; 36fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 37fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport javax.annotation.concurrent.GuardedBy; 38fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengimport javax.annotation.concurrent.ThreadSafe; 39fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 40fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng/** Some utility methods for making integration testing smoother. */ 41fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng@ThreadSafe 42fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Chengpublic class IntegrationTestUtils { 43fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng private static final String TAG = "IntegrationTestUtils"; 44fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 45fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng private final Instrumentation mInstrumentation; 46fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng private final Object mLock = new Object(); 47fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng @GuardedBy("mLock") private PowerManager.WakeLock mWakeLock; 48fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 49fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public IntegrationTestUtils(Instrumentation instrumentation) { 50fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng mInstrumentation = instrumentation; 51fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 52fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 53fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** 54fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Find a view by a given resource id, from the given activity, and click it, iff it is 55fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * enabled according to {@link View#isEnabled()}. 56fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng */ 57fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public void clickButton(final Activity activity, final int buttonResourceId) throws Throwable { 58fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng runOnUiThreadAndGetTheResult(new Callable<Void>() { 59fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng @Override 60fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public Void call() throws Exception { 61fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng View view = activity.findViewById(buttonResourceId); 62fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng Assert.assertNotNull(view); 63fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng if (view.isEnabled()) { 64fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng view.performClick(); 65fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 66fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return null; 67fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 68fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng }); 69fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 70fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 71fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** Returns the result of running {@link TextView#getText()} on the ui thread. */ 72fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public CharSequence getText(final TextView view) throws Throwable { 73fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return runOnUiThreadAndGetTheResult(new Callable<CharSequence>() { 74fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng @Override 75fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public CharSequence call() { 76fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return view.getText(); 77fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 78fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng }); 79fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 80fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 81fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // TODO: Move this class and the appropriate documentation into a test library, having checked 82fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // first to see if exactly this code already exists or not. 83fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** 84fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Execute a callable on the ui thread, returning its result synchronously. 85fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * <p> 86fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Waits for an idle sync on the main thread (see {@link Instrumentation#waitForIdle(Runnable)}) 87fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * before executing this callable. 88fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng */ 89fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public <T> T runOnUiThreadAndGetTheResult(Callable<T> callable) throws Throwable { 90fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng FutureTask<T> future = new FutureTask<T>(callable); 91fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng mInstrumentation.waitForIdle(future); 92fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng try { 93fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return future.get(); 94fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } catch (ExecutionException e) { 95fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // Unwrap the cause of the exception and re-throw it. 96fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng throw e.getCause(); 97fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 98fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 99fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 100fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** 101fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Wake up the screen, useful in tests that want or need the screen to be on. 102fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * <p> 103fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * This is usually called from setUp() for tests that require it. After calling this method, 104fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * {@link #releaseScreenWakeLock()} must be called, this is usually done from tearDown(). 105fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng */ 106fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public void acquireScreenWakeLock(Context context) { 107fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng synchronized (mLock) { 108fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng Preconditions.checkState(mWakeLock == null, "mWakeLock was already held"); 109fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)) 110fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng .newWakeLock( 111fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE | PowerManager.FULL_WAKE_LOCK, TAG); 112fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng mWakeLock.acquire(); 113fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 114fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 115fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 116fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** Release the wake lock previously acquired with {@link #acquireScreenWakeLock(Context)}. */ 117fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public void releaseScreenWakeLock() { 118fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng synchronized (mLock) { 119fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // We don't use Preconditions to force you to have acquired before release. 120fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // This is because we don't want unnecessary exceptions in tearDown() since they'll 121fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // typically mask the actual exception that happened during the test. 122fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // The other reason is that this method is most likely to be called from tearDown(), 123fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // which is invoked within a finally block, so it's not infrequently the case that 124fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // the setUp() method fails before getting the lock, at which point we don't want 125fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng // to fail in tearDown(). 126fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng if (mWakeLock != null) { 127fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng mWakeLock.release(); 128fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng mWakeLock = null; 129fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 130fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 131fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 132fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 133fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** 134fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Gets all {@link TextView} objects whose {@link TextView#getText()} contains the given text as 135fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * a substring. 136fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng */ 137fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public List<TextView> getTextViewsWithString(final Activity activity, final String text) 138fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng throws Throwable { 139953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee return getTextViewsWithString(getRootView(activity), text); 140953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee } 141953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee 142953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee /** 143953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee * Gets all {@link TextView} objects whose {@link TextView#getText()} contains the given text as 144953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee * a substring for the given root view. 145953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee */ 146953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee public List<TextView> getTextViewsWithString(final View rootView, final String text) 147953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee throws Throwable { 148fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return runOnUiThreadAndGetTheResult(new Callable<List<TextView>>() { 149fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng @Override 150fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public List<TextView> call() throws Exception { 151fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng List<TextView> matchingViews = new ArrayList<TextView>(); 152953bf02036ae9880db8f5f5e1046c0530ccdfe9dAndrew Lee for (TextView textView : getAllViews(TextView.class, rootView)) { 153fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng if (textView.getText().toString().contains(text)) { 154fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng matchingViews.add(textView); 155fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 156fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 157fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return matchingViews; 158fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 159fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng }); 160fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 161fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 162fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** Find the root view for a given activity. */ 163fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng public static View getRootView(Activity activity) { 164fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return activity.findViewById(android.R.id.content).getRootView(); 165fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 166fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng 167fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng /** 168fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * Gets a list of all views of a given type, rooted at the given parent. 169fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * <p> 170fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * This method will recurse down through all {@link ViewGroup} instances looking for 171fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * {@link View} instances of the supplied class type. Specifically it will use the 172fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * {@link Class#isAssignableFrom(Class)} method as the test for which views to add to the list, 173fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * so if you provide {@code View.class} as your type, you will get every view. The parent itself 174fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * will be included also, should it be of the right type. 175fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * <p> 176fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * This call manipulates the ui, and as such should only be called from the application's main 177fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng * thread. 178fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng */ 179fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng private static <T extends View> List<T> getAllViews(final Class<T> clazz, final View parent) { 180fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng List<T> results = new ArrayList<T>(); 181fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng if (parent.getClass().equals(clazz)) { 182fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng results.add(clazz.cast(parent)); 183fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 184fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng if (parent instanceof ViewGroup) { 185fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng ViewGroup viewGroup = (ViewGroup) parent; 186fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng for (int i = 0; i < viewGroup.getChildCount(); ++i) { 187fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng results.addAll(getAllViews(clazz, viewGroup.getChildAt(i))); 188fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 189fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 190fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng return results; 191fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng } 192fbecde24e96a5d95adc0e50c5ff5418346e37298Chiao Cheng} 193