Merge "stats_event.h/c tests"
This commit is contained in:
commit
4169e9e47d
2 changed files with 363 additions and 0 deletions
|
@ -75,3 +75,22 @@ cc_benchmark {
|
|||
"libgtest_prod",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libstatssocket_test",
|
||||
srcs: ["tests/stats_event_test.cpp"],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
"libstatssocket",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"libutils",
|
||||
],
|
||||
test_suites: ["device_tests"],
|
||||
}
|
||||
|
|
344
libstats/socket/tests/stats_event_test.cpp
Normal file
344
libstats/socket/tests/stats_event_test.cpp
Normal file
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "stats_event.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// Side-effect: this function moves the start of the buffer past the read value
|
||||
template <class T>
|
||||
T readNext(uint8_t** buffer) {
|
||||
T value = *(T*)(*buffer);
|
||||
*buffer += sizeof(T);
|
||||
return value;
|
||||
}
|
||||
|
||||
void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
|
||||
uint8_t typeHeader = (numAnnotations << 4) | typeId;
|
||||
EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void checkScalar(uint8_t** buffer, T expectedValue) {
|
||||
EXPECT_EQ(readNext<T>(buffer), expectedValue);
|
||||
}
|
||||
|
||||
void checkString(uint8_t** buffer, const string& expectedString) {
|
||||
uint32_t size = readNext<uint32_t>(buffer);
|
||||
string parsedString((char*)(*buffer), size);
|
||||
EXPECT_EQ(parsedString, expectedString);
|
||||
*buffer += size; // move buffer past string we just read
|
||||
}
|
||||
|
||||
void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
|
||||
uint32_t size = readNext<uint32_t>(buffer);
|
||||
vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
|
||||
EXPECT_EQ(parsedByteArray, expectedByteArray);
|
||||
*buffer += size; // move buffer past byte array we just read
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
|
||||
EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
|
||||
EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
|
||||
checkScalar<T>(buffer, annotationValue);
|
||||
}
|
||||
|
||||
void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
|
||||
uint32_t atomId) {
|
||||
// All events start with OBJECT_TYPE id.
|
||||
checkTypeHeader(buffer, OBJECT_TYPE);
|
||||
|
||||
// We increment by 2 because the number of elements listed in the
|
||||
// serialization accounts for the timestamp and atom id as well.
|
||||
checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
|
||||
|
||||
// Check timestamp
|
||||
checkTypeHeader(buffer, INT64_TYPE);
|
||||
int64_t timestamp = readNext<int64_t>(buffer);
|
||||
EXPECT_GE(timestamp, startTime);
|
||||
EXPECT_LE(timestamp, endTime);
|
||||
|
||||
// Check atom id
|
||||
checkTypeHeader(buffer, INT32_TYPE);
|
||||
checkScalar(buffer, atomId);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestScalars) {
|
||||
uint32_t atomId = 100;
|
||||
int32_t int32Value = -5;
|
||||
int64_t int64Value = -2 * android::elapsedRealtimeNano();
|
||||
float floatValue = 2.0;
|
||||
bool boolValue = false;
|
||||
|
||||
int64_t startTime = android::elapsedRealtimeNano();
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
stats_event_set_atom_id(event, atomId);
|
||||
stats_event_write_int32(event, int32Value);
|
||||
stats_event_write_int64(event, int64Value);
|
||||
stats_event_write_float(event, floatValue);
|
||||
stats_event_write_bool(event, boolValue);
|
||||
stats_event_build(event);
|
||||
int64_t endTime = android::elapsedRealtimeNano();
|
||||
|
||||
size_t bufferSize;
|
||||
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
|
||||
uint8_t* bufferEnd = buffer + bufferSize;
|
||||
|
||||
checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
|
||||
|
||||
// check int32 element
|
||||
checkTypeHeader(&buffer, INT32_TYPE);
|
||||
checkScalar(&buffer, int32Value);
|
||||
|
||||
// check int64 element
|
||||
checkTypeHeader(&buffer, INT64_TYPE);
|
||||
checkScalar(&buffer, int64Value);
|
||||
|
||||
// check float element
|
||||
checkTypeHeader(&buffer, FLOAT_TYPE);
|
||||
checkScalar(&buffer, floatValue);
|
||||
|
||||
// check bool element
|
||||
checkTypeHeader(&buffer, BOOL_TYPE);
|
||||
checkScalar(&buffer, boolValue);
|
||||
|
||||
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
|
||||
EXPECT_EQ(stats_event_get_errors(event), 0);
|
||||
stats_event_release(event);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestStrings) {
|
||||
uint32_t atomId = 100;
|
||||
string str = "test_string";
|
||||
|
||||
int64_t startTime = android::elapsedRealtimeNano();
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
stats_event_set_atom_id(event, atomId);
|
||||
stats_event_write_string8(event, str.c_str());
|
||||
stats_event_build(event);
|
||||
int64_t endTime = android::elapsedRealtimeNano();
|
||||
|
||||
size_t bufferSize;
|
||||
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
|
||||
uint8_t* bufferEnd = buffer + bufferSize;
|
||||
|
||||
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
|
||||
|
||||
checkTypeHeader(&buffer, STRING_TYPE);
|
||||
checkString(&buffer, str);
|
||||
|
||||
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
|
||||
EXPECT_EQ(stats_event_get_errors(event), 0);
|
||||
stats_event_release(event);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestByteArrays) {
|
||||
uint32_t atomId = 100;
|
||||
vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
|
||||
|
||||
int64_t startTime = android::elapsedRealtimeNano();
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
stats_event_set_atom_id(event, atomId);
|
||||
stats_event_write_byte_array(event, message.data(), message.size());
|
||||
stats_event_build(event);
|
||||
int64_t endTime = android::elapsedRealtimeNano();
|
||||
|
||||
size_t bufferSize;
|
||||
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
|
||||
uint8_t* bufferEnd = buffer + bufferSize;
|
||||
|
||||
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
|
||||
|
||||
checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
|
||||
checkByteArray(&buffer, message);
|
||||
|
||||
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
|
||||
EXPECT_EQ(stats_event_get_errors(event), 0);
|
||||
stats_event_release(event);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestAttributionChains) {
|
||||
uint32_t atomId = 100;
|
||||
|
||||
uint8_t numNodes = 50;
|
||||
uint32_t uids[numNodes];
|
||||
vector<string> tags(numNodes); // storage that cTag elements point to
|
||||
const char* cTags[numNodes];
|
||||
for (int i = 0; i < (int)numNodes; i++) {
|
||||
uids[i] = i;
|
||||
tags.push_back("test" + std::to_string(i));
|
||||
cTags[i] = tags[i].c_str();
|
||||
}
|
||||
|
||||
int64_t startTime = android::elapsedRealtimeNano();
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
stats_event_set_atom_id(event, atomId);
|
||||
stats_event_write_attribution_chain(event, uids, cTags, numNodes);
|
||||
stats_event_build(event);
|
||||
int64_t endTime = android::elapsedRealtimeNano();
|
||||
|
||||
size_t bufferSize;
|
||||
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
|
||||
uint8_t* bufferEnd = buffer + bufferSize;
|
||||
|
||||
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
|
||||
|
||||
checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
|
||||
checkScalar(&buffer, numNodes);
|
||||
for (int i = 0; i < numNodes; i++) {
|
||||
checkScalar(&buffer, uids[i]);
|
||||
checkString(&buffer, tags[i]);
|
||||
}
|
||||
|
||||
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
|
||||
EXPECT_EQ(stats_event_get_errors(event), 0);
|
||||
stats_event_release(event);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestKeyValuePairs) {
|
||||
uint32_t atomId = 100;
|
||||
|
||||
uint8_t numPairs = 4;
|
||||
struct key_value_pair pairs[numPairs];
|
||||
pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = -1};
|
||||
pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
|
||||
pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 5.5};
|
||||
string str = "test_key_value_pair_string";
|
||||
pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
|
||||
|
||||
int64_t startTime = android::elapsedRealtimeNano();
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
stats_event_set_atom_id(event, atomId);
|
||||
stats_event_write_key_value_pairs(event, pairs, numPairs);
|
||||
stats_event_build(event);
|
||||
int64_t endTime = android::elapsedRealtimeNano();
|
||||
|
||||
size_t bufferSize;
|
||||
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
|
||||
uint8_t* bufferEnd = buffer + bufferSize;
|
||||
|
||||
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
|
||||
|
||||
checkTypeHeader(&buffer, KEY_VALUE_PAIRS_TYPE);
|
||||
checkScalar(&buffer, numPairs);
|
||||
|
||||
// first pair
|
||||
checkScalar(&buffer, pairs[0].key);
|
||||
checkTypeHeader(&buffer, pairs[0].valueType);
|
||||
checkScalar(&buffer, pairs[0].int32Value);
|
||||
|
||||
// second pair
|
||||
checkScalar(&buffer, pairs[1].key);
|
||||
checkTypeHeader(&buffer, pairs[1].valueType);
|
||||
checkScalar(&buffer, pairs[1].int64Value);
|
||||
|
||||
// third pair
|
||||
checkScalar(&buffer, pairs[2].key);
|
||||
checkTypeHeader(&buffer, pairs[2].valueType);
|
||||
checkScalar(&buffer, pairs[2].floatValue);
|
||||
|
||||
// fourth pair
|
||||
checkScalar(&buffer, pairs[3].key);
|
||||
checkTypeHeader(&buffer, pairs[3].valueType);
|
||||
checkString(&buffer, str);
|
||||
|
||||
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
|
||||
EXPECT_EQ(stats_event_get_errors(event), 0);
|
||||
stats_event_release(event);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestAnnotations) {
|
||||
uint32_t atomId = 100;
|
||||
|
||||
// first element information
|
||||
bool boolValue = false;
|
||||
uint8_t boolAnnotation1Id = 1;
|
||||
uint8_t boolAnnotation2Id = 2;
|
||||
bool boolAnnotation1Value = true;
|
||||
int32_t boolAnnotation2Value = 3;
|
||||
|
||||
// second element information
|
||||
float floatValue = -5.0;
|
||||
uint8_t floatAnnotation1Id = 3;
|
||||
uint8_t floatAnnotation2Id = 4;
|
||||
int32_t floatAnnotation1Value = 8;
|
||||
bool floatAnnotation2Value = false;
|
||||
|
||||
int64_t startTime = android::elapsedRealtimeNano();
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
stats_event_set_atom_id(event, 100);
|
||||
stats_event_write_bool(event, boolValue);
|
||||
stats_event_add_bool_annotation(event, boolAnnotation1Id, boolAnnotation1Value);
|
||||
stats_event_add_int32_annotation(event, boolAnnotation2Id, boolAnnotation2Value);
|
||||
stats_event_write_float(event, floatValue);
|
||||
stats_event_add_int32_annotation(event, floatAnnotation1Id, floatAnnotation1Value);
|
||||
stats_event_add_bool_annotation(event, floatAnnotation2Id, floatAnnotation2Value);
|
||||
stats_event_build(event);
|
||||
int64_t endTime = android::elapsedRealtimeNano();
|
||||
|
||||
size_t bufferSize;
|
||||
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
|
||||
uint8_t* bufferEnd = buffer + bufferSize;
|
||||
|
||||
checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
|
||||
|
||||
// check first element
|
||||
checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
|
||||
checkScalar(&buffer, boolValue);
|
||||
checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
|
||||
checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
|
||||
|
||||
// check second element
|
||||
checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
|
||||
checkScalar(&buffer, floatValue);
|
||||
checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
|
||||
checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
|
||||
|
||||
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
|
||||
EXPECT_EQ(stats_event_get_errors(event), 0);
|
||||
stats_event_release(event);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestNoAtomIdError) {
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
// Don't set the atom id in order to trigger the error.
|
||||
stats_event_build(event);
|
||||
|
||||
uint32_t errors = stats_event_get_errors(event);
|
||||
EXPECT_NE(errors | ERROR_NO_ATOM_ID, 0);
|
||||
|
||||
stats_event_release(event);
|
||||
}
|
||||
|
||||
TEST(StatsEventTest, TestOverflowError) {
|
||||
struct stats_event* event = stats_event_obtain();
|
||||
stats_event_set_atom_id(event, 100);
|
||||
// Add 1000 int32s to the event. Each int32 takes 5 bytes so this will
|
||||
// overflow the 4068 byte buffer.
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
stats_event_write_int32(event, 0);
|
||||
}
|
||||
stats_event_build(event);
|
||||
|
||||
uint32_t errors = stats_event_get_errors(event);
|
||||
EXPECT_NE(errors | ERROR_OVERFLOW, 0);
|
||||
|
||||
stats_event_release(event);
|
||||
}
|
Loading…
Reference in a new issue