blob: f4779dd497fb23bf05ce0ac214ab8d43693d71d9 [file]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/reporting/util/shared_vector.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace reporting {
namespace {
template <typename VectorType>
class VectorTester {
public:
// FindType must be copyable.
template <typename FindType>
class Finder {
public:
explicit Finder(const FindType& item)
: sought_item_(item), run_loop_(std::make_unique<base::RunLoop>()) {}
bool Compare(const FindType& item) const { return sought_item_ == item; }
void OnFound(FindType& item) {
found_result_ = item;
run_loop_->Quit();
}
void OnNotFound() { run_loop_->Quit(); }
const FindType& sought_item() const { return sought_item_; }
const absl::optional<FindType>& found_result() const {
return found_result_;
}
void Wait() {
run_loop_->Run();
run_loop_ = std::make_unique<base::RunLoop>();
}
private:
const FindType sought_item_;
std::unique_ptr<base::RunLoop> run_loop_;
absl::optional<FindType> found_result_;
};
template <typename ExecuteType>
class Executor {
public:
explicit Executor(size_t expected_value_count)
: expected_value_count_(expected_value_count),
run_loop_(std::make_unique<base::RunLoop>()) {}
void CountValue(ExecuteType& item) { found_count_++; }
void Complete() { run_loop_->Quit(); }
void Wait() {
run_loop_->Run();
run_loop_ = std::make_unique<base::RunLoop>();
}
size_t DifferenceInCount() const {
return expected_value_count_ - found_count_;
}
size_t found_count() const { return found_count_; }
private:
const size_t expected_value_count_;
std::unique_ptr<base::RunLoop> run_loop_;
size_t found_count_{0};
};
VectorTester()
: vector_(SharedVector<VectorType>::Create()),
sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner({})),
run_loop_(std::make_unique<base::RunLoop>()) {}
~VectorTester() = default;
// Find only works on copyable items - so VectorType must be copyable.
Finder<VectorType> GetFinder(VectorType sought_item) {
return Finder<VectorType>(sought_item);
}
Executor<VectorType> GetExecutor(size_t expected_value_count) {
return Executor<VectorType>(expected_value_count);
}
void PushBack(VectorType item) {
vector_->PushBack(
std::move(item),
base::BindOnce(&VectorTester<VectorType>::OnPushBackComplete,
base::Unretained(this)));
}
// Resets |insert_success| before returning its value.
absl::optional<bool> GetPushBackSuccess() {
absl::optional<bool> return_value;
return_value.swap(insert_success_);
return return_value;
}
void Erase(VectorType value) {
auto predicate_cb = base::BindRepeating(
[](const VectorType& expected_value, const VectorType& comparison_value)
-> bool { return expected_value == comparison_value; },
value);
vector_->Erase(std::move(predicate_cb),
base::BindOnce(&VectorTester<VectorType>::OnEraseComplete,
base::Unretained(this)));
}
void Erase(base::RepeatingCallback<bool(const VectorType&)> predicate_cb) {
vector_->Erase(std::move(predicate_cb),
base::BindOnce(&VectorTester<VectorType>::OnEraseComplete,
base::Unretained(this)));
}
absl::optional<uint64_t> GetEraseValue() {
absl::optional<uint64_t> return_value;
return_value.swap(number_deleted_);
return return_value;
}
void ExecuteIfFound(Finder<VectorType>* finder) {
vector_->ExecuteIfFound(
base::BindRepeating(&Finder<VectorType>::Compare,
base::Unretained(finder)),
base::BindOnce(&Finder<VectorType>::OnFound, base::Unretained(finder)),
base::BindOnce(&Finder<VectorType>::OnNotFound,
base::Unretained(finder)));
}
void ExecuteOnEachElement(Executor<VectorType>* executor) {
vector_->ExecuteOnEachElement(
base::BindRepeating(&Executor<VectorType>::CountValue,
base::Unretained(executor)),
base::BindOnce(&Executor<VectorType>::Complete,
base::Unretained(executor)));
}
void Wait() {
run_loop_->Run();
run_loop_ = std::make_unique<base::RunLoop>();
}
private:
void OnPushBackComplete() {
sequenced_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VectorTester<VectorType>::VectorPushBackSuccess,
base::Unretained(this)));
}
void VectorPushBackSuccess() {
insert_success_ = true;
Signal();
}
void OnEraseComplete(size_t number_deleted) {
sequenced_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&VectorTester<VectorType>::VectorEraseValue,
base::Unretained(this), number_deleted));
}
void VectorEraseValue(uint64_t number_deleted) {
number_deleted_ = number_deleted;
Signal();
}
void Signal() { run_loop_->Quit(); }
scoped_refptr<SharedVector<VectorType>> vector_;
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
std::unique_ptr<base::RunLoop> run_loop_;
absl::optional<bool> insert_success_;
absl::optional<uint64_t> number_deleted_;
};
// Ensures that the vector accept values, and will erase inserted values.
TEST(SharedVectorTest, PushBackAndEraseWorkCorrectly) {
base::test::TaskEnvironment task_envrionment;
const std::vector<int> kValues = {1, 2, 3, 4, 5};
const int kInsertLoopTimes = 10;
VectorTester<int> vector_tester;
// PushBack Values
for (auto value : kValues) {
vector_tester.PushBack(value);
vector_tester.Wait();
auto insert_success = vector_tester.GetPushBackSuccess();
ASSERT_TRUE(insert_success.has_value());
EXPECT_TRUE(insert_success.value());
}
// Attempt to erase inserted values - should find one each.
for (auto value : kValues) {
vector_tester.Erase(value);
vector_tester.Wait();
auto erase_success = vector_tester.GetEraseValue();
ASSERT_TRUE(erase_success.has_value());
EXPECT_EQ(erase_success.value(), uint64_t(1));
}
// Attempt to erase the values again - shouldn't find any.
for (auto value : kValues) {
vector_tester.Erase(value);
vector_tester.Wait();
auto erase_success = vector_tester.GetEraseValue();
ASSERT_TRUE(erase_success.has_value());
EXPECT_EQ(erase_success.value(), uint64_t(0));
}
// Attempt to insert the values multiple times - should succeed.
for (int i = 0; i < kInsertLoopTimes; i++) {
for (auto value : kValues) {
vector_tester.PushBack(value);
vector_tester.Wait();
auto insert_success = vector_tester.GetPushBackSuccess();
ASSERT_TRUE(insert_success.has_value());
EXPECT_TRUE(insert_success.value());
}
}
// Attempt to erase inserted values - should find kInsertLoopTimes each.
for (auto value : kValues) {
vector_tester.Erase(value);
vector_tester.Wait();
auto erase_success = vector_tester.GetEraseValue();
ASSERT_TRUE(erase_success.has_value());
EXPECT_EQ(erase_success.value(), uint64_t(kInsertLoopTimes));
}
}
// Ensures that SharedVector::ExecuteIfFound works correctly
TEST(SharedVectorTest, ExecuteIfFoundSucceeds) {
base::test::TaskEnvironment task_envrionment;
const int kExpectedValue = 1701;
const int kUnexpectedValue = 42;
VectorTester<int> vector_tester;
vector_tester.PushBack(kExpectedValue);
vector_tester.Wait();
auto expected_finder = vector_tester.GetFinder(kExpectedValue);
vector_tester.ExecuteIfFound(&expected_finder);
expected_finder.Wait();
auto found_result = expected_finder.found_result();
ASSERT_TRUE(found_result.has_value());
EXPECT_EQ(found_result.value(), kExpectedValue);
auto unexpected_finder = vector_tester.GetFinder(kUnexpectedValue);
vector_tester.ExecuteIfFound(&unexpected_finder);
unexpected_finder.Wait();
found_result = unexpected_finder.found_result();
EXPECT_FALSE(found_result.has_value());
}
TEST(SharedVectorTest, ExecuteAllElements) {
base::test::TaskEnvironment task_envrionment;
const std::vector<int> kValues = {1, 2, 3, 4, 5};
VectorTester<int> vector_tester;
// PushBack Values
for (auto value : kValues) {
vector_tester.PushBack(value);
vector_tester.Wait();
auto insert_success = vector_tester.GetPushBackSuccess();
ASSERT_TRUE(insert_success.has_value());
EXPECT_TRUE(insert_success.value());
}
auto executor = vector_tester.GetExecutor(kValues.size());
vector_tester.ExecuteOnEachElement(&executor);
executor.Wait();
EXPECT_EQ(executor.DifferenceInCount(), 0u);
}
// Ensures that execution can happen on elements that are moveable but not
// copyable.
TEST(SharedVectorTest, InsertAndExecuteAndEraseOnUniquePtr) {
base::test::TaskEnvironment task_envrionment;
const std::vector<int> kValues = {1, 2, 3, 4, 5};
VectorTester<std::unique_ptr<int>> vector_tester;
for (auto value : kValues) {
vector_tester.PushBack(std::make_unique<int>(value));
vector_tester.Wait();
auto insert_success = vector_tester.GetPushBackSuccess();
ASSERT_TRUE(insert_success.has_value());
EXPECT_TRUE(insert_success.value());
}
auto executor = vector_tester.GetExecutor(kValues.size());
vector_tester.ExecuteOnEachElement(&executor);
executor.Wait();
EXPECT_EQ(executor.DifferenceInCount(), 0u);
for (auto value : kValues) {
auto comparator_cb = base::BindRepeating(
[](int expected_value, const std::unique_ptr<int>& comparison_value)
-> bool { return expected_value == *comparison_value; },
value);
vector_tester.Erase(comparator_cb);
vector_tester.Wait();
auto erase_success = vector_tester.GetEraseValue();
ASSERT_TRUE(erase_success.has_value());
EXPECT_EQ(erase_success.value(), uint64_t(1));
}
}
} // namespace
} // namespace reporting