FakeKeyedAppStatesReporter.java

/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.enterprise.feedback;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * A fake {@link KeyedAppStatesReporter} for testing.
 *
 * <p>Example usage:
 * <pre>{@code
 *   FakeKeyedAppStatesReporter reporter = new FakeKeyedAppStatesReporter();
 *   // Inject the reporter to the part of your code it will be used.
 *   assertThat(reporter.getKeyedAppStatesByKey().get("myKey").getMessage()).isEqualTo("expected");
 * }</pre>
 */
public class FakeKeyedAppStatesReporter extends KeyedAppStatesReporter {

    // States are stored in both a List and a Map (rather than using Map#values) to ensure that
    // order and duplicates are preserved.
    private List<KeyedAppState> mKeyedAppStates =
            Collections.synchronizedList(new ArrayList<KeyedAppState>());
    private List<KeyedAppState> mOnDeviceKeyedAppStates =
            Collections.synchronizedList(new ArrayList<KeyedAppState>());
    private List<KeyedAppState> mUploadedKeyedAppStates =
            Collections.synchronizedList(new ArrayList<KeyedAppState>());
    private Map<String, KeyedAppState> mKeyedAppStatesByKey =
            Collections.synchronizedMap(new HashMap<String, KeyedAppState>());
    private Map<String, KeyedAppState> mOnDeviceKeyedAppStatesByKey =
            Collections.synchronizedMap(new HashMap<String, KeyedAppState>());
    private Map<String, KeyedAppState> mUploadedKeyedAppStatesByKey =
            Collections.synchronizedMap(new HashMap<String, KeyedAppState>());
    private AtomicInteger mNumberOfUploads = new AtomicInteger();

    @Override
    public void setStates(@NonNull Collection<KeyedAppState> states) {
        for (KeyedAppState state : states) {
            mOnDeviceKeyedAppStates.add(state);
            mOnDeviceKeyedAppStatesByKey.put(state.getKey(), state);
            mKeyedAppStates.add(state);
            mKeyedAppStatesByKey.put(state.getKey(), state);
        }
    }

    /**
     * Record the set states and immediately mark all states as having been uploaded.
     *
     * <p>Does not enforce any quota on uploading.
     */
    @Override
    public void setStatesImmediate(@NonNull Collection<KeyedAppState> states) {
        setStates(states);
        upload();
    }

    private void upload() {
        for (KeyedAppState state : mOnDeviceKeyedAppStates) {
            mUploadedKeyedAppStates.add(state);
            mUploadedKeyedAppStatesByKey.put(state.getKey(), state);
        }
        mOnDeviceKeyedAppStates.clear();
        mOnDeviceKeyedAppStatesByKey.clear();
        mNumberOfUploads.addAndGet(1);
    }

    /**
     * Get a list of all {@link KeyedAppState} instances that have been set.
     *
     * <p>This is in the order that they were set, and may contain multiple with the same key, if
     * that key has been set twice.
     */
    @NonNull
    public List<KeyedAppState> getKeyedAppStates() {
        return new ArrayList<>(mKeyedAppStates);
    }

    /**
     * Get a map of the latest {@link KeyedAppState} set for each key.
     */
    @NonNull
    public Map<String, KeyedAppState> getKeyedAppStatesByKey() {
        return new HashMap<>(mKeyedAppStatesByKey);
    }

    /**
     * Get a list of {@link KeyedAppState} instances that have been set but not yet uploaded.
     *
     * <p>This is in the order that they were set, and may contain multiple with the same key, if
     * that key has been set twice.
     *
     * <p>Once uploaded (using {@link #setStatesImmediate(Collection)}) instances will no longer be
     * returned by this method.
     */
    @NonNull
    public List<KeyedAppState> getOnDeviceKeyedAppStates() {
        return new ArrayList<>(mOnDeviceKeyedAppStates);
    }

    /**
     * Get a map of the latest {@link KeyedAppState} set for each key that has not yet uploaded.
     *
     * <p>Once uploaded (using {@link #setStatesImmediate(Collection)}) instances will no longer be
     * returned by this method.
     */
    @NonNull
    public Map<String, KeyedAppState> getOnDeviceKeyedAppStatesByKey() {
        return new HashMap<>(mOnDeviceKeyedAppStatesByKey);
    }

    /**
     * Get a list of {@link KeyedAppState} instances that have been set and uploaded.
     *
     * <p>This is in the order that they were set, and may contain multiple with the same key, if
     * that key has been set twice.
     *
     * <p>States will be returned by this method if they were set using
     * {@link #setStatesImmediate(Collection)} or if {@link #setStatesImmediate(Collection)} has
     * been called since they were set.
     */
    @NonNull
    public List<KeyedAppState> getUploadedKeyedAppStates() {
        return new ArrayList<>(mUploadedKeyedAppStates);
    }

    /**
     * Get a list of the latest {@link KeyedAppState} set for each key that has been uploaded.
     *
     * <p>This is in the order that they were set, and may contain multiple with the same key, if
     * that key has been set twice.
     *
     * <p>States will be returned by this method if they were set using
     * {@link #setStatesImmediate(Collection)} or if {@link #setStatesImmediate(Collection)} has
     * been called since they were set.
     */
    @NonNull
    public Map<String, KeyedAppState> getUploadedKeyedAppStatesByKey() {
        return new HashMap<>(mUploadedKeyedAppStatesByKey);
    }

    /**
     * Get the number of times {@link #setStatesImmediate(Collection)} has been called.
     */
    public int getNumberOfUploads() {
        return mNumberOfUploads.get();
    }
}