blob: 3d98940128deedf87d7451348abdb15dd06d32e0 [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 "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