// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/file_manager/speedometer.h"
#include <cmath>
#include <limits>
#include "base/test/scoped_mock_clock_override.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace file_manager {
namespace {
using base::Milliseconds;
using base::Seconds;
using testing::IsNan;
TEST(SpeedometerTest, RemainingTime) {
base::ScopedMockClockOverride clock;
Speedometer meter;
// Testing without setting the total bytes:
EXPECT_EQ(meter.GetSampleCount(), 0u);
EXPECT_TRUE(meter.GetRemainingTime().is_max());
// Sets total number of bytes.
meter.SetTotalBytes(2000);
EXPECT_EQ(meter.GetSampleCount(), 0u);
EXPECT_TRUE(meter.GetRemainingTime().is_max());
// 1st sample.
// 1st sample, but not enough to calculate the remaining time.
clock.Advance(Seconds(11));
EXPECT_TRUE(meter.Update(100));
EXPECT_EQ(meter.GetSampleCount(), 1u);
EXPECT_TRUE(meter.GetRemainingTime().is_max());
// Sample received less than 3 second after the previous one should be
// ignored.
clock.Advance(Milliseconds(2999));
EXPECT_FALSE(meter.Update(300));
EXPECT_EQ(meter.GetSampleCount(), 1u);
EXPECT_TRUE(meter.GetRemainingTime().is_max());
// 2nd sample, the remaining time can be computed.
clock.Advance(Milliseconds(1));
EXPECT_TRUE(meter.Update(300));
EXPECT_EQ(meter.GetSampleCount(), 2u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 25);
// 3rd sample. +3 seconds and still only processed 300 bytes.
clock.Advance(Seconds(3));
EXPECT_TRUE(meter.Update(300));
EXPECT_EQ(meter.GetSampleCount(), 3u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 50);
// 4th sample, +5 seconds and still only 300 bytes.
clock.Advance(Seconds(5));
EXPECT_TRUE(meter.Update(300));
EXPECT_EQ(meter.GetSampleCount(), 4u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 109);
// 5th sample, +3 seconds and now bumped from 300 to 600 bytes.
clock.Advance(Seconds(3));
EXPECT_TRUE(meter.Update(600));
EXPECT_EQ(meter.GetSampleCount(), 5u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 55);
// Elapsed time should impact the remaining time.
clock.Advance(Seconds(12));
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 43);
// GetRemainingTime() can return negative value.
clock.Advance(Seconds(60));
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), -16);
}
TEST(SpeedometerTest, Samples) {
base::ScopedMockClockOverride clock;
Speedometer meter;
constexpr size_t kMaxSamples = 20;
meter.SetTotalBytes(20000);
// Slow speed of 100 bytes per second.
int total_transferred = 0;
for (size_t i = 0; i < kMaxSamples; i++) {
EXPECT_EQ(meter.GetSampleCount(), i);
clock.Advance(Seconds(3));
total_transferred = i * 100;
EXPECT_TRUE(meter.Update(total_transferred));
}
EXPECT_EQ(meter.GetSampleCount(), kMaxSamples);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 543);
// +200 to make it compatible with the values in the unittest in the JS
// version.
const int initial_transferred_bytes = total_transferred + 200;
// Faster speed of 300 bytes per second.
for (size_t i = 0; i < kMaxSamples; i++) {
// Check buffer not expanded more than the specified length.
EXPECT_EQ(meter.GetSampleCount(), kMaxSamples);
clock.Advance(Seconds(3));
total_transferred = initial_transferred_bytes + (i * 300);
EXPECT_TRUE(meter.Update(total_transferred));
// Current speed should be seen as accelerating, thus the remaining time
// decreasing.
EXPECT_LT(meter.GetRemainingTime().InSeconds(), 543);
}
EXPECT_EQ(meter.GetSampleCount(), kMaxSamples);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 122);
// Stalling.
for (size_t i = 0; i < kMaxSamples; i++) {
// Check buffer not expanded more than the specified length.
EXPECT_EQ(meter.GetSampleCount(), kMaxSamples);
clock.Advance(Seconds(3));
EXPECT_TRUE(meter.Update(total_transferred));
}
// When all samples have the same value the remaining time goes to infinity,
// because the Linear Interpolation expects an inclination/slope, but with all
// values the same, it becomes a horizontal line, meaning that the bytes will
// never grow towards the total bytes.
EXPECT_TRUE(meter.GetRemainingTime().is_max());
}
TEST(SpeedometerTest, ProgressGoingBackwards) {
base::ScopedMockClockOverride clock;
Speedometer meter;
// Sets total number of bytes and a couple of samples.
meter.SetTotalBytes(2000);
EXPECT_TRUE(meter.Update(100));
clock.Advance(Seconds(5));
EXPECT_TRUE(meter.Update(300));
EXPECT_EQ(meter.GetSampleCount(), 2u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 42);
// Progress going backwards should clear the samples.
clock.Advance(Seconds(3));
EXPECT_TRUE(meter.Update(200));
EXPECT_EQ(meter.GetSampleCount(), 1u);
EXPECT_TRUE(meter.GetRemainingTime().is_max());
clock.Advance(Seconds(5));
EXPECT_TRUE(meter.Update(300));
EXPECT_EQ(meter.GetSampleCount(), 2u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 85);
}
TEST(SpeedometerTest, ChangeTotalBytes) {
base::ScopedMockClockOverride clock;
Speedometer meter;
// Sets total number of bytes and a couple of samples.
meter.SetTotalBytes(2000);
EXPECT_TRUE(meter.Update(100));
clock.Advance(Seconds(5));
EXPECT_TRUE(meter.Update(300));
EXPECT_EQ(meter.GetSampleCount(), 2u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 42);
// Setting the total number of bytes to the existing value shouldn't change
// anything.
meter.SetTotalBytes(2000);
EXPECT_EQ(meter.GetSampleCount(), 2u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 42);
// Changing the total number of bytes should clear the samples.
meter.SetTotalBytes(3000);
EXPECT_EQ(meter.GetSampleCount(), 0u);
EXPECT_TRUE(meter.GetRemainingTime().is_max());
clock.Advance(Seconds(5));
EXPECT_TRUE(meter.Update(3000));
EXPECT_EQ(meter.GetSampleCount(), 1u);
// Go beyond the number of expected bytes.
clock.Advance(Seconds(5));
EXPECT_TRUE(meter.Update(4000));
EXPECT_EQ(meter.GetSampleCount(), 2u);
EXPECT_EQ(meter.GetRemainingTime().InSeconds(), 0);
}
} // namespace
} // namespace file_manager