168 lines
5.2 KiB
C++
168 lines
5.2 KiB
C++
|
|
// 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 "base/barrier_callback.h"
|
||
|
|
|
||
|
|
#include "base/functional/bind.h"
|
||
|
|
#include "base/functional/callback.h"
|
||
|
|
#include "base/memory/raw_ptr.h"
|
||
|
|
#include "base/test/bind.h"
|
||
|
|
#include "base/test/gtest_util.h"
|
||
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
||
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, RunsImmediatelyForZeroCallbacks) {
|
||
|
|
bool done = false;
|
||
|
|
auto barrier_callback = base::BarrierCallback<int>(
|
||
|
|
0, base::BindLambdaForTesting([&done](std::vector<int> results) {
|
||
|
|
EXPECT_THAT(results, testing::IsEmpty());
|
||
|
|
done = true;
|
||
|
|
}));
|
||
|
|
EXPECT_TRUE(done);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, ErrorToCallCallbackWithZeroCallbacks) {
|
||
|
|
auto barrier_callback =
|
||
|
|
base::BarrierCallback<int>(0, base::BindOnce([](std::vector<int>) {}));
|
||
|
|
EXPECT_FALSE(barrier_callback.is_null());
|
||
|
|
|
||
|
|
EXPECT_CHECK_DEATH(barrier_callback.Run(3));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, RunAfterNumCallbacks) {
|
||
|
|
bool done = false;
|
||
|
|
auto barrier_callback = base::BarrierCallback<int>(
|
||
|
|
3, base::BindLambdaForTesting([&done](std::vector<int> results) {
|
||
|
|
EXPECT_THAT(results, testing::ElementsAre(1, 3, 2));
|
||
|
|
done = true;
|
||
|
|
}));
|
||
|
|
EXPECT_FALSE(done);
|
||
|
|
|
||
|
|
barrier_callback.Run(1);
|
||
|
|
EXPECT_FALSE(done);
|
||
|
|
|
||
|
|
barrier_callback.Run(3);
|
||
|
|
EXPECT_FALSE(done);
|
||
|
|
|
||
|
|
barrier_callback.Run(2);
|
||
|
|
EXPECT_TRUE(done);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, CopiesShareState) {
|
||
|
|
bool done = false;
|
||
|
|
const auto barrier_callback = base::BarrierCallback<int>(
|
||
|
|
3, base::BindLambdaForTesting([&done](std::vector<int> results) {
|
||
|
|
EXPECT_THAT(results, testing::ElementsAre(1, 3, 2));
|
||
|
|
done = true;
|
||
|
|
}));
|
||
|
|
EXPECT_FALSE(done);
|
||
|
|
|
||
|
|
const auto barrier_copy1 = barrier_callback;
|
||
|
|
const auto barrier_copy2 = barrier_callback;
|
||
|
|
const auto barrier_copy3 = barrier_callback;
|
||
|
|
|
||
|
|
barrier_copy1.Run(1);
|
||
|
|
EXPECT_FALSE(done);
|
||
|
|
|
||
|
|
barrier_copy2.Run(3);
|
||
|
|
EXPECT_FALSE(done);
|
||
|
|
|
||
|
|
barrier_copy3.Run(2);
|
||
|
|
EXPECT_TRUE(done);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename... Args>
|
||
|
|
class DestructionIndicator {
|
||
|
|
public:
|
||
|
|
// Sets `*destructed` to true in destructor.
|
||
|
|
explicit DestructionIndicator(bool* destructed) : destructed_(destructed) {
|
||
|
|
*destructed_ = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
~DestructionIndicator() { *destructed_ = true; }
|
||
|
|
|
||
|
|
void DoNothing(Args...) {}
|
||
|
|
|
||
|
|
private:
|
||
|
|
raw_ptr<bool> destructed_;
|
||
|
|
};
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, ReleasesDoneCallbackWhenDone) {
|
||
|
|
bool done_destructed = false;
|
||
|
|
auto barrier_callback = base::BarrierCallback<bool>(
|
||
|
|
1,
|
||
|
|
base::BindOnce(&DestructionIndicator<std::vector<bool>>::DoNothing,
|
||
|
|
std::make_unique<DestructionIndicator<std::vector<bool>>>(
|
||
|
|
&done_destructed)));
|
||
|
|
EXPECT_FALSE(done_destructed);
|
||
|
|
barrier_callback.Run(true);
|
||
|
|
EXPECT_TRUE(done_destructed);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Tests a case when `done_callback` resets the `barrier_callback`.
|
||
|
|
// `barrier_callback` is a RepeatingCallback holding the `done_callback`.
|
||
|
|
// `done_callback` holds a reference back to the `barrier_callback`. When
|
||
|
|
// `barrier_callback` is Run() it calls `done_callback` which erases the
|
||
|
|
// `barrier_callback` while still inside of its Run(). The Run() implementation
|
||
|
|
// (in base::BarrierCallback) must not try use itself after executing
|
||
|
|
// ResetBarrierCallback() or this test would crash inside Run().
|
||
|
|
TEST(BarrierCallbackTest, KeepingCallbackAliveUntilDone) {
|
||
|
|
base::RepeatingCallback<void(bool)> barrier_callback;
|
||
|
|
barrier_callback = base::BarrierCallback<bool>(
|
||
|
|
1, base::BindLambdaForTesting(
|
||
|
|
[&barrier_callback](std::vector<bool> results) {
|
||
|
|
barrier_callback = base::RepeatingCallback<void(bool)>();
|
||
|
|
EXPECT_THAT(results, testing::ElementsAre(true));
|
||
|
|
}));
|
||
|
|
barrier_callback.Run(true);
|
||
|
|
EXPECT_TRUE(barrier_callback.is_null());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, SupportsMoveonlyTypes) {
|
||
|
|
class MoveOnly {
|
||
|
|
public:
|
||
|
|
MoveOnly() = default;
|
||
|
|
MoveOnly(MoveOnly&&) = default;
|
||
|
|
MoveOnly& operator=(MoveOnly&&) = default;
|
||
|
|
};
|
||
|
|
|
||
|
|
// No need to assert anything here, since if BarrierCallback didn't work with
|
||
|
|
// move-only types, this wouldn't compile.
|
||
|
|
auto barrier_callback = base::BarrierCallback<MoveOnly>(
|
||
|
|
1, base::BindOnce([](std::vector<MoveOnly>) {}));
|
||
|
|
barrier_callback.Run(MoveOnly());
|
||
|
|
|
||
|
|
auto barrier_callback2 = base::BarrierCallback<MoveOnly>(
|
||
|
|
1, base::BindOnce([](const std::vector<MoveOnly>&) {}));
|
||
|
|
barrier_callback2.Run(MoveOnly());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, SupportsConstRefResults) {
|
||
|
|
auto barrier_callback = base::BarrierCallback<int>(
|
||
|
|
1, base::BindOnce([](const std::vector<int>&) {}));
|
||
|
|
|
||
|
|
barrier_callback.Run(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BarrierCallbackTest, SupportsReferenceTypes) {
|
||
|
|
class Referenceable {
|
||
|
|
// Must be copyable.
|
||
|
|
};
|
||
|
|
Referenceable ref;
|
||
|
|
|
||
|
|
// No need to assert anything here, since if BarrierCallback didn't work with
|
||
|
|
// by-reference args, this wouldn't compile.
|
||
|
|
auto barrier_callback = base::BarrierCallback<const Referenceable&>(
|
||
|
|
1, base::BindOnce([](std::vector<Referenceable>) {}));
|
||
|
|
barrier_callback.Run(ref);
|
||
|
|
|
||
|
|
auto barrier_callback2 = base::BarrierCallback<const Referenceable&>(
|
||
|
|
1, base::BindOnce([](const std::vector<Referenceable>&) {}));
|
||
|
|
barrier_callback2.Run(ref);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|