public final class

CountingIdlingResource

extends java.lang.Object

implements IdlingResource

 java.lang.Object

↳androidx.test.espresso.idling.CountingIdlingResource

Gradle dependencies

compile group: 'androidx.test.espresso', name: 'espresso-idling-resource', version: '3.5.0-alpha06'

  • groupId: androidx.test.espresso
  • artifactId: espresso-idling-resource
  • version: 3.5.0-alpha06

Artifact androidx.test.espresso:espresso-idling-resource:3.5.0-alpha06 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.test.espresso:espresso-idling-resource com.android.support.test.espresso:espresso-idling-resource

Androidx class mapping:

androidx.test.espresso.idling.CountingIdlingResource android.support.test.espresso.idling.CountingIdlingResource

Overview

An implementation of IdlingResource that determines idleness by maintaining an internal counter. When the counter is 0 - it is considered to be idle, when it is non-zero it is not idle. This is very similar to the way a java.util.concurrent.Semaphore behaves.

The counter may be incremented or decremented from any thread. If it reaches an illogical state (like counter less than zero) it will throw an IllegalStateException.

This class can then be used to wrap up operations that while in progress should block tests from accessing the UI.

 public interface FooServer {
   public Foo newFoo();
   public void updateFoo(Foo foo);
 }

 public DecoratedFooServer implements FooServer {
   private final FooServer realFooServer;
   private final CountingIdlingResource fooServerIdlingResource;

   public DecoratedFooServer(FooServer realFooServer,
       CountingIdlingResource fooServerIdlingResource) {
     this.realFooServer = checkNotNull(realFooServer);
     this.fooServerIdlingResource = checkNotNull(fooServerIdlingResource);
   }

   public Foo newFoo() {
     fooServerIdlingResource.increment();
     try {
       return realFooServer.newFoo();
     } finally {
       fooServerIdlingResource.decrement();
     }
   }

   public void updateFoo(Foo foo) {
     fooServerIdlingResource.increment();
     try {
       realFooServer.updateFoo(foo);
     } finally {
       fooServerIdlingResource.decrement();
     }
   }
 }
 
Then in your test setup:
 public void setUp() throws Exception {
   super.setUp();
   FooServer realServer = FooApplication.getFooServer();
   CountingIdlingResource countingResource = new CountingIdlingResource("FooServerCalls");
   FooApplication.setFooServer(new DecoratedFooServer(realServer, countingResource));
   Espresso.registerIdlingResource(countingResource);
 }
 

Summary

Constructors
publicCountingIdlingResource(java.lang.String resourceName)

Creates a CountingIdlingResource without debug tracing.

publicCountingIdlingResource(java.lang.String resourceName, boolean debugCounting)

Creates a CountingIdlingResource.

Methods
public voiddecrement()

Decrements the count of in-flight transactions to the resource being monitored.

public voiddumpStateToLogs()

Prints the current state of this resource to the logcat at info level.

public java.lang.StringgetName()

public voidincrement()

Increments the count of in-flight transactions to the resource being monitored.

public booleanisIdleNow()

public voidregisterIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback)

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public CountingIdlingResource(java.lang.String resourceName)

Creates a CountingIdlingResource without debug tracing.

Parameters:

resourceName: the resource name this resource should report to Espresso.

public CountingIdlingResource(java.lang.String resourceName, boolean debugCounting)

Creates a CountingIdlingResource.

Parameters:

resourceName: the resource name this resource should report to Espresso.
debugCounting: if true increment & decrement calls will print trace information to logs.

Methods

public java.lang.String getName()

public boolean isIdleNow()

public void registerIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback)

public void increment()

Increments the count of in-flight transactions to the resource being monitored.

This method can be called from any thread.

public void decrement()

Decrements the count of in-flight transactions to the resource being monitored.

If this operation results in the counter falling below 0 - an exception is raised.

public void dumpStateToLogs()

Prints the current state of this resource to the logcat at info level.

Source

/*
 * Copyright (C) 2017 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 androidx.test.espresso.idling;

import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import androidx.test.espresso.IdlingResource;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * An implementation of {@link IdlingResource} that determines idleness by maintaining an internal
 * counter. When the counter is 0 - it is considered to be idle, when it is non-zero it is not idle.
 * This is very similar to the way a {@link java.util.concurrent.Semaphore} behaves.
 *
 * <p>The counter may be incremented or decremented from any thread. If it reaches an illogical
 * state (like counter less than zero) it will throw an IllegalStateException.
 *
 * <p>This class can then be used to wrap up operations that while in progress should block tests
 * from accessing the UI.
 *
 * <pre>{@code
 * public interface FooServer {
 *   public Foo newFoo();
 *   public void updateFoo(Foo foo);
 * }
 *
 * public DecoratedFooServer implements FooServer {
 *   private final FooServer realFooServer;
 *   private final CountingIdlingResource fooServerIdlingResource;
 *
 *   public DecoratedFooServer(FooServer realFooServer,
 *       CountingIdlingResource fooServerIdlingResource) {
 *     this.realFooServer = checkNotNull(realFooServer);
 *     this.fooServerIdlingResource = checkNotNull(fooServerIdlingResource);
 *   }
 *
 *   public Foo newFoo() {
 *     fooServerIdlingResource.increment();
 *     try {
 *       return realFooServer.newFoo();
 *     } finally {
 *       fooServerIdlingResource.decrement();
 *     }
 *   }
 *
 *   public void updateFoo(Foo foo) {
 *     fooServerIdlingResource.increment();
 *     try {
 *       realFooServer.updateFoo(foo);
 *     } finally {
 *       fooServerIdlingResource.decrement();
 *     }
 *   }
 * }
 * }</pre>
 *
 * Then in your test setup:
 *
 * <pre>{@code
 * public void setUp() throws Exception {
 *   super.setUp();
 *   FooServer realServer = FooApplication.getFooServer();
 *   CountingIdlingResource countingResource = new CountingIdlingResource("FooServerCalls");
 *   FooApplication.setFooServer(new DecoratedFooServer(realServer, countingResource));
 *   Espresso.registerIdlingResource(countingResource);
 * }
 * }</pre>
 */
@SuppressWarnings("javadoc")
public final class CountingIdlingResource implements IdlingResource {
  private static final String TAG = "CountingIdlingResource";
  private final String resourceName;
  private final AtomicInteger counter = new AtomicInteger(0);
  private final boolean debugCounting;

  // written from main thread, read from any thread.
  private volatile ResourceCallback resourceCallback;

  // read/written from any thread - used for debugging messages.
  private volatile long becameBusyAt = 0;
  private volatile long becameIdleAt = 0;

  /**
   * Creates a CountingIdlingResource without debug tracing.
   *
   * @param resourceName the resource name this resource should report to Espresso.
   */
  public CountingIdlingResource(String resourceName) {
    this(resourceName, false);
  }

  /**
   * Creates a CountingIdlingResource.
   *
   * @param resourceName the resource name this resource should report to Espresso.
   * @param debugCounting if true increment & decrement calls will print trace information to logs.
   */
  public CountingIdlingResource(String resourceName, boolean debugCounting) {
    // TODO: replace this with Checks once we have a separate utils module
    if (TextUtils.isEmpty(resourceName)) {
      throw new IllegalArgumentException("resourceName cannot be empty or null!");
    }
    this.resourceName = resourceName;
    this.debugCounting = debugCounting;
  }

  @Override
  public String getName() {
    return resourceName;
  }

  @Override
  public boolean isIdleNow() {
    return counter.get() == 0;
  }

  @Override
  public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
    this.resourceCallback = resourceCallback;
  }

  /**
   * Increments the count of in-flight transactions to the resource being monitored.
   *
   * <p>This method can be called from any thread.
   */
  public void increment() {
    int counterVal = counter.getAndIncrement();
    if (0 == counterVal) {
      becameBusyAt = SystemClock.uptimeMillis();
    }

    if (debugCounting) {
      Log.i(TAG, "Resource: " + resourceName + " in-use-count incremented to: " + (counterVal + 1));
    }
  }

  /**
   * Decrements the count of in-flight transactions to the resource being monitored.
   *
   * <p>If this operation results in the counter falling below 0 - an exception is raised.
   *
   * @throws IllegalStateException if the counter is below 0.
   */
  public void decrement() {
    int counterVal = counter.decrementAndGet();

    if (counterVal == 0) {
      // we've gone from non-zero to zero. That means we're idle now! Tell espresso.
      if (null != resourceCallback) {
        resourceCallback.onTransitionToIdle();
      }
      becameIdleAt = SystemClock.uptimeMillis();
    }

    if (debugCounting) {
      if (counterVal == 0) {
        Log.i(
            TAG,
            "Resource: "
                + resourceName
                + " went idle! (Time spent not idle: "
                + (becameIdleAt - becameBusyAt)
                + ")");
      } else {
        Log.i(TAG, "Resource: " + resourceName + " in-use-count decremented to: " + counterVal);
      }
    }
    // TODO: replace this with Checks once we have a separate utils module
    if (counterVal <= -1) {
      throw new IllegalStateException("Counter has been corrupted! counterVal=" + counterVal);
    }
  }

  /** Prints the current state of this resource to the logcat at info level. */
  public void dumpStateToLogs() {
    StringBuilder message =
        new StringBuilder("Resource: ")
            .append(resourceName)
            .append(" inflight transaction count: ")
            .append(counter.get());
    if (0 == becameBusyAt) {
      Log.i(TAG, message.append(" and has never been busy!").toString());
    } else {
      message.append(" and was last busy at: ").append(becameBusyAt);
      if (0 == becameIdleAt) {
        Log.w(TAG, message.append(" AND NEVER WENT IDLE!").toString());
      } else {
        message.append(" and last went idle at: ").append(becameIdleAt);
        Log.i(TAG, message.toString());
      }
    }
  }
}