/* * 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 #include // Keep in sync with stats_event.c. Consider moving to separate header file to avoid duplication. /* ERRORS */ #define ERROR_NO_TIMESTAMP 0x1 #define ERROR_NO_ATOM_ID 0x2 #define ERROR_OVERFLOW 0x4 #define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8 #define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10 #define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20 #define ERROR_INVALID_ANNOTATION_ID 0x40 #define ERROR_ANNOTATION_ID_TOO_LARGE 0x80 #define ERROR_TOO_MANY_ANNOTATIONS 0x100 #define ERROR_TOO_MANY_FIELDS 0x200 #define ERROR_INVALID_VALUE_TYPE 0x400 #define ERROR_STRING_NOT_NULL_TERMINATED 0x800 #define ERROR_ATOM_ID_INVALID_POSITION 0x2000 #define ERROR_LIST_TOO_LONG 0x4000 /* TYPE IDS */ #define INT32_TYPE 0x00 #define INT64_TYPE 0x01 #define STRING_TYPE 0x02 #define LIST_TYPE 0x03 #define FLOAT_TYPE 0x04 #define BOOL_TYPE 0x05 #define BYTE_ARRAY_TYPE 0x06 #define OBJECT_TYPE 0x07 #define KEY_VALUE_PAIRS_TYPE 0x08 #define ATTRIBUTION_CHAIN_TYPE 0x09 #define ERROR_TYPE 0x0F using std::string; using std::vector; // Side-effect: this function moves the start of the buffer past the read value template T readNext(uint8_t** buffer) { T value; if ((reinterpret_cast(*buffer) % alignof(T)) == 0) { value = *(T*)(*buffer); } else { memcpy(&value, *buffer, sizeof(T)); } *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(buffer), typeHeader); } template void checkScalar(uint8_t** buffer, T expectedValue) { EXPECT_EQ(readNext(buffer), expectedValue); } void checkString(uint8_t** buffer, const string& expectedString) { uint32_t size = readNext(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& expectedByteArray) { uint32_t size = readNext(buffer); vector parsedByteArray(*buffer, *buffer + size); EXPECT_EQ(parsedByteArray, expectedByteArray); *buffer += size; // move buffer past byte array we just read } void checkArrayMetadata(uint8_t** buffer, uint8_t numElements, uint8_t elementTypeId, uint8_t numAnnotations = 0) { checkTypeHeader(buffer, LIST_TYPE, numAnnotations); EXPECT_EQ(readNext(buffer), numElements); checkTypeHeader(buffer, elementTypeId); } template void checkScalarArray(uint8_t** buffer, uint8_t numElements, uint8_t elementTypeId, const T* expectedArrayValues, uint8_t numAnnotations = 0) { checkArrayMetadata(buffer, numElements, elementTypeId, numAnnotations); for (int i = 0; i < numElements; i++) { checkScalar(buffer, expectedArrayValues[i]); } } template void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) { EXPECT_EQ(readNext(buffer), annotationId); EXPECT_EQ(readNext(buffer), typeId); checkScalar(buffer, annotationValue); } void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime, uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) { // 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(numElements + 2)); // Check timestamp checkTypeHeader(buffer, INT64_TYPE); int64_t timestamp = readNext(buffer); EXPECT_GE(timestamp, startTime); EXPECT_LE(timestamp, endTime); // Check atom id checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations); 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(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeInt32(event, int32Value); AStatsEvent_writeInt64(event, int64Value); AStatsEvent_writeFloat(event, floatValue); AStatsEvent_writeBool(event, boolValue); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(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(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestStrings) { uint32_t atomId = 100; string str = "test_string"; int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeString(event, str.c_str()); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(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(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestNullString) { uint32_t atomId = 100; char* str = nullptr; int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeString(event, str); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize); uint8_t* bufferEnd = buffer + bufferSize; checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId); checkTypeHeader(&buffer, STRING_TYPE); checkString(&buffer, ""); EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer EXPECT_EQ(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestByteArrays) { uint32_t atomId = 100; vector message = {'b', 'y', 't', '\0', 'e', 's'}; int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeByteArray(event, message.data(), message.size()); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(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(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestNullByteArrays) { uint32_t atomId = 100; uint8_t* buf = nullptr; vector message; int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeByteArray(event, buf, 2); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(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(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestAllArrays) { uint32_t atomId = 100; uint8_t numElements = 3; int32_t int32Array[3] = {3, 6, 9}; int64_t int64Array[3] = {1000L, 1001L, 1002L}; float floatArray[3] = {0.1f, 0.3f, 0.09f}; bool boolArray[3] = {0, 1, 1}; vector stringArray = {"str1", "str2", "str3"}; const char* cStringArray[3]; for (int i = 0; i < numElements; i++) { cStringArray[i] = stringArray[i].c_str(); } int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeInt32Array(event, int32Array, numElements); AStatsEvent_writeInt64Array(event, int64Array, numElements); AStatsEvent_writeFloatArray(event, floatArray, numElements); AStatsEvent_writeBoolArray(event, boolArray, numElements); AStatsEvent_writeStringArray(event, cStringArray, numElements); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize); uint8_t* bufferEnd = buffer + bufferSize; checkMetadata(&buffer, /*numTopLevelElements=*/5, startTime, endTime, atomId); // check int32Array element checkScalarArray(&buffer, numElements, INT32_TYPE, int32Array); // check int64Array element checkScalarArray(&buffer, numElements, INT64_TYPE, int64Array); // check floatArray element checkScalarArray(&buffer, numElements, FLOAT_TYPE, floatArray); // check boolArray element checkScalarArray(&buffer, numElements, BOOL_TYPE, boolArray); // check stringArray element checkArrayMetadata(&buffer, numElements, STRING_TYPE); for (int i = 0; i < numElements; i++) { checkString(&buffer, stringArray[i]); } EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer EXPECT_EQ(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestAttributionChains) { uint32_t atomId = 100; uint8_t numNodes = 50; uint32_t uids[numNodes]; vector tags(numNodes); // storage that cTag elements point to const char* cTags[numNodes]; for (int i = 0; i < (int)numNodes; i++) { uids[i] = i; if (0 == i) { tags.push_back(""); cTags[i] = nullptr; } else { tags.push_back("test" + std::to_string(i)); cTags[i] = tags[i].c_str(); } } int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeAttributionChain(event, uids, cTags, numNodes); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(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(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestFieldAnnotations) { 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(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeBool(event, boolValue); AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value); AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value); AStatsEvent_writeFloat(event, floatValue); AStatsEvent_addInt32Annotation(event, floatAnnotation1Id, floatAnnotation1Value); AStatsEvent_addBoolAnnotation(event, floatAnnotation2Id, floatAnnotation2Value); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(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(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestArrayFieldAnnotations) { uint32_t atomId = 100; // array annotation info uint8_t boolAnnotationId = 1; uint8_t int32AnnotationId = 2; bool boolAnnotationValue = true; int32_t int32AnnotationValue = 4; uint8_t numElements = 3; int32_t int32Array[3] = {3, 6, 9}; int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeInt32Array(event, int32Array, numElements); AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue); AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize); uint8_t* bufferEnd = buffer + bufferSize; checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId); // check first element checkScalarArray(&buffer, numElements, INT32_TYPE, int32Array, /*numAnnotations=*/2); checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue); checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue); EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer EXPECT_EQ(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestAtomLevelAnnotations) { uint32_t atomId = 100; // atom-level annotation information uint8_t boolAnnotationId = 1; uint8_t int32AnnotationId = 2; bool boolAnnotationValue = false; int32_t int32AnnotationValue = 5; float fieldValue = -3.5; int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue); AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue); AStatsEvent_writeFloat(event, fieldValue); AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize); uint8_t* bufferEnd = buffer + bufferSize; checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId, /*numAtomLevelAnnotations=*/2); // check atom-level annotations checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue); checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue); // check first element checkTypeHeader(&buffer, FLOAT_TYPE); checkScalar(&buffer, fieldValue); EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer EXPECT_EQ(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestNoAtomIdError) { AStatsEvent* event = AStatsEvent_obtain(); // Don't set the atom id in order to trigger the error. AStatsEvent_build(event); uint32_t errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_NO_ATOM_ID, ERROR_NO_ATOM_ID); AStatsEvent_release(event); } TEST(StatsEventTest, TestPushOverflowError) { const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const int writeCount = 120; // Number of times to write str in the event. AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, 100); // Add str to the event 120 times. Each str takes >35 bytes so this will // overflow the 4068 byte buffer. // We want to keep writeCount less than 127 to avoid hitting // ERROR_TOO_MANY_FIELDS. for (int i = 0; i < writeCount; i++) { AStatsEvent_writeString(event, str); } AStatsEvent_write(event); uint32_t errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW); AStatsEvent_release(event); } TEST(StatsEventTest, TestHeapBufferOverflowError) { const std::string testString(4039, 'A'); const std::string testString2(47135, 'B'); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, 100); AStatsEvent_writeString(event, testString.c_str()); size_t bufferSize = 0; AStatsEvent_getBuffer(event, &bufferSize); EXPECT_EQ(bufferSize, 4060); uint32_t errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors, 0); // expand the buffer and fill with data up to the very last byte AStatsEvent_writeString(event, testString2.c_str()); bufferSize = 0; AStatsEvent_getBuffer(event, &bufferSize); EXPECT_EQ(bufferSize, 50 * 1024); errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors, 0); // this write is no-op due to buffer reached its max capacity // should set the overflow flag AStatsEvent_writeString(event, testString2.c_str()); bufferSize = 0; AStatsEvent_getBuffer(event, &bufferSize); EXPECT_EQ(bufferSize, 50 * 1024); errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW); // here should be crash AStatsEvent_addBoolAnnotation(event, 1, false); AStatsEvent_write(event); errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW); AStatsEvent_release(event); } TEST(StatsEventTest, TestPullOverflowError) { const uint32_t atomId = 10100; const vector bytes(430 /* number of elements */, 1 /* value of each element */); const int writeCount = 120; // Number of times to write bytes in the event. AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); // Add bytes to the event 120 times. Size of bytes is 430 so this will // overflow the 50 KB pulled event buffer. // We want to keep writeCount less than 127 to avoid hitting // ERROR_TOO_MANY_FIELDS. for (int i = 0; i < writeCount; i++) { AStatsEvent_writeByteArray(event, bytes.data(), bytes.size()); } AStatsEvent_build(event); uint32_t errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW); AStatsEvent_release(event); } TEST(StatsEventTest, TestLargePull) { const uint32_t atomId = 100; const string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const int writeCount = 120; // Number of times to write str in the event. const int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); // Add str to the event 120 times. // We want to keep writeCount less than 127 to avoid hitting // ERROR_TOO_MANY_FIELDS. for (int i = 0; i < writeCount; i++) { AStatsEvent_writeString(event, str.c_str()); } AStatsEvent_build(event); int64_t endTime = android::elapsedRealtimeNano(); size_t bufferSize; uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize); uint8_t* bufferEnd = buffer + bufferSize; checkMetadata(&buffer, writeCount, startTime, endTime, atomId); // Check all instances of str have been written. for (int i = 0; i < writeCount; i++) { checkTypeHeader(&buffer, STRING_TYPE); checkString(&buffer, str); } EXPECT_EQ(buffer, bufferEnd); // Ensure that we have read the entire buffer. EXPECT_EQ(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestAtomIdInvalidPositionError) { AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_writeInt32(event, 0); AStatsEvent_setAtomId(event, 100); AStatsEvent_writeBool(event, true); AStatsEvent_build(event); uint32_t errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_ATOM_ID_INVALID_POSITION, ERROR_ATOM_ID_INVALID_POSITION); AStatsEvent_release(event); } TEST(StatsEventTest, TestOverwriteTimestamp) { uint32_t atomId = 100; int64_t expectedTimestamp = 0x123456789; AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_overwriteTimestamp(event, expectedTimestamp); AStatsEvent_build(event); uint8_t* buffer = AStatsEvent_getBuffer(event, NULL); // Make sure that the timestamp is being overwritten. checkMetadata(&buffer, /*numElements=*/0, /*startTime=*/expectedTimestamp, /*endTime=*/expectedTimestamp, atomId); EXPECT_EQ(AStatsEvent_getErrors(event), 0); AStatsEvent_release(event); } TEST(StatsEventTest, TestAttributionChainTooLongError) { uint32_t atomId = 100; uint8_t numNodes = 128; uint32_t uids[numNodes]; vector tags(numNodes); // storage that cTag elements point to const char* cTags[numNodes]; for (int i = 0; i < (int)numNodes; i++) { uids[i] = i; if (0 == i) { tags.push_back(""); cTags[i] = nullptr; } else { tags.push_back("test" + std::to_string(i)); cTags[i] = tags[i].c_str(); } } AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeAttributionChain(event, uids, cTags, numNodes); AStatsEvent_build(event); uint32_t errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_ATTRIBUTION_CHAIN_TOO_LONG, ERROR_ATTRIBUTION_CHAIN_TOO_LONG); } TEST(StatsEventTest, TestListTooLongError) { uint32_t atomId = 100; uint8_t numElements = 128; int32_t int32Array[128] = {1}; AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeInt32Array(event, int32Array, numElements); AStatsEvent_build(event); uint32_t errors = AStatsEvent_getErrors(event); EXPECT_EQ(errors & ERROR_LIST_TOO_LONG, ERROR_LIST_TOO_LONG); }