public class

UriIdlingResource

extends java.lang.Object

implements IdlingResource

 java.lang.Object

↳androidx.test.espresso.idling.net.UriIdlingResource

Gradle dependencies

compile group: 'androidx.test.espresso.idling', name: 'idling-net', version: '3.6.1'

  • groupId: androidx.test.espresso.idling
  • artifactId: idling-net
  • version: 3.6.1

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

Androidx artifact mapping:

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

Androidx class mapping:

androidx.test.espresso.idling.net.UriIdlingResource android.support.test.espresso.idling.net.UriIdlingResource

Overview

An implementation of IdlingResource useful for monitoring idleness of network traffic.

This is similar to , with the additional idleness constraint that the counter must be 0 for a set period of time before the resource becomes idle.

A network timeout is required to be reasonably sure that the webview has finished loading. Imagine the case where each response that comes back causes another request to be made until loading is complete. The counter will go from 0->1->0->1->0->1..., but we don't want to report the webview as idle each time this happens.

Summary

Constructors
publicUriIdlingResource(java.lang.String resourceName, long timeoutMs)

Methods
public voidbeginLoad(java.lang.String uri)

Called when a request is made.

public voidendLoad(java.lang.String uri)

Called when a request is completed (ie the response is returned).

public java.lang.StringgetName()

public voidignoreUri(java.util.regex.Pattern pattern)

Add a regex pattern to the ignore list.

public booleanisIdleNow()

public voidregisterIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback)

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

Constructors

public UriIdlingResource(java.lang.String resourceName, long timeoutMs)

Methods

public java.lang.String getName()

public boolean isIdleNow()

public void registerIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback)

public void ignoreUri(java.util.regex.Pattern pattern)

Add a regex pattern to the ignore list.

All request URIs are checked against all patterns, and matches are ignored for the purposes of detecting when the webview is idle.

Ignored patterns can only be added when the webview is idle.

public void beginLoad(java.lang.String uri)

Called when a request is made.

If the URI is not blacklisted the idle counter is incremented.

public void endLoad(java.lang.String uri)

Called when a request is completed (ie the response is returned).

If the URI is not blacklisted the idle counter is decremented. Once the idle counter reaches 0, the idle update thread will set the resource as idle after the appropriate timeout.

Source

/*
 * Copyright (C) 2016 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 Lice`nse 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.net;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.test.espresso.IdlingResource;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;

/**
 * An implementation of {@link IdlingResource} useful for monitoring idleness of network traffic.
 *
 * <p>This is similar to {@link androidx.test.espresso.contrib.CountingIdlingResource}, with
 * the additional idleness constraint that the counter must be 0 for a set period of time before the
 * resource becomes idle.
 *
 * <p>A network timeout is required to be reasonably sure that the webview has finished loading.
 * Imagine the case where each response that comes back causes another request to be made until
 * loading is complete. The counter will go from 0->1->0->1->0->1..., but we don't want to report
 * the webview as idle each time this happens.
 */
public class UriIdlingResource implements IdlingResource {

  private static final String TAG = "UriIdlingResource";
  private final String resourceName;
  private final long timeoutMs;
  private final boolean debug;

  // Read and modified from multiple threads
  private final AtomicInteger counter = new AtomicInteger(0);
  private final CopyOnWriteArrayList<Pattern> ignoredRegexes = new CopyOnWriteArrayList<>();
  private final AtomicBoolean idle = new AtomicBoolean(true);
  private final Runnable transitionToIdle;
  private volatile ResourceCallback resourceCallback;
  private final HandlerIntf handler;

  public UriIdlingResource(String resourceName, long timeoutMs) {
    this(resourceName, timeoutMs, false, new DefaultHandler(new Handler(Looper.getMainLooper())));
  }

  @VisibleForTesting
  UriIdlingResource(String resourceName, long timeoutMs, boolean debug, HandlerIntf handler) {
    if (timeoutMs <= 0) {
      throw new IllegalArgumentException("timeoutMs has to be greater than 0");
    }
    this.resourceName = resourceName;
    this.timeoutMs = timeoutMs;
    this.debug = debug;
    this.handler = handler;

    transitionToIdle =
        new Runnable() {
          @Override
          public void run() {
            idle.set(true);
            if (resourceCallback != null) {
              resourceCallback.onTransitionToIdle();
            }
          }
        };
  }

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

  @Override
  public boolean isIdleNow() {
    return idle.get();
  }

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

  /**
   * Add a regex pattern to the ignore list.
   *
   * <p>All request URIs are checked against all patterns, and matches are ignored for the purposes
   * of detecting when the webview is idle.
   *
   * <p>Ignored patterns can only be added when the webview is idle.
   */
  public void ignoreUri(Pattern pattern) {
    if (!isIdleNow()) {
      Log.e(TAG, "Ignored patterns can only be added when the resource is idle.");
    } else {
      ignoredRegexes.add(pattern);
    }
  }

  /**
   * Called when a request is made.
   *
   * <p>If the URI is not blacklisted the idle counter is incremented.
   */
  public void beginLoad(String uri) {
    if (uriIsIgnored(uri)) {
      return;
    }
    idle.set(false);
    long count = counter.getAndIncrement();
    if (count == 0) {
      handler.removeCallbacks(transitionToIdle);
    }
    if (debug) {
      Log.i(TAG, "Resource " + resourceName + " counter increased to " + (count + 1));
    }
  }

  /**
   * Called when a request is completed (ie the response is returned).
   *
   * <p>If the URI is not blacklisted the idle counter is decremented. Once the idle counter reaches
   * 0, the idle update thread will set the resource as idle after the appropriate timeout.
   */
  public void endLoad(String uri) {
    if (uriIsIgnored(uri)) {
      return;
    }
    int count = counter.decrementAndGet();
    if (count < 0) {
      throw new IllegalStateException("Counter has been corrupted! Count=" + count);
    } else if (count == 0) {
      handler.postDelayed(transitionToIdle, timeoutMs);
    }
    if (debug) {
      Log.i(TAG, "Resource " + resourceName + " counter decreased to " + count);
    }
  }

  private boolean uriIsIgnored(String uri) {
    for (Pattern pattern : ignoredRegexes) {
      if (pattern.matcher(uri).matches()) {
        Log.i(TAG, "Resource " + resourceName + " ignored URI: <" + uri + ">");
        return true;
      }
    }
    return false;
  }

  @VisibleForTesting
  void forceIdleTransition() {
    transitionToIdle.run();
  }

  /**
   * Wraps a Handler object.
   *
   * <p>Mock this for testing purposes.
   */
  public static interface HandlerIntf {
    public void postDelayed(Runnable runnable, long millis);

    public void removeCallbacks(Runnable runnable);
  }

  private static final class DefaultHandler implements HandlerIntf {
    private final Handler handler;

    public DefaultHandler(Handler handler) {
      this.handler = handler;
    }

    @Override
    public void postDelayed(Runnable runnable, long millis) {
      handler.postDelayed(runnable, millis);
    }

    @Override
    public void removeCallbacks(Runnable runnable) {
      handler.removeCallbacks(runnable);
    }
  }
}