Skip to content

Instantly share code, notes, and snippets.

@patrickhammond
Last active September 2, 2018 12:14
Show Gist options
  • Select an option

  • Save patrickhammond/19e584b90d7aae20f8f4 to your computer and use it in GitHub Desktop.

Select an option

Save patrickhammond/19e584b90d7aae20f8f4 to your computer and use it in GitHub Desktop.

Revisions

  1. Patrick Hammond revised this gist Apr 13, 2016. 1 changed file with 7 additions and 8 deletions.
    15 changes: 7 additions & 8 deletions EspressoTestRule.java
    Original file line number Diff line number Diff line change
    @@ -45,8 +45,8 @@ public class EspressoTestRule<T extends Activity> extends ActivityTestRule<T> {
    private static final float ANIMATION_DISABLED = 0.0f;
    private static final float ANIMATION_DEFAULT = 1.0f;

    // This delay is kind of a sweet spot to ensure rise and shine has a chance to work
    private static final long SCREEN_ON_ATTEMPT_DELAY = 250;
    // Giving the device a full second to turn the screen on. This should be a one time hit.
    private static final long SCREEN_ON_ATTEMPT_DELAY = 1000;
    private static final int MAX_SCREEN_ON_ATTEMPTS = 20;

    public interface UIRunner {
    @@ -83,7 +83,7 @@ protected void afterActivityLaunched() {
    PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);

    int count = 0;
    while (count == 0 || (!powerManager.isScreenOn() && count < MAX_SCREEN_ON_ATTEMPTS)) {
    while (!powerManager.isScreenOn() && count < MAX_SCREEN_ON_ATTEMPTS) {
    ui(new UIRunner() {
    @Override
    public void run() {
    @@ -120,16 +120,15 @@ public void run() {
    // Inspired from https://gist.github.com/JakeWharton/f50f3b4d87e57d8e96e9
    @SuppressWarnings("deprecation")
    private void riseAndShine(Activity activity) {

    KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
    KeyguardLock keyguardLock = keyguardManager.newKeyguardLock(activity.getLocalClassName());
    keyguardLock.disableKeyguard();

    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

    PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
    WakeLock lock = powerManager.newWakeLock(
    PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE,
    "wakeup!");
    WakeLock lock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "wakeup!");

    lock.acquire();
    lock.release();
    @@ -139,15 +138,15 @@ private void disableAllAnimations() {
    if (getAnimationPermissionStatus() == PackageManager.PERMISSION_GRANTED) {
    setSystemAnimationsScale(ANIMATION_DISABLED);
    } else {
    Log.w(TAG, "Not granted permission to change animation scale.");
    Log.e(TAG, "Not granted permission to change animation scale.");
    }
    }

    private void enableAllAnimations() {
    if (getAnimationPermissionStatus() == PackageManager.PERMISSION_GRANTED) {
    setSystemAnimationsScale(ANIMATION_DEFAULT);
    } else {
    Log.w(TAG, "Not granted permission to change animation scale.");
    Log.e(TAG, "Not granted permission to change animation scale.");
    }
    }

  2. Patrick Hammond revised this gist Apr 11, 2016. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion EspressoTestRule.java
    Original file line number Diff line number Diff line change
    @@ -45,8 +45,9 @@ public class EspressoTestRule<T extends Activity> extends ActivityTestRule<T> {
    private static final float ANIMATION_DISABLED = 0.0f;
    private static final float ANIMATION_DEFAULT = 1.0f;

    // This delay is kind of a sweet spot to ensure rise and shine has a chance to work
    private static final long SCREEN_ON_ATTEMPT_DELAY = 250;
    private static final int MAX_SCREEN_ON_ATTEMPTS = 20;
    private static final long SCREEN_ON_ATTEMPT_DELAY = 100;

    public interface UIRunner {
    void run();
  3. Patrick Hammond revised this gist Apr 8, 2016. 1 changed file with 6 additions and 3 deletions.
    9 changes: 6 additions & 3 deletions EspressoTestRule.java
    Original file line number Diff line number Diff line change
    @@ -45,6 +45,9 @@ public class EspressoTestRule<T extends Activity> extends ActivityTestRule<T> {
    private static final float ANIMATION_DISABLED = 0.0f;
    private static final float ANIMATION_DEFAULT = 1.0f;

    private static final int MAX_SCREEN_ON_ATTEMPTS = 20;
    private static final long SCREEN_ON_ATTEMPT_DELAY = 100;

    public interface UIRunner {
    void run();
    }
    @@ -79,7 +82,7 @@ protected void afterActivityLaunched() {
    PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);

    int count = 0;
    while (count == 0 || (!powerManager.isScreenOn() && count < 10)) {
    while (count == 0 || (!powerManager.isScreenOn() && count < MAX_SCREEN_ON_ATTEMPTS)) {
    ui(new UIRunner() {
    @Override
    public void run() {
    @@ -88,14 +91,14 @@ public void run() {
    });

    try {
    Thread.sleep(100);
    Thread.sleep(SCREEN_ON_ATTEMPT_DELAY);
    } catch (InterruptedException ex) {
    Log.e(TAG, "Couldn't sleep the test thread after trying to turn the screen on", ex);
    }
    count++;
    }

    if (count >= 10) {
    if (count >= MAX_SCREEN_ON_ATTEMPTS) {
    Log.e(TAG, "Giving up trying to turn the screen on");
    }
    }
  4. Patrick Hammond revised this gist Apr 8, 2016. 1 changed file with 2 additions and 5 deletions.
    7 changes: 2 additions & 5 deletions EspressoTestRule.java
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    import android.app.Activity;
    import android.app.Instrumentation;
    import android.app.KeyguardManager;
    import android.app.KeyguardManager.KeyguardLock;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.os.IBinder;
    @@ -116,7 +117,7 @@ public void run() {
    @SuppressWarnings("deprecation")
    private void riseAndShine(Activity activity) {
    KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
    KeyguardManager.KeyguardLock keyguardLock = keyguardManager.newKeyguardLock(activity.getLocalClassName());
    KeyguardLock keyguardLock = keyguardManager.newKeyguardLock(activity.getLocalClassName());
    keyguardLock.disableKeyguard();

    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
    @@ -128,10 +129,6 @@ private void riseAndShine(Activity activity) {

    lock.acquire();
    lock.release();

    if (!powerManager.isScreenOn()) {

    }
    }

    private void disableAllAnimations() {
  5. Patrick Hammond revised this gist Apr 8, 2016. 1 changed file with 28 additions and 8 deletions.
    36 changes: 28 additions & 8 deletions EspressoTestRule.java
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,6 @@
    import android.app.Activity;
    import android.app.Instrumentation;
    import android.app.KeyguardManager;
    import android.app.KeyguardManager.KeyguardLock;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.os.IBinder;
    @@ -75,12 +74,29 @@ public Statement apply(Statement base, Description description) {
    @Override
    protected void afterActivityLaunched() {
    super.afterActivityLaunched();
    ui(new UIRunner() {
    @Override
    public void run() {
    riseAndShine(getActivity());
    final Activity activity = getActivity();
    PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);

    int count = 0;
    while (count == 0 || (!powerManager.isScreenOn() && count < 10)) {
    ui(new UIRunner() {
    @Override
    public void run() {
    riseAndShine(activity);
    }
    });

    try {
    Thread.sleep(100);
    } catch (InterruptedException ex) {
    Log.e(TAG, "Couldn't sleep the test thread after trying to turn the screen on", ex);
    }
    });
    count++;
    }

    if (count >= 10) {
    Log.e(TAG, "Giving up trying to turn the screen on");
    }
    }

    public void ui(final UIRunner runner) {
    @@ -100,7 +116,7 @@ public void run() {
    @SuppressWarnings("deprecation")
    private void riseAndShine(Activity activity) {
    KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
    KeyguardLock keyguardLock = keyguardManager.newKeyguardLock(activity.getLocalClassName());
    KeyguardManager.KeyguardLock keyguardLock = keyguardManager.newKeyguardLock(activity.getLocalClassName());
    keyguardLock.disableKeyguard();

    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
    @@ -112,6 +128,10 @@ private void riseAndShine(Activity activity) {

    lock.acquire();
    lock.release();

    if (!powerManager.isScreenOn()) {

    }
    }

    private void disableAllAnimations() {
    @@ -233,4 +253,4 @@ public static Set<Activity> getActivitiesInStages(Stage... stages) {
    }
    return activities;
    }
    }
    }
  6. Patrick Hammond created this gist May 14, 2015.
    236 changes: 236 additions & 0 deletions EspressoTestRule.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,236 @@
    import android.app.Activity;
    import android.app.Instrumentation;
    import android.app.KeyguardManager;
    import android.app.KeyguardManager.KeyguardLock;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.os.IBinder;
    import android.os.PowerManager;
    import android.os.PowerManager.WakeLock;
    import android.support.test.InstrumentationRegistry;
    import android.support.test.rule.ActivityTestRule;
    import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
    import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
    import android.support.test.runner.lifecycle.Stage;
    import android.util.Log;
    import android.view.WindowManager;

    import com.google.common.base.Throwables;
    import com.google.common.collect.Sets;

    import org.junit.runner.Description;
    import org.junit.runners.model.Statement;

    import java.lang.reflect.Method;
    import java.util.Collection;
    import java.util.Set;
    import java.util.concurrent.Callable;
    import java.util.concurrent.atomic.AtomicReference;

    /**
    * Make sure this gets added to the manifest of the application under test (typically a manifest in the debug variant).
    *
    <code>
    <uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    </code>
    *
    * @param <T>
    */
    public class EspressoTestRule<T extends Activity> extends ActivityTestRule<T> {
    private static final String TAG = EspressoTestRule.class.getSimpleName();

    private static final String ANIMATION_PERMISSION = "android.permission.SET_ANIMATION_SCALE";
    private static final float ANIMATION_DISABLED = 0.0f;
    private static final float ANIMATION_DEFAULT = 1.0f;

    public interface UIRunner {
    void run();
    }

    public EspressoTestRule(Class<T> activityClass) {
    super(activityClass);
    }

    public EspressoTestRule(Class<T> activityClass, boolean initialTouchMode) {
    super(activityClass, initialTouchMode);
    }

    public EspressoTestRule(Class<T> activityClass, boolean initialTouchMode, boolean launchActivity) {
    super(activityClass, initialTouchMode, launchActivity);
    }

    @Override
    public Statement apply(Statement base, Description description) {
    try {
    disableAllAnimations();
    return super.apply(base, description);
    } finally {
    enableAllAnimations();
    closeAllActivities();
    }
    }

    @Override
    protected void afterActivityLaunched() {
    super.afterActivityLaunched();
    ui(new UIRunner() {
    @Override
    public void run() {
    riseAndShine(getActivity());
    }
    });
    }

    public void ui(final UIRunner runner) {
    try {
    runOnUiThread(new Runnable() {
    @Override
    public void run() {
    runner.run();
    }
    });
    } catch (Throwable throwable) {
    throw new RuntimeException(throwable);
    }
    }

    // Inspired from https://gist.github.com/JakeWharton/f50f3b4d87e57d8e96e9
    @SuppressWarnings("deprecation")
    private void riseAndShine(Activity activity) {
    KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
    KeyguardLock keyguardLock = keyguardManager.newKeyguardLock(activity.getLocalClassName());
    keyguardLock.disableKeyguard();

    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

    PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
    WakeLock lock = powerManager.newWakeLock(
    PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE,
    "wakeup!");

    lock.acquire();
    lock.release();
    }

    private void disableAllAnimations() {
    if (getAnimationPermissionStatus() == PackageManager.PERMISSION_GRANTED) {
    setSystemAnimationsScale(ANIMATION_DISABLED);
    } else {
    Log.w(TAG, "Not granted permission to change animation scale.");
    }
    }

    private void enableAllAnimations() {
    if (getAnimationPermissionStatus() == PackageManager.PERMISSION_GRANTED) {
    setSystemAnimationsScale(ANIMATION_DEFAULT);
    } else {
    Log.w(TAG, "Not granted permission to change animation scale.");
    }
    }

    private int getAnimationPermissionStatus() {
    Context context = InstrumentationRegistry.getTargetContext();
    return context.checkCallingOrSelfPermission(ANIMATION_PERMISSION);
    }

    // https://code.google.com/p/android-test-kit/wiki/DisablingAnimations
    private void setSystemAnimationsScale(float animationScale) {
    try {
    Class<?> windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub");
    Method asInterface = windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class);
    Class<?> serviceManagerClazz = Class.forName("android.os.ServiceManager");
    Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class);
    Class<?> windowManagerClazz = Class.forName("android.view.IWindowManager");
    Method setAnimationScales = windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class);
    Method getAnimationScales = windowManagerClazz.getDeclaredMethod("getAnimationScales");

    IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window");
    Object windowManagerObj = asInterface.invoke(null, windowManagerBinder);
    float[] currentScales = (float[]) getAnimationScales.invoke(windowManagerObj);
    for (int i = 0; i < currentScales.length; i++) {
    currentScales[i] = animationScale;
    }
    setAnimationScales.invoke(windowManagerObj, new Object[]{currentScales});
    } catch (Exception ex) {
    Log.e(TAG, "Could not use reflection to change animation scale to: " + animationScale, ex);
    }
    }

    // See https://code.google.com/p/android-test-kit/issues/detail?id=66
    private void closeAllActivities() {
    try {
    Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
    closeAllActivities(instrumentation);
    } catch (Exception ex) {
    Log.e(TAG, "Could not use close all activities", ex);
    }
    }

    private void closeAllActivities(Instrumentation instrumentation) throws Exception {
    final int NUMBER_OF_RETRIES = 100;
    int i = 0;
    while (closeActivity(instrumentation)) {
    if (i++ > NUMBER_OF_RETRIES) {
    throw new AssertionError("Limit of retries excesses");
    }
    Thread.sleep(200);
    }
    }

    private boolean closeActivity(Instrumentation instrumentation) throws Exception {
    final Boolean activityClosed = callOnMainSync(instrumentation, new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
    final Set<Activity> activities = getActivitiesInStages(Stage.RESUMED,
    Stage.STARTED, Stage.PAUSED, Stage.STOPPED, Stage.CREATED);
    activities.removeAll(getActivitiesInStages(Stage.DESTROYED));
    if (activities.size() > 0) {
    final Activity activity = activities.iterator().next();
    activity.finish();
    return true;
    } else {
    return false;
    }
    }
    });
    if (activityClosed) {
    instrumentation.waitForIdleSync();
    }
    return activityClosed;
    }

    private <X> X callOnMainSync(Instrumentation instrumentation, final Callable<X> callable) throws Exception {
    final AtomicReference<X> retAtomic = new AtomicReference<>();
    final AtomicReference<Throwable> exceptionAtomic = new AtomicReference<>();
    instrumentation.runOnMainSync(new Runnable() {
    @Override
    public void run() {
    try {
    retAtomic.set(callable.call());
    } catch (Throwable e) {
    exceptionAtomic.set(e);
    }
    }
    });
    final Throwable exception = exceptionAtomic.get();
    if (exception != null) {
    Throwables.propagateIfInstanceOf(exception, Exception.class);
    Throwables.propagate(exception);
    }
    return retAtomic.get();
    }

    public static Set<Activity> getActivitiesInStages(Stage... stages) {
    final Set<Activity> activities = Sets.newHashSet();
    final ActivityLifecycleMonitor instance = ActivityLifecycleMonitorRegistry.getInstance();
    for (Stage stage : stages) {
    final Collection<Activity> activitiesInStage = instance.getActivitiesInStage(stage);
    if (activitiesInStage != null) {
    activities.addAll(activitiesInStage);
    }
    }
    return activities;
    }
    }
    26 changes: 26 additions & 0 deletions SampleTest.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    import android.support.test.runner.AndroidJUnit4;
    import android.test.suitebuilder.annotation.LargeTest;

    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.runner.RunWith;

    import static android.support.test.espresso.Espresso.*;
    import static android.support.test.espresso.assertion.ViewAssertions.*;
    import static android.support.test.espresso.matcher.ViewMatchers.*;
    import static org.hamcrest.Matchers.is;

    @RunWith(AndroidJUnit4.class)
    @LargeTest
    public class SampleTest {

    @Rule
    public EspressoTestRule<SampleActivity> activityRule = new EspressoTestRule<>(SampleActivity.class);

    @Test
    public void testInitialDisplay() {
    onView(withId(R.id.progress)).check(matches(withEffectiveVisibility(Visibility.VISIBLE)));
    onView(withId(R.id.content)).check(matches(withEffectiveVisibility(Visibility.INVISIBLE)));
    onView(withId(R.id.header)).check(matches(withText("Sample Header")));
    }
    }