Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fc9fd20d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Ifea7ce09,If1222b60

* changes:
  Invalidate the bucket when global base is missing.
  Fix usage of flush buckets.
parents a18cf7e3 a8b70115
Loading
Loading
Loading
Loading
+126 −73
Original line number Diff line number Diff line
@@ -155,7 +155,7 @@ ValueMetricProducer::ValueMetricProducer(
    mCurrentBucketStartTimeNs = startTimeNs;
    // Kicks off the puller immediately if condition is true and diff based.
    if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
        pullAndMatchEventsLocked(startTimeNs);
        pullAndMatchEventsLocked(startTimeNs, mCondition);
    }
    VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
         (long long)mBucketSizeNs, (long long)mTimeBaseNs);
@@ -174,13 +174,17 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition
}

void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
    flushIfNeededLocked(dropTimeNs);
    StatsdStats::getInstance().noteBucketDropped(mMetricId);
    mPastBuckets.clear();
    // We are going to flush the data without doing a pull first so we need to invalidte the data.
    bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
    if (pullNeeded) {
        invalidateCurrentBucket();
    }
    flushIfNeededLocked(dropTimeNs);
    clearPastBucketsLocked(dropTimeNs);
}

void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
    flushIfNeededLocked(dumpTimeNs);
    mPastBuckets.clear();
    mSkippedBuckets.clear();
}
@@ -192,7 +196,6 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                             std::set<string> *str_set,
                                             ProtoOutputStream* protoOutput) {
    VLOG("metric %lld dump report now...", (long long)mMetricId);
    flushIfNeededLocked(dumpTimeNs);
    if (include_current_partial_bucket) {
        // For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the
        // current bucket will have incomplete data and the next will have the wrong snapshot to do
@@ -205,7 +208,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                    invalidateCurrentBucket();
                    break;
                case NO_TIME_CONSTRAINTS:
                    pullAndMatchEventsLocked(dumpTimeNs);
                    pullAndMatchEventsLocked(dumpTimeNs, mCondition);
                    break;
            }
        }
@@ -325,12 +328,16 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
    }
}

void ValueMetricProducer::invalidateCurrentBucket() {
void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() {
    if (!mCurrentBucketIsInvalid) {
        // Only report once per invalid bucket.
        StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
    }
    mCurrentBucketIsInvalid = true;
}

void ValueMetricProducer::invalidateCurrentBucket() {
    invalidateCurrentBucketWithoutResetBase();
    resetBase();
}

@@ -345,86 +352,112 @@ void ValueMetricProducer::resetBase() {

void ValueMetricProducer::onConditionChangedLocked(const bool condition,
                                                   const int64_t eventTimeNs) {
    if (eventTimeNs < mCurrentBucketStartTimeNs) {
        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
             (long long)mCurrentBucketStartTimeNs);
        StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
    bool isEventTooLate  = eventTimeNs < mCurrentBucketStartTimeNs;
    if (!isEventTooLate) {
        if (mCondition == ConditionState::kUnknown) {
            // If the condition was unknown, we mark the bucket as invalid since the bucket will
            // contain partial data. For instance, the condition change might happen close to the
            // end of the bucket and we might miss lots of data.
            //
            // We still want to pull to set the base.
            invalidateCurrentBucket();
        return;
        }

    flushIfNeededLocked(eventTimeNs);

    if (mCondition != ConditionState::kUnknown) {
        // Pull on condition changes.
        bool conditionChanged = mCondition != condition;
        ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
        bool conditionChanged =
                (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
                || (mCondition == ConditionState::kFalse && newCondition == ConditionState::kTrue);
        // We do not need to pull when we go from unknown to false.
        if (mIsPulled && conditionChanged) {
            pullAndMatchEventsLocked(eventTimeNs);
        //
        // We also pull if the condition was already true in order to be able to flush the bucket at
        // the end if needed.
        //
        // onConditionChangedLocked might happen on bucket boundaries if this is called before
        // #onDataPulled.
        if (mIsPulled && (conditionChanged || condition)) {
            pullAndMatchEventsLocked(eventTimeNs, newCondition);
        }

        // when condition change from true to false, clear diff base but don't
        // When condition change from true to false, clear diff base but don't
        // reset other counters as we may accumulate more value in the bucket.
        if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) {
        if (mUseDiff && mCondition == ConditionState::kTrue
                && newCondition == ConditionState::kFalse) {
            resetBase();
        }
        mCondition = newCondition;

    } else {
        // If the condition was unknown, we mark the bucket as invalid since the bucket will contain
        // partial data. For instance, the condition change might happen close to the end of the
        // bucket and we might miss lots of data.
        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
             (long long)mCurrentBucketStartTimeNs);
        StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
        invalidateCurrentBucket();
        // Something weird happened. If we received another event if the future, the condition might
        // be wrong.
        mCondition = ConditionState::kUnknown;
    }
    mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;

    // This part should alway be called.
    flushIfNeededLocked(eventTimeNs);
}

void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition) {
    vector<std::shared_ptr<LogEvent>> allData;
    if (!mPullerManager->Pull(mPullTagId, &allData)) {
        ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
        ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
        invalidateCurrentBucket();
        return;
    }

    accumulateEvents(allData, timestampNs, timestampNs);
    accumulateEvents(allData, timestampNs, timestampNs, condition);
}

int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
    return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
}

// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely
// to be delayed. Other events like condition changes or app upgrade which are not based on
// AlarmManager might have arrived earlier and close the bucket.
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
                                       bool pullSuccess, int64_t originalPullTimeNs) {
    std::lock_guard<std::mutex> lock(mMutex);
        if (mCondition == ConditionState::kTrue) {
        if (!pullSuccess) {
            // If the pull failed, we won't be able to compute a diff.
            if (!pullSuccess) {
                invalidateCurrentBucket();
            return;
        }

            } else {
                bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
                if (isEventLate) {
                    // If the event is late, we are in the middle of a bucket. Just
                    // process the data without trying to snap the data to the nearest bucket.
                    accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition);
                } else {
                    // For scheduled pulled data, the effective event time is snap to the nearest
                    // bucket end. In the case of waking up from a deep sleep state, we will
        // attribute to the previous bucket end. If the sleep was long but not very long, we
        // will be in the immediate next bucket. Previous bucket may get a larger number as
        // we pull at a later time than real bucket end.
        // If the sleep was very long, we skip more than one bucket before sleep. In this case,
        // if the diff base will be cleared and this new data will serve as new diff base.
                    // attribute to the previous bucket end. If the sleep was long but not very
                    // long, we will be in the immediate next bucket. Previous bucket may get a
                    // larger number as we pull at a later time than real bucket end.
                    //
                    // If the sleep was very long, we skip more than one bucket before sleep. In
                    // this case, if the diff base will be cleared and this new data will serve as
                    // new diff base.
                    int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
                    StatsdStats::getInstance().noteBucketBoundaryDelayNs(
                            mMetricId, originalPullTimeNs - bucketEndTime);
        accumulateEvents(allData, originalPullTimeNs, bucketEndTime);
                    accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition);
                }
            }
        }

    // We can probably flush the bucket. Since we used bucketEndTime when calling
    // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
    flushIfNeededLocked(originalPullTimeNs);

    } else {
        VLOG("No need to commit data on condition false.");
    }
}

void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
                                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) {
                                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
                                           ConditionState condition) {
    bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs;
    if (isEventLate) {
        VLOG("Skip bucket end pull due to late arrival: %lld vs %lld",
@@ -587,7 +620,10 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
    }
    mMatchedMetricDimensionKeys.insert(eventKey);

    if (!mIsPulled) {
        // We cannot flush without doing a pull first.
        flushIfNeededLocked(eventTimeNs);
    }

    // For pulled data, we already check condition when we decide to pull or
    // in onDataPulled. So take all of them.
@@ -722,26 +758,26 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
    }
}

// For pulled metrics, we always need to make sure we do a pull before flushing the bucket
// if mCondition is true!
void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
    int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();

    if (eventTimeNs < currentBucketEndTimeNs) {
        VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
             (long long)(currentBucketEndTimeNs));
        return;
    }

    int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
    int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
    int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
    flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs);
}

    mCurrentBucketNum += numBucketsForward;
    if (numBucketsForward > 1) {
        VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
        StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
        // take base again in future good bucket.
        resetBase();
int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const {
    int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
    if (eventTimeNs < currentBucketEndTimeNs) {
        return 0;
    }
    return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
}

void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
@@ -750,6 +786,16 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
        StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
    }

    int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
    mCurrentBucketNum += numBucketsForward;
    if (numBucketsForward > 1) {
        VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
        StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
        // Something went wrong. Maybe the device was sleeping for a long time. It is better
        // to mark the current bucket as invalid. The last pull might have been successful through.
        invalidateCurrentBucketWithoutResetBase();
    }

    VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
         (int)mCurrentSlicedBucket.size());
    int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
@@ -773,12 +819,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
    if (!mCurrentBucketIsInvalid) {
        appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
    }
    StatsdStats::getInstance().noteBucketCount(mMetricId);
    initCurrentSlicedBucket();
    mCurrentBucketIsInvalid = false;
    mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
         (long long)mCurrentBucketStartTimeNs);
    initCurrentSlicedBucket(nextBucketStartTimeNs);
}

ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
@@ -805,7 +846,9 @@ ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
    return bucket;
}

void ValueMetricProducer::initCurrentSlicedBucket() {
void ValueMetricProducer::initCurrentSlicedBucket(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) {
@@ -823,6 +866,16 @@ void ValueMetricProducer::initCurrentSlicedBucket() {
            it++;
        }
    }

    mCurrentBucketIsInvalid = false;
    // If we do not have a global base when the condition is true,
    // we will have incomplete bucket for the next bucket.
    if (mUseDiff && !mHasGlobalBase && mCondition) {
        mCurrentBucketIsInvalid = false;
    }
    mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
         (long long)mCurrentBucketStartTimeNs);
}

void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
+56 −36
Original line number Diff line number Diff line
@@ -39,6 +39,13 @@ struct ValueBucket {
    std::vector<Value> values;
};


// Aggregates values within buckets.
//
// There are different events that might complete a bucket
// - a condition change
// - an app upgrade
// - an alarm set to the end of the bucket
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
@@ -61,9 +68,8 @@ public:
        if (!mSplitBucketForAppUpgrade) {
            return;
        }
        flushIfNeededLocked(eventTimeNs - 1);
        if (mIsPulled && mCondition) {
            pullAndMatchEventsLocked(eventTimeNs - 1);
            pullAndMatchEventsLocked(eventTimeNs, mCondition);
        }
        flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
    };
@@ -94,9 +100,12 @@ private:

    void dumpStatesLocked(FILE* out, bool verbose) const override;

    // Util function to flush the old packet.
    // For pulled metrics, this method should only be called if a pull has be done. Else we will
    // not have complete data for the bucket.
    void flushIfNeededLocked(const int64_t& eventTime) override;

    // For pulled metrics, this method should only be called if a pulled have be done. Else we will
    // not have complete data for the bucket.
    void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                  const int64_t& nextBucketStartTimeNs) override;

@@ -105,8 +114,12 @@ private:
    // Calculate previous bucket end time based on current time.
    int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs);

    // Calculate how many buckets are present between the current bucket and eventTimeNs.
    int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const;

    // Mark the data as invalid.
    void invalidateCurrentBucket();
    void invalidateCurrentBucketWithoutResetBase();

    const int mWhatMatcherIndex;

@@ -163,14 +176,15 @@ private:

    bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);

    void pullAndMatchEventsLocked(const int64_t timestampNs);
    void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition);

    void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, 
                          int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
                          int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
                          ConditionState condition);

    ValueBucket buildPartialBucket(int64_t bucketEndTime,
                                   const std::vector<Interval>& intervals);
    void initCurrentSlicedBucket();
    void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
    void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs);

    // Reset diff base and mHasGlobalBase
@@ -214,48 +228,54 @@ private:

    const bool mSplitBucketForAppUpgrade;

    FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
    FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet);
    FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
    FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged);
    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary);
    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged);
    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
    FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
    FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff);
    FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff);
    FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
    FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
    FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
    FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
    FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
    FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
    FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
    FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
    FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
    FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
    FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed);
    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed);
    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
    FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged);
    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary);
    FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
    FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid);
    friend class ValueMetricProducerTestHelper;
};

+281 −79

File changed.

Preview size limit exceeded, changes collapsed.