// Copyright 2021 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 "base/barrier_callback.h" #include "base/bind.h" #include "base/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( 0, base::BindLambdaForTesting([&done](std::vector results) { EXPECT_THAT(results, testing::IsEmpty()); done = true; })); EXPECT_TRUE(done); } TEST(BarrierCallbackTest, ErrorToCallCallbackWithZeroCallbacks) { auto barrier_callback = base::BarrierCallback(0, base::BindOnce([](std::vector) {})); EXPECT_FALSE(barrier_callback.is_null()); EXPECT_CHECK_DEATH(barrier_callback.Run(3)); } TEST(BarrierCallbackTest, RunAfterNumCallbacks) { bool done = false; auto barrier_callback = base::BarrierCallback( 3, base::BindLambdaForTesting([&done](std::vector 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( 3, base::BindLambdaForTesting([&done](std::vector 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 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 destructed_; }; TEST(BarrierCallbackTest, ReleasesDoneCallbackWhenDone) { bool done_destructed = false; auto barrier_callback = base::BarrierCallback( 1, base::BindOnce(&DestructionIndicator>::DoNothing, std::make_unique>>( &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 barrier_callback; barrier_callback = base::BarrierCallback( 1, base::BindLambdaForTesting( [&barrier_callback](std::vector results) { barrier_callback = base::RepeatingCallback(); 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( 1, base::BindOnce([](std::vector) {})); barrier_callback.Run(MoveOnly()); auto barrier_callback2 = base::BarrierCallback( 1, base::BindOnce([](const std::vector&) {})); barrier_callback2.Run(MoveOnly()); } TEST(BarrierCallbackTest, SupportsConstRefResults) { auto barrier_callback = base::BarrierCallback( 1, base::BindOnce([](const std::vector&) {})); 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( 1, base::BindOnce([](std::vector) {})); barrier_callback.Run(ref); auto barrier_callback2 = base::BarrierCallback( 1, base::BindOnce([](const std::vector&) {})); barrier_callback2.Run(ref); } } // namespace