package org.robolectric;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.internal.ConfigUtils;
import org.robolectric.internal.SdkConfig;
import org.robolectric.manifest.AndroidManifest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
public class SdkPicker {
private final Set<SdkConfig> supportedSdks;
private final Properties properties;
private final SdkConfig minSupportedSdk;
private final SdkConfig maxSupportedSdk;
public SdkPicker() {
this(map(SdkConfig.getSupportedApis()), System.getProperties());
}
public SdkPicker(Properties properties, int... supportedSdks) {
this(map(supportedSdks), properties);
}
public SdkPicker(Collection<SdkConfig> supportedSdks, Properties properties) {
TreeSet<SdkConfig> sdkConfigs = new TreeSet<>(supportedSdks);
this.supportedSdks = sdkConfigs;
minSupportedSdk = sdkConfigs.first();
maxSupportedSdk = sdkConfigs.last();
this.properties = properties;
}
/**
* Enumerate the SDKs to be used for this test.
*
* @param config a {@link Config} specifying one or more SDKs
* @param appManifest the {@link AndroidManifest} for the test
* @return the list of {@link SdkConfig}s.
* @since 3.2
*/
@Nonnull
public List<SdkConfig> selectSdks(Config config, AndroidManifest appManifest) {
Set<SdkConfig> sdks = new TreeSet<>(configuredSdks(config, appManifest));
Set<SdkConfig> enabledSdks = enumerateEnabledSdks();
if (enabledSdks != null) {
sdks = Sets.intersection(sdks, enabledSdks);
}
return Lists.newArrayList(sdks);
}
@Nullable
protected Set<SdkConfig> enumerateEnabledSdks() {
String overrideSupportedApis = properties.getProperty("robolectric.enabledSdks");
if (overrideSupportedApis == null || overrideSupportedApis.isEmpty()) {
return null;
} else {
Set<SdkConfig> enabledSdks = new HashSet<>();
for (int sdk : ConfigUtils.parseSdkArrayProperty(overrideSupportedApis)) {
enabledSdks.add(new SdkConfig(sdk));
}
return enabledSdks;
}
}
protected Set<SdkConfig> configuredSdks(Config config, AndroidManifest appManifest) {
int appMinSdk = Math.max(appManifest.getMinSdkVersion(), minSupportedSdk.getApiLevel());
int appTargetSdk = Math.max(appManifest.getTargetSdkVersion(), minSupportedSdk.getApiLevel());
Integer appMaxSdk = appManifest.getMaxSdkVersion();
if (appMaxSdk == null) {
appMaxSdk = maxSupportedSdk.getApiLevel();
}
// For min/max SDK ranges...
int minSdk = config.minSdk();
int maxSdk = config.maxSdk();
if (minSdk != -1 || maxSdk != -1) {
int rangeMin = decodeSdk(minSdk, appMinSdk, appMinSdk, appTargetSdk, appMaxSdk);
int rangeMax = decodeSdk(maxSdk, appMaxSdk, appMinSdk, appTargetSdk, appMaxSdk);
if (rangeMin > rangeMax && (minSdk == -1 || maxSdk == -1)) {
return Collections.emptySet();
}
return sdkRange(rangeMin, rangeMax);
}
// For explicitly-enumerated SDKs...
if (config.sdk().length == 0) {
return Collections.singleton(new SdkConfig(appTargetSdk));
}
if (config.sdk().length == 1 && config.sdk()[0] == Config.ALL_SDKS) {
return sdkRange(appMinSdk, appMaxSdk);
}
Set<SdkConfig> sdkConfigs = new HashSet<>();
for (int sdk : config.sdk()) {
int decodedApiLevel = decodeSdk(sdk, appTargetSdk, appMinSdk, appTargetSdk, appMaxSdk);
sdkConfigs.add(new SdkConfig(decodedApiLevel));
}
return sdkConfigs;
}
protected int decodeSdk(int value, int defaultSdk, int appMinSdk, int appTargetSdk, int appMaxSdk) {
if (value == Config.DEFAULT_VALUE_INT) {
return defaultSdk;
} else if (value == Config.NEWEST_SDK) {
return appMaxSdk;
} else if (value == Config.OLDEST_SDK) {
return appMinSdk;
} else if (value == Config.TARGET_SDK) {
return appTargetSdk;
} else {
return value;
}
}
@Nonnull
protected Set<SdkConfig> sdkRange(int minSdk, int maxSdk) {
if (maxSdk < minSdk) {
throw new IllegalArgumentException("minSdk=" + minSdk + " is greater than maxSdk=" + maxSdk);
}
Set<SdkConfig> sdkConfigs = new HashSet<>();
for (SdkConfig supportedSdk : supportedSdks) {
int apiLevel = supportedSdk.getApiLevel();
if (apiLevel >= minSdk && supportedSdk.getApiLevel() <= maxSdk) {
sdkConfigs.add(supportedSdk);
}
}
if (sdkConfigs.isEmpty()) {
throw new IllegalArgumentException("No matching SDKs found for minSdk=" + minSdk + ", maxSdk=" + maxSdk);
}
return sdkConfigs;
}
@Nonnull
private static List<SdkConfig> map(Collection<Integer> supportedSdks) {
ArrayList<SdkConfig> sdkConfigs = new ArrayList<>();
for (int supportedSdk : supportedSdks) {
sdkConfigs.add(new SdkConfig(supportedSdk));
}
return sdkConfigs;
}
@Nonnull
private static List<SdkConfig> map(int[] supportedSdks) {
ArrayList<SdkConfig> sdkConfigs = new ArrayList<>();
for (int supportedSdk : supportedSdks) {
sdkConfigs.add(new SdkConfig(supportedSdk));
}
return sdkConfigs;
}
}