1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/callback.h"
6
7#include <memory>
8
9#include "base/bind.h"
10#include "base/callback_helpers.h"
11#include "base/callback_internal.h"
12#include "base/memory/ref_counted.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15namespace base {
16
17void NopInvokeFunc() {}
18
19// White-box testpoints to inject into a Callback<> object for checking
20// comparators and emptiness APIs.  Use a BindState that is specialized
21// based on a type we declared in the anonymous namespace above to remove any
22// chance of colliding with another instantiation and breaking the
23// one-definition-rule.
24struct FakeBindState : internal::BindStateBase {
25  FakeBindState() : BindStateBase(&NopInvokeFunc, &Destroy, &IsCancelled) {}
26
27 private:
28  ~FakeBindState() {}
29  static void Destroy(const internal::BindStateBase* self) {
30    delete static_cast<const FakeBindState*>(self);
31  }
32  static bool IsCancelled(const internal::BindStateBase*) {
33    return false;
34  }
35};
36
37namespace {
38
39class CallbackTest : public ::testing::Test {
40 public:
41  CallbackTest()
42      : callback_a_(new FakeBindState()), callback_b_(new FakeBindState()) {}
43
44  ~CallbackTest() override {}
45
46 protected:
47  Callback<void()> callback_a_;
48  const Callback<void()> callback_b_;  // Ensure APIs work with const.
49  Callback<void()> null_callback_;
50};
51
52// Ensure we can create unbound callbacks. We need this to be able to store
53// them in class members that can be initialized later.
54TEST_F(CallbackTest, DefaultConstruction) {
55  Callback<void()> c0;
56  Callback<void(int)> c1;
57  Callback<void(int,int)> c2;
58  Callback<void(int,int,int)> c3;
59  Callback<void(int,int,int,int)> c4;
60  Callback<void(int,int,int,int,int)> c5;
61  Callback<void(int,int,int,int,int,int)> c6;
62
63  EXPECT_TRUE(c0.is_null());
64  EXPECT_TRUE(c1.is_null());
65  EXPECT_TRUE(c2.is_null());
66  EXPECT_TRUE(c3.is_null());
67  EXPECT_TRUE(c4.is_null());
68  EXPECT_TRUE(c5.is_null());
69  EXPECT_TRUE(c6.is_null());
70}
71
72TEST_F(CallbackTest, IsNull) {
73  EXPECT_TRUE(null_callback_.is_null());
74  EXPECT_FALSE(callback_a_.is_null());
75  EXPECT_FALSE(callback_b_.is_null());
76}
77
78TEST_F(CallbackTest, Equals) {
79  EXPECT_TRUE(callback_a_.Equals(callback_a_));
80  EXPECT_FALSE(callback_a_.Equals(callback_b_));
81  EXPECT_FALSE(callback_b_.Equals(callback_a_));
82
83  // We should compare based on instance, not type.
84  Callback<void()> callback_c(new FakeBindState());
85  Callback<void()> callback_a2 = callback_a_;
86  EXPECT_TRUE(callback_a_.Equals(callback_a2));
87  EXPECT_FALSE(callback_a_.Equals(callback_c));
88
89  // Empty, however, is always equal to empty.
90  Callback<void()> empty2;
91  EXPECT_TRUE(null_callback_.Equals(empty2));
92}
93
94TEST_F(CallbackTest, Reset) {
95  // Resetting should bring us back to empty.
96  ASSERT_FALSE(callback_a_.is_null());
97  ASSERT_FALSE(callback_a_.Equals(null_callback_));
98
99  callback_a_.Reset();
100
101  EXPECT_TRUE(callback_a_.is_null());
102  EXPECT_TRUE(callback_a_.Equals(null_callback_));
103}
104
105TEST_F(CallbackTest, Move) {
106  // Moving should reset the callback.
107  ASSERT_FALSE(callback_a_.is_null());
108  ASSERT_FALSE(callback_a_.Equals(null_callback_));
109
110  auto tmp = std::move(callback_a_);
111
112  EXPECT_TRUE(callback_a_.is_null());
113  EXPECT_TRUE(callback_a_.Equals(null_callback_));
114}
115
116struct TestForReentrancy {
117  TestForReentrancy()
118      : cb_already_run(false),
119        cb(Bind(&TestForReentrancy::AssertCBIsNull, Unretained(this))) {
120  }
121  void AssertCBIsNull() {
122    ASSERT_TRUE(cb.is_null());
123    cb_already_run = true;
124  }
125  bool cb_already_run;
126  Closure cb;
127};
128
129TEST_F(CallbackTest, ResetAndReturn) {
130  TestForReentrancy tfr;
131  ASSERT_FALSE(tfr.cb.is_null());
132  ASSERT_FALSE(tfr.cb_already_run);
133  ResetAndReturn(&tfr.cb).Run();
134  ASSERT_TRUE(tfr.cb.is_null());
135  ASSERT_TRUE(tfr.cb_already_run);
136}
137
138TEST_F(CallbackTest, NullAfterMoveRun) {
139  Closure cb = Bind([] {});
140  ASSERT_TRUE(cb);
141  std::move(cb).Run();
142  ASSERT_FALSE(cb);
143
144  const Closure cb2 = Bind([] {});
145  ASSERT_TRUE(cb2);
146  std::move(cb2).Run();
147  ASSERT_TRUE(cb2);
148
149  OnceClosure cb3 = BindOnce([] {});
150  ASSERT_TRUE(cb3);
151  std::move(cb3).Run();
152  ASSERT_FALSE(cb3);
153}
154
155class CallbackOwner : public base::RefCounted<CallbackOwner> {
156 public:
157  explicit CallbackOwner(bool* deleted) {
158    callback_ = Bind(&CallbackOwner::Unused, this);
159    deleted_ = deleted;
160  }
161  void Reset() {
162    callback_.Reset();
163    // We are deleted here if no-one else had a ref to us.
164  }
165
166 private:
167  friend class base::RefCounted<CallbackOwner>;
168  virtual ~CallbackOwner() {
169    *deleted_ = true;
170  }
171  void Unused() {
172    FAIL() << "Should never be called";
173  }
174
175  Closure callback_;
176  bool* deleted_;
177};
178
179TEST_F(CallbackTest, CallbackHasLastRefOnContainingObject) {
180  bool deleted = false;
181  CallbackOwner* owner = new CallbackOwner(&deleted);
182  owner->Reset();
183  ASSERT_TRUE(deleted);
184}
185
186}  // namespace
187}  // namespace base
188