| #include "src/metrics/RestrictedEventMetricProducer.h" |
| |
| #include <android-modules-utils/sdk_level.h> |
| #include <gtest/gtest.h> |
| |
| #include "flags/FlagProvider.h" |
| #include "metrics_test_helper.h" |
| #include "stats_annotations.h" |
| #include "tests/statsd_test_util.h" |
| #include "utils/DbUtils.h" |
| |
| using namespace testing; |
| using std::string; |
| using std::stringstream; |
| using std::vector; |
| |
| #ifdef __ANDROID__ |
| |
| namespace android { |
| namespace os { |
| namespace statsd { |
| |
| using android::modules::sdklevel::IsAtLeastU; |
| |
| namespace { |
| const ConfigKey configKey(/*uid=*/0, /*id=*/12345); |
| const int64_t metricId1 = 123; |
| const int64_t metricId2 = 456; |
| |
| bool metricTableExist(int64_t metricId) { |
| stringstream query; |
| query << "SELECT * FROM metric_" << metricId; |
| vector<int32_t> columnTypes; |
| vector<vector<string>> rows; |
| vector<string> columnNames; |
| string err; |
| return dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err); |
| } |
| } // anonymous namespace |
| |
| class RestrictedEventMetricProducerTest : public Test { |
| protected: |
| void SetUp() override { |
| if (!IsAtLeastU()) { |
| GTEST_SKIP(); |
| } |
| } |
| void TearDown() override { |
| if (!IsAtLeastU()) { |
| GTEST_SKIP(); |
| } |
| dbutils::deleteDb(configKey); |
| FlagProvider::getInstance().resetOverrides(); |
| } |
| }; |
| |
| TEST_F(RestrictedEventMetricProducerTest, TestOnMatchedLogEventMultipleEvents) { |
| EventMetric metric; |
| metric.set_id(metricId1); |
| RestrictedEventMetricProducer producer(configKey, metric, |
| /*conditionIndex=*/-1, |
| /*initialConditionCache=*/{}, new ConditionWizard(), |
| /*protoHash=*/0x1234567890, |
| /*startTimeNs=*/0); |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/1); |
| std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/3); |
| |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1); |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event2); |
| producer.flushRestrictedData(); |
| |
| stringstream query; |
| query << "SELECT * FROM metric_" << metricId1; |
| string err; |
| vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| vector<vector<string>> rows; |
| dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err); |
| ASSERT_EQ(rows.size(), 2); |
| EXPECT_EQ(columnTypes.size(), |
| 3 + event1->getValues().size()); // col 0:2 are reserved for metadata. |
| EXPECT_EQ(/*tagId=*/rows[0][0], to_string(event1->GetTagId())); |
| EXPECT_EQ(/*elapsedTimestampNs=*/rows[0][1], to_string(event1->GetElapsedTimestampNs())); |
| EXPECT_EQ(/*elapsedTimestampNs=*/rows[1][1], to_string(event2->GetElapsedTimestampNs())); |
| |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| } |
| |
| TEST_F(RestrictedEventMetricProducerTest, TestOnMatchedLogEventMultipleFields) { |
| EventMetric metric; |
| metric.set_id(metricId2); |
| RestrictedEventMetricProducer producer(configKey, metric, |
| /*conditionIndex=*/-1, |
| /*initialConditionCache=*/{}, new ConditionWizard(), |
| /*protoHash=*/0x1234567890, |
| /*startTimeNs=*/0); |
| AStatsEvent* statsEvent = AStatsEvent_obtain(); |
| AStatsEvent_setAtomId(statsEvent, 1); |
| AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, |
| ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC); |
| AStatsEvent_overwriteTimestamp(statsEvent, 1); |
| |
| AStatsEvent_writeString(statsEvent, "111"); |
| AStatsEvent_writeInt32(statsEvent, 11); |
| AStatsEvent_writeFloat(statsEvent, 11.0); |
| LogEvent logEvent(/*uid=*/0, /*pid=*/0); |
| parseStatsEventToLogEvent(statsEvent, &logEvent); |
| |
| producer.onMatchedLogEvent(/*matcherIndex=1*/ 1, logEvent); |
| producer.flushRestrictedData(); |
| |
| stringstream query; |
| query << "SELECT * FROM metric_" << metricId2; |
| string err; |
| vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| vector<vector<string>> rows; |
| EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err)); |
| ASSERT_EQ(rows.size(), 1); |
| EXPECT_EQ(columnTypes.size(), |
| 3 + logEvent.getValues().size()); // col 0:2 are reserved for metadata. |
| EXPECT_EQ(/*field1=*/rows[0][3], "111"); |
| EXPECT_EQ(/*field2=*/rows[0][4], "11"); |
| EXPECT_FLOAT_EQ(/*field3=*/std::stof(rows[0][5]), 11.0); |
| |
| EXPECT_THAT(columnNames, ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", |
| "field_1", "field_2", "field_3")); |
| } |
| |
| TEST_F(RestrictedEventMetricProducerTest, TestOnMatchedLogEventWithCondition) { |
| EventMetric metric; |
| metric.set_id(metricId1); |
| metric.set_condition(StringToId("SCREEN_ON")); |
| RestrictedEventMetricProducer producer(configKey, metric, |
| /*conditionIndex=*/0, |
| /*initialConditionCache=*/{ConditionState::kUnknown}, |
| new ConditionWizard(), |
| /*protoHash=*/0x1234567890, |
| /*startTimeNs=*/0); |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/1); |
| std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/3); |
| |
| producer.onConditionChanged(true, 0); |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1); |
| producer.onConditionChanged(false, 1); |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event2); |
| producer.flushRestrictedData(); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << metricId1; |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err); |
| ASSERT_EQ(rows.size(), 1); |
| EXPECT_EQ(columnTypes.size(), 3 + event1->getValues().size()); |
| EXPECT_EQ(/*elapsedTimestampNs=*/rows[0][1], to_string(event1->GetElapsedTimestampNs())); |
| |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| } |
| |
| TEST_F(RestrictedEventMetricProducerTest, TestOnDumpReportNoOp) { |
| EventMetric metric; |
| metric.set_id(metricId1); |
| RestrictedEventMetricProducer producer(configKey, metric, |
| /*conditionIndex=*/-1, |
| /*initialConditionCache=*/{}, new ConditionWizard(), |
| /*protoHash=*/0x1234567890, |
| /*startTimeNs=*/0); |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*timestampNs=*/1); |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1); |
| ProtoOutputStream output; |
| std::set<string> strSet; |
| producer.onDumpReport(/*dumpTimeNs=*/10, |
| /*include_current_partial_bucket=*/true, |
| /*erase_data=*/true, FAST, &strSet, &output); |
| |
| ASSERT_EQ(output.size(), 0); |
| ASSERT_EQ(strSet.size(), 0); |
| } |
| |
| TEST_F(RestrictedEventMetricProducerTest, TestOnMetricRemove) { |
| EventMetric metric; |
| metric.set_id(metricId1); |
| RestrictedEventMetricProducer producer(configKey, metric, |
| /*conditionIndex=*/-1, |
| /*initialConditionCache=*/{}, new ConditionWizard(), |
| /*protoHash=*/0x1234567890, |
| /*startTimeNs=*/0); |
| EXPECT_FALSE(metricTableExist(metricId1)); |
| |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*timestampNs=*/1); |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1); |
| producer.flushRestrictedData(); |
| EXPECT_TRUE(metricTableExist(metricId1)); |
| |
| producer.onMetricRemove(); |
| EXPECT_FALSE(metricTableExist(metricId1)); |
| } |
| |
| TEST_F(RestrictedEventMetricProducerTest, TestRestrictedEventMetricTtlDeletesFirstEvent) { |
| EventMetric metric; |
| metric.set_id(metricId1); |
| RestrictedEventMetricProducer producer(configKey, metric, |
| /*conditionIndex=*/-1, |
| /*initialConditionCache=*/{}, new ConditionWizard(), |
| /*protoHash=*/0x1234567890, |
| /*startTimeNs=*/0); |
| |
| int64_t currentTimeNs = getWallClockNs(); |
| int64_t eightDaysAgo = currentTimeNs - 8 * 24 * 3600 * NS_PER_SEC; |
| std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/1); |
| event1->setLogdWallClockTimestampNs(eightDaysAgo); |
| std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/3); |
| event2->setLogdWallClockTimestampNs(currentTimeNs); |
| |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1); |
| producer.onMatchedLogEvent(/*matcherIndex=*/1, *event2); |
| producer.flushRestrictedData(); |
| sqlite3* dbHandle = dbutils::getDb(configKey); |
| producer.enforceRestrictedDataTtl(dbHandle, currentTimeNs + 100); |
| dbutils::closeDb(dbHandle); |
| |
| std::stringstream query; |
| query << "SELECT * FROM metric_" << metricId1; |
| string err; |
| std::vector<int32_t> columnTypes; |
| std::vector<string> columnNames; |
| std::vector<std::vector<std::string>> rows; |
| dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err); |
| ASSERT_EQ(rows.size(), 1); |
| EXPECT_EQ(columnTypes.size(), 3 + event1->getValues().size()); |
| EXPECT_THAT(columnNames, |
| ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1")); |
| EXPECT_THAT(rows[0], ElementsAre(to_string(event2->GetTagId()), |
| to_string(event2->GetElapsedTimestampNs()), |
| to_string(currentTimeNs), _)); |
| } |
| |
| TEST_F(RestrictedEventMetricProducerTest, TestLoadMetricMetadataSetsCategory) { |
| metadata::MetricMetadata metricMetadata; |
| metricMetadata.set_metric_id(metricId1); |
| metricMetadata.set_restricted_category(1); // CATEGORY_DIAGNOSTIC |
| EventMetric metric; |
| metric.set_id(metricId1); |
| RestrictedEventMetricProducer producer(configKey, metric, |
| /*conditionIndex=*/-1, |
| /*initialConditionCache=*/{}, new ConditionWizard(), |
| /*protoHash=*/0x1234567890, |
| /*startTimeNs=*/0); |
| |
| producer.loadMetricMetadataFromProto(metricMetadata); |
| |
| EXPECT_EQ(producer.getRestrictionCategory(), CATEGORY_DIAGNOSTIC); |
| } |
| |
| } // namespace statsd |
| } // namespace os |
| } // namespace android |
| |
| #else |
| GTEST_LOG_(INFO) << "This test does nothing.\n"; |
| #endif |