OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 // Note: This file tests both binding.h (mojo::Binding) and strong_binding.h |
| 6 // (mojo::StrongBinding). |
| 7 |
5 #include "mojo/public/cpp/bindings/binding.h" | 8 #include "mojo/public/cpp/bindings/binding.h" |
| 9 #include "mojo/public/cpp/bindings/strong_binding.h" |
6 #include "mojo/public/cpp/environment/environment.h" | 10 #include "mojo/public/cpp/environment/environment.h" |
| 11 #include "mojo/public/cpp/system/macros.h" |
7 #include "mojo/public/cpp/utility/run_loop.h" | 12 #include "mojo/public/cpp/utility/run_loop.h" |
8 #include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" | 13 #include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" |
9 #include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" | 14 #include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" |
10 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
11 | 16 |
12 namespace mojo { | 17 namespace mojo { |
13 namespace { | 18 namespace { |
14 | 19 |
| 20 class BindingTestBase : public testing::Test { |
| 21 public: |
| 22 BindingTestBase() {} |
| 23 ~BindingTestBase() override {} |
| 24 |
| 25 RunLoop& loop() { return loop_; } |
| 26 |
| 27 private: |
| 28 Environment env_; |
| 29 RunLoop loop_; |
| 30 |
| 31 MOJO_DISALLOW_COPY_AND_ASSIGN(BindingTestBase); |
| 32 }; |
| 33 |
15 class ServiceImpl : public sample::Service { | 34 class ServiceImpl : public sample::Service { |
16 public: | 35 public: |
17 ServiceImpl() {} | 36 explicit ServiceImpl(bool* was_deleted = nullptr) |
18 ~ServiceImpl() override {} | 37 : was_deleted_(was_deleted) {} |
| 38 ~ServiceImpl() override { |
| 39 if (was_deleted_) |
| 40 *was_deleted_ = true; |
| 41 } |
19 | 42 |
20 private: | 43 private: |
21 // sample::Service implementation | 44 // sample::Service implementation |
22 void Frobinate(sample::FooPtr foo, | 45 void Frobinate(sample::FooPtr foo, |
23 BazOptions options, | 46 BazOptions options, |
24 sample::PortPtr port, | 47 sample::PortPtr port, |
25 const FrobinateCallback& callback) override { | 48 const FrobinateCallback& callback) override { |
26 callback.Run(1); | 49 callback.Run(1); |
27 } | 50 } |
28 void GetPort(InterfaceRequest<sample::Port> port) override {} | 51 void GetPort(InterfaceRequest<sample::Port> port) override {} |
| 52 |
| 53 bool* const was_deleted_; |
| 54 |
| 55 MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImpl); |
29 }; | 56 }; |
30 | 57 |
31 class IntegerAccessorImpl : public sample::IntegerAccessor { | 58 // BindingTest ----------------------------------------------------------------- |
32 public: | |
33 IntegerAccessorImpl() {} | |
34 ~IntegerAccessorImpl() override {} | |
35 | 59 |
36 private: | 60 using BindingTest = BindingTestBase; |
37 // sample::IntegerAccessor implementation. | |
38 void GetInteger(const GetIntegerCallback& callback) override { | |
39 callback.Run(1, sample::ENUM_VALUE); | |
40 } | |
41 void SetInteger(int64_t data, sample::Enum type) override {} | |
42 }; | |
43 | 61 |
44 class BindingTest : public testing::Test { | 62 TEST_F(BindingTest, Close) { |
45 public: | 63 bool called = false; |
46 BindingTest() {} | 64 sample::ServicePtr ptr; |
47 ~BindingTest() override {} | 65 auto request = GetProxy(&ptr); |
| 66 ptr.set_connection_error_handler([&called]() { called = true; }); |
| 67 ServiceImpl impl; |
| 68 Binding<sample::Service> binding(&impl, request.Pass()); |
48 | 69 |
49 protected: | 70 binding.Close(); |
50 Environment env_; | 71 EXPECT_FALSE(called); |
51 RunLoop loop_; | 72 loop().RunUntilIdle(); |
52 }; | 73 EXPECT_TRUE(called); |
| 74 } |
53 | 75 |
54 // Tests that destroying a mojo::Binding closes the bound message pipe handle. | 76 // Tests that destroying a mojo::Binding closes the bound message pipe handle. |
55 TEST_F(BindingTest, DestroyClosesMessagePipe) { | 77 TEST_F(BindingTest, DestroyClosesMessagePipe) { |
56 bool encountered_error = false; | 78 bool encountered_error = false; |
57 ServiceImpl impl; | 79 ServiceImpl impl; |
58 sample::ServicePtr ptr; | 80 sample::ServicePtr ptr; |
59 auto request = GetProxy(&ptr); | 81 auto request = GetProxy(&ptr); |
60 ptr.set_connection_error_handler( | 82 ptr.set_connection_error_handler( |
61 [&encountered_error]() { encountered_error = true; }); | 83 [&encountered_error]() { encountered_error = true; }); |
62 bool called = false; | 84 bool called = false; |
63 auto called_cb = [&called](int32_t result) { called = true; }; | 85 auto called_cb = [&called](int32_t result) { called = true; }; |
64 { | 86 { |
65 Binding<sample::Service> binding(&impl, request.Pass()); | 87 Binding<sample::Service> binding(&impl, request.Pass()); |
66 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 88 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
67 called_cb); | 89 called_cb); |
68 loop_.RunUntilIdle(); | 90 loop().RunUntilIdle(); |
69 EXPECT_TRUE(called); | 91 EXPECT_TRUE(called); |
70 EXPECT_FALSE(encountered_error); | 92 EXPECT_FALSE(encountered_error); |
71 } | 93 } |
72 // Now that the Binding is out of scope we should detect an error on the other | 94 // Now that the Binding is out of scope we should detect an error on the other |
73 // end of the pipe. | 95 // end of the pipe. |
74 loop_.RunUntilIdle(); | 96 loop().RunUntilIdle(); |
75 EXPECT_TRUE(encountered_error); | 97 EXPECT_TRUE(encountered_error); |
76 | 98 |
77 // And calls should fail. | 99 // And calls should fail. |
78 called = false; | 100 called = false; |
79 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 101 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
80 called_cb); | 102 called_cb); |
81 loop_.RunUntilIdle(); | 103 loop().RunUntilIdle(); |
82 EXPECT_FALSE(called); | 104 EXPECT_FALSE(called); |
83 } | 105 } |
84 | 106 |
| 107 // Tests that the binding's connection error handler gets called when the other |
| 108 // end is closed. |
| 109 TEST_F(BindingTest, ConnectionError) { |
| 110 bool called = false; |
| 111 { |
| 112 ServiceImpl impl; |
| 113 sample::ServicePtr ptr; |
| 114 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); |
| 115 binding.set_connection_error_handler([&called]() { called = true; }); |
| 116 ptr.reset(); |
| 117 EXPECT_FALSE(called); |
| 118 loop().RunUntilIdle(); |
| 119 EXPECT_TRUE(called); |
| 120 // We want to make sure that it isn't called again during destruction. |
| 121 called = false; |
| 122 } |
| 123 EXPECT_FALSE(called); |
| 124 } |
| 125 |
| 126 // Tests that calling Close doesn't result in the connection error handler being |
| 127 // called. |
| 128 TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) { |
| 129 ServiceImpl impl; |
| 130 sample::ServicePtr ptr; |
| 131 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); |
| 132 bool called = false; |
| 133 binding.set_connection_error_handler([&called]() { called = true; }); |
| 134 binding.Close(); |
| 135 loop().RunUntilIdle(); |
| 136 EXPECT_FALSE(called); |
| 137 |
| 138 // We can also close the other end, and the error handler still won't be |
| 139 // called. |
| 140 ptr.reset(); |
| 141 loop().RunUntilIdle(); |
| 142 EXPECT_FALSE(called); |
| 143 } |
| 144 |
| 145 class ServiceImplWithBinding : public ServiceImpl { |
| 146 public: |
| 147 ServiceImplWithBinding(bool* was_deleted, |
| 148 InterfaceRequest<sample::Service> request) |
| 149 : ServiceImpl(was_deleted), binding_(this, request.Pass()) { |
| 150 binding_.set_connection_error_handler([this]() { delete this; }); |
| 151 } |
| 152 |
| 153 private: |
| 154 Binding<sample::Service> binding_; |
| 155 |
| 156 MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding); |
| 157 }; |
| 158 |
| 159 // Tests that the binding may be deleted in the connection error handler. |
| 160 TEST_F(BindingTest, SelfDeleteOnConnectionError) { |
| 161 bool was_deleted = false; |
| 162 sample::ServicePtr ptr; |
| 163 // This should delete itself on connection error. |
| 164 new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr)); |
| 165 ptr.reset(); |
| 166 EXPECT_FALSE(was_deleted); |
| 167 loop().RunUntilIdle(); |
| 168 EXPECT_TRUE(was_deleted); |
| 169 } |
| 170 |
85 // Tests that explicitly calling Unbind followed by rebinding works. | 171 // Tests that explicitly calling Unbind followed by rebinding works. |
86 TEST_F(BindingTest, Unbind) { | 172 TEST_F(BindingTest, Unbind) { |
87 ServiceImpl impl; | 173 ServiceImpl impl; |
88 sample::ServicePtr ptr; | 174 sample::ServicePtr ptr; |
89 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); | 175 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); |
90 | 176 |
91 bool called = false; | 177 bool called = false; |
92 auto called_cb = [&called](int32_t result) { called = true; }; | 178 auto called_cb = [&called](int32_t result) { called = true; }; |
93 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 179 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
94 called_cb); | 180 called_cb); |
95 loop_.RunUntilIdle(); | 181 loop().RunUntilIdle(); |
96 EXPECT_TRUE(called); | 182 EXPECT_TRUE(called); |
97 | 183 |
98 called = false; | 184 called = false; |
99 auto request = binding.Unbind(); | 185 auto request = binding.Unbind(); |
100 EXPECT_FALSE(binding.is_bound()); | 186 EXPECT_FALSE(binding.is_bound()); |
101 // All calls should fail when not bound... | 187 // All calls should fail when not bound... |
102 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 188 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
103 called_cb); | 189 called_cb); |
104 loop_.RunUntilIdle(); | 190 loop().RunUntilIdle(); |
105 EXPECT_FALSE(called); | 191 EXPECT_FALSE(called); |
106 | 192 |
107 called = false; | 193 called = false; |
108 binding.Bind(request.Pass()); | 194 binding.Bind(request.Pass()); |
109 EXPECT_TRUE(binding.is_bound()); | 195 EXPECT_TRUE(binding.is_bound()); |
110 // ...and should succeed again when the rebound. | 196 // ...and should succeed again when the rebound. |
111 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 197 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
112 called_cb); | 198 called_cb); |
113 loop_.RunUntilIdle(); | 199 loop().RunUntilIdle(); |
114 EXPECT_TRUE(called); | 200 EXPECT_TRUE(called); |
115 } | 201 } |
116 | 202 |
| 203 class IntegerAccessorImpl : public sample::IntegerAccessor { |
| 204 public: |
| 205 IntegerAccessorImpl() {} |
| 206 ~IntegerAccessorImpl() override {} |
| 207 |
| 208 private: |
| 209 // sample::IntegerAccessor implementation. |
| 210 void GetInteger(const GetIntegerCallback& callback) override { |
| 211 callback.Run(1, sample::ENUM_VALUE); |
| 212 } |
| 213 void SetInteger(int64_t data, sample::Enum type) override {} |
| 214 |
| 215 MOJO_DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl); |
| 216 }; |
| 217 |
117 TEST_F(BindingTest, SetInterfacePtrVersion) { | 218 TEST_F(BindingTest, SetInterfacePtrVersion) { |
118 IntegerAccessorImpl impl; | 219 IntegerAccessorImpl impl; |
119 sample::IntegerAccessorPtr ptr; | 220 sample::IntegerAccessorPtr ptr; |
120 Binding<sample::IntegerAccessor> binding(&impl, &ptr); | 221 Binding<sample::IntegerAccessor> binding(&impl, &ptr); |
121 EXPECT_EQ(3u, ptr.version()); | 222 EXPECT_EQ(3u, ptr.version()); |
122 } | 223 } |
123 | 224 |
| 225 // StrongBindingTest ----------------------------------------------------------- |
| 226 |
| 227 using StrongBindingTest = BindingTestBase; |
| 228 |
| 229 // Tests that destroying a mojo::StrongBinding closes the bound message pipe |
| 230 // handle but does *not* destroy the implementation object. |
| 231 TEST_F(StrongBindingTest, DestroyClosesMessagePipe) { |
| 232 bool encountered_error = false; |
| 233 bool was_deleted = false; |
| 234 ServiceImpl impl(&was_deleted); |
| 235 sample::ServicePtr ptr; |
| 236 auto request = GetProxy(&ptr); |
| 237 ptr.set_connection_error_handler( |
| 238 [&encountered_error]() { encountered_error = true; }); |
| 239 bool called = false; |
| 240 auto called_cb = [&called](int32_t result) { called = true; }; |
| 241 { |
| 242 StrongBinding<sample::Service> binding(&impl, request.Pass()); |
| 243 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
| 244 called_cb); |
| 245 loop().RunUntilIdle(); |
| 246 EXPECT_TRUE(called); |
| 247 EXPECT_FALSE(encountered_error); |
| 248 } |
| 249 // Now that the StrongBinding is out of scope we should detect an error on the |
| 250 // other end of the pipe. |
| 251 loop().RunUntilIdle(); |
| 252 EXPECT_TRUE(encountered_error); |
| 253 // But destroying the StrongBinding doesn't destroy the object. |
| 254 ASSERT_FALSE(was_deleted); |
| 255 } |
| 256 |
| 257 class ServiceImplWithStrongBinding : public ServiceImpl { |
| 258 public: |
| 259 ServiceImplWithStrongBinding(bool* was_deleted, |
| 260 InterfaceRequest<sample::Service> request) |
| 261 : ServiceImpl(was_deleted), binding_(this, request.Pass()) {} |
| 262 |
| 263 StrongBinding<sample::Service>& binding() { return binding_; } |
| 264 |
| 265 private: |
| 266 StrongBinding<sample::Service> binding_; |
| 267 |
| 268 MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithStrongBinding); |
| 269 }; |
| 270 |
| 271 // Tests the typical case, where the implementation object owns the |
| 272 // StrongBinding (and should be destroyed on connection error). |
| 273 TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) { |
| 274 sample::ServicePtr ptr; |
| 275 bool was_deleted = false; |
| 276 // Will delete itself. |
| 277 new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr)); |
| 278 |
| 279 loop().RunUntilIdle(); |
| 280 EXPECT_FALSE(was_deleted); |
| 281 |
| 282 ptr.reset(); |
| 283 EXPECT_FALSE(was_deleted); |
| 284 loop().RunUntilIdle(); |
| 285 EXPECT_TRUE(was_deleted); |
| 286 } |
| 287 |
| 288 // Tests that even when the implementation object owns the StrongBinding, that |
| 289 // the implementation can still be deleted (which should result in the message |
| 290 // pipe being closed). Also checks that the connection error handler doesn't get |
| 291 // called. |
| 292 TEST_F(StrongBindingTest, ExplicitDeleteImpl) { |
| 293 bool ptr_error_handler_called = false; |
| 294 sample::ServicePtr ptr; |
| 295 auto request = GetProxy(&ptr); |
| 296 ptr.set_connection_error_handler( |
| 297 [&ptr_error_handler_called]() { ptr_error_handler_called = true; }); |
| 298 bool was_deleted = false; |
| 299 ServiceImplWithStrongBinding* impl = |
| 300 new ServiceImplWithStrongBinding(&was_deleted, request.Pass()); |
| 301 bool binding_error_handler_called = false; |
| 302 impl->binding().set_connection_error_handler( |
| 303 [&binding_error_handler_called]() { |
| 304 binding_error_handler_called = true; |
| 305 }); |
| 306 |
| 307 loop().RunUntilIdle(); |
| 308 EXPECT_FALSE(ptr_error_handler_called); |
| 309 EXPECT_FALSE(was_deleted); |
| 310 |
| 311 delete impl; |
| 312 EXPECT_FALSE(ptr_error_handler_called); |
| 313 EXPECT_TRUE(was_deleted); |
| 314 was_deleted = false; // It shouldn't be double-deleted! |
| 315 loop().RunUntilIdle(); |
| 316 EXPECT_TRUE(ptr_error_handler_called); |
| 317 EXPECT_FALSE(was_deleted); |
| 318 |
| 319 EXPECT_FALSE(binding_error_handler_called); |
| 320 } |
| 321 |
124 } // namespace | 322 } // namespace |
125 } // mojo | 323 } // mojo |
OLD | NEW |