trim ValueMetric dimension key

Bug: 120129929
Test: unit test
Change-Id: I26446f8eabbfbb20e1c715058d2e6b4bd5793a43
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1bd34f5..5524503 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1469,7 +1469,7 @@
 }
 
 /*
- * Tests pulled atoms with no conditions
+ * Tests zero default base.
  */
 TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
     ValueMetric metric;
@@ -1554,7 +1554,7 @@
 }
 
 /*
- * Tests pulled atoms with no conditions
+ * Tests using zero default base with failed pull.
  */
 TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
     ValueMetric metric;
@@ -1681,6 +1681,129 @@
     EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
 }
 
+/*
+ * Tests trim unused dimension key if no new data is seen in an entire bucket.
+ */
+TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.mutable_dimensions_in_what()->set_field(tagId);
+    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write(1);
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    auto iter = valueProducer.mCurrentSlicedBucket.begin();
+    auto& interval1 = iter->second[0];
+    EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, interval1.hasBase);
+    EXPECT_EQ(3, interval1.base.long_value);
+    EXPECT_EQ(false, interval1.hasValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    vector<shared_ptr<LogEvent>> allData;
+
+    allData.clear();
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event1->write(2);
+    event1->write(4);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event2->write(1);
+    event2->write(11);
+    event2->init();
+    allData.push_back(event1);
+    allData.push_back(event2);
+
+    valueProducer.onDataPulled(allData);
+    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(true, interval1.hasBase);
+    EXPECT_EQ(11, interval1.base.long_value);
+    EXPECT_EQ(true, interval1.hasValue);
+    EXPECT_EQ(8, interval1.value.long_value);
+    EXPECT_TRUE(interval1.seenNewData);
+
+    auto it = valueProducer.mCurrentSlicedBucket.begin();
+    for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+        if (it != iter) {
+            break;
+        }
+    }
+    EXPECT_TRUE(it != iter);
+    auto& interval2 = it->second[0];
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, interval2.hasBase);
+    EXPECT_EQ(4, interval2.base.long_value);
+    EXPECT_EQ(false, interval2.hasValue);
+    EXPECT_TRUE(interval2.seenNewData);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    // next pull somehow did not happen, skip to end of bucket 3
+    allData.clear();
+    event1 = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
+    event1->write(2);
+    event1->write(5);
+    event1->init();
+    allData.push_back(event1);
+    valueProducer.onDataPulled(allData);
+
+    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+
+    EXPECT_EQ(false, interval1.hasBase);
+    EXPECT_EQ(false, interval1.hasValue);
+    EXPECT_EQ(8, interval1.value.long_value);
+    // on probation now
+    EXPECT_FALSE(interval1.seenNewData);
+
+    EXPECT_EQ(true, interval2.hasBase);
+    EXPECT_EQ(5, interval2.base.long_value);
+    EXPECT_EQ(false, interval2.hasValue);
+    // back to good status
+    EXPECT_TRUE(interval2.seenNewData);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+
+    allData.clear();
+    event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
+    event1->write(2);
+    event1->write(13);
+    event1->init();
+    allData.push_back(event1);
+    valueProducer.onDataPulled(allData);
+
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(true, interval2.hasBase);
+    EXPECT_EQ(13, interval2.base.long_value);
+    EXPECT_EQ(true, interval2.hasValue);
+    EXPECT_EQ(8, interval2.value.long_value);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android