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); |
}; |