/*
 * Copyright (C) 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 com.android.server.wifi.util;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThat;

import androidx.test.filters.SmallTest;

import com.android.server.wifi.WifiBaseTest;
import com.android.server.wifi.util.ObjectCounter.ProtobufConverter;

import org.hamcrest.collection.IsIterableContainingInAnyOrder;
import org.junit.Test;

import java.util.Arrays;
import java.util.Objects;


/**
 * Unit test for ObjectCounter
 */
@SmallTest
public class ObjectCounterTest extends WifiBaseTest {

    /**
     * Test Key type: composite key with 3 fields
     */
    public static class TestKey {
        public int key1;
        public String key2;
        public boolean key3;

        public TestKey(int key1, String key2, boolean key3) {
            this.key1 = key1;
            this.key2 = key2;
            this.key3 = key3;
        }

        // generated by IntelliJ
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TestKey testKey = (TestKey) o;
            return key1 == testKey.key1
                    && key3 == testKey.key3
                    && Objects.equals(key2, testKey.key2);
        }

        // generated by IntelliJ
        @Override
        public int hashCode() {
            return Objects.hash(key1, key2, key3);
        }

        // generated by IntelliJ
        @Override
        public String toString() {
            return "TestKey{"
                    + "key1=" + key1
                    + ", key2='" + key2 + '\''
                    + ", key3=" + key3
                    + '}';
        }
    }

    /**
     * Test Proto type with 3 keys and a count field
     */
    public static class TestKeyProto {
        public int key1;
        public String key2;
        public boolean key3;
        public int count;

        public TestKeyProto(int key1, String key2, boolean key3, int count) {
            this.key1 = key1;
            this.key2 = key2;
            this.key3 = key3;
            this.count = count;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TestKeyProto that = (TestKeyProto) o;
            return key1 == that.key1
                    && key3 == that.key3
                    && count == that.count
                    && Objects.equals(key2, that.key2);
        }

        @Override
        public int hashCode() {
            return Objects.hash(key1, key2, key3, count);
        }
    }

    private static final TestKey[] TEST_KEYS = {
        new TestKey(10, "asdf", false),
        new TestKey(5, "qwer", true),
        new TestKey(10, "asdf", false),
        new TestKey(10, "asdf", false),
        new TestKey(-1, "zxcv", true),
        new TestKey(-1, "zxcv", true),
        new TestKey(-1, "zxcv", true),
    };


    private static final ProtobufConverter<TestKey, TestKeyProto> CONVERTER =
            (TestKey key, int count) -> new TestKeyProto(key.key1, key.key2, key.key3, count);

    /**
     * Tests when the counter is empty.
     */
    @Test
    public void testEmpty() {
        assertArrayEquals(new TestKeyProto[0],
                new ObjectCounter<TestKey>().toProto(TestKeyProto.class, CONVERTER));
    }

    /**
     * Tests adding to the counter.
     */
    @Test
    public void testAddToCounter() {
        ObjectCounter<TestKey> counter = new ObjectCounter<>();
        for (TestKey key : TEST_KEYS) {
            counter.increment(key);
        }

        TestKeyProto[] expected = {
                new TestKeyProto(10, "asdf", false, 3),
                new TestKeyProto(5, "qwer", true, 1),
                new TestKeyProto(-1, "zxcv", true, 3)
        };

        TestKeyProto[] actual = counter.toProto(TestKeyProto.class, CONVERTER);

        assertThat("Unexpected output protobuf array (ignoring order)",
                Arrays.asList(actual), IsIterableContainingInAnyOrder.containsInAnyOrder(expected));
    }
}
