1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.platform.test.flag.junit; 18 19 import static java.util.Objects.requireNonNull; 20 21 import java.util.ArrayList; 22 import java.util.HashMap; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.TreeMap; 26 27 import javax.annotation.Nonnull; 28 29 /** An object which holds aconfig flags values, and can be used for parameterized testing. */ 30 public final class FlagsParameterization { 31 public final Map<String, Boolean> mOverrides; 32 33 /** Construct a values wrapper class */ FlagsParameterization(Map<String, Boolean> overrides)34 public FlagsParameterization(Map<String, Boolean> overrides) { 35 mOverrides = Map.copyOf(overrides); 36 } 37 38 @Override toString()39 public String toString() { 40 if (mOverrides.isEmpty()) { 41 return "EMPTY"; 42 } 43 StringBuilder sb = new StringBuilder(); 44 for (Map.Entry<String, Boolean> entry : new TreeMap<>(mOverrides).entrySet()) { 45 if (sb.length() != 0) { 46 sb.append(','); 47 } 48 sb.append(entry.getKey()).append('=').append(entry.getValue()); 49 } 50 return sb.toString(); 51 } 52 53 /** 54 * Determines whether the dependency <code>alpha dependsOn beta</code> is met for the defined 55 * values. 56 * 57 * @param alpha a flag which must be defined in this object 58 * @param beta a flag which must be defined in this object 59 * @return true in all cases except when alpha is enabled but beta is disabled. 60 */ isDependencyMet(String alpha, String beta)61 public boolean isDependencyMet(String alpha, String beta) { 62 boolean alphaEnabled = requireNonNull(mOverrides.get(alpha), alpha + " is not defined"); 63 boolean betaEnabled = requireNonNull(mOverrides.get(beta), beta + " is not defined"); 64 return betaEnabled || !alphaEnabled; 65 } 66 67 @Override equals(Object other)68 public boolean equals(Object other) { 69 if (other == null) return false; 70 if (other == this) return true; 71 if (!(other instanceof FlagsParameterization)) return false; 72 return mOverrides.equals(((FlagsParameterization) other).mOverrides); 73 } 74 75 @Override hashCode()76 public int hashCode() { 77 return mOverrides.hashCode(); 78 } 79 80 /** 81 * Produces a list containing every combination of boolean values for the given flags. 82 * 83 * @return a list of size 2^N for N provided flags. 84 */ 85 @Nonnull allCombinationsOf(@onnull String... flagNames)86 public static List<FlagsParameterization> allCombinationsOf(@Nonnull String... flagNames) { 87 List<Map<String, Boolean>> currentList = List.of(new HashMap<>()); 88 for (String flagName : flagNames) { 89 List<Map<String, Boolean>> next = new ArrayList<>(currentList.size() * 2); 90 for (Map<String, Boolean> current : currentList) { 91 // copy the current map and add this flag as disabled 92 Map<String, Boolean> plusDisabled = new HashMap<>(current); 93 plusDisabled.put(flagName, false); 94 next.add(plusDisabled); 95 // re-use the current map and add this flag as enabled 96 current.put(flagName, true); 97 next.add(current); 98 } 99 currentList = next; 100 } 101 List<FlagsParameterization> result = new ArrayList<>(); 102 for (Map<String, Boolean> valuesMap : currentList) { 103 result.add(new FlagsParameterization(valuesMap)); 104 } 105 return result; 106 } 107 108 /** 109 * Produces a list containing the flag parameterizations where each flag is turned on in the 110 * given sequence. 111 * 112 * <p><code>progressionOf("a", "b", "c")</code> produces the following parameterizations: 113 * 114 * <ul> 115 * <li><code>{"a": false, "b": false, "c": false}</code> 116 * <li><code>{"a": true, "b": false, "c": false}</code> 117 * <li><code>{"a": true, "b": true, "c": false}</code> 118 * <li><code>{"a": true, "b": true, "c": true}</code> 119 * </ul> 120 * 121 * @return a list of size N+1 for N provided flags. 122 */ 123 @Nonnull progressionOf(@onnull String... flagNames)124 public static List<FlagsParameterization> progressionOf(@Nonnull String... flagNames) { 125 final List<FlagsParameterization> result = new ArrayList<>(); 126 final Map<String, Boolean> currentMap = new HashMap<>(); 127 for (String flagName : flagNames) { 128 currentMap.put(flagName, false); 129 } 130 result.add(new FlagsParameterization(currentMap)); 131 for (String flagName : flagNames) { 132 currentMap.put(flagName, true); 133 result.add(new FlagsParameterization(currentMap)); 134 } 135 return result; 136 } 137 } 138