Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4697c7f5 authored by Howard Ro's avatar Howard Ro Committed by Android (Google) Code Review
Browse files

Merge "StatsEvent unit tests"

parents 039a30fb 557859eb
Loading
Loading
Loading
Loading
+466 −0
Original line number Original line Diff line number Diff line
/*
 * 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 android.util;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import static java.nio.charset.StandardCharsets.UTF_8;

import android.os.SystemClock;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.google.common.collect.Range;

import org.junit.Test;
import org.junit.runner.RunWith;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * Internal tests for {@link StatsEvent}.
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StatsEventTest {

    @Test
    public void testNoFields() {
        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent = StatsEvent.newBuilder().build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        final int expectedAtomId = 0;
        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_ERRORS")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get()).isEqualTo(3);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id")
                .that(buffer.getInt()).isEqualTo(expectedAtomId);

        final int errorMask = buffer.getInt();

        assertWithMessage("ERROR_NO_ATOM_ID should be the only error in the error mask")
                .that(errorMask).isEqualTo(StatsEvent.ERROR_NO_ATOM_ID);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    @Test
    public void testIntBooleanIntInt() {
        final int expectedAtomId = 109;
        final int field1 = 1;
        final boolean field2 = true;
        final int field3 = 3;
        final int field4 = 4;

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent = StatsEvent.newBuilder()
                .setAtomId(expectedAtomId)
                .writeInt(field1)
                .writeBoolean(field2)
                .writeInt(field3)
                .writeInt(field4)
                .build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get()).isEqualTo(6);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id")
                .that(buffer.getInt()).isEqualTo(expectedAtomId);

        assertWithMessage("First field is not Int")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect field 1")
                .that(buffer.getInt()).isEqualTo(field1);

        assertWithMessage("Second field is not Boolean")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN);

        assertWithMessage("Incorrect field 2")
                .that(buffer.get()).isEqualTo(1);

        assertWithMessage("Third field is not Int")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect field 3")
                .that(buffer.getInt()).isEqualTo(field3);

        assertWithMessage("Fourth field is not Int")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect field 4")
                .that(buffer.getInt()).isEqualTo(field4);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    @Test
    public void testStringFloatByteArray() {
        final int expectedAtomId = 109;
        final String field1 = "Str 1";
        final float field2 = 9.334f;
        final byte[] field3 = new byte[] { 56, 23, 89, -120 };

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent = StatsEvent.newBuilder()
                .setAtomId(expectedAtomId)
                .writeString(field1)
                .writeFloat(field2)
                .writeByteArray(field3)
                .build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get()).isEqualTo(5);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id")
                .that(buffer.getInt()).isEqualTo(expectedAtomId);

        assertWithMessage("First field is not String")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_STRING);

        final String field1Actual = getStringFromByteBuffer(buffer);
        assertWithMessage("Incorrect field 1")
                .that(field1Actual).isEqualTo(field1);

        assertWithMessage("Second field is not Float")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_FLOAT);

        assertWithMessage("Incorrect field 2")
                .that(buffer.getFloat()).isEqualTo(field2);

        assertWithMessage("Third field is not byte array")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BYTE_ARRAY);

        final byte[] field3Actual = getByteArrayFromByteBuffer(buffer);
        assertWithMessage("Incorrect field 3")
                .that(field3Actual).isEqualTo(field3);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    @Test
    public void testAttributionChainLong() {
        final int expectedAtomId = 109;
        final int[] uids = new int[] { 1, 2, 3, 4, 5 };
        final String[] tags = new String[] { "1", "2", "3", "4", "5" };
        final long field2 = -230909823L;

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent = StatsEvent.newBuilder()
                .setAtomId(expectedAtomId)
                .writeAttributionChain(uids, tags)
                .writeLong(field2)
                .build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get()).isEqualTo(4);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id")
                .that(buffer.getInt()).isEqualTo(expectedAtomId);

        assertWithMessage("First field is not Attribution Chain")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ATTRIBUTION_CHAIN);

        assertWithMessage("Incorrect number of attribution nodes")
                .that(buffer.get()).isEqualTo((byte) uids.length);

        for (int i = 0; i < tags.length; i++) {
            assertWithMessage("Incorrect uid in Attribution Chain")
                    .that(buffer.getInt()).isEqualTo(uids[i]);

            final String tag = getStringFromByteBuffer(buffer);
            assertWithMessage("Incorrect tag in Attribution Chain")
                    .that(tag).isEqualTo(tags[i]);
        }

        assertWithMessage("Second field is not Long")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect field 2")
                .that(buffer.getLong()).isEqualTo(field2);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    @Test
    public void testKeyValuePairs() {
        final int expectedAtomId = 109;
        final SparseIntArray intMap = new SparseIntArray();
        final SparseLongArray longMap = new SparseLongArray();
        final SparseArray<String> stringMap = new SparseArray<>();
        final SparseArray<Float> floatMap = new SparseArray<>();
        intMap.put(1, -1);
        intMap.put(2, -2);
        stringMap.put(3, "abc");
        stringMap.put(4, "2h");
        floatMap.put(9, -234.344f);

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent = StatsEvent.newBuilder()
                .setAtomId(expectedAtomId)
                .writeKeyValuePairs(intMap, longMap, stringMap, floatMap)
                .build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get()).isEqualTo(3);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id")
                .that(buffer.getInt()).isEqualTo(expectedAtomId);

        assertWithMessage("First field is not KeyValuePairs")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_KEY_VALUE_PAIRS);

        assertWithMessage("Incorrect number of key value pairs")
                .that(buffer.get()).isEqualTo(
                        (byte) (intMap.size() + longMap.size() + stringMap.size()
                                + floatMap.size()));

        for (int i = 0; i < intMap.size(); i++) {
            assertWithMessage("Incorrect key in intMap")
                    .that(buffer.getInt()).isEqualTo(intMap.keyAt(i));
            assertWithMessage("The type id of the value should be TYPE_INT in intMap")
                    .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
            assertWithMessage("Incorrect value in intMap")
                    .that(buffer.getInt()).isEqualTo(intMap.valueAt(i));
        }

        for (int i = 0; i < longMap.size(); i++) {
            assertWithMessage("Incorrect key in longMap")
                    .that(buffer.getInt()).isEqualTo(longMap.keyAt(i));
            assertWithMessage("The type id of the value should be TYPE_LONG in longMap")
                    .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
            assertWithMessage("Incorrect value in longMap")
                    .that(buffer.getLong()).isEqualTo(longMap.valueAt(i));
        }

        for (int i = 0; i < stringMap.size(); i++) {
            assertWithMessage("Incorrect key in stringMap")
                    .that(buffer.getInt()).isEqualTo(stringMap.keyAt(i));
            assertWithMessage("The type id of the value should be TYPE_STRING in stringMap")
                    .that(buffer.get()).isEqualTo(StatsEvent.TYPE_STRING);
            final String value = getStringFromByteBuffer(buffer);
            assertWithMessage("Incorrect value in stringMap")
                    .that(value).isEqualTo(stringMap.valueAt(i));
        }

        for (int i = 0; i < floatMap.size(); i++) {
            assertWithMessage("Incorrect key in floatMap")
                    .that(buffer.getInt()).isEqualTo(floatMap.keyAt(i));
            assertWithMessage("The type id of the value should be TYPE_FLOAT in floatMap")
                    .that(buffer.get()).isEqualTo(StatsEvent.TYPE_FLOAT);
            assertWithMessage("Incorrect value in floatMap")
                    .that(buffer.getFloat()).isEqualTo(floatMap.valueAt(i));
        }

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    @Test
    public void testSingleAnnotations() {
        final int expectedAtomId = 109;
        final int field1 = 1;
        final byte field1AnnotationId = 45;
        final boolean field1AnnotationValue = false;
        final boolean field2 = true;
        final byte field2AnnotationId = 1;
        final int field2AnnotationValue = 23;

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent = StatsEvent.newBuilder()
                .setAtomId(expectedAtomId)
                .writeInt(field1)
                .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
                .writeBoolean(field2)
                .addIntAnnotation(field2AnnotationId, field2AnnotationValue)
                .build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get()).isEqualTo(4);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id")
                .that(buffer.getInt()).isEqualTo(expectedAtomId);

        final byte field1Header = buffer.get();
        final int field1AnnotationValueCount = field1Header >> 4;
        final byte field1Type = (byte) (field1Header & 0x0F);
        assertWithMessage("First field is not Int")
                .that(field1Type).isEqualTo(StatsEvent.TYPE_INT);
        assertWithMessage("First field annotation count is wrong")
                .that(field1AnnotationValueCount).isEqualTo(1);
        assertWithMessage("Incorrect field 1")
                .that(buffer.getInt()).isEqualTo(field1);
        assertWithMessage("First field's annotation id is wrong")
                .that(buffer.get()).isEqualTo(field1AnnotationId);
        assertWithMessage("First field's annotation type is wrong")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN);
        assertWithMessage("First field's annotation value is wrong")
                .that(buffer.get()).isEqualTo(field1AnnotationValue ? 1 : 0);

        final byte field2Header = buffer.get();
        final int field2AnnotationValueCount = field2Header >> 4;
        final byte field2Type = (byte) (field2Header & 0x0F);
        assertWithMessage("Second field is not boolean")
                .that(field2Type).isEqualTo(StatsEvent.TYPE_BOOLEAN);
        assertWithMessage("Second field annotation count is wrong")
                .that(field2AnnotationValueCount).isEqualTo(1);
        assertWithMessage("Incorrect field 2")
                .that(buffer.get()).isEqualTo(field2 ? 1 : 0);
        assertWithMessage("Second field's annotation id is wrong")
                .that(buffer.get()).isEqualTo(field2AnnotationId);
        assertWithMessage("Second field's annotation type is wrong")
                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
        assertWithMessage("Second field's annotation value is wrong")
                .that(buffer.getInt()).isEqualTo(field2AnnotationValue);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
        final int numBytes = buffer.getInt();
        byte[] bytes = new byte[numBytes];
        buffer.get(bytes);
        return bytes;
    }

    private static String getStringFromByteBuffer(final ByteBuffer buffer) {
        final byte[] bytes = getByteArrayFromByteBuffer(buffer);
        return new String(bytes, UTF_8);
    }
}