1 package org.robolectric.shadows; 2 3 import static org.robolectric.util.reflector.Reflector.reflector; 4 5 import android.os.Parcel; 6 import android.os.health.HealthStats; 7 import android.os.health.TimerStat; 8 import android.util.ArrayMap; 9 import com.google.common.annotations.VisibleForTesting; 10 import com.google.errorprone.annotations.CanIgnoreReturnValue; 11 import java.util.Arrays; 12 import java.util.HashMap; 13 import java.util.Set; 14 import org.robolectric.util.reflector.Accessor; 15 import org.robolectric.util.reflector.ForType; 16 17 /** Test helper class to build {@link HealthStats} */ 18 final class HealthStatsBuilder { 19 20 // Header fields 21 private String dataType = null; 22 23 // TimerStat fields 24 private final HashMap<Integer, TimerStat> timerMap = new HashMap<>(); 25 26 // Measurement fields 27 private final HashMap<Integer, Long> measurementMap = new HashMap<>(); 28 29 // Stats fields 30 private final HashMap<Integer, ArrayMap<String, HealthStats>> statsMap = new HashMap<>(); 31 32 // Timers fields 33 private final HashMap<Integer, ArrayMap<String, TimerStat>> timersMap = new HashMap<>(); 34 35 // Measurements fields 36 private final HashMap<Integer, ArrayMap<String, Long>> measurementsMap = new HashMap<>(); 37 newBuilder()38 public static HealthStatsBuilder newBuilder() { 39 return new HealthStatsBuilder(); 40 } 41 42 /** Sets the DataType. Defaults to null. */ 43 @CanIgnoreReturnValue setDataType(String dataType)44 public HealthStatsBuilder setDataType(String dataType) { 45 this.dataType = dataType; 46 return this; 47 } 48 49 /** 50 * Adds a TimerStat for the given key. If the same key is used multiple times, the last provided 51 * TimerStat is used. 52 */ 53 @CanIgnoreReturnValue addTimerStat(int key, TimerStat value)54 public HealthStatsBuilder addTimerStat(int key, TimerStat value) { 55 timerMap.put(key, value); 56 return this; 57 } 58 59 /** 60 * Adds a measurement for the given key. If the same key is used multiple times, the last provided 61 * measurement is used. 62 */ 63 @CanIgnoreReturnValue addMeasurement(int key, long value)64 public HealthStatsBuilder addMeasurement(int key, long value) { 65 measurementMap.put(key, value); 66 return this; 67 } 68 69 /** 70 * Adds a map of HealthStats for the given key. If the same key is used multiple times, the last 71 * provided map is used. 72 */ 73 @CanIgnoreReturnValue addStats(int key, ArrayMap<String, HealthStats> value)74 public HealthStatsBuilder addStats(int key, ArrayMap<String, HealthStats> value) { 75 statsMap.put(key, new ArrayMap<String, HealthStats>(value)); 76 return this; 77 } 78 79 /** 80 * Adds a map of TimerStats for the given key. If the same key is used multiple times, the last 81 * provided map is used. 82 */ 83 @CanIgnoreReturnValue addTimers(int key, ArrayMap<String, TimerStat> value)84 public HealthStatsBuilder addTimers(int key, ArrayMap<String, TimerStat> value) { 85 timersMap.put(key, new ArrayMap<String, TimerStat>(value)); 86 return this; 87 } 88 89 /** 90 * Adds a map of measurements for the given key. If the same key is used multiple times, the last 91 * provided map is used. 92 */ 93 @CanIgnoreReturnValue addMeasurements(int key, ArrayMap<String, Long> value)94 public HealthStatsBuilder addMeasurements(int key, ArrayMap<String, Long> value) { 95 measurementsMap.put(key, new ArrayMap<String, Long>(value)); 96 return this; 97 } 98 99 // HealthStats has internal fields that are generic arrays, so this is unavoidable. 100 @SuppressWarnings("unchecked") build()101 public HealthStats build() { 102 // HealthStats' default constructor throws an exception 103 HealthStats result = new HealthStats(Parcel.obtain()); 104 105 reflector(HealthStatsReflector.class, result).setDataType(dataType); 106 107 int[] mTimerKeys = toSortedIntArray(timerMap.keySet()); 108 reflector(HealthStatsReflector.class, result).setTimerKeys(mTimerKeys); 109 int[] mTimerCounts = new int[mTimerKeys.length]; 110 long[] mTimerTimes = new long[mTimerKeys.length]; 111 for (int i = 0; i < mTimerKeys.length; i++) { 112 TimerStat timerStat = timerMap.get(mTimerKeys[i]); 113 mTimerCounts[i] = timerStat.getCount(); 114 mTimerTimes[i] = timerStat.getTime(); 115 } 116 reflector(HealthStatsReflector.class, result).setTimerCounts(mTimerCounts); 117 reflector(HealthStatsReflector.class, result).setTimerTimes(mTimerTimes); 118 119 int[] mMeasurementKeys = toSortedIntArray(measurementMap.keySet()); 120 reflector(HealthStatsReflector.class, result).setMeasurementKeys(mMeasurementKeys); 121 long[] mMeasurementValues = new long[mMeasurementKeys.length]; 122 for (int i = 0; i < mMeasurementKeys.length; i++) { 123 long measurementValue = measurementMap.get(mMeasurementKeys[i]); 124 mMeasurementValues[i] = measurementValue; 125 } 126 reflector(HealthStatsReflector.class, result).setMeasurementValues(mMeasurementValues); 127 128 int[] mStatsKeys = toSortedIntArray(statsMap.keySet()); 129 reflector(HealthStatsReflector.class, result).setStatsKeys(mStatsKeys); 130 ArrayMap<String, HealthStats>[] mStatsValues = 131 (ArrayMap<String, HealthStats>[]) new ArrayMap<?, ?>[mStatsKeys.length]; 132 for (int i = 0; i < mStatsKeys.length; i++) { 133 ArrayMap<String, HealthStats> stats = statsMap.get(mStatsKeys[i]); 134 mStatsValues[i] = stats; 135 } 136 reflector(HealthStatsReflector.class, result).setStatsValues(mStatsValues); 137 138 int[] mTimersKeys = toSortedIntArray(timersMap.keySet()); 139 reflector(HealthStatsReflector.class, result).setTimersKeys(mTimersKeys); 140 ArrayMap<String, TimerStat>[] mTimersValues = 141 (ArrayMap<String, TimerStat>[]) new ArrayMap<?, ?>[mTimersKeys.length]; 142 for (int i = 0; i < mTimersKeys.length; i++) { 143 ArrayMap<String, TimerStat> timers = timersMap.get(mTimersKeys[i]); 144 mTimersValues[i] = timers; 145 } 146 reflector(HealthStatsReflector.class, result).setTimersValues(mTimersValues); 147 148 int[] mMeasurementsKeys = toSortedIntArray(measurementsMap.keySet()); 149 reflector(HealthStatsReflector.class, result).setMeasurementsKeys(mMeasurementsKeys); 150 ArrayMap<String, Long>[] mMeasurementsValues = 151 (ArrayMap<String, Long>[]) new ArrayMap<?, ?>[mMeasurementsKeys.length]; 152 for (int i = 0; i < mMeasurementsKeys.length; i++) { 153 ArrayMap<String, Long> measurements = measurementsMap.get(mMeasurementsKeys[i]); 154 mMeasurementsValues[i] = measurements; 155 } 156 reflector(HealthStatsReflector.class, result).setMeasurementsValues(mMeasurementsValues); 157 158 return result; 159 } 160 HealthStatsBuilder()161 private HealthStatsBuilder() {} 162 163 @ForType(HealthStats.class) 164 private interface HealthStatsReflector { 165 166 @Accessor("mDataType") setDataType(String dataType)167 void setDataType(String dataType); 168 169 @Accessor("mTimerKeys") setTimerKeys(int[] timerKeys)170 void setTimerKeys(int[] timerKeys); 171 172 @Accessor("mTimerCounts") setTimerCounts(int[] timerCounts)173 void setTimerCounts(int[] timerCounts); 174 175 @Accessor("mTimerTimes") setTimerTimes(long[] timerTimes)176 void setTimerTimes(long[] timerTimes); 177 178 @Accessor("mMeasurementKeys") setMeasurementKeys(int[] measurementKeys)179 void setMeasurementKeys(int[] measurementKeys); 180 181 @Accessor("mMeasurementValues") setMeasurementValues(long[] measurementValues)182 void setMeasurementValues(long[] measurementValues); 183 184 @Accessor("mStatsKeys") setStatsKeys(int[] statsKeys)185 void setStatsKeys(int[] statsKeys); 186 187 @Accessor("mStatsValues") setStatsValues(ArrayMap<String, HealthStats>[] statsValues)188 void setStatsValues(ArrayMap<String, HealthStats>[] statsValues); 189 190 @Accessor("mTimersKeys") setTimersKeys(int[] timersKeys)191 void setTimersKeys(int[] timersKeys); 192 193 @Accessor("mTimersValues") setTimersValues(ArrayMap<String, TimerStat>[] timersValues)194 void setTimersValues(ArrayMap<String, TimerStat>[] timersValues); 195 196 @Accessor("mMeasurementsKeys") setMeasurementsKeys(int[] measurementsKeys)197 void setMeasurementsKeys(int[] measurementsKeys); 198 199 @Accessor("mMeasurementsValues") setMeasurementsValues(ArrayMap<String, Long>[] measurementsValues)200 void setMeasurementsValues(ArrayMap<String, Long>[] measurementsValues); 201 } 202 203 @VisibleForTesting toSortedIntArray(Set<Integer> set)204 static final int[] toSortedIntArray(Set<Integer> set) { 205 int[] result = new int[set.size()]; 206 Object[] inputObjArray = set.toArray(); 207 for (int i = 0; i < inputObjArray.length; i++) { 208 result[i] = (int) inputObjArray[i]; 209 } 210 // mFooKeys fields of HealthStats are used in Arrays.binarySearch, so they have to be sorted. 211 Arrays.sort(result); 212 return result; 213 } 214 } 215