Chromium Code Reviews| Index: remoting/host/mock_callback.h |
| diff --git a/remoting/host/mock_callback.h b/remoting/host/mock_callback.h |
| index ca3feb6d06a415f1853b363e7b089f8b0a03ad7a..8f6bedfa44f06ef5b4e86dc0ba1e3ab6516556b8 100644 |
| --- a/remoting/host/mock_callback.h |
| +++ b/remoting/host/mock_callback.h |
| @@ -10,11 +10,69 @@ |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/synchronization/lock.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| namespace remoting { |
| +// MockCallback<Sig> can be used to mock Callback<Sig> from base/callback.h |
| +// |
| +// Basic usage example: |
| +// |
| +// MockCallback<void(int)> mock_callback; |
| +// EXPECT_CALL(mock_callback, Run(123)).Times(2); |
| +// base::Callback<void(int)> callback = mock_callback.GetCallback(); |
|
Wez
2014/12/03 19:45:49
Is it important that GetCallback() is only called
Łukasz Anforowicz
2014/12/03 22:00:44
Sort of. EXPECT_CALL is not thread-safe wrt Callb
|
| +// |
| +// // Use |callback| in the remainder of the test. |
| +// // GMock will enforce the expectations from above. |
| +// |
| +// HasRemainingCallbacks usage example: |
|
Wez
2014/12/03 19:45:49
HasRemainingCallbacks has not been introduced yet,
Łukasz Anforowicz
2014/12/03 22:00:44
Good point. I am not sure about this
- Some tests
|
| +// |
| +// MockCallback<int()> mock_callback; |
| +// EXPECT_CALL(mock_callback, Run()).WillOnce(Return(123)); |
| +// HypotheticalFunctionUnderTest(mock_callback.GetCallback()); |
| +// EXPECT_FALSE(mock_callback.HasRemainingCallbacks()) |
| +// << "Verify that FunctionUnderTest didn't store the callback " |
| +// << "and therefore callers of FunctionUnderTest don't have to " |
| +// << "worry about lifetime of arguments bound to the callback"; |
|
Wez
2014/12/03 19:45:49
You need to be very clear on the threading semanti
Łukasz Anforowicz
2014/12/03 22:00:44
I don't understand:
- HasRemainingCallbacks is thr
|
| +// |
| +// Thread safety notes: |
| +// |
| +// Calling Run on a Callback returned from MockCallback::GetCallback |
| +// - Is *not* thread-safe wrt setting default actions and expectations (via |
| +// ON_CALL and EXPECT_CALL gMock macros). |
|
Wez
2014/12/03 19:45:49
This is not very clear; you mean that the call exp
Łukasz Anforowicz
2014/12/03 22:00:44
One cannot call EXPECT_CALL and Run in parallel.
|
| +// - Is thread-safe wrt other Run calls and destroying MockCallback. |
|
Wez
2014/12/03 19:45:49
What are the semantics if a callback still exists
Łukasz Anforowicz
2014/12/03 22:00:44
This is documented in "What happens when Callback:
|
| +// |
| +// This is similar to the guarantees described at |
| +// https://code.google.com/p/googlemock/wiki/CookBook#Using_Google_Mock_and_Threads |
| +// except that the guarantees hold on all platforms. |
| +// |
| +// What happens when Callback::Run is called after MockCallback is destroyed: |
|
Wez
2014/12/03 19:45:49
This contradicts the GetCallback comment, though.
Łukasz Anforowicz
2014/12/03 22:00:44
Acknowledged.
|
| +// |
| +// - gTest EXPECT failure will be triggered |
| +// - gMock's default return value will be returned - see: |
| +// https://code.google.com/p/googlemock/wiki/CookBook#Setting_the_Default_Value_for_a_Return_Type |
| + |
| +// Implementation notes: |
| +// |
| +// WeakPtr-like code |
| +// ----------------- |
| +// |
| +// Relationship between MockCallback and MockCallbackTracker is similar |
| +// to the relationship between WeakPtrFactory<T> and WeakPtr<T>. We don't |
| +// use WeakPtrFactory<T> and WeakPtr<T>, because: |
|
Wez
2014/12/03 19:45:49
This comment is very confusing; it's not clear wha
|
| +// - WeakPtrs can only bind to methods without return values |
| +// (enforced by a static assert in bind_internal.h:402). |
| +// - WeakPtr impose task-specific, thread-safety-restrictions that are |
| +// not present in Callback that MockCallback is mocking. |
| +// - For MockCallback we don't necessarily want the behavior where Callbacks |
| +// bound to WeakPtr are translated into no-ops, before the bound function |
| +// gets to execute. In MockCallback case we want the test to have control of |
| +// what happens in this case (i.e. gtest EXPECT, rather than a no-op; |
| +// note that we cannot use gtest ASSERT for non-void-returning Run method). |
| + |
| template <typename Sig> |
| class MockCallback; |
| @@ -23,16 +81,114 @@ class MockCallback<R()> { |
| public: |
| MOCK_CONST_METHOD0_T(Run, R()); |
| - MockCallback() { |
| + MockCallback() : mock_callback_tracker_(new MockCallbackTracker(this)) {} |
| + |
| + ~MockCallback() { mock_callback_tracker_->MarkMockCallbackAsDead(); } |
| + |
| + // Caller of GetCallback has to guarantee that the returned callback |
| + // will not be run after |this| is destroyed. |
| + base::Callback<R()> GetCallback() const { |
| + return base::Bind(&MockCallbackTracker::Run, mock_callback_tracker_); |
| + } |
| + |
| + // Returns |true| if callbacks returned by |GetCallback| have not been |
| + // destroyed yet. |
| + bool HasRemainingCallbacks() const { |
| + bool is_tracker_field_the_only_ref = mock_callback_tracker_->HasOneRef(); |
| + return !is_tracker_field_the_only_ref; |
| } |
| + private: |
| + class MockCallbackTracker |
| + : public base::RefCountedThreadSafe<MockCallbackTracker> { |
| + public: |
| + MockCallbackTracker(MockCallback* mock_callback) |
| + : mock_callback_(mock_callback), is_mock_callback_alive_(true) {} |
| + |
| + R Run() { |
| + base::AutoLock auto_lock(lock_); |
| + EXPECT_TRUE(is_mock_callback_alive_) |
| + << "MockCallback should be alive when Callback::Run happens."; |
| + return is_mock_callback_alive_ ? mock_callback_->Run() |
| + : testing::DefaultValue<R>::Get(); |
| + } |
| + |
| + void MarkMockCallbackAsDead() { |
| + base::AutoLock auto_lock(lock_); |
| + is_mock_callback_alive_ = false; |
| + mock_callback_ = nullptr; |
| + } |
| + |
| + private: |
| + // Ref-counting helpers. |
| + friend class base::RefCountedThreadSafe<MockCallbackTracker>; |
| + ~MockCallbackTracker() {} |
| + |
| + MockCallback<R()>* mock_callback_; |
| + base::Lock lock_; |
| + bool is_mock_callback_alive_; |
| + }; |
| + |
| + scoped_refptr<MockCallbackTracker> mock_callback_tracker_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MockCallback); |
| +}; |
| + |
| +template <typename R, typename A1> |
| +class MockCallback<R(A1)> { |
| + public: |
| + MOCK_CONST_METHOD1_T(Run, R(A1)); |
| + |
| + MockCallback() : mock_callback_tracker_(new MockCallbackTracker(this)) {} |
| + |
| + ~MockCallback() { mock_callback_tracker_->MarkMockCallbackAsDead(); } |
| + |
| // Caller of GetCallback has to guarantee that the returned callback |
| // will not be run after |this| is destroyed. |
| - base::Callback<R()> GetCallback() { |
| - return base::Bind(&MockCallback<R()>::Run, base::Unretained(this)); |
| + base::Callback<R(A1)> GetCallback() const { |
| + return base::Bind(&MockCallbackTracker::Run, mock_callback_tracker_); |
| + } |
| + |
| + // Returns |true| if callbacks returned by |GetCallback| have not been |
| + // destroyed yet. |
| + bool HasRemainingCallbacks() const { |
| + bool is_tracker_field_the_only_ref = mock_callback_tracker_->HasOneRef(); |
| + return !is_tracker_field_the_only_ref; |
| } |
| private: |
| + class MockCallbackTracker |
| + : public base::RefCountedThreadSafe<MockCallbackTracker> { |
| + public: |
| + MockCallbackTracker(MockCallback* mock_callback) |
| + : mock_callback_(mock_callback), is_mock_callback_alive_(true) {} |
| + |
| + R Run(A1 a1) { |
| + base::AutoLock auto_lock(lock_); |
| + EXPECT_TRUE(is_mock_callback_alive_) |
| + << "MockCallback should be alive when Callback::Run happens."; |
| + return is_mock_callback_alive_ ? mock_callback_->Run(a1) |
| + : testing::DefaultValue<R>::Get(); |
| + } |
| + |
| + void MarkMockCallbackAsDead() { |
| + base::AutoLock auto_lock(lock_); |
| + is_mock_callback_alive_ = false; |
| + mock_callback_ = nullptr; |
| + } |
| + |
| + private: |
| + // Ref-counting helpers. |
| + friend class base::RefCountedThreadSafe<MockCallbackTracker>; |
| + ~MockCallbackTracker() {} |
| + |
| + MockCallback<R(A1)>* mock_callback_; |
| + base::Lock lock_; |
| + bool is_mock_callback_alive_; |
| + }; |
| + |
| + scoped_refptr<MockCallbackTracker> mock_callback_tracker_; |
| + |
| DISALLOW_COPY_AND_ASSIGN(MockCallback); |
| }; |