1 /* 2 * Copyright 2019 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 androidx.enterprise.feedback; 18 19 import org.jspecify.annotations.NonNull; 20 import org.jspecify.annotations.Nullable; 21 22 import java.util.ArrayList; 23 import java.util.Collection; 24 import java.util.Collections; 25 import java.util.HashMap; 26 import java.util.List; 27 import java.util.Map; 28 import java.util.concurrent.atomic.AtomicInteger; 29 30 /** 31 * A fake {@link KeyedAppStatesReporter} for testing. 32 * 33 * <p>Example usage: 34 * <pre>{@code 35 * FakeKeyedAppStatesReporter reporter = new FakeKeyedAppStatesReporter(); 36 * // Inject the reporter to the part of your code it will be used. 37 * assertThat(reporter.getKeyedAppStatesByKey().get("myKey").getMessage()).isEqualTo("expected"); 38 * }</pre> 39 */ 40 public class FakeKeyedAppStatesReporter extends KeyedAppStatesReporter { 41 42 // States are stored in both a List and a Map (rather than using Map#values) to ensure that 43 // order and duplicates are preserved. 44 private List<KeyedAppState> mKeyedAppStates = 45 Collections.synchronizedList(new ArrayList<KeyedAppState>()); 46 private List<KeyedAppState> mOnDeviceKeyedAppStates = 47 Collections.synchronizedList(new ArrayList<KeyedAppState>()); 48 private List<KeyedAppState> mUploadedKeyedAppStates = 49 Collections.synchronizedList(new ArrayList<KeyedAppState>()); 50 private Map<String, KeyedAppState> mKeyedAppStatesByKey = 51 Collections.synchronizedMap(new HashMap<String, KeyedAppState>()); 52 private Map<String, KeyedAppState> mOnDeviceKeyedAppStatesByKey = 53 Collections.synchronizedMap(new HashMap<String, KeyedAppState>()); 54 private Map<String, KeyedAppState> mUploadedKeyedAppStatesByKey = 55 Collections.synchronizedMap(new HashMap<String, KeyedAppState>()); 56 private AtomicInteger mNumberOfUploads = new AtomicInteger(); 57 58 /** @deprecated see {@link #setStates(Collection, KeyedAppStatesCallback)}. */ 59 @Override 60 @Deprecated setStates(@onNull Collection<KeyedAppState> states)61 public void setStates(@NonNull Collection<KeyedAppState> states) { 62 setStates(states, /* callback= */ null); 63 } 64 65 /** 66 * Record the states set. 67 * 68 * <p>Does not enforce any limit on total size of states collection. 69 */ 70 @Override setStates(@onNull Collection<KeyedAppState> states, @Nullable KeyedAppStatesCallback callback)71 public void setStates(@NonNull Collection<KeyedAppState> states, 72 @Nullable KeyedAppStatesCallback callback) { 73 for (KeyedAppState state : states) { 74 mOnDeviceKeyedAppStates.add(state); 75 mOnDeviceKeyedAppStatesByKey.put(state.getKey(), state); 76 mKeyedAppStates.add(state); 77 mKeyedAppStatesByKey.put(state.getKey(), state); 78 } 79 if (callback != null) { 80 callback.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null); 81 } 82 } 83 84 /** @deprecated See {@link #setStatesImmediate(Collection, KeyedAppStatesCallback)}. */ 85 @Override 86 @Deprecated setStatesImmediate(@onNull Collection<KeyedAppState> states)87 public void setStatesImmediate(@NonNull Collection<KeyedAppState> states) { 88 setStatesImmediate(states, /* callback= */ null); 89 } 90 91 /** 92 * Record the set states and immediately mark all states as having been uploaded. 93 * 94 * <p>Does not enforce any quota on uploading, or limit on total size of states collection. 95 */ 96 @Override setStatesImmediate(@onNull Collection<KeyedAppState> states, @Nullable KeyedAppStatesCallback callback)97 public void setStatesImmediate(@NonNull Collection<KeyedAppState> states, 98 @Nullable KeyedAppStatesCallback callback) { 99 setStates(states, callback); 100 upload(); 101 } 102 upload()103 private void upload() { 104 for (KeyedAppState state : mOnDeviceKeyedAppStates) { 105 mUploadedKeyedAppStates.add(state); 106 mUploadedKeyedAppStatesByKey.put(state.getKey(), state); 107 } 108 mOnDeviceKeyedAppStates.clear(); 109 mOnDeviceKeyedAppStatesByKey.clear(); 110 mNumberOfUploads.addAndGet(1); 111 } 112 113 /** 114 * Get a list of all {@link KeyedAppState} instances that have been set. 115 * 116 * <p>This is in the order that they were set, and may contain multiple with the same key, if 117 * that key has been set twice. 118 */ getKeyedAppStates()119 public @NonNull List<KeyedAppState> getKeyedAppStates() { 120 return new ArrayList<>(mKeyedAppStates); 121 } 122 123 /** 124 * Get a map of the latest {@link KeyedAppState} set for each key. 125 */ getKeyedAppStatesByKey()126 public @NonNull Map<String, KeyedAppState> getKeyedAppStatesByKey() { 127 return new HashMap<>(mKeyedAppStatesByKey); 128 } 129 130 /** 131 * Get a list of {@link KeyedAppState} instances that have been set but not yet uploaded. 132 * 133 * <p>This is in the order that they were set, and may contain multiple with the same key, if 134 * that key has been set twice. 135 * 136 * <p>Once uploaded (using {@link #setStatesImmediate(Collection)}) instances will no longer be 137 * returned by this method. 138 */ getOnDeviceKeyedAppStates()139 public @NonNull List<KeyedAppState> getOnDeviceKeyedAppStates() { 140 return new ArrayList<>(mOnDeviceKeyedAppStates); 141 } 142 143 /** 144 * Get a map of the latest {@link KeyedAppState} set for each key that has not yet uploaded. 145 * 146 * <p>Once uploaded (using {@link #setStatesImmediate(Collection)}) instances will no longer be 147 * returned by this method. 148 */ getOnDeviceKeyedAppStatesByKey()149 public @NonNull Map<String, KeyedAppState> getOnDeviceKeyedAppStatesByKey() { 150 return new HashMap<>(mOnDeviceKeyedAppStatesByKey); 151 } 152 153 /** 154 * Get a list of {@link KeyedAppState} instances that have been set and uploaded. 155 * 156 * <p>This is in the order that they were set, and may contain multiple with the same key, if 157 * that key has been set twice. 158 * 159 * <p>States will be returned by this method if they were set using 160 * {@link #setStatesImmediate(Collection)} or if {@link #setStatesImmediate(Collection)} has 161 * been called since they were set. 162 */ getUploadedKeyedAppStates()163 public @NonNull List<KeyedAppState> getUploadedKeyedAppStates() { 164 return new ArrayList<>(mUploadedKeyedAppStates); 165 } 166 167 /** 168 * Get a list of the latest {@link KeyedAppState} set for each key that has been uploaded. 169 * 170 * <p>This is in the order that they were set, and may contain multiple with the same key, if 171 * that key has been set twice. 172 * 173 * <p>States will be returned by this method if they were set using 174 * {@link #setStatesImmediate(Collection)} or if {@link #setStatesImmediate(Collection)} has 175 * been called since they were set. 176 */ getUploadedKeyedAppStatesByKey()177 public @NonNull Map<String, KeyedAppState> getUploadedKeyedAppStatesByKey() { 178 return new HashMap<>(mUploadedKeyedAppStatesByKey); 179 } 180 181 /** 182 * Get the number of times {@link #setStatesImmediate(Collection)} has been called. 183 */ getNumberOfUploads()184 public int getNumberOfUploads() { 185 return mNumberOfUploads.get(); 186 } 187 } 188