blob: 022da6bec5cb257d6c769f3a07eed04cd9032e64 [file] [log] [blame]
// 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 "components/system_cpu/cpu_probe.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/sequence_checker.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/system_cpu/cpu_probe.h"
#include "components/system_cpu/cpu_sample.h"
#include "components/system_cpu/pressure_test_support.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace system_cpu {
class CpuProbeTest : public testing::Test {
public:
CpuProbeTest() : cpu_probe_(std::make_unique<FakeCpuProbe>()) {}
void WaitForUpdate() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::RunLoop run_loop;
cpu_probe_->RequestSample(
base::BindOnce(&CpuProbeTest::CollectorCallback, base::Unretained(this))
.Then(run_loop.QuitClosure()));
run_loop.Run();
}
void CollectorCallback(std::optional<CpuSample> sample) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (sample.has_value()) {
samples_.push_back(sample->cpu_utilization);
}
}
void RequestRepeatedUpdates(base::TimeDelta interval) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
timer_.Start(
FROM_HERE, interval, base::BindLambdaForTesting([&] {
cpu_probe_->RequestSample(base::BindOnce(
&CpuProbeTest::CollectorCallback, base::Unretained(this)));
}));
}
protected:
SEQUENCE_CHECKER(sequence_checker_);
base::test::TaskEnvironment task_environment_;
// This member is a std::unique_ptr instead of a plain CpuProbe
// so it can be replaced inside tests.
std::unique_ptr<CpuProbe> cpu_probe_;
// The samples reported by the callback.
std::vector<double> samples_ GUARDED_BY_CONTEXT(sequence_checker_);
// Timer used to request samples repeatedly.
base::RepeatingTimer timer_ GUARDED_BY_CONTEXT(sequence_checker_);
};
using CpuProbeDeathTest = CpuProbeTest;
TEST_F(CpuProbeTest, RequestSample) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static_cast<FakeCpuProbe*>(cpu_probe_.get())->SetLastSample(CpuSample{0.9});
cpu_probe_->StartSampling();
WaitForUpdate();
EXPECT_THAT(samples_, testing::ElementsAre(0.9));
}
TEST_F(CpuProbeTest, RepeatedSamples) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<CpuSample> samples = {
// Value right after construction.
CpuSample{0.6},
// Value after first Update(), should be discarded.
CpuSample{0.9},
// Value after second Update(), should be reported.
CpuSample{0.4},
};
base::RunLoop run_loop;
cpu_probe_ = std::make_unique<StreamingCpuProbe>(std::move(samples),
run_loop.QuitClosure());
cpu_probe_->StartSampling();
RequestRepeatedUpdates(base::Milliseconds(1));
run_loop.Run();
EXPECT_THAT(samples_, testing::ElementsAre(0.4));
}
TEST_F(CpuProbeTest, DestroyWhileSampling) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static_cast<FakeCpuProbe*>(cpu_probe_.get())->SetLastSample(CpuSample{0.9});
cpu_probe_->StartSampling();
// Test passes as long as it doesn't crash.
base::RunLoop run_loop;
cpu_probe_->RequestSample(base::BindOnce(
[](base::ScopedClosureRunner quit_closure_runner,
std::optional<CpuSample>) {
// `quit_closure_runner` will run the bound `quit_closure` when the
// callback is destroyed. The callback function shouldn't actually
// execute because the CpuProbe is destroyed before the RunLoop
// starts, so there's no object to receive it.
FAIL();
},
base::ScopedClosureRunner(run_loop.QuitClosure())));
cpu_probe_.reset();
run_loop.Run();
}
// TODO(crbug.com/41485857): Fix test flakily timing out and re-enable.
TEST_F(CpuProbeDeathTest, DISABLED_CpuUtilizationTooLarge) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<CpuSample> samples = {
// Value right after construction.
CpuSample{0.6},
// Value after first Update(), should be discarded.
CpuSample{0.9},
// Crash expected.
CpuSample{1.1},
};
base::RunLoop run_loop;
cpu_probe_ = std::make_unique<StreamingCpuProbe>(std::move(samples),
run_loop.QuitClosure());
cpu_probe_->StartSampling();
RequestRepeatedUpdates(base::Milliseconds(1));
EXPECT_CHECK_DEATH_WITH(run_loop.Run(), "cpu_utilization <= 1.0");
}
// TODO(crbug.com/41485857): Fix test flakily timing out and re-enable.
TEST_F(CpuProbeDeathTest, DISABLED_CpuUtilizationTooSmall) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<CpuSample> samples = {
// Value right after construction.
CpuSample{0.6},
// Value after first Update(), should be discarded.
CpuSample{0.9},
// Crash expected.
CpuSample{-0.5},
};
base::RunLoop run_loop;
cpu_probe_ = std::make_unique<StreamingCpuProbe>(std::move(samples),
run_loop.QuitClosure());
cpu_probe_->StartSampling();
RequestRepeatedUpdates(base::Milliseconds(1));
EXPECT_CHECK_DEATH_WITH(run_loop.Run(), "cpu_utilization >= 0.0");
}
// TODO(crbug.com/41485857): Fix test flakily timing out and re-enable.
TEST_F(CpuProbeDeathTest, DISABLED_RequestSampleWithoutStartSampling) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static_cast<FakeCpuProbe*>(cpu_probe_.get())->SetLastSample(CpuSample{0.9});
EXPECT_CHECK_DEATH(WaitForUpdate());
}
} // namespace system_cpu