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

Commit 4eeba11b authored by Dmitri Plotnikov's avatar Dmitri Plotnikov Committed by Android (Google) Code Review
Browse files

Merge "Add reset() and setEnabled() to MultiStateCounter"

parents 7d0cc9c2 99d7c712
Loading
Loading
Loading
Loading
+63 −24
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ class MultiStateCounter {
    T lastValue;
    time_t lastUpdateTimestamp;
    T deltaValue;
    bool isEnabled;

    struct State {
        time_t timeInStateSinceUpdate;
@@ -55,12 +56,16 @@ public:

    virtual ~MultiStateCounter();

    void setEnabled(bool enabled, time_t timestamp);

    void setState(state_t state, time_t timestamp);

    void setValue(state_t state, const T& value);

    void updateValue(const T& value, time_t timestamp);

    void reset();

    uint16_t getStateCount();

    const T& getCount(state_t state);
@@ -96,7 +101,8 @@ MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue
        emptyValue(emptyValue),
        lastValue(emptyValue),
        lastUpdateTimestamp(-1),
        deltaValue(emptyValue) {
        deltaValue(emptyValue),
        isEnabled(true) {
    states = new State[stateCount];
    for (int i = 0; i < stateCount; i++) {
        states[i].timeInStateSinceUpdate = 0;
@@ -110,8 +116,27 @@ MultiStateCounter<T>::~MultiStateCounter() {
};

template <class T>
void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
    if (enabled == isEnabled) {
        return;
    }

    if (!enabled) {
        // Confirm the current state for the side-effect of updating the time-in-state
        // counter for the current state.
        setState(currentState, timestamp);
    }

    isEnabled = enabled;

    if (lastStateChangeTimestamp >= 0) {
        lastStateChangeTimestamp = timestamp;
    }
}

template <class T>
void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
    if (isEnabled && lastStateChangeTimestamp >= 0) {
        if (timestamp >= lastStateChangeTimestamp) {
            states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
        } else {
@@ -137,6 +162,9 @@ void MultiStateCounter<T>::setValue(state_t state, const T& value) {

template <class T>
void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
    // If the counter is disabled, we ignore the update, except when the counter got disabled after
    // the previous update, in which case we still need to pick up the residual delta.
    if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
        // Confirm the current state for the side-effect of updating the time-in-state
        // counter for the current state.
        setState(currentState, timestamp);
@@ -160,14 +188,25 @@ void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
                    ALOGE("%s", str.str().c_str());
                }
            } else if (timestamp < lastUpdateTimestamp) {
            ALOGE("updateValue is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
                ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
                      (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
            }
        }
    }
    lastValue = value;
    lastUpdateTimestamp = timestamp;
}

template <class T>
void MultiStateCounter<T>::reset() {
    lastStateChangeTimestamp = -1;
    lastUpdateTimestamp = -1;
    for (int i = 0; i < stateCount; i++) {
        states[i].timeInStateSinceUpdate = 0;
        states[i].counter = emptyValue;
    }
}

template <class T>
uint16_t MultiStateCounter<T>::getStateCount() {
    return stateCount;
+77 −0
Original line number Diff line number Diff line
@@ -71,6 +71,83 @@ TEST_F(MultiStateCounterTest, stateChange) {
    EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
}

TEST_F(MultiStateCounterTest, setEnabled) {
    DoubleMultiStateCounter testCounter(3, 0);
    testCounter.updateValue(0, 0);
    testCounter.setState(1, 0);
    testCounter.setEnabled(false, 1000);
    testCounter.setState(2, 2000);
    testCounter.updateValue(6.0, 3000);

    // In state 1: accumulated 1000 before disabled, that's 6.0 * 1000/3000 = 2.0
    // In state 2: 0, since it is still disabled
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));

    // Should have no effect since the counter is disabled
    testCounter.setState(0, 3500);

    // Should have no effect since the counter is disabled
    testCounter.updateValue(10.0, 4000);

    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));

    testCounter.setState(2, 4500);

    // Enable the counter to partially accumulate deltas for the current state, 2
    testCounter.setEnabled(true, 5000);
    testCounter.setEnabled(false, 6000);
    testCounter.setEnabled(true, 7000);
    testCounter.updateValue(20.0, 8000);

    // The delta is 10.0 over 5000-3000=2000.
    // Counter has been enabled in state 2 for (6000-5000)+(8000-7000) = 2000,
    // so its share is (20.0-10.0) * 2000/(8000-4000) = 5.0
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
    EXPECT_DOUBLE_EQ(5.0, testCounter.getCount(2));

    testCounter.reset();
    testCounter.setState(0, 0);
    testCounter.updateValue(0, 0);
    testCounter.setState(1, 2000);
    testCounter.setEnabled(false, 3000);
    testCounter.updateValue(200, 5000);

    // 200 over 5000 = 40 per second
    // Counter was in state 0 from 0 to 2000, so 2 sec, so the count should be 40 * 2 = 80
    // It stayed in state 1 from 2000 to 3000, at which point the counter was disabled,
    // so the count for state 1 should be 40 * 1 = 40.
    // The remaining 2 seconds from 3000 to 5000 don't count because the counter was disabled.
    EXPECT_DOUBLE_EQ(80.0, testCounter.getCount(0));
    EXPECT_DOUBLE_EQ(40.0, testCounter.getCount(1));
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
}

TEST_F(MultiStateCounterTest, reset) {
    DoubleMultiStateCounter testCounter(3, 0);
    testCounter.updateValue(0, 0);
    testCounter.setState(1, 0);
    testCounter.updateValue(2.72, 3000);

    testCounter.reset();

    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(1));
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));

    // Assert that we can still continue accumulating after a reset
    testCounter.updateValue(0, 4000);
    testCounter.updateValue(3.14, 5000);

    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
    EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
}

TEST_F(MultiStateCounterTest, timeAdjustment_setState) {
    DoubleMultiStateCounter testCounter(3, 0);
    testCounter.updateValue(0, 0);