1 /*
2  *  Copyright 2019 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "rtc_base/numerics/event_based_exponential_moving_average.h"
12 
13 #include <cmath>
14 
15 #include "test/gtest.h"
16 
17 namespace {
18 
19 constexpr int kHalfTime = 500;
20 constexpr double kError = 0.1;
21 
22 }  // namespace
23 
24 namespace rtc {
25 
TEST(EventBasedExponentialMovingAverageTest,NoValue)26 TEST(EventBasedExponentialMovingAverageTest, NoValue) {
27   EventBasedExponentialMovingAverage average(kHalfTime);
28 
29   EXPECT_TRUE(std::isnan(average.GetAverage()));
30   EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
31   EXPECT_EQ(std::numeric_limits<double>::infinity(),
32             average.GetConfidenceInterval());
33 }
34 
TEST(EventBasedExponentialMovingAverageTest,FirstValue)35 TEST(EventBasedExponentialMovingAverageTest, FirstValue) {
36   EventBasedExponentialMovingAverage average(kHalfTime);
37 
38   int64_t time = 23;
39   constexpr int value = 1000;
40   average.AddSample(time, value);
41   EXPECT_NEAR(value, average.GetAverage(), kError);
42   EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
43   EXPECT_EQ(std::numeric_limits<double>::infinity(),
44             average.GetConfidenceInterval());
45 }
46 
TEST(EventBasedExponentialMovingAverageTest,Half)47 TEST(EventBasedExponentialMovingAverageTest, Half) {
48   EventBasedExponentialMovingAverage average(kHalfTime);
49 
50   int64_t time = 23;
51   constexpr int value = 1000;
52   average.AddSample(time, value);
53   average.AddSample(time + kHalfTime, 0);
54   EXPECT_NEAR(666.7, average.GetAverage(), kError);
55   EXPECT_NEAR(1000000, average.GetVariance(), kError);
56   EXPECT_NEAR(1460.9, average.GetConfidenceInterval(), kError);
57 }
58 
TEST(EventBasedExponentialMovingAverageTest,Same)59 TEST(EventBasedExponentialMovingAverageTest, Same) {
60   EventBasedExponentialMovingAverage average(kHalfTime);
61 
62   int64_t time = 23;
63   constexpr int value = 1000;
64   average.AddSample(time, value);
65   average.AddSample(time + kHalfTime, value);
66   EXPECT_NEAR(value, average.GetAverage(), kError);
67   EXPECT_NEAR(0, average.GetVariance(), kError);
68   EXPECT_NEAR(0, average.GetConfidenceInterval(), kError);
69 }
70 
TEST(EventBasedExponentialMovingAverageTest,Almost100)71 TEST(EventBasedExponentialMovingAverageTest, Almost100) {
72   EventBasedExponentialMovingAverage average(kHalfTime);
73 
74   int64_t time = 23;
75   constexpr int value = 100;
76   average.AddSample(time + 0 * kHalfTime, value - 10);
77   average.AddSample(time + 1 * kHalfTime, value + 10);
78   average.AddSample(time + 2 * kHalfTime, value - 15);
79   average.AddSample(time + 3 * kHalfTime, value + 15);
80   EXPECT_NEAR(100.2, average.GetAverage(), kError);
81   EXPECT_NEAR(372.6, average.GetVariance(), kError);
82   EXPECT_NEAR(19.7, average.GetConfidenceInterval(), kError);  // 100 +/- 20
83 
84   average.AddSample(time + 4 * kHalfTime, value);
85   average.AddSample(time + 5 * kHalfTime, value);
86   average.AddSample(time + 6 * kHalfTime, value);
87   average.AddSample(time + 7 * kHalfTime, value);
88   EXPECT_NEAR(100.0, average.GetAverage(), kError);
89   EXPECT_NEAR(73.6, average.GetVariance(), kError);
90   EXPECT_NEAR(7.6, average.GetConfidenceInterval(), kError);  // 100 +/- 7
91 }
92 
93 // Test that getting a value at X and another at X+1
94 // is almost the same as getting another at X and a value at X+1.
TEST(EventBasedExponentialMovingAverageTest,AlmostSameTime)95 TEST(EventBasedExponentialMovingAverageTest, AlmostSameTime) {
96   int64_t time = 23;
97   constexpr int value = 100;
98 
99   {
100     EventBasedExponentialMovingAverage average(kHalfTime);
101     average.AddSample(time + 0, value);
102     average.AddSample(time + 1, 0);
103     EXPECT_NEAR(50, average.GetAverage(), kError);
104     EXPECT_NEAR(10000, average.GetVariance(), kError);
105     EXPECT_NEAR(138.6, average.GetConfidenceInterval(),
106                 kError);  // 50 +/- 138.6
107   }
108 
109   {
110     EventBasedExponentialMovingAverage average(kHalfTime);
111     average.AddSample(time + 0, 0);
112     average.AddSample(time + 1, 100);
113     EXPECT_NEAR(50, average.GetAverage(), kError);
114     EXPECT_NEAR(10000, average.GetVariance(), kError);
115     EXPECT_NEAR(138.6, average.GetConfidenceInterval(),
116                 kError);  // 50 +/- 138.6
117   }
118 }
119 
120 // This test shows behavior of estimator with a half_time of 100.
121 // It is unclear if these set of observations are representative
122 // of any real world scenarios.
TEST(EventBasedExponentialMovingAverageTest,NonUniformSamplesHalftime100)123 TEST(EventBasedExponentialMovingAverageTest, NonUniformSamplesHalftime100) {
124   int64_t time = 23;
125   constexpr int value = 100;
126 
127   {
128     // The observations at 100 and 101, are significantly close in
129     // time that the estimator returns approx. the average.
130     EventBasedExponentialMovingAverage average(100);
131     average.AddSample(time + 0, value);
132     average.AddSample(time + 100, value);
133     average.AddSample(time + 101, 0);
134     EXPECT_NEAR(50.2, average.GetAverage(), kError);
135     EXPECT_NEAR(86.2, average.GetConfidenceInterval(), kError);  // 50 +/- 86
136   }
137 
138   {
139     EventBasedExponentialMovingAverage average(100);
140     average.AddSample(time + 0, value);
141     average.AddSample(time + 1, value);
142     average.AddSample(time + 100, 0);
143     EXPECT_NEAR(66.5, average.GetAverage(), kError);
144     EXPECT_NEAR(65.4, average.GetConfidenceInterval(), kError);  // 66 +/- 65
145   }
146 
147   {
148     EventBasedExponentialMovingAverage average(100);
149     for (int i = 0; i < 10; i++) {
150       average.AddSample(time + i, value);
151     }
152     average.AddSample(time + 100, 0);
153     EXPECT_NEAR(65.3, average.GetAverage(), kError);
154     EXPECT_NEAR(59.1, average.GetConfidenceInterval(), kError);  // 55 +/- 59
155   }
156 
157   {
158     EventBasedExponentialMovingAverage average(100);
159     average.AddSample(time + 0, 100);
160     for (int i = 90; i <= 100; i++) {
161       average.AddSample(time + i, 0);
162     }
163     EXPECT_NEAR(0.05, average.GetAverage(), kError);
164     EXPECT_NEAR(4.9, average.GetConfidenceInterval(), kError);  // 0 +/- 5
165   }
166 }
167 
TEST(EventBasedExponentialMovingAverageTest,Reset)168 TEST(EventBasedExponentialMovingAverageTest, Reset) {
169   constexpr int64_t time = 23;
170   constexpr int value = 100;
171 
172   EventBasedExponentialMovingAverage average(100);
173   EXPECT_TRUE(std::isnan(average.GetAverage()));
174   EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
175   EXPECT_EQ(std::numeric_limits<double>::infinity(),
176             average.GetConfidenceInterval());
177 
178   average.AddSample(time + 0, value);
179   average.AddSample(time + 100, value);
180   average.AddSample(time + 101, 0);
181   EXPECT_FALSE(std::isnan(average.GetAverage()));
182 
183   average.Reset();
184   EXPECT_TRUE(std::isnan(average.GetAverage()));
185   EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
186   EXPECT_EQ(std::numeric_limits<double>::infinity(),
187             average.GetConfidenceInterval());
188 }
189 
190 // Test that SetHalfTime modifies behavior and resets average.
TEST(EventBasedExponentialMovingAverageTest,SetHalfTime)191 TEST(EventBasedExponentialMovingAverageTest, SetHalfTime) {
192   constexpr int64_t time = 23;
193   constexpr int value = 100;
194 
195   EventBasedExponentialMovingAverage average(100);
196 
197   average.AddSample(time + 0, value);
198   average.AddSample(time + 100, 0);
199   EXPECT_NEAR(66.7, average.GetAverage(), kError);
200 
201   average.SetHalfTime(1000);
202   EXPECT_TRUE(std::isnan(average.GetAverage()));
203   EXPECT_EQ(std::numeric_limits<double>::infinity(), average.GetVariance());
204   EXPECT_EQ(std::numeric_limits<double>::infinity(),
205             average.GetConfidenceInterval());
206 
207   average.AddSample(time + 0, value);
208   average.AddSample(time + 100, 0);
209   EXPECT_NEAR(51.7, average.GetAverage(), kError);
210 }
211 
TEST(EventBasedExponentialMovingAverageTest,SimultaneousSamples)212 TEST(EventBasedExponentialMovingAverageTest, SimultaneousSamples) {
213   constexpr int64_t time = 23;
214   constexpr int value = 100;
215 
216   EventBasedExponentialMovingAverage average(100);
217 
218   average.AddSample(time, value);
219   // This should really NOT be supported,
220   // i.e 2 samples with same timestamp.
221   // But there are tests running with simulated clock
222   // that produce this.
223   // TODO(webrtc:11140) : Fix those tests and remove this!
224   average.AddSample(time, value);
225 }
226 
227 }  // namespace rtc
228