| // Copyright 2013 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/one_shot_event.h" | 
 |  | 
 | #include "base/functional/bind.h" | 
 | #include "base/memory/raw_ptr.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/task/single_thread_task_runner.h" | 
 | #include "base/test/task_environment.h" | 
 | #include "base/test/test_simple_task_runner.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | void Increment(int* i) { | 
 |   ++*i; | 
 | } | 
 |  | 
 | // |*did_delete_instance| will be set to true upon its destruction. | 
 | class RefCountedClass : public base::RefCounted<RefCountedClass> { | 
 |  public: | 
 |   explicit RefCountedClass(bool* did_delete_instance) | 
 |       : did_delete_instance_(did_delete_instance) { | 
 |     DCHECK(!*did_delete_instance_); | 
 |   } | 
 |   RefCountedClass(const RefCountedClass&) = delete; | 
 |   RefCountedClass& operator=(const RefCountedClass&) = delete; | 
 |  | 
 |   void PerformTask() { did_perform_task_ = true; } | 
 |   bool did_perform_task() const { return did_perform_task_; } | 
 |  | 
 |  private: | 
 |   friend class base::RefCounted<RefCountedClass>; | 
 |  | 
 |   ~RefCountedClass() { *did_delete_instance_ = true; } | 
 |  | 
 |   const raw_ptr<bool> did_delete_instance_;  // Not owned. | 
 |  | 
 |   bool did_perform_task_ = false; | 
 | }; | 
 |  | 
 | TEST(OneShotEventTest, RecordsSignal) { | 
 |   OneShotEvent event; | 
 |   EXPECT_FALSE(event.is_signaled()); | 
 |   event.Signal(); | 
 |   EXPECT_TRUE(event.is_signaled()); | 
 | } | 
 |  | 
 | TEST(OneShotEventTest, CallsQueueAsDistinctTask) { | 
 |   OneShotEvent event; | 
 |   scoped_refptr<base::TestSimpleTaskRunner> runner( | 
 |       new base::TestSimpleTaskRunner); | 
 |   int i = 0; | 
 |   event.Post(FROM_HERE, base::BindOnce(&Increment, &i), runner); | 
 |   event.Post(FROM_HERE, base::BindOnce(&Increment, &i), runner); | 
 |   EXPECT_EQ(0U, runner->NumPendingTasks()); | 
 |   event.Signal(); | 
 |  | 
 |   auto pending_tasks = runner->TakePendingTasks(); | 
 |   ASSERT_EQ(2U, pending_tasks.size()); | 
 |   EXPECT_NE(pending_tasks[0].location.line_number(), | 
 |             pending_tasks[1].location.line_number()) | 
 |       << "Make sure FROM_HERE is propagated."; | 
 | } | 
 |  | 
 | TEST(OneShotEventTest, CallsQueue) { | 
 |   OneShotEvent event; | 
 |   scoped_refptr<base::TestSimpleTaskRunner> runner( | 
 |       new base::TestSimpleTaskRunner); | 
 |   int i = 0; | 
 |   event.Post(FROM_HERE, base::BindOnce(&Increment, &i), runner); | 
 |   event.Post(FROM_HERE, base::BindOnce(&Increment, &i), runner); | 
 |   EXPECT_EQ(0U, runner->NumPendingTasks()); | 
 |   event.Signal(); | 
 |   ASSERT_EQ(2U, runner->NumPendingTasks()); | 
 |  | 
 |   EXPECT_EQ(0, i); | 
 |   runner->RunPendingTasks(); | 
 |   EXPECT_EQ(2, i); | 
 | } | 
 |  | 
 | TEST(OneShotEventTest, CallsAfterSignalDontRunInline) { | 
 |   OneShotEvent event; | 
 |   scoped_refptr<base::TestSimpleTaskRunner> runner( | 
 |       new base::TestSimpleTaskRunner); | 
 |   int i = 0; | 
 |  | 
 |   event.Signal(); | 
 |   event.Post(FROM_HERE, base::BindOnce(&Increment, &i), runner); | 
 |   EXPECT_EQ(1U, runner->NumPendingTasks()); | 
 |   EXPECT_EQ(0, i); | 
 |   runner->RunPendingTasks(); | 
 |   EXPECT_EQ(1, i); | 
 | } | 
 |  | 
 | TEST(OneShotEventTest, PostDefaultsToCurrentMessageLoop) { | 
 |   OneShotEvent event; | 
 |   scoped_refptr<base::TestSimpleTaskRunner> runner( | 
 |       new base::TestSimpleTaskRunner); | 
 |   base::test::SingleThreadTaskEnvironment task_environment; | 
 |   int runner_i = 0; | 
 |   int loop_i = 0; | 
 |  | 
 |   event.Post(FROM_HERE, base::BindOnce(&Increment, &runner_i), runner); | 
 |   event.Post(FROM_HERE, base::BindOnce(&Increment, &loop_i)); | 
 |   event.Signal(); | 
 |   EXPECT_EQ(1U, runner->NumPendingTasks()); | 
 |   EXPECT_EQ(0, runner_i); | 
 |   runner->RunPendingTasks(); | 
 |   EXPECT_EQ(1, runner_i); | 
 |   EXPECT_EQ(0, loop_i); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(1, loop_i); | 
 | } | 
 |  | 
 | void CheckSignaledAndPostIncrement( | 
 |     OneShotEvent* event, | 
 |     const scoped_refptr<base::SingleThreadTaskRunner>& runner, | 
 |     int* i) { | 
 |   EXPECT_TRUE(event->is_signaled()); | 
 |   event->Post(FROM_HERE, base::BindOnce(&Increment, i), runner); | 
 | } | 
 |  | 
 | TEST(OneShotEventTest, IsSignaledAndPostsFromCallbackWork) { | 
 |   OneShotEvent event; | 
 |   scoped_refptr<base::TestSimpleTaskRunner> runner( | 
 |       new base::TestSimpleTaskRunner); | 
 |   int i = 0; | 
 |  | 
 |   event.Post(FROM_HERE, | 
 |              base::BindOnce(&CheckSignaledAndPostIncrement, &event, runner, &i), | 
 |              runner); | 
 |   EXPECT_EQ(0, i); | 
 |   event.Signal(); | 
 |  | 
 |   // CheckSignaledAndPostIncrement is queued on |runner|. | 
 |   EXPECT_EQ(1U, runner->NumPendingTasks()); | 
 |   EXPECT_EQ(0, i); | 
 |   runner->RunPendingTasks(); | 
 |   // Increment is queued on |runner|. | 
 |   EXPECT_EQ(1U, runner->NumPendingTasks()); | 
 |   EXPECT_EQ(0, i); | 
 |   runner->RunPendingTasks(); | 
 |   // Increment has run. | 
 |   EXPECT_EQ(0U, runner->NumPendingTasks()); | 
 |   EXPECT_EQ(1, i); | 
 | } | 
 |  | 
 | // Tests that OneShotEvent does not keep references to tasks once OneShotEvent | 
 | // Signal()s. | 
 | TEST(OneShotEventTest, DropsCallbackRefUponSignalled) { | 
 |   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>(); | 
 |   bool did_delete_instance = false; | 
 |   OneShotEvent event; | 
 |  | 
 |   { | 
 |     auto ref_counted_class = | 
 |         base::MakeRefCounted<RefCountedClass>(&did_delete_instance); | 
 |     event.Post(FROM_HERE, | 
 |                base::BindOnce(&RefCountedClass::PerformTask, ref_counted_class), | 
 |                runner); | 
 |     event.Signal(); | 
 |     runner->RunPendingTasks(); | 
 |     EXPECT_TRUE(ref_counted_class->did_perform_task()); | 
 |   } | 
 |  | 
 |   // Once OneShotEvent doesn't have any queued events, it should have dropped | 
 |   // all the references to the callbacks it received through Post(). | 
 |   EXPECT_TRUE(did_delete_instance); | 
 | } | 
 |  | 
 | }  // namespace base |