Source code

Revision control

Copy as Markdown

Other Tools

/*
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "rtc_base/numerics/event_based_exponential_moving_average.h"
#include <cmath>
#include <cstdint>
#include <limits>
#include "test/gtest.h"
namespace {
constexpr int kHalfTime = 500;
constexpr double kError = 0.1;
} // namespace
namespace rtc {
TEST(EventBasedExponentialMovingAverageTest, NoValue) {
EventBasedExponentialMovingAverage average(kHalfTime);
EXPECT_TRUE(std::isnan(average.GetAverage()));
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
EXPECT_EQ(std::numeric_limits<double>::infinity(),
average.GetConfidenceInterval());
}
TEST(EventBasedExponentialMovingAverageTest, FirstValue) {
EventBasedExponentialMovingAverage average(kHalfTime);
int64_t time = 23;
constexpr int value = 1000;
average.AddSample(time, value);
EXPECT_NEAR(value, average.GetAverage(), kError);
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
EXPECT_EQ(std::numeric_limits<double>::infinity(),
average.GetConfidenceInterval());
}
TEST(EventBasedExponentialMovingAverageTest, Half) {
EventBasedExponentialMovingAverage average(kHalfTime);
int64_t time = 23;
constexpr int value = 1000;
average.AddSample(time, value);
average.AddSample(time + kHalfTime, 0);
EXPECT_NEAR(666.7, average.GetAverage(), kError);
EXPECT_NEAR(1000000, average.GetVariance(), kError);
EXPECT_NEAR(1460.9, average.GetConfidenceInterval(), kError);
}
TEST(EventBasedExponentialMovingAverageTest, Same) {
EventBasedExponentialMovingAverage average(kHalfTime);
int64_t time = 23;
constexpr int value = 1000;
average.AddSample(time, value);
average.AddSample(time + kHalfTime, value);
EXPECT_NEAR(value, average.GetAverage(), kError);
EXPECT_NEAR(0, average.GetVariance(), kError);
EXPECT_NEAR(0, average.GetConfidenceInterval(), kError);
}
TEST(EventBasedExponentialMovingAverageTest, Almost100) {
EventBasedExponentialMovingAverage average(kHalfTime);
int64_t time = 23;
constexpr int value = 100;
average.AddSample(time + 0 * kHalfTime, value - 10);
average.AddSample(time + 1 * kHalfTime, value + 10);
average.AddSample(time + 2 * kHalfTime, value - 15);
average.AddSample(time + 3 * kHalfTime, value + 15);
EXPECT_NEAR(100.2, average.GetAverage(), kError);
EXPECT_NEAR(372.6, average.GetVariance(), kError);
EXPECT_NEAR(19.7, average.GetConfidenceInterval(), kError); // 100 +/- 20
average.AddSample(time + 4 * kHalfTime, value);
average.AddSample(time + 5 * kHalfTime, value);
average.AddSample(time + 6 * kHalfTime, value);
average.AddSample(time + 7 * kHalfTime, value);
EXPECT_NEAR(100.0, average.GetAverage(), kError);
EXPECT_NEAR(73.6, average.GetVariance(), kError);
EXPECT_NEAR(7.6, average.GetConfidenceInterval(), kError); // 100 +/- 7
}
// Test that getting a value at X and another at X+1
// is almost the same as getting another at X and a value at X+1.
TEST(EventBasedExponentialMovingAverageTest, AlmostSameTime) {
int64_t time = 23;
constexpr int value = 100;
{
EventBasedExponentialMovingAverage average(kHalfTime);
average.AddSample(time + 0, value);
average.AddSample(time + 1, 0);
EXPECT_NEAR(50, average.GetAverage(), kError);
EXPECT_NEAR(10000, average.GetVariance(), kError);
EXPECT_NEAR(138.6, average.GetConfidenceInterval(),
kError); // 50 +/- 138.6
}
{
EventBasedExponentialMovingAverage average(kHalfTime);
average.AddSample(time + 0, 0);
average.AddSample(time + 1, 100);
EXPECT_NEAR(50, average.GetAverage(), kError);
EXPECT_NEAR(10000, average.GetVariance(), kError);
EXPECT_NEAR(138.6, average.GetConfidenceInterval(),
kError); // 50 +/- 138.6
}
}
// This test shows behavior of estimator with a half_time of 100.
// It is unclear if these set of observations are representative
// of any real world scenarios.
TEST(EventBasedExponentialMovingAverageTest, NonUniformSamplesHalftime100) {
int64_t time = 23;
constexpr int value = 100;
{
// The observations at 100 and 101, are significantly close in
// time that the estimator returns approx. the average.
EventBasedExponentialMovingAverage average(100);
average.AddSample(time + 0, value);
average.AddSample(time + 100, value);
average.AddSample(time + 101, 0);
EXPECT_NEAR(50.2, average.GetAverage(), kError);
EXPECT_NEAR(86.2, average.GetConfidenceInterval(), kError); // 50 +/- 86
}
{
EventBasedExponentialMovingAverage average(100);
average.AddSample(time + 0, value);
average.AddSample(time + 1, value);
average.AddSample(time + 100, 0);
EXPECT_NEAR(66.5, average.GetAverage(), kError);
EXPECT_NEAR(65.4, average.GetConfidenceInterval(), kError); // 66 +/- 65
}
{
EventBasedExponentialMovingAverage average(100);
for (int i = 0; i < 10; i++) {
average.AddSample(time + i, value);
}
average.AddSample(time + 100, 0);
EXPECT_NEAR(65.3, average.GetAverage(), kError);
EXPECT_NEAR(59.1, average.GetConfidenceInterval(), kError); // 55 +/- 59
}
{
EventBasedExponentialMovingAverage average(100);
average.AddSample(time + 0, 100);
for (int i = 90; i <= 100; i++) {
average.AddSample(time + i, 0);
}
EXPECT_NEAR(0.05, average.GetAverage(), kError);
EXPECT_NEAR(4.9, average.GetConfidenceInterval(), kError); // 0 +/- 5
}
}
TEST(EventBasedExponentialMovingAverageTest, Reset) {
constexpr int64_t time = 23;
constexpr int value = 100;
EventBasedExponentialMovingAverage average(100);
EXPECT_TRUE(std::isnan(average.GetAverage()));
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
EXPECT_EQ(std::numeric_limits<double>::infinity(),
average.GetConfidenceInterval());
average.AddSample(time + 0, value);
average.AddSample(time + 100, value);
average.AddSample(time + 101, 0);
EXPECT_FALSE(std::isnan(average.GetAverage()));
average.Reset();
EXPECT_TRUE(std::isnan(average.GetAverage()));
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
EXPECT_EQ(std::numeric_limits<double>::infinity(),
average.GetConfidenceInterval());
}
// Test that SetHalfTime modifies behavior and resets average.
TEST(EventBasedExponentialMovingAverageTest, SetHalfTime) {
constexpr int64_t time = 23;
constexpr int value = 100;
EventBasedExponentialMovingAverage average(100);
average.AddSample(time + 0, value);
average.AddSample(time + 100, 0);
EXPECT_NEAR(66.7, average.GetAverage(), kError);
average.SetHalfTime(1000);
EXPECT_TRUE(std::isnan(average.GetAverage()));
EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
EXPECT_EQ(std::numeric_limits<double>::infinity(),
average.GetConfidenceInterval());
average.AddSample(time + 0, value);
average.AddSample(time + 100, 0);
EXPECT_NEAR(51.7, average.GetAverage(), kError);
}
TEST(EventBasedExponentialMovingAverageTest, SimultaneousSamples) {
constexpr int64_t time = 23;
constexpr int value = 100;
EventBasedExponentialMovingAverage average(100);
average.AddSample(time, value);
// This should really NOT be supported,
// i.e 2 samples with same timestamp.
// But there are tests running with simulated clock
// that produce this.
// TODO(webrtc:11140) : Fix those tests and remove this!
average.AddSample(time, value);
}
} // namespace rtc