summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2022-04-22 00:03:27 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-04-22 00:03:27 +0000
commit213186c87b645a44924264150b65ccd9546d1197 (patch)
tree254dbb68ba945fed761ae665c4fc9e79716135cc
parent71e22a5970acee496299322b89045e209cfc63e7 (diff)
parent3bf67009d8c958fe31ee6a2dba06e2a06504553e (diff)
downloadStatsD-213186c87b645a44924264150b65ccd9546d1197.tar.gz
Merge "Value metric memory improvements"
-rw-r--r--statsd/src/metrics/KllMetricProducer.cpp8
-rw-r--r--statsd/src/metrics/KllMetricProducer.h2
-rw-r--r--statsd/src/metrics/NumericValueMetricProducer.cpp25
-rw-r--r--statsd/src/metrics/NumericValueMetricProducer.h11
-rw-r--r--statsd/src/metrics/ValueMetricProducer.cpp33
-rw-r--r--statsd/src/metrics/ValueMetricProducer.h11
-rw-r--r--statsd/tests/metrics/NumericValueMetricProducer_test.cpp1121
7 files changed, 876 insertions, 335 deletions
diff --git a/statsd/src/metrics/KllMetricProducer.cpp b/statsd/src/metrics/KllMetricProducer.cpp
index 5fe08fc7..e5658a93 100644
--- a/statsd/src/metrics/KllMetricProducer.cpp
+++ b/statsd/src/metrics/KllMetricProducer.cpp
@@ -109,9 +109,10 @@ optional<int64_t> getInt64ValueFromEvent(const LogEvent& event, const Matcher& m
return nullopt;
}
-void KllMetricProducer::aggregateFields(const int64_t eventTimeNs,
+bool KllMetricProducer::aggregateFields(const int64_t eventTimeNs,
const MetricDimensionKey& eventKey, const LogEvent& event,
vector<Interval>& intervals, Empty& empty) {
+ bool seenNewData = false;
for (size_t i = 0; i < mFieldMatchers.size(); i++) {
const Matcher& matcher = mFieldMatchers[i];
Interval& interval = intervals[i];
@@ -120,7 +121,7 @@ void KllMetricProducer::aggregateFields(const int64_t eventTimeNs,
if (!valueOpt) {
VLOG("Failed to get value %zu from event %s", i, event.ToString().c_str());
StatsdStats::getInstance().noteBadValueType(mMetricId);
- return;
+ return seenNewData;
}
// interval.aggregate can be nullptr from cases:
@@ -130,10 +131,11 @@ void KllMetricProducer::aggregateFields(const int64_t eventTimeNs,
if (!interval.aggregate) {
interval.aggregate = KllQuantile::Create();
}
- interval.seenNewData = true;
+ seenNewData = true;
interval.aggregate->Add(valueOpt.value());
interval.sampleSize += 1;
}
+ return seenNewData;
}
PastBucket<unique_ptr<KllQuantile>> KllMetricProducer::buildPartialBucket(
diff --git a/statsd/src/metrics/KllMetricProducer.h b/statsd/src/metrics/KllMetricProducer.h
index b8182bb0..b1c59f27 100644
--- a/statsd/src/metrics/KllMetricProducer.h
+++ b/statsd/src/metrics/KllMetricProducer.h
@@ -101,7 +101,7 @@ private:
const std::unique_ptr<KllQuantile>& kll,
ProtoOutputStream* const protoOutput) const override;
- void aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey,
+ bool aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey,
const LogEvent& event, std::vector<Interval>& intervals,
Empty& empty) override;
diff --git a/statsd/src/metrics/NumericValueMetricProducer.cpp b/statsd/src/metrics/NumericValueMetricProducer.cpp
index fd2e207a..00792047 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.cpp
+++ b/statsd/src/metrics/NumericValueMetricProducer.cpp
@@ -302,20 +302,18 @@ void NumericValueMetricProducer::accumulateEvents(const vector<shared_ptr<LogEve
// 1. Tracked in mCurrentSlicedBucket and
// 2. A superset of the current mStateChangePrimaryKey
// was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
- // then we need to reset the base.
+ // then we clear the data from mDimInfos to reset the base and current state key.
for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat();
bool presentInPulledData =
mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
- if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
+ if (!presentInPulledData &&
+ containsLinkedStateValues(whatKey, mStateChangePrimaryKey.second, mMetric2StateLinks,
+ mStateChangePrimaryKey.first)) {
auto it = mDimInfos.find(whatKey);
- for (optional<Value>& base : it->second.dimExtras) {
- base.reset();
+ if (it != mDimInfos.end()) {
+ mDimInfos.erase(it);
}
- // Set to false when DimensionInWhat key is not present in a pull.
- // Used in onMatchedLogEventInternalLocked() to ensure the condition
- // timer is turned on the next pull when data is present.
- it->second.hasCurrentState = false;
// Turn OFF condition timer for keys not present in pulled data.
currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs);
}
@@ -381,7 +379,7 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret)
return false;
}
-void NumericValueMetricProducer::aggregateFields(const int64_t eventTimeNs,
+bool NumericValueMetricProducer::aggregateFields(const int64_t eventTimeNs,
const MetricDimensionKey& eventKey,
const LogEvent& event, vector<Interval>& intervals,
ValueBases& bases) {
@@ -397,6 +395,7 @@ void NumericValueMetricProducer::aggregateFields(const int64_t eventTimeNs,
// Whoever next works on it should look into the cases where it is triggered in this function.
// Discussion here: http://ag/6124370.
bool useAnomalyDetection = true;
+ bool seenNewData = false;
for (size_t i = 0; i < mFieldMatchers.size(); i++) {
const Matcher& matcher = mFieldMatchers[i];
Interval& interval = intervals[i];
@@ -406,11 +405,9 @@ void NumericValueMetricProducer::aggregateFields(const int64_t eventTimeNs,
if (!getDoubleOrLong(event, matcher, value)) {
VLOG("Failed to get value %zu from event %s", i, event.ToString().c_str());
StatsdStats::getInstance().noteBadValueType(mMetricId);
- return;
+ return seenNewData;
}
-
- interval.seenNewData = true;
-
+ seenNewData = true;
if (mUseDiff) {
if (!base.has_value()) {
if (mHasGlobalBase && mUseZeroDefaultBase) {
@@ -428,7 +425,6 @@ void NumericValueMetricProducer::aggregateFields(const int64_t eventTimeNs,
continue;
}
}
-
Value diff;
switch (mValueDirection) {
case ValueMetric::INCREASING:
@@ -505,6 +501,7 @@ void NumericValueMetricProducer::aggregateFields(const int64_t eventTimeNs,
wholeBucketVal);
}
}
+ return seenNewData;
}
PastBucket<Value> NumericValueMetricProducer::buildPartialBucket(int64_t bucketEndTimeNs,
diff --git a/statsd/src/metrics/NumericValueMetricProducer.h b/statsd/src/metrics/NumericValueMetricProducer.h
index cba0f860..542975dc 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.h
+++ b/statsd/src/metrics/NumericValueMetricProducer.h
@@ -120,7 +120,7 @@ private:
(isPulled() && !mUseDiff && mCondition != ConditionState::kTrue);
}
- void aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey,
+ bool aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey,
const LogEvent& event, std::vector<Interval>& intervals,
ValueBases& bases) override;
@@ -217,6 +217,9 @@ private:
FRIEND_TEST(NumericValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange);
FRIEND_TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket);
FRIEND_TEST(NumericValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary);
+ FRIEND_TEST(NumericValueMetricProducerTest, TestSlicedStateWithConditionFalseMultipleBuckets);
+ FRIEND_TEST(NumericValueMetricProducerTest,
+ TestSlicedStateWithMultipleDimensionsMissingDataInPull);
FRIEND_TEST(NumericValueMetricProducerTest, TestUploadThreshold);
FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
@@ -224,7 +227,11 @@ private:
FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed);
FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit);
FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop,
+ TestInvalidBucketWhenDumpReportRequested);
+ FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop,
TestInvalidBucketWhenAccumulateEventWrongBucket);
+ FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop,
+ TestInvalidBucketWhenMultipleBucketsSkipped);
FRIEND_TEST(NumericValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket);
FRIEND_TEST(NumericValueMetricProducerTest_PartialBucket,
@@ -257,6 +264,8 @@ private:
FRIEND_TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadSkip);
FRIEND_TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLateStateChangeSlicedAtoms);
+ FRIEND_TEST(NumericValueMetricProducerTest_SubsetDimensions, TestSubsetDimensions_FlagTrue);
+
FRIEND_TEST(ConfigUpdateTest, TestUpdateValueMetrics);
friend class NumericValueMetricProducerTestHelper;
diff --git a/statsd/src/metrics/ValueMetricProducer.cpp b/statsd/src/metrics/ValueMetricProducer.cpp
index 530de931..dccdbeff 100644
--- a/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/statsd/src/metrics/ValueMetricProducer.cpp
@@ -688,7 +688,8 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::onMatchedLogEventInternalL
dimensionsInWhatInfo.hasCurrentState = true;
dimensionsInWhatInfo.currentState = stateKey;
- aggregateFields(eventTimeNs, eventKey, event, intervals, dimensionsInWhatInfo.dimExtras);
+ dimensionsInWhatInfo.seenNewData |= aggregateFields(eventTimeNs, eventKey, event, intervals,
+ dimensionsInWhatInfo.dimExtras);
// State change.
if (!mSlicedStateAtoms.empty() && stateChange) {
@@ -834,18 +835,15 @@ template <typename AggregatedValue, typename DimExtras>
void ValueMetricProducer<AggregatedValue, DimExtras>::initNextSlicedBucket(
int64_t nextBucketStartTimeNs) {
StatsdStats::getInstance().noteBucketCount(mMetricId);
- // Cleanup data structure to aggregate values.
- for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
- bool obsolete = true;
- for (auto& interval : it->second.intervals) {
- interval.sampleSize = 0;
- if (interval.seenNewData) {
- obsolete = false;
+ if (mSlicedStateAtoms.empty()) {
+ mCurrentSlicedBucket.clear();
+ } else {
+ for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
+ bool obsolete = true;
+ for (auto& interval : it->second.intervals) {
+ interval.sampleSize = 0;
}
- interval.seenNewData = false;
- }
- if (obsolete && !mSlicedStateAtoms.empty()) {
// When slicing by state, only delete the MetricDimensionKey when the
// state key in the MetricDimensionKey is not the current state key.
const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat();
@@ -855,13 +853,20 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::initNextSlicedBucket(
(it->first.getStateValuesKey() == currentDimInfoItr->second.currentState)) {
obsolete = false;
}
+ if (obsolete) {
+ it = mCurrentSlicedBucket.erase(it);
+ } else {
+ it++;
+ }
}
- if (obsolete) {
- it = mCurrentSlicedBucket.erase(it);
+ }
+ for (auto it = mDimInfos.begin(); it != mDimInfos.end();) {
+ if (!it->second.seenNewData) {
+ it = mDimInfos.erase(it);
} else {
+ it->second.seenNewData = false;
it++;
}
- // TODO(b/157655103): remove mDimInfos entries when obsolete
}
mCurrentBucketIsSkipped = false;
diff --git a/statsd/src/metrics/ValueMetricProducer.h b/statsd/src/metrics/ValueMetricProducer.h
index 1111c208..6a9b931f 100644
--- a/statsd/src/metrics/ValueMetricProducer.h
+++ b/statsd/src/metrics/ValueMetricProducer.h
@@ -266,9 +266,6 @@ protected:
// Number of samples collected.
int sampleSize = 0;
- // Whether new data is seen in the bucket.
- bool seenNewData = false;
-
inline bool hasValue() const {
return sampleSize > 0;
}
@@ -299,6 +296,10 @@ protected:
DimExtras dimExtras;
+ // Whether new data is seen in the bucket.
+ // TODO, this could be per base in the dim extras.
+ bool seenNewData = false;
+
// Last seen state value(s).
HashableDimensionKey currentState;
// Whether this dimensions in what key has a current state key.
@@ -357,7 +358,9 @@ protected:
return eventTimeNs < mCurrentBucketStartTimeNs;
}
- virtual void aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey,
+ // Returns true if any of the intervals have seen new data.
+ // This should return true unless there is an error parsing the value fields from the event.
+ virtual bool aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey,
const LogEvent& event, std::vector<Interval>& intervals,
DimExtras& dimExtras) = 0;
diff --git a/statsd/tests/metrics/NumericValueMetricProducer_test.cpp b/statsd/tests/metrics/NumericValueMetricProducer_test.cpp
index 3cc06f0f..232ea0e2 100644
--- a/statsd/tests/metrics/NumericValueMetricProducer_test.cpp
+++ b/statsd/tests/metrics/NumericValueMetricProducer_test.cpp
@@ -347,31 +347,28 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsNoCondition) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is flushed
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ // dimInfos holds the base
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(11, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(8, curInterval.aggregate.long_value);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is cleared
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ // dimInfos holds the base
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(23, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(12, curInterval.aggregate.long_value);
assertPastBucketValuesSingleKey(
valueProducer->mPastBuckets, {8, 12}, {bucketSizeNs, bucketSizeNs}, {0, 0},
{bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
@@ -379,14 +376,14 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsNoCondition) {
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is cleared
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ // dimInfos holds the base
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(36, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(13, curInterval.aggregate.long_value);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8, 12, 13},
{bucketSizeNs, bucketSizeNs, bucketSizeNs}, {0, 0, 0},
{bucketStartTimeNs, bucket2StartTimeNs, bucket3StartTimeNs},
@@ -475,16 +472,14 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsWithFiltering) {
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is cleared
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ // dimInfos holds the base
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(11, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(8, curInterval.aggregate.long_value);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
@@ -493,25 +488,18 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsWithFiltering) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// No new data seen, so data has been cleared.
ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-
- EXPECT_EQ(true, curBase.has_value());
- EXPECT_EQ(11, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(8, curInterval.aggregate.long_value);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
// the base was reset
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(36, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
@@ -535,41 +523,37 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is cleared
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ // dimInfos holds the base
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(11, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
allData.clear();
+ // 10 is less than 11, so we reset and keep 10 as the value.
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since the bucket is flushed.
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(10, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(10, curInterval.aggregate.long_value);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, {0},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(36, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(26, curInterval.aggregate.long_value);
assertPastBucketValuesSingleKey(
valueProducer->mPastBuckets, {10, 26}, {bucketSizeNs, bucketSizeNs}, {0, 0},
{bucket2StartTimeNs, bucket3StartTimeNs}, {bucket3StartTimeNs, bucket4StartTimeNs});
@@ -592,39 +576,35 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is cleared
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ // mDimInfos holds the base
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(11, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
allData.clear();
+ // 10 is less than 11, so we reset. 10 only updates the base.
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(10, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(36, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(26, curInterval.aggregate.long_value);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {26}, {bucketSizeNs}, {0},
{bucket3StartTimeNs}, {bucket4StartTimeNs});
}
@@ -670,6 +650,7 @@ TEST(NumericValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
NumericValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
// startUpdated:false sum:0 start:100
EXPECT_EQ(true, curBase.has_value());
@@ -684,14 +665,11 @@ TEST(NumericValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(110, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(10, curInterval.aggregate.long_value);
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, {0},
@@ -700,6 +678,7 @@ TEST(NumericValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_TRUE(curInterval.hasValue());
EXPECT_EQ(20, curInterval.aggregate.long_value);
@@ -796,7 +775,7 @@ TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPulledValue) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
switch (GetParam()) {
case APP_UPGRADE:
@@ -842,7 +821,8 @@ TEST(NumericValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 150);
ASSERT_EQ(0UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
@@ -933,6 +913,7 @@ TEST(NumericValueMetricProducerTest, TestPushedEventsWithoutCondition) {
valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
}
TEST(NumericValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -989,6 +970,7 @@ TEST(NumericValueMetricProducerTest, TestPushedEventsWithCondition) {
valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {50}, {20}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
}
TEST(NumericValueMetricProducerTest, TestAnomalyDetection) {
@@ -1124,30 +1106,27 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryNoCondition) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is finished
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
// startUpdated:true sum:0 start:11
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(11, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// pull 2 at correct time
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // empty since bucket is finished
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
// tartUpdated:false sum:12
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(23, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, {0},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
@@ -1158,13 +1137,12 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryNoCondition) {
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
// startUpdated:false sum:12
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(36, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, {0},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
// The 1st bucket is dropped because of no data
@@ -1217,6 +1195,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition) {
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -1227,7 +1206,8 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition) {
// pull on bucket boundary come late, condition change happens before it
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
{bucketStartTimeNs}, {bucket2StartTimeNs});
@@ -1241,10 +1221,10 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition) {
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(false, curBase.has_value());
- EXPECT_EQ(0, curInterval.sampleSize);
}
/*
@@ -1302,16 +1282,17 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(false, curBase.has_value());
- EXPECT_EQ(0, curInterval.sampleSize);
// condition changed to true again, before the pull alarm is delivered
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
{bucketStartTimeNs}, {bucket2StartTimeNs});
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
@@ -1324,6 +1305,8 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
@@ -1336,6 +1319,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
assertPastBucketValuesSingleKey(
valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24}, {1, -1},
{bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
@@ -1376,6 +1360,7 @@ TEST(NumericValueMetricProducerTest, TestPushedAggregateMin) {
EXPECT_EQ(10, curInterval.aggregate.long_value);
valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
@@ -1519,6 +1504,7 @@ TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutput) {
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -1532,7 +1518,11 @@ TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutput) {
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
+ EXPECT_EQ(true, curBase.has_value());
+ EXPECT_EQ(15, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
EXPECT_EQ(5, curInterval.aggregate.long_value);
@@ -1542,6 +1532,7 @@ TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutput) {
valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
@@ -1555,6 +1546,7 @@ TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutput) {
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(15, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
@@ -1589,26 +1581,32 @@ TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(10, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(20, curBase.value().long_value);
+ EXPECT_EQ(0, curInterval.sampleSize);
valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
+ EXPECT_EQ(true, curBase.has_value());
+ EXPECT_EQ(15, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
EXPECT_EQ(5, curInterval.aggregate.long_value);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[1];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
+ EXPECT_EQ(true, curBase.has_value());
+ EXPECT_EQ(22, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
EXPECT_EQ(2, curInterval.aggregate.long_value);
@@ -1618,17 +1616,19 @@ TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
-
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(15, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
+ EXPECT_EQ(0, curInterval.aggregate.long_value);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[1];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(25, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
+ EXPECT_EQ(3, curInterval.aggregate.long_value);
LogEvent event4(/*uid=*/0, /*pid=*/0);
CreateThreeValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15, 29);
@@ -1640,11 +1640,13 @@ TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(15, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
+ EXPECT_EQ(0, curInterval.aggregate.long_value);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[1];
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(29, curBase.value().long_value);
EXPECT_TRUE(curInterval.hasValue());
+ EXPECT_EQ(3, curInterval.aggregate.long_value);
valueProducer->flushIfNeededLocked(bucket3StartTimeNs);
@@ -1687,6 +1689,7 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBase) {
metric);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
auto iter = valueProducer->mCurrentSlicedBucket.begin();
auto& interval1 = iter->second.intervals[0];
auto iterBase = valueProducer->mDimInfos.begin();
@@ -1704,33 +1707,21 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBase) {
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
EXPECT_EQ(true, base1.has_value());
EXPECT_EQ(11, base1.value().long_value);
- EXPECT_EQ(0, interval1.sampleSize);
- EXPECT_EQ(8, interval1.aggregate.long_value);
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
- if (it != iter) {
- break;
- }
- }
auto itBase = valueProducer->mDimInfos.begin();
- for (; itBase != valueProducer->mDimInfos.end(); it++) {
+ for (; itBase != valueProducer->mDimInfos.end(); itBase++) {
if (itBase != iterBase) {
break;
}
}
- EXPECT_TRUE(it != iter);
EXPECT_TRUE(itBase != iterBase);
- auto& interval2 = it->second.intervals[0];
auto& base2 = itBase->second.dimExtras[0];
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, base2.has_value());
EXPECT_EQ(4, base2.value().long_value);
- EXPECT_EQ(0, interval2.sampleSize);
- EXPECT_EQ(4, interval2.aggregate.long_value);
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
auto iterator = valueProducer->mPastBuckets.begin();
@@ -1764,6 +1755,7 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
metric);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
const auto& it = valueProducer->mCurrentSlicedBucket.begin();
NumericValueMetricProducer::Interval& interval1 = it->second.intervals[0];
optional<Value>& base1 =
@@ -1781,38 +1773,36 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
EXPECT_EQ(true, base1.has_value());
EXPECT_EQ(11, base1.value().long_value);
- EXPECT_EQ(0, interval1.sampleSize);
- EXPECT_EQ(8, interval1.aggregate.long_value);
- auto it2 = valueProducer->mCurrentSlicedBucket.begin();
- for (; it2 != valueProducer->mCurrentSlicedBucket.end(); it2++) {
- if (it2 != it) {
+ auto itBase2 = valueProducer->mDimInfos.begin();
+ for (; itBase2 != valueProducer->mDimInfos.end(); itBase2++) {
+ if (itBase2->second.dimExtras[0] != base1) {
break;
}
}
- EXPECT_TRUE(it2 != it);
- NumericValueMetricProducer::Interval& interval2 = it2->second.intervals[0];
- optional<Value>& base2 =
- valueProducer->mDimInfos.find(it2->first.getDimensionKeyInWhat())->second.dimExtras[0];
- EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ optional<Value>& base2 = itBase2->second.dimExtras[0];
+ EXPECT_TRUE(base2 != base1);
+ EXPECT_EQ(2, itBase2->first.getValues()[0].mValue.int_value);
EXPECT_EQ(true, base2.has_value());
EXPECT_EQ(4, base2.value().long_value);
- EXPECT_EQ(0, interval2.sampleSize);
- EXPECT_EQ(4, interval2.aggregate.long_value);
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
// next pull somehow did not happen, skip to end of bucket 3
+ // This pull is incomplete since it's missing dimension 1. Will cause mDimInfos to be trimmed
allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, base2.has_value());
- EXPECT_EQ(5, base2.value().long_value);
- EXPECT_EQ(0, interval2.sampleSize);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ EXPECT_EQ(2, valueProducer->mDimInfos.begin()->first.getValues()[0].mValue.int_value);
+ optional<Value>& base3 = valueProducer->mDimInfos.begin()->second.dimExtras[0];
+ EXPECT_EQ(true, base3.has_value());
+ EXPECT_EQ(5, base3.value().long_value);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
@@ -1821,29 +1811,16 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 5));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Get new references now that entries have been deleted from the map
- const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
- const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
- ASSERT_EQ(it3->second.intervals.size(), 1);
- ASSERT_EQ(it4->second.intervals.size(), 1);
- NumericValueMetricProducer::Interval& interval3 = it3->second.intervals[0];
- NumericValueMetricProducer::Interval& interval4 = it4->second.intervals[0];
- optional<Value>& base3 =
- valueProducer->mDimInfos.find(it3->first.getDimensionKeyInWhat())->second.dimExtras[0];
- optional<Value>& base4 =
- valueProducer->mDimInfos.find(it4->first.getDimensionKeyInWhat())->second.dimExtras[0];
-
- EXPECT_EQ(true, base3.has_value());
- EXPECT_EQ(5, base3.value().long_value);
- EXPECT_EQ(0, interval3.sampleSize);
- EXPECT_EQ(5, interval3.aggregate.long_value);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
+ optional<Value>& base4 = valueProducer->mDimInfos.begin()->second.dimExtras[0];
+ optional<Value>& base5 = std::next(valueProducer->mDimInfos.begin())->second.dimExtras[0];
EXPECT_EQ(true, base4.has_value());
- EXPECT_EQ(13, base4.value().long_value);
- EXPECT_EQ(0, interval4.sampleSize);
- EXPECT_EQ(8, interval4.aggregate.long_value);
+ EXPECT_EQ(5, base4.value().long_value);
+ EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+ EXPECT_EQ(true, base5.has_value());
+ EXPECT_EQ(13, base5.value().long_value);
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
}
@@ -1870,6 +1847,7 @@ TEST(NumericValueMetricProducerTest, TestTrimUnusedDimensionKey) {
metric);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
auto iter = valueProducer->mCurrentSlicedBucket.begin();
auto& interval1 = iter->second.intervals[0];
auto iterBase = valueProducer->mDimInfos.begin();
@@ -1886,74 +1864,85 @@ TEST(NumericValueMetricProducerTest, TestTrimUnusedDimensionKey) {
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
EXPECT_EQ(true, base1.has_value());
EXPECT_EQ(11, base1.value().long_value);
- EXPECT_EQ(0, interval1.sampleSize);
- EXPECT_EQ(8, interval1.aggregate.long_value);
- EXPECT_FALSE(interval1.seenNewData);
+ EXPECT_FALSE(iterBase->second.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
- if (it != iter) {
- break;
- }
- }
auto itBase = valueProducer->mDimInfos.begin();
- for (; itBase != valueProducer->mDimInfos.end(); it++) {
+ for (; itBase != valueProducer->mDimInfos.end(); itBase++) {
if (itBase != iterBase) {
break;
}
}
- EXPECT_TRUE(it != iter);
EXPECT_TRUE(itBase != iterBase);
- auto interval2 = it->second.intervals[0];
auto base2 = itBase->second.dimExtras[0];
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(2, itBase->first.getValues()[0].mValue.int_value);
EXPECT_EQ(true, base2.has_value());
EXPECT_EQ(4, base2.value().long_value);
- EXPECT_EQ(0, interval2.sampleSize);
- EXPECT_FALSE(interval2.seenNewData);
+ EXPECT_FALSE(itBase->second.seenNewData);
// next pull somehow did not happen, skip to end of bucket 3
allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- // Only one interval left. One was trimmed.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ // Only one dimension left. One was trimmed.
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
base2 = valueProducer->mDimInfos.begin()->second.dimExtras[0];
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(2, valueProducer->mDimInfos.begin()->first.getValues()[0].mValue.int_value);
EXPECT_EQ(true, base2.has_value());
EXPECT_EQ(5, base2.value().long_value);
- EXPECT_EQ(0, interval2.sampleSize);
- EXPECT_FALSE(interval2.seenNewData);
+ EXPECT_FALSE(valueProducer->mDimInfos.begin()->second.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 14));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
- interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- base2 = valueProducer->mDimInfos.begin()->second.dimExtras[0];
- EXPECT_EQ(true, base2.has_value());
- EXPECT_EQ(14, base2.value().long_value);
- EXPECT_EQ(0, interval2.sampleSize);
- EXPECT_FALSE(interval2.seenNewData);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
+
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket6StartTimeNs + 1, 1, 19));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket6StartTimeNs + 1, 2, 20));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
+
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
+
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
+ // Dimension = 2
auto iterator = valueProducer->mPastBuckets.begin();
+ ASSERT_EQ(1, iterator->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, iterator->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(2, iterator->second.size());
EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs);
EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs);
EXPECT_EQ(9, iterator->second[0].aggregates[0].long_value);
EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
+ EXPECT_EQ(bucket5StartTimeNs, iterator->second[1].mBucketStartNs);
+ EXPECT_EQ(bucket6StartTimeNs, iterator->second[1].mBucketEndNs);
+ EXPECT_EQ(6, iterator->second[1].aggregates[0].long_value);
+ EXPECT_EQ(bucketSizeNs, iterator->second[1].mConditionTrueNs);
iterator++;
+ // Dimension = 1
+ ASSERT_EQ(1, iterator->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, iterator->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(2, iterator->second.size());
EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs);
EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs);
EXPECT_EQ(8, iterator->second[0].aggregates[0].long_value);
EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
+ EXPECT_EQ(bucket5StartTimeNs, iterator->second[1].mBucketStartNs);
+ EXPECT_EQ(bucket6StartTimeNs, iterator->second[1].mBucketEndNs);
+ EXPECT_EQ(5, iterator->second[1].aggregates[0].long_value);
+ EXPECT_EQ(bucketSizeNs, iterator->second[1].mConditionTrueNs);
}
TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
@@ -1985,10 +1974,12 @@ TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange
vector<shared_ptr<LogEvent>> allData;
valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
EXPECT_EQ(false, curBase.has_value());
- EXPECT_EQ(0, curInterval.sampleSize);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
}
TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
@@ -2013,6 +2004,7 @@ TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value>& curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -2025,6 +2017,7 @@ TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
EXPECT_EQ(0, curInterval.sampleSize);
EXPECT_EQ(false, curBase.has_value());
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2054,15 +2047,13 @@ TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChang
NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
pullerManager, metric, ConditionState::kFalse);
- // Don't directly set mCondition; the real code never does that. Go through regular code path
- // to avoid unexpected behaviors.
- // valueProducer->mCondition = ConditionState::kTrue;
valueProducer->onConditionChanged(true, bucketStartTimeNs);
-
ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -2130,11 +2121,10 @@ TEST(NumericValueMetricProducerTest, TestBaseSetOnConditionChange) {
sp<NumericValueMetricProducer> valueProducer =
NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
pullerManager, metric, ConditionState::kFalse);
- valueProducer->mHasGlobalBase = false;
valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
- valueProducer->mHasGlobalBase = true;
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -2187,13 +2177,11 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditio
ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// Contains base from last pull which was successful.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(140, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
// Check dump report.
@@ -2325,13 +2313,11 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPull
ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// Contains base from last pull which was successful.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
EXPECT_EQ(140, curBase.value().long_value);
- EXPECT_EQ(0, curInterval.sampleSize);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
// Check dump report.
@@ -2404,12 +2390,10 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFai
ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// Last pull failed so base has been reset.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- NumericValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(false, curBase.has_value());
- EXPECT_EQ(0, curInterval.sampleSize);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
// Check dump report.
@@ -2455,16 +2439,41 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ EXPECT_EQ(valueProducer->mDimInfos.begin()->second.seenNewData, false);
ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size());
// Bucket 3 empty.
allData.clear();
allData.push_back(CreateNoValuesLogEvent(tagId, bucket3StartTimeNs + 1));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// Data has been trimmed.
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
+
+ // Bucket 4 start.
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 150));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mSkippedBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+
+ // Bucket 5 start.
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket5StartTimeNs + 1, 170));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
+ assertPastBucketValuesSingleKey(
+ valueProducer->mPastBuckets, {107, 20}, {bucketSizeNs, bucketSizeNs}, {0, 0},
+ {bucketStartTimeNs, bucket4StartTimeNs}, {bucket2StartTimeNs, bucket5StartTimeNs});
+ ASSERT_EQ(2UL, valueProducer->mSkippedBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
}
TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
@@ -2480,11 +2489,19 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged)
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}))
+ // Empty pull when change to false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
return true;
+ }))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
+ return true;
}));
sp<NumericValueMetricProducer> valueProducer =
@@ -2493,6 +2510,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged)
valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -2501,13 +2519,34 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged)
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
// Empty pull.
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 20);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
- EXPECT_EQ(false, curBase.has_value());
EXPECT_EQ(0, curInterval.sampleSize);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 30);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ EXPECT_EQ(0, curInterval.sampleSize);
+ curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
+ EXPECT_EQ(true, curBase.has_value());
+ EXPECT_EQ(10, curBase.value().long_value);
+ EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
+ EXPECT_EQ(true, curBase.has_value());
+ EXPECT_EQ(120, curBase.value().long_value);
+ EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {110}, {bucketSizeNs - 20}, {0},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
@@ -2546,6 +2585,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -2557,13 +2597,8 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
vector<shared_ptr<LogEvent>> allData;
allData.clear();
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
- // Data is empty, base should be reset.
- EXPECT_EQ(false, curBase.has_value());
- EXPECT_EQ(0, curInterval.sampleSize);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}, {0},
@@ -2592,6 +2627,7 @@ TEST(NumericValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// End of bucket
vector<shared_ptr<LogEvent>> allData;
@@ -2599,17 +2635,12 @@ TEST(NumericValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // Key 1 should be reset since in not present in the most pull.
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- auto iterator = valueProducer->mCurrentSlicedBucket.begin();
+ // Key 1 should be removed from mDimInfos since in not present in the most pull.
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
auto baseInfoIter = valueProducer->mDimInfos.begin();
EXPECT_EQ(true, baseInfoIter->second.dimExtras[0].has_value());
EXPECT_EQ(2, baseInfoIter->second.dimExtras[0].value().long_value);
- EXPECT_EQ(0, iterator->second.intervals[0].sampleSize);
- iterator++;
- baseInfoIter++;
- EXPECT_EQ(false, baseInfoIter->second.dimExtras[0].has_value());
- EXPECT_EQ(0, iterator->second.intervals[0].sampleSize);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
}
@@ -2709,6 +2740,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
auto curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(true, curBase.has_value());
@@ -2716,6 +2748,8 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
EXPECT_EQ(0, curInterval.sampleSize);
valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}, {10},
@@ -3008,6 +3042,9 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
// Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, {30},
{bucketStartTimeNs}, {bucket2StartTimeNs});
+ ASSERT_EQ(0, valueProducer->mCurrentSlicedBucket.size());
+ // TODO: mDimInfos is not needed for non-diffed data, but an entry is still created.
+ ASSERT_EQ(1, valueProducer->mDimInfos.size());
}
TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
@@ -3040,6 +3077,7 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withMultipleCondition
valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
// has one slice
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
NumericValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
@@ -3054,10 +3092,10 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withMultipleCondition
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(false, curBase.has_value());
- EXPECT_EQ(0, curInterval.sampleSize);
}
TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
@@ -3079,18 +3117,17 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
- // Now the alarm is delivered. Condition is off though.
+ // Now the alarm is delivered. Condition is on.
vector<shared_ptr<LogEvent>> allData;
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}, {0},
{bucketStartTimeNs}, {bucket2StartTimeNs});
- NumericValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
EXPECT_EQ(false, curBase.has_value());
- EXPECT_EQ(0, curInterval.sampleSize);
}
TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
@@ -3132,11 +3169,16 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
+ // First event is skipped because the metric is not diffed, so no entry is created in the map
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
// Now the alarm is delivered. Condition is off though.
vector<shared_ptr<LogEvent>> allData;
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
// No buckets, we had a failure.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}, {});
@@ -3177,6 +3219,8 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportR
std::set<string> strSet;
valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true,
FAST /* dumpLatency */, &strSet, &output);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
@@ -3459,6 +3503,9 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBuc
// Condition change event that skips forward by three buckets.
valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10);
+ // Ensure data structures are appropriately trimmed when multiple buckets are skipped.
+ ASSERT_EQ(valueProducer->mCurrentSlicedBucket.size(), 0);
+ ASSERT_EQ(valueProducer->mDimInfos.size(), 1);
int64_t dumpTimeNs = bucket4StartTimeNs + 1000;
@@ -3982,7 +4029,8 @@ TEST(NumericValueMetricProducerTest, TestSlicedState) {
// Bucket status after metric initialized.
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ // Base for dimension key {
auto it = valueProducer->mCurrentSlicedBucket.begin();
auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
@@ -4004,6 +4052,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedState) {
bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4036,6 +4085,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedState) {
android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4077,6 +4127,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedState) {
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4123,6 +4174,25 @@ TEST(NumericValueMetricProducerTest, TestSlicedState) {
true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
&strSet, &output);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
+ EXPECT_EQ(30, itBase->second.dimExtras[0].value().long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(it->second.intervals[0].sampleSize, 0);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 50 * NS_PER_SEC);
+
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
@@ -4233,6 +4303,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMap) {
// Bucket status after metric initialized.
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
auto it = valueProducer->mCurrentSlicedBucket.begin();
auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4255,6 +4326,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMap) {
bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4287,6 +4359,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMap) {
android::view::DisplayStateEnum::DISPLAY_STATE_VR);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4319,6 +4392,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMap) {
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4350,6 +4424,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMap) {
android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4393,6 +4468,24 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMap) {
true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
&strSet, &output);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
+ EXPECT_EQ(30, itBase->second.dimExtras[0].value().long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(screenOffGroup.group_id(),
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOffGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 50 * NS_PER_SEC);
+
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
@@ -4559,6 +4652,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensi
// Bucket status after metric initialized.
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
// Bucket status after uid 1 process state change kStateUnknown -> Foreground.
auto uidProcessEvent =
@@ -4566,6 +4660,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensi
android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
// Bucket status after uid 2 process state change kStateUnknown -> Background.
uidProcessEvent =
@@ -4573,6 +4668,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensi
android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4580,19 +4676,37 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensi
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 15));
valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+ // Ensure the MetricDimensionKeys for the current state are kept.
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin(); // dimension, state key {2, BACKGROUND}
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ it++; // dimension, state key {1, FOREGROUND}
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+
// Bucket status after uid 1 process state change from Foreground -> Background.
uidProcessEvent =
CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
// Bucket status after uid 1 process state change Background->Foreground.
uidProcessEvent =
CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
// Start dump report and check output.
ProtoOutputStream output;
@@ -4745,6 +4859,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange
// Bucket status after metric initialized.
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
auto it = valueProducer->mCurrentSlicedBucket.begin();
auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
@@ -4796,15 +4911,10 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange
CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ // Base for dimension key {} is cleared.
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
- EXPECT_FALSE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {{}, ON}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
@@ -4860,6 +4970,8 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange
&strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
+ backfillDimensionPath(&report);
+ backfillStartEndTimestamp(&report);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(2, report.value_metrics().data_size());
@@ -4868,14 +4980,16 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC,
+ {2}, 10 * NS_PER_SEC, -1);
// {{}, ON}
data = report.value_metrics().data(1);
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC,
+ {8}, 30 * NS_PER_SEC, -1);
}
/*
@@ -4963,7 +5077,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBuck
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
}
@@ -5131,45 +5245,33 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary)
CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ // Bucket split. all MetricDimensionKeys other than the current state key are trimmed.
ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, OFF}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
- bucket2StartTimeNs + 30 * NS_PER_SEC, 30 * NS_PER_SEC);
-
// Value for key {{}, ON}
- it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
-
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
&strSet, &output);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
StatsLogReport report = outputStreamToProto(&output);
+ backfillDimensionPath(&report);
+ backfillStartEndTimestamp(&report);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
@@ -5178,22 +5280,26 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary)
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {2},
+ 10 * NS_PER_SEC, -1);
// {{}, ON}
data = report.value_metrics().data(1);
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {2},
+ 10 * NS_PER_SEC, -1);
+ ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs,
+ bucket2StartTimeNs + 50 * NS_PER_SEC, {5}, 20 * NS_PER_SEC, -1);
// {{}, OFF}
data = report.value_metrics().data(2);
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {3},
+ 40 * NS_PER_SEC, -1);
}
/*
@@ -5213,9 +5319,10 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionCh
|-------------------------------------------------------|--
T F T (Condition)
- x (ON)
- |----------------------| -
- 20
+ x x (ON)
+ |----------------------| - |----|
+ 20 5
+ x (OFF)
*/
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Battery saver mode state changed to ON.
@@ -5244,6 +5351,15 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionCh
data->clear();
return true;
}))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 45 * NS_PER_SEC, 14));
+ return true;
+ }))
// Dump report pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
@@ -5323,17 +5439,18 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionCh
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 35 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+
// Bucket status after condition change to true.
valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ // Base for dimension key {}. The pull returned no data, so mDimInfos is trimmed.
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
- EXPECT_FALSE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {{}, ON}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
@@ -5350,14 +5467,29 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionCh
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 45 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
&strSet, &output);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
StatsLogReport report = outputStreamToProto(&output);
+ backfillDimensionPath(&report);
+ backfillStartEndTimestamp(&report);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(1, report.value_metrics().data_size());
@@ -5366,7 +5498,8 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionCh
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC,
+ {2 + 6}, 25 * NS_PER_SEC, -1);
}
/*
@@ -5579,8 +5712,8 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
- // // Buckets flushed after end of first bucket.
- ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Buckets flushed. MetricDimensionKeys not corresponding to the current state are removed.
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
@@ -5589,7 +5722,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
- ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
// Start dump report and check output.
ProtoOutputStream output;
@@ -5597,6 +5730,8 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
&strSet, &output);
+ ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
StatsLogReport report = outputStreamToProto(&output);
backfillDimensionPath(&report);
@@ -5787,8 +5922,9 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithCondition) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Base for dimension key {}
ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
itBase = valueProducer->mDimInfos.find(DEFAULT_DIMENSION_KEY);
@@ -5801,12 +5937,6 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithCondition) {
// Value for key {{}, OFF}
it = valueProducer->mCurrentSlicedBucket.begin();
assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
- // Value for key {{}, ON}
- it++;
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
- // Value for key {{}, -1}
- it++;
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket 2 status after condition change to false.
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
@@ -5819,7 +5949,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithCondition) {
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for key {{}, OFF}
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
@@ -5829,17 +5959,6 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithCondition) {
EXPECT_EQ(4, it->second.intervals[0].aggregate.long_value);
assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
bucket2StartTimeNs + 10 * NS_PER_SEC);
- // Value for key {{}, ON}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_EQ(0, it->second.intervals[0].sampleSize);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
- // Value for key {{}, -1}
- it++;
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Start dump report and check output.
ProtoOutputStream output;
@@ -5871,6 +5990,417 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithCondition) {
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
+TEST(NumericValueMetricProducerTest, TestSlicedStateWithConditionFalseMultipleBuckets) {
+ // Set up NumericValueMetricProducer.
+ ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithConditionAndState(
+ "BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Condition changed to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 9));
+ return true;
+ }))
+ // Condition changed to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket3StartTimeNs + 10 * NS_PER_SEC, 35));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket3StartTimeNs + 30 * NS_PER_SEC, 53));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<NumericValueMetricProducer> valueProducer =
+ NumericValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
+ ConditionState::kFalse);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after battery saver mode ON event.
+ // Condition is false so we do nothing.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(0UL, valueProducer->mDimInfos.size());
+
+ // Bucket status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Bucket status after battery saver mode OFF event.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Bucket status after condition change to false.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Pull at end of first bucket.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+
+ // Battery saver mode ON event. Nothing change since the condition is false.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Pull at end of second bucket. Since no new data is seen, mDimInfos will be cleared.
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
+
+ // Bucket2 status after condition change to true.
+ valueProducer->onConditionChanged(true, bucket3StartTimeNs + 10 * NS_PER_SEC);
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
+ // This currently keys into the old state key, which is unknown since mDimInfos was cleared.
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket3StartTimeNs + 30 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ backfillDimensionPath(&report);
+ backfillStartEndTimestamp(&report);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(2, report.value_metrics().data_size());
+
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {2},
+ 10 * NS_PER_SEC, -1);
+ ValidateValueBucket(data.bucket_info(1), bucket3StartTimeNs,
+ bucket3StartTimeNs + 30 * NS_PER_SEC, {18}, 20 * NS_PER_SEC, -1);
+
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {4},
+ 10 * NS_PER_SEC, -1);
+}
+
+/*
+ * Test slicing by state for metric that slices by state with a primary field,
+ * has multiple dimensions, and a pull that returns incomplete data.
+ */
+TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensionsMissingDataInPull) {
+ // Set up NumericValueMetricProducer.
+ ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithConditionAndState(
+ "UID_PROCESS_STATE");
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(3);
+
+ MetricStateLink* stateLink = metric.add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+ (kUnknown)
+ x {1, 14}
+ |-------------|
+ 20
+ x - {1, 16}
+ |-------------|
+ 20
+ x {2, 8}
+ |-----------------|
+ 25
+ {FOREGROUND}
+ x {2, 8}
+ |-------------|
+ 20
+ (BACKGROUND)
+ x {1, 14}
+ |----------------------------|---------------------------------|
+ 40 50
+ - {1, 16}
+ |---------------------------------|
+ 50
+ x - {2, 8}
+ |-------------------------|
+ 45
+ */
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Initial Pull
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 1,
+ 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 1,
+ 16 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 1,
+ 8 /*tag*/));
+ return true;
+ }))
+ // Uid 1 process state change from kStateUnknown -> Background. Tag 16 is missing.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 14 /*tag*/));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 7, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 2 process state change from kStateUnknown -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 2 /*uid*/, 8, 8 /*tag*/));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 8, 14 /* tag */));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 8, 16 /* tag */));
+ return true;
+ }))
+ // Uid 2 process state change from Background -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 22, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 22, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 22, 8 /*tag*/));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<NumericValueMetricProducer> valueProducer =
+ NumericValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Tag 16 is missing and gets trimmed from mDimInfos
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Pull at end of first bucket. Uid 2 is missing and gets trimmed from mDimInfos
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Buckets flushed. MetricDimensionKeys not corresponding to the current state are removed.
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
+ // {1, 16, kUnknown}, {2, 8, BACKGROUND} aren't present since the pulls were missing the dims.
+ ASSERT_EQ(3UL, valueProducer->mPastBuckets.size());
+
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+ ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+
+ StatsLogReport report = outputStreamToProto(&output);
+ backfillDimensionPath(&report);
+ backfillStartEndTimestamp(&report);
+ EXPECT_TRUE(report.has_value_metrics());
+ StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
+
+ // {1, 16, kUnknown}, {2, 8, BACKGROUND} aren't present since the pulls were missing the dims.
+ ASSERT_EQ(5, valueMetrics.data_size());
+ ASSERT_EQ(0, report.value_metrics().skipped_size());
+
+ // {{uid 1, tag 14}, kStateUnknown}.
+ ValueMetricData data = valueMetrics.data(0);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ -1 /*StateTracker::kStateUnknown*/);
+ EXPECT_EQ(data.dimensions_in_what().field(), tagId);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 14);
+ ASSERT_EQ(1, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {4},
+ 20 * NS_PER_SEC, -1);
+
+ // {{uid 1, tag 14}, BACKGROUND}.
+ data = valueMetrics.data(1);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ EXPECT_EQ(data.dimensions_in_what().field(), tagId);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 14);
+ ASSERT_EQ(2, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {8},
+ 40 * NS_PER_SEC, -1);
+ ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs,
+ bucket2StartTimeNs + 50 * NS_PER_SEC, {9}, 50 * NS_PER_SEC, -1);
+
+ // {{uid 1, tag 16}, BACKGROUND}.
+ data = valueMetrics.data(2);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ EXPECT_EQ(data.dimensions_in_what().field(), tagId);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 16);
+ ASSERT_EQ(1, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucket2StartTimeNs,
+ bucket2StartTimeNs + 50 * NS_PER_SEC, {9}, 50 * NS_PER_SEC, -1);
+
+ // {{uid 2, tag 8}, kStateUnknown}.
+ data = valueMetrics.data(3);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ -1 /*StateTracker::kStateUnknown*/);
+ EXPECT_EQ(data.dimensions_in_what().field(), tagId);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 8);
+ ASSERT_EQ(1, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {7},
+ 25 * NS_PER_SEC, -1);
+
+ // {{uid 2, tag 8}, FOREGROUND}.
+ data = valueMetrics.data(4);
+ ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ EXPECT_EQ(data.dimensions_in_what().field(), tagId);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 8);
+ ASSERT_EQ(1, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucket2StartTimeNs,
+ bucket2StartTimeNs + 50 * NS_PER_SEC, {4}, 20 * NS_PER_SEC, -1);
+}
+
/*
* Test bucket splits when condition is unknown.
*/
@@ -6732,7 +7262,9 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLateStateChangeSlic
screenEvent = CreateScreenStateChangedEvent(bucket2StartTimeNs + 10 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ // Bucket flush will trim all MetricDimensionKeys besides the current state key.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// mCurrentSlicedBucket represents second bucket
// Value for dimension, state key {{}, ON}
@@ -6741,28 +7273,16 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLateStateChangeSlic
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 10 * NS_PER_SEC);
- // Value for dimension, state key {{}, OFF}
- it++;
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucket2StartTimeNs + 10 * NS_PER_SEC, 10 * NS_PER_SEC);
-
// Bucket status after screen state change ON->OFF, forces bucket flush and new bucket start
screenEvent = CreateScreenStateChangedEvent(bucket3StartTimeNs,
android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// mCurrentSlicedBucket represents third bucket
- // Value for dimension, state key {{}, ON}
- it = valueProducer->mCurrentSlicedBucket.begin();
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucket3StartTimeNs, 0);
-
// Value for dimension, state key {{}, OFF}
- it++;
+ it = valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
assertConditionTimer(it->second.conditionTimer, true, 0, bucket3StartTimeNs, 0);
@@ -6771,7 +7291,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLateStateChangeSlic
screenEvent = CreateScreenStateChangedEvent(bucket4StartTimeNs,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
// Start dump report and check output.
ProtoOutputStream output;
@@ -6859,6 +7380,8 @@ TEST_F(NumericValueMetricProducerTest_SubsetDimensions, TestSubsetDimensions_Fla
allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 8, 5));
allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 9, 7));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
// Check dump report.
ProtoOutputStream output;
@@ -6866,6 +7389,8 @@ TEST_F(NumericValueMetricProducerTest_SubsetDimensions, TestSubsetDimensions_Fla
int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000;
valueProducer->onDumpReport(dumpReportTimeNs, true /* include current buckets */, true,
NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
StatsLogReport report = outputStreamToProto(&output);
backfillDimensionPath(&report);