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

org.robolectric.annotation.Config Maven / Gradle / Ivy

package org.robolectric.annotation;

import android.app.Application;

import org.jetbrains.annotations.NotNull;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
 * Configuration settings that can be used on a per-class or per-test basis.
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Config {
  /**
   * TODO(vnayar): Create named constants for default values instead of magic numbers.
   * Array named contants must be avoided in order to dodge a JDK 1.7 bug.
   *   error: annotation Config is missing value for the attribute <clinit>
   * See JDK-8013485.
   */
  String NONE = "--none";
  String DEFAULT_MANIFEST = "--default";
  String DEFAULT_PACKAGE_NAME = "";
  String DEFAULT_ABI_SPLIT = "";
  String DEFAULT_QUALIFIERS = "";
  String DEFAULT_RES_FOLDER = "res";
  String DEFAULT_ASSET_FOLDER = "assets";
  String DEFAULT_BUILD_FOLDER = "build";

  /**
   * The Android SDK level to emulate. If not specified, Robolectric defaults to API 16.
   * This value will also be set as Build.VERSION.SDK_INT.
   *
   * @return The Android SDK level to emulate.
   */
  int[] sdk() default {};  // DEFAULT_SDK

  /**
   * The Android manifest file to load; Robolectric will look relative to the current directory.
   * Resources and assets will be loaded relative to the manifest.
   *
   * If not specified, Robolectric defaults to {@code AndroidManifest.xml}.
   *
   * If your project has no manifest or resources, use {@link Config#NONE}.
   *
   * @return The Android manifest file to load.
   */
  String manifest() default DEFAULT_MANIFEST;

  /**
   * Reference to the BuildConfig class created by the Gradle build system.
   *
   * @return Reference to BuildConfig class.
   */
  Class constants() default Void.class;  // DEFAULT_CONSTANTS

  /**
   * The {@link android.app.Application} class to use in the test, this takes precedence over any application
   * specified in the AndroidManifest.xml.
   *
   * @return The {@link android.app.Application} class to use in the test.
   */
  Class application() default Application.class;  // DEFAULT_APPLICATION

  /**
   * Java package name where the "R.class" file is located. This only needs to be specified if you define
   * an {@code applicationId} associated with {@code productFlavors} or specify {@code applicationIdSuffix}
   * in your build.gradle.
   *
   * 

If not specified, Robolectric defaults to the {@code applicationId}.

* * @return The java package name for R.class. */ String packageName() default DEFAULT_PACKAGE_NAME; /** * The ABI split to use when locating resources and AndroidManifest.xml * *

You do not typically have to set this, unless you are utilizing the ABI split feature

* * @return The ABI split to test with */ String abiSplit() default DEFAULT_ABI_SPLIT; /** * Qualifiers for the resource resolution, such as "fr-normal-port-hdpi". * * @return Qualifiers used for resource resolution. */ String qualifiers() default DEFAULT_QUALIFIERS; /** * The directory from which to load resources. This should be relative to the directory containing AndroidManifest.xml. * *

If not specified, Robolectric defaults to {@code res}.

* * @return Android resource directory. */ String resourceDir() default DEFAULT_RES_FOLDER; /** * The directory from which to load assets. This should be relative to the directory containing AndroidManifest.xml. * *

If not specified, Robolectric defaults to {@code assets}.

* * @return Android asset directory. */ String assetDir() default DEFAULT_ASSET_FOLDER; /** * The directory where application files are created during the application build process. * *

If not specified, Robolectric defaults to {@code build}.

* * @return Android build directory. */ String buildDir() default DEFAULT_BUILD_FOLDER; /** * A list of shadow classes to enable, in addition to those that are already present. * * @return A list of additional shadow classes to enable. */ Class[] shadows() default {}; // DEFAULT_SHADOWS /** * A list of instrumented packages, in addition to those that are already instrumented. * * @return A list of additional instrumented packages. */ String[] instrumentedPackages() default {}; // DEFAULT_INSTRUMENTED_PACKAGES /** * A list of folders containing Android Libraries on which this project depends. * * @return A list of Android Libraries. */ String[] libraries() default {}; // DEFAULT_LIBRARIES; class Implementation implements Config { private final int[] sdk; private final String manifest; private final String qualifiers; private final String resourceDir; private final String assetDir; private final String buildDir; private final String packageName; private final String abiSplit; private final Class constants; private final Class[] shadows; private final String[] instrumentedPackages; private final Class application; private final String[] libraries; public static Config fromProperties(Properties properties) { if (properties == null || properties.size() == 0) return null; return new Implementation( parseIntArrayProperty(properties.getProperty("sdk", "")), properties.getProperty("manifest", DEFAULT_MANIFEST), properties.getProperty("qualifiers", DEFAULT_QUALIFIERS), properties.getProperty("packageName", DEFAULT_PACKAGE_NAME), properties.getProperty("abiSplit", DEFAULT_ABI_SPLIT), properties.getProperty("resourceDir", DEFAULT_RES_FOLDER), properties.getProperty("assetDir", DEFAULT_ASSET_FOLDER), properties.getProperty("buildDir", DEFAULT_BUILD_FOLDER), parseClasses(properties.getProperty("shadows", "")), parseStringArrayProperty(properties.getProperty("instrumentedPackages", "")), parseApplication(properties.getProperty("application", "android.app.Application")), parseStringArrayProperty(properties.getProperty("libraries", "")), parseClass(properties.getProperty("constants", "")) ); } private static Class parseClass(String className) { if (className.isEmpty()) return null; try { return Implementation.class.getClassLoader().loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException("Could not load class: " + className); } } private static Class[] parseClasses(String input) { if (input.isEmpty()) return new Class[0]; final String[] classNames = input.split("[, ]+"); final Class[] classes = new Class[classNames.length]; for (int i = 0; i < classNames.length; i++) { classes[i] = parseClass(classNames[i]); } return classes; } @SuppressWarnings("unchecked") private static Class parseApplication(String className) { return (Class) parseClass(className); } private static String[] parseStringArrayProperty(String property) { if (property.isEmpty()) return new String[0]; return property.split("[, ]+"); } private static int[] parseIntArrayProperty(String property) { String[] parts = parseStringArrayProperty(property); int[] result = new int[parts.length]; for (int i = 0; i < parts.length; i++) { result[i] = Integer.parseInt(parts[i]); } return result; } public Implementation(int[] sdk, String manifest, String qualifiers, String packageName, String abiSplit, String resourceDir, String assetDir, String buildDir, Class[] shadows, String[] instrumentedPackages, Class application, String[] libraries, Class constants) { this.sdk = sdk; this.manifest = manifest; this.qualifiers = qualifiers; this.packageName = packageName; this.abiSplit = abiSplit; this.resourceDir = resourceDir; this.assetDir = assetDir; this.buildDir = buildDir; this.shadows = shadows; this.instrumentedPackages = instrumentedPackages; this.application = application; this.libraries = libraries; this.constants = constants; } public Implementation(Config other) { this.sdk = other.sdk(); this.manifest = other.manifest(); this.qualifiers = other.qualifiers(); this.packageName = other.packageName(); this.abiSplit = other.abiSplit(); this.resourceDir = other.resourceDir(); this.assetDir = other.assetDir(); this.buildDir = other.buildDir(); this.constants = other.constants(); this.shadows = other.shadows(); this.instrumentedPackages = other.instrumentedPackages(); this.application = other.application(); this.libraries = other.libraries(); } public Implementation(Config baseConfig, Config overlayConfig) { this.sdk = pickSdk(baseConfig.sdk(), overlayConfig.sdk(), new int[0]); this.manifest = pick(baseConfig.manifest(), overlayConfig.manifest(), DEFAULT_MANIFEST); this.qualifiers = pick(baseConfig.qualifiers(), overlayConfig.qualifiers(), ""); this.packageName = pick(baseConfig.packageName(), overlayConfig.packageName(), ""); this.abiSplit = pick(baseConfig.abiSplit(), overlayConfig.abiSplit(), ""); this.resourceDir = pick(baseConfig.resourceDir(), overlayConfig.resourceDir(), Config.DEFAULT_RES_FOLDER); this.assetDir = pick(baseConfig.assetDir(), overlayConfig.assetDir(), Config.DEFAULT_ASSET_FOLDER); this.buildDir = pick(baseConfig.buildDir(), overlayConfig.buildDir(), Config.DEFAULT_BUILD_FOLDER); this.constants = pick(baseConfig.constants(), overlayConfig.constants(), Void.class); Set> shadows = new HashSet<>(); shadows.addAll(Arrays.asList(baseConfig.shadows())); shadows.addAll(Arrays.asList(overlayConfig.shadows())); this.shadows = shadows.toArray(new Class[shadows.size()]); Set instrumentedPackages = new HashSet<>(); instrumentedPackages.addAll(Arrays.asList(baseConfig.instrumentedPackages())); instrumentedPackages.addAll(Arrays.asList(overlayConfig.instrumentedPackages())); this.instrumentedPackages = instrumentedPackages.toArray(new String[instrumentedPackages.size()]); this.application = pick(baseConfig.application(), overlayConfig.application(), Application.class); Set libraries = new HashSet<>(); libraries.addAll(Arrays.asList(baseConfig.libraries())); libraries.addAll(Arrays.asList(overlayConfig.libraries())); this.libraries = libraries.toArray(new String[libraries.size()]); } private T pick(T baseValue, T overlayValue, T nullValue) { return overlayValue != null ? (overlayValue.equals(nullValue) ? baseValue : overlayValue) : null; } private int[] pickSdk(int[] baseValue, int[] overlayValue, int[] nullValue) { return Arrays.equals(overlayValue, nullValue) ? baseValue : overlayValue; } @Override public int[] sdk() { return sdk; } @Override public String manifest() { return manifest; } @Override public Class constants() { return constants; } @Override public Class application() { return application; } @Override public String qualifiers() { return qualifiers; } @Override public String packageName() { return packageName; } @Override public String abiSplit() { return abiSplit; } @Override public String resourceDir() { return resourceDir; } @Override public String assetDir() { return assetDir; } @Override public String buildDir() { return buildDir; } @Override public Class[] shadows() { return shadows; } @Override public String[] instrumentedPackages() { return instrumentedPackages; } @Override public String[] libraries() { return libraries; } @NotNull @Override public Class annotationType() { return Config.class; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy