All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowCloseGuard Maven / Gradle / Ivy

package org.robolectric.shadows;

import static org.robolectric.util.reflector.Reflector.reflector;

import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import dalvik.system.CloseGuard;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.ReflectorObject;
import org.robolectric.annotation.Resetter;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;

/**
 * Shadow for {@link CloseGuard}. {@code CloseGuardRule} can be used to easily verify all
 * CloseGuards have been closed.
 */
@Implements(value = CloseGuard.class, isInAndroidSdk = false)
public class ShadowCloseGuard {

  private static final Set openCloseGuards =
      Collections.synchronizedSet(new HashSet<>());
  private static final Set warnedThrowables =
      Collections.synchronizedSet(new HashSet<>());

  @RealObject private CloseGuard realCloseGuard;
  @ReflectorObject private CloseGuardReflector closeGuardReflector;

  @Implementation
  protected void open(String closer) {
    closeGuardReflector.open(closer);
    openCloseGuards.add(realCloseGuard);
  }

  @Implementation
  protected void close() {
    closeGuardReflector.close();
    openCloseGuards.remove(realCloseGuard);
  }

  @Implementation
  protected void warnIfOpen() {
    closeGuardReflector.warnIfOpen();
    if (openCloseGuards.contains(realCloseGuard)) {
      warnedThrowables.add(createThrowableFromCloseGuard(realCloseGuard));
    }
  }

  @Resetter
  public static void reset() {
    openCloseGuards.clear();
    warnedThrowables.clear();
  }

  public static ArrayList getErrors() {
    ArrayList errors = new ArrayList<>(openCloseGuards.size() + warnedThrowables.size());
    for (CloseGuard closeGuard : openCloseGuards) {
      errors.add(createThrowableFromCloseGuard(closeGuard));
    }
    errors.addAll(warnedThrowables);
    return errors;
  }

  private static Throwable createThrowableFromCloseGuard(CloseGuard closeGuard) {
    if (VERSION.SDK_INT >= VERSION_CODES.P) {
      Object closerNameOrAllocationInfo =
          reflector(CloseGuardReflector.class, closeGuard).getCloserNameOrAllocationInfo();
      if (closerNameOrAllocationInfo instanceof Throwable) {
        return (Throwable) closerNameOrAllocationInfo;
      } else if (closerNameOrAllocationInfo instanceof String) {
        return new Throwable((String) closerNameOrAllocationInfo);
      }
    } else {
      Throwable allocationSite =
          reflector(CloseGuardReflector.class, closeGuard).getAllocationSite();
      if (allocationSite != null) {
        return allocationSite;
      }
    }
    return new Throwable("CloseGuard with no allocation info");
  }

  @ForType(CloseGuard.class)
  interface CloseGuardReflector {

    @Direct
    void open(String closer);

    @Direct
    void close();

    @Direct
    void warnIfOpen();

    // For API 29+
    @Accessor("closerNameOrAllocationInfo")
    Object getCloserNameOrAllocationInfo();

    // For API <= 28
    @Accessor("allocationSite")
    Throwable getAllocationSite();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy