/* * Copyright (c) 2026 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 #include #include #include "absl/random/random.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "rtc_base/rate_tracker.h" #include "rtc_base/rate_tracker_ffi.rs.h" #include "test/gtest.h" namespace webrtc { namespace { struct RateTrackerUpdate { int64_t samples; TimeDelta delta; TimeDelta interval; }; void RustAndCppMatch(TimeDelta bucket, size_t bucket_count, Timestamp start_time, const std::vector& updates) { RateTracker cpp_tracker(bucket.ms(), bucket_count); rust::Box rust_tracker = create_rate_tracker(bucket, bucket_count); Timestamp current_time = start_time; for (const auto& update : updates) { current_time += update.delta; cpp_tracker.Update(update.samples, current_time); rust_tracker->update(update.samples, current_time); EXPECT_EQ(cpp_tracker.TotalSampleCount(), rust_tracker->total_sample_count()); EXPECT_NEAR(cpp_tracker.Rate(current_time), rust_tracker->rate(current_time), 1e-7); EXPECT_NEAR( cpp_tracker.ComputeRateForInterval(current_time, update.interval), rust_tracker->compute_rate_for_interval(current_time, update.interval), 1e-6); } } TEST(RateTrackerComparisonTest, RustVsCppFuzzTest) { // This should use FuzzTest instead, but for now we use rng. absl::BitGen gen; for (int i = 0; i < 100; ++i) { TimeDelta bucket = TimeDelta::Millis(absl::Uniform(gen, 1, 1000)); size_t bucket_count = absl::Uniform(gen, 1, 1000); Timestamp start_time = Timestamp::Millis(absl::Uniform(gen, 0, 1000000)); size_t num_updates = absl::Uniform(gen, 0, 100); std::vector updates; for (size_t j = 0; j < num_updates; ++j) { RateTrackerUpdate update; update.samples = absl::Uniform(gen, 0, 1000000); update.delta = TimeDelta::Millis(absl::Uniform(gen, 1, 1000)); update.interval = TimeDelta::Millis(absl::Uniform(gen, 1, 10000)); updates.push_back(update); } RustAndCppMatch(bucket, bucket_count, start_time, updates); } } TEST(RateTrackerComparisonTest, LargeSamples) { const TimeDelta kBucket = TimeDelta::Millis(100); const size_t kBucketCount = 10; RateTracker cpp_tracker(kBucket.ms(), kBucketCount); rust::Box rust_tracker = create_rate_tracker(kBucket, kBucketCount); // Use a large sample count that might cause overflow if not handled. // microseconds * samples: 100,000 * 1,000,000,000,000 = 10^17 (fits in i64, // max i64 is ~9.2 * 10^18) But let's go larger: 10^15 samples. 100,000 * // 10^15 = 10^20 (EXCEEDS i64!) const int64_t kLargeSamples = 1000000000000000LL; // 10^15 Timestamp current_time = Timestamp::Millis(1000); cpp_tracker.Update(kLargeSamples, current_time); rust_tracker->update(kLargeSamples, current_time); // Advance by half a bucket. current_time += kBucket / 2; // Rate should be (samples / 2) / (bucket / 2) * 1000 = samples * 1000 / // bucket available_interval is current_time - initialization_time = 50ms. // Wait, C++'s available_interval_milliseconds would be 50ms. // total_samples calculation: // start_bucket = current_bucket (0) // samples_to_count = samples * (100 - 50) + 50 / 100 = samples * 50 / 100 = // samples / 2. rate = (samples / 2 * 1000) / 50 = samples * 10. EXPECT_NEAR(cpp_tracker.Rate(current_time), rust_tracker->rate(current_time), 1e-6); } } // namespace } // namespace webrtc