// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/moving_window.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { constexpr int kTestValues[] = { 33, 1, 2, 7, 5, 2, 4, 45, 1000, 1, 100, 2, 200, 2, 2, 2, 300, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 1, 4, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1, 16, 1, 2, 1}; } // namespace class MovingMaxTest : public testing::TestWithParam {}; INSTANTIATE_TEST_SUITE_P(All, MovingMaxTest, testing::ValuesIn({1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 17u, 20u, 100u})); TEST_P(MovingMaxTest, BlanketTest) { const size_t window_size = GetParam(); MovingMax window(window_size); for (size_t i = 0; i < std::size(kTestValues); ++i) { window.AddSample(kTestValues[i]); int slow_max = kTestValues[i]; for (size_t j = 1; j < window_size && j <= i; ++j) { slow_max = std::max(slow_max, kTestValues[i - j]); } EXPECT_EQ(window.Max(), slow_max); } } TEST(MovingMax, SingleElementWindow) { MovingMax window(1u); window.AddSample(100); EXPECT_EQ(window.Max(), 100); window.AddSample(1000); EXPECT_EQ(window.Max(), 1000); window.AddSample(1); EXPECT_EQ(window.Max(), 1); window.AddSample(3); EXPECT_EQ(window.Max(), 3); window.AddSample(4); EXPECT_EQ(window.Max(), 4); } TEST(MovingMax, VeryLargeWindow) { MovingMax window(100u); window.AddSample(100); EXPECT_EQ(window.Max(), 100); window.AddSample(1000); EXPECT_EQ(window.Max(), 1000); window.AddSample(1); EXPECT_EQ(window.Max(), 1000); window.AddSample(3); EXPECT_EQ(window.Max(), 1000); window.AddSample(4); EXPECT_EQ(window.Max(), 1000); } TEST(MovingMax, Counts) { MovingMax window(3u); EXPECT_EQ(window.Count(), 0u); window.AddSample(100); EXPECT_EQ(window.Count(), 1u); window.AddSample(1000); EXPECT_EQ(window.Count(), 2u); window.AddSample(1); EXPECT_EQ(window.Count(), 3u); window.AddSample(3); EXPECT_EQ(window.Count(), 4u); window.AddSample(4); EXPECT_EQ(window.Count(), 5u); } TEST(MovingAverage, Unrounded) { MovingAverage window(4u); window.AddSample(1); EXPECT_EQ(window.Mean(), 1.0); window.AddSample(2); EXPECT_EQ(window.Mean(), 1.5); window.AddSample(3); EXPECT_EQ(window.Mean(), 2.0); window.AddSample(4); EXPECT_EQ(window.Mean(), 2.5); window.AddSample(101); EXPECT_EQ(window.Mean(), 27.5); } class MovingMinTest : public testing::TestWithParam {}; INSTANTIATE_TEST_SUITE_P(All, MovingMinTest, testing::ValuesIn({1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 17u, 20u, 100u})); TEST_P(MovingMinTest, BlanketTest) { const size_t window_size = GetParam(); MovingMin window(window_size); for (int repeats = 0; repeats < 2; ++repeats) { for (size_t i = 0; i < std::size(kTestValues); ++i) { window.AddSample(kTestValues[i]); int slow_min = kTestValues[i]; for (size_t j = 1; j < window_size && j <= i; ++j) { slow_min = std::min(slow_min, kTestValues[i - j]); } EXPECT_EQ(window.Min(), slow_min); } window.Reset(); } } class MovingAverageTest : public testing::TestWithParam {}; INSTANTIATE_TEST_SUITE_P(All, MovingAverageTest, testing::ValuesIn({1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 17u, 20u, 100u})); TEST_P(MovingAverageTest, BlanketTest) { const size_t window_size = GetParam(); MovingAverage window(window_size); for (int repeats = 0; repeats < 2; ++repeats) { for (size_t i = 0; i < std::size(kTestValues); ++i) { window.AddSample(kTestValues[i]); int slow_mean = 0; for (size_t j = 0; j < window_size && j <= i; ++j) { slow_mean += kTestValues[i - j]; } slow_mean /= std::min(window_size, i + 1); EXPECT_EQ(window.Mean(), slow_mean); } window.Reset(); } } class MovingDeviationTest : public testing::TestWithParam {}; INSTANTIATE_TEST_SUITE_P(All, MovingDeviationTest, testing::ValuesIn({1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 17u, 20u, 100u})); TEST_P(MovingDeviationTest, BlanketTest) { const size_t window_size = GetParam(); MovingAverageDeviation window(window_size); for (int repeats = 0; repeats < 2; ++repeats) { for (size_t i = 0; i < std::size(kTestValues); ++i) { window.AddSample(kTestValues[i]); double slow_deviation = 0; double mean = window.Mean(); for (size_t j = 0; j < window_size && j <= i; ++j) { slow_deviation += (kTestValues[i - j] - mean) * (kTestValues[i - j] - mean); } slow_deviation /= std::min(window_size, i + 1); slow_deviation = sqrt(slow_deviation); double fast_deviation = window.Deviation(); EXPECT_TRUE(std::abs(fast_deviation - slow_deviation) < 1e-9); } window.Reset(); } } TEST(MovingWindowTest, Iteration) { const size_t kWindowSize = 10; MovingWindow window(kWindowSize); for (int repeats = 0; repeats < 2; ++repeats) { for (size_t i = 0; i < std::size(kTestValues); ++i) { window.AddSample(kTestValues[i]); size_t j = 0; const size_t in_window = std::min(i + 1, kWindowSize); for (int value : window) { ASSERT_LT(j, in_window); EXPECT_EQ(value, kTestValues[i + j + 1 - in_window]); ++j; } EXPECT_EQ(j, in_window); } window.Reset(); } } TEST(MovingMeanDeviation, WorksWithTimeDelta) { MovingAverageDeviation window(2); window.AddSample(base::Milliseconds(400)); window.AddSample(base::Milliseconds(200)); EXPECT_EQ(window.Mean(), base::Milliseconds(300)); EXPECT_EQ(window.Deviation(), base::Milliseconds(100)); window.AddSample(base::Seconds(40)); window.AddSample(base::Seconds(20)); EXPECT_EQ(window.Mean(), base::Seconds(30)); EXPECT_EQ(window.Deviation(), base::Seconds(10)); } } // namespace base