/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include #include "Math/RunningAverage.hpp" #include "Math/Math.hpp" using namespace OpenVulkano::Math; TEST_CASE("RunningAverage Basic Functionality - Integer Type", "[RunningAverage]") { SECTION("Dynamic Size Constructor with Default Value") { RunningAverage avg(5); REQUIRE(avg.Get() == 0); } SECTION("Dynamic Size Constructor with Custom Default") { RunningAverage avg(5, 10); REQUIRE(avg.Get() == 10); } SECTION("Static Size Constructor with Default Value") { RunningAverage avg; REQUIRE(avg.Get() == 0); } SECTION("Static Size Constructor with Custom Default") { RunningAverage avg(10); REQUIRE(avg.Get() == 10); } SECTION("Push Single Value") { RunningAverage avg(5, 0); avg.Push(5); REQUIRE(avg.Get() == 1); // (0+0+0+0+5)/5 = 1 } SECTION("Push Multiple Values") { RunningAverage avg(4, 0); avg.Push(4); avg.Push(8); avg.Push(12); avg.Push(16); REQUIRE(avg.Get() == 10); // (4+8+12+16)/4 = 10 } SECTION("Push with Overwrite") { RunningAverage avg(3, 0); avg.Push(3); avg.Push(6); avg.Push(9); REQUIRE(avg.Get() == 6); // (3+6+9)/3 = 6 avg.Push(12); // Overwrite 3 REQUIRE(avg.Get() == 9); // (6+9+12)/3 = 9 } } TEST_CASE("RunningAverage with Floating Point Types", "[RunningAverage]") { SECTION("Basic Floating Point Operation") { RunningAverage avg(3, 0.0); avg.Push(1.5); avg.Push(2.5); avg.Push(3.5); REQUIRE(avg.Get() == Catch::Approx(2.5)); // (1.5+2.5+3.5)/3 = 2.5 } SECTION("Recalculation with Floating Point") { // Set recalculation to happen after 3 operations RunningAverage::max(), true, 3> avg(3, 0.0); avg.Push(1.0); avg.Push(2.0); avg.Push(3.0); // This should trigger recalculation REQUIRE(avg.Get() == Catch::Approx(2.0)); // (1.0+2.0+3.0)/3 = 2.0 // Push another value to check that recalculation didn't affect the result avg.Push(4.0); REQUIRE(avg.Get() == Catch::Approx(3.0)); // (2.0+3.0+4.0)/3 = 3.0 } SECTION("Manual Recalculation") { RunningAverage avg(3, 0.0); avg.Push(1.5); avg.Push(2.5); avg.Push(3.5); // Force recalculation and verify it doesn't change the result avg.Recalculate(); REQUIRE(avg.Get() == Catch::Approx(2.5)); } } TEST_CASE("RunningAverage Edge Cases", "[RunningAverage]") { SECTION("Large Values") { RunningAverage avg(3, 0); avg.Push(1000000); avg.Push(2000000); avg.Push(3000000); REQUIRE(avg.Get() == 2000000); } SECTION("Negative Values") { RunningAverage avg(3, 0); avg.Push(-10); avg.Push(-20); avg.Push(-30); REQUIRE(avg.Get() == -20); } SECTION("Mixed Sign Values") { RunningAverage avg(4, 0); avg.Push(-10); avg.Push(10); avg.Push(-10); avg.Push(10); REQUIRE(avg.Get() == 0); } } TEST_CASE("Custom Types with RunningAverage", "[RunningAverage]") { SECTION("Vector Type Average") { RunningAverage avg(3, Vector2f(0, 0)); avg.Push(Vector2f(1, 2)); avg.Push(Vector2f(3, 4)); avg.Push(Vector2f(5, 6)); Vector2f result = avg.Get(); REQUIRE(result.x == Catch::Approx(3.0f)); REQUIRE(result.y == Catch::Approx(4.0f)); } } // This test specifically checks the correctness of recalculation for floating point TEST_CASE("Precision with Floating Point Types", "[RunningAverage]") { SECTION("Accumulation Error Correction") { // Create a running average that would normally suffer from floating point errors // Force recalculation after 20 operations RunningAverage::max(), true, 20> avg(5, 0.0); // Push values that would cause floating point rounding errors for (int i = 0; i < 100; i++) { avg.Push(0.1); // 0.1 cannot be represented exactly in binary floating point } // Check that the result is still accurate due to periodic recalculation REQUIRE(avg.Get() == Catch::Approx(0.1).epsilon(0.0001)); } } TEST_CASE("Class size", "[RunningAverage]") { REQUIRE(sizeof(RunningAverage) == 40); REQUIRE(sizeof(RunningAverage) == 48); REQUIRE(sizeof(RunningAverage) == 48); REQUIRE(sizeof(RunningAverage) == 48); REQUIRE(sizeof(RunningAverage) == 40); }