• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.plugins;
2 
3 import com.google.auto.service.AutoService;
4 import com.google.common.annotations.VisibleForTesting;
5 import com.google.common.collect.Lists;
6 import com.google.common.collect.Sets;
7 import java.util.Collections;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.NoSuchElementException;
11 import java.util.Properties;
12 import java.util.Set;
13 import java.util.SortedSet;
14 import java.util.TreeSet;
15 import javax.annotation.Nonnull;
16 import javax.annotation.Nullable;
17 import javax.annotation.Priority;
18 import javax.inject.Inject;
19 import org.robolectric.annotation.Config;
20 import org.robolectric.annotation.internal.ConfigUtils;
21 import org.robolectric.pluginapi.Sdk;
22 import org.robolectric.pluginapi.SdkPicker;
23 import org.robolectric.pluginapi.UsesSdk;
24 import org.robolectric.pluginapi.config.ConfigurationStrategy.Configuration;
25 
26 /** Robolectric's default {@link SdkPicker}. */
27 @SuppressWarnings("NewApi")
28 @AutoService(SdkPicker.class)
29 @Priority(Integer.MIN_VALUE)
30 public class DefaultSdkPicker implements SdkPicker {
31   @Nonnull private final SdkCollection sdkCollection;
32 
33   private final Set<Sdk> enabledSdks;
34   @Nonnull private final Sdk minKnownSdk;
35   @Nonnull private final Sdk maxKnownSdk;
36 
37   @Inject
DefaultSdkPicker(@onnull SdkCollection sdkCollection, Properties systemProperties)38   public DefaultSdkPicker(@Nonnull SdkCollection sdkCollection, Properties systemProperties) {
39     this(sdkCollection,
40         systemProperties == null ? null : systemProperties.getProperty("robolectric.enabledSdks"));
41   }
42 
43   @VisibleForTesting
DefaultSdkPicker(@onnull SdkCollection sdkCollection, String enabledSdks)44   protected DefaultSdkPicker(@Nonnull SdkCollection sdkCollection, String enabledSdks) {
45     this.sdkCollection = sdkCollection;
46     this.enabledSdks = enumerateEnabledSdks(sdkCollection, enabledSdks);
47 
48     SortedSet<Sdk> sdks = this.sdkCollection.getKnownSdks();
49     try {
50       minKnownSdk = sdks.first();
51       maxKnownSdk = sdks.last();
52     } catch (NoSuchElementException e) {
53       throw new RuntimeException("no SDKs are supported among " + sdkCollection.getKnownSdks(), e);
54     }
55   }
56 
57   /**
58    * Enumerate the SDKs to be used for this test.
59    *
60    * @param configuration a collection of configuration objects, including {@link Config}
61    * @param usesSdk the {@link UsesSdk} for the test
62    * @return the list of candidate {@link Sdk}s.
63    * @since 3.9
64    */
65   @Override
66   @Nonnull
selectSdks(Configuration configuration, UsesSdk usesSdk)67   public List<Sdk> selectSdks(Configuration configuration, UsesSdk usesSdk) {
68     Config config = configuration.get(Config.class);
69     Set<Sdk> sdks = new TreeSet<>(configuredSdks(config, usesSdk));
70     if (enabledSdks != null) {
71       sdks = Sets.intersection(sdks, enabledSdks);
72     }
73     return Lists.newArrayList(sdks);
74   }
75 
76   @Nullable
enumerateEnabledSdks( SdkCollection sdkCollection, String enabledSdksString)77   protected static Set<Sdk> enumerateEnabledSdks(
78       SdkCollection sdkCollection, String enabledSdksString) {
79     if (enabledSdksString == null || enabledSdksString.isEmpty()) {
80       return null;
81     } else {
82       Set<Sdk> enabledSdks = new HashSet<>();
83       for (int sdk : ConfigUtils.parseSdkArrayProperty(enabledSdksString)) {
84         enabledSdks.add(sdkCollection.getSdk(sdk));
85       }
86       return enabledSdks;
87     }
88   }
89 
configuredSdks(Config config, UsesSdk usesSdk)90   protected Set<Sdk> configuredSdks(Config config, UsesSdk usesSdk) {
91     int appMinSdk = Math.max(usesSdk.getMinSdkVersion(), minKnownSdk.getApiLevel());
92     int appTargetSdk = Math.max(usesSdk.getTargetSdkVersion(), minKnownSdk.getApiLevel());
93     Integer appMaxSdk = usesSdk.getMaxSdkVersion();
94     if (appMaxSdk == null) {
95       appMaxSdk = maxKnownSdk.getApiLevel();
96     }
97 
98     // For min/max SDK ranges...
99     int minSdk = config.minSdk();
100     int maxSdk = config.maxSdk();
101     if (minSdk != -1 || maxSdk != -1) {
102       int rangeMin = decodeSdk(minSdk, appMinSdk, appMinSdk, appTargetSdk, appMaxSdk);
103       int rangeMax = decodeSdk(maxSdk, appMaxSdk, appMinSdk, appTargetSdk, appMaxSdk);
104 
105       if (rangeMin > rangeMax && (minSdk == -1 || maxSdk == -1)) {
106         return Collections.emptySet();
107       }
108 
109       return sdkRange(rangeMin, rangeMax);
110     }
111 
112     // For explicitly-enumerated SDKs...
113     if (config.sdk().length == 0) {
114       if (appTargetSdk < appMinSdk) {
115         throw new IllegalArgumentException(
116             "Package targetSdkVersion=" + appTargetSdk + " < minSdkVersion=" + appMinSdk);
117       } else if (appMaxSdk != 0 && appTargetSdk > appMaxSdk) {
118         throw new IllegalArgumentException(
119             "Package targetSdkVersion=" + appTargetSdk + " > maxSdkVersion=" + appMaxSdk);
120       }
121       return Collections.singleton(sdkCollection.getSdk(appTargetSdk));
122     }
123 
124     if (config.sdk().length == 1 && config.sdk()[0] == Config.ALL_SDKS) {
125       return sdkRange(appMinSdk, appMaxSdk);
126     }
127 
128     Set<Sdk> sdks = new HashSet<>();
129     for (int sdk : config.sdk()) {
130       int decodedApiLevel = decodeSdk(sdk, appTargetSdk, appMinSdk, appTargetSdk, appMaxSdk);
131       sdks.add(sdkCollection.getSdk(decodedApiLevel));
132     }
133     return sdks;
134   }
135 
decodeSdk( int value, int defaultSdk, int appMinSdk, int appTargetSdk, int appMaxSdk)136   protected int decodeSdk(
137       int value, int defaultSdk, int appMinSdk, int appTargetSdk, int appMaxSdk) {
138     if (value == Config.DEFAULT_VALUE_INT) {
139       return defaultSdk;
140     } else if (value == Config.NEWEST_SDK) {
141       return appMaxSdk;
142     } else if (value == Config.OLDEST_SDK) {
143       return appMinSdk;
144     } else if (value == Config.TARGET_SDK) {
145       return appTargetSdk;
146     } else {
147       return value;
148     }
149   }
150 
151   @Nonnull
sdkRange(int minSdk, int maxSdk)152   protected Set<Sdk> sdkRange(int minSdk, int maxSdk) {
153     if (maxSdk < minSdk) {
154       throw new IllegalArgumentException("minSdk=" + minSdk + " is greater than maxSdk=" + maxSdk);
155     }
156 
157     Set<Sdk> sdks = new HashSet<>();
158     for (Sdk knownSdk : sdkCollection.getKnownSdks()) {
159       int apiLevel = knownSdk.getApiLevel();
160       if (apiLevel >= minSdk && knownSdk.getApiLevel() <= maxSdk) {
161         sdks.add(knownSdk);
162       }
163     }
164 
165     if (sdks.isEmpty()) {
166       throw new IllegalArgumentException(
167           "No matching SDKs found for minSdk=" + minSdk + ", maxSdk=" + maxSdk);
168     }
169 
170     return sdks;
171   }
172 
173 }
174