OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 <stddef.h> | |
6 #include <stdint.h> | |
7 | |
8 #include <utility> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/guid.h" | |
12 #include "base/macros.h" | |
13 #include "base/run_loop.h" | |
14 #include "base/test/test_suite.h" | |
15 #include "mojo/public/cpp/bindings/binding_set.h" | |
16 #include "mojo/shell/public/cpp/names.h" | |
17 #include "mojo/shell/public/cpp/shell_test.h" | |
18 #include "mojo/shell/public/interfaces/shell.mojom.h" | |
19 #include "mojo/shell/tests/connect/connect_test.mojom.h" | |
20 | |
21 // Tests that multiple applications can be packaged in a single Mojo application | |
22 // implementing ShellClientFactory; that these applications can be specified by | |
23 // the package's manifest and are thus registered with the PackageManager. | |
24 | |
25 namespace mojo { | |
26 namespace shell { | |
27 namespace { | |
28 const char kTestPackageName[] = "mojo:connect_test_package"; | |
29 const char kTestAppName[] = "mojo:connect_test_app"; | |
30 const char kTestAppAName[] = "mojo:connect_test_a"; | |
31 const char kTestAppBName[] = "mojo:connect_test_b"; | |
32 const char kTestClassAppName[] = "mojo:connect_test_class_app"; | |
33 const char kTestSingletonAppName[] = "mojo:connect_test_singleton_app"; | |
34 const char kTestDriverName[] = "exe:connect_test_driver"; | |
35 | |
36 void ReceiveOneString(std::string* out_string, | |
37 base::RunLoop* loop, | |
38 const String& in_string) { | |
39 *out_string = in_string; | |
40 loop->Quit(); | |
41 } | |
42 | |
43 void ReceiveTwoStrings(std::string* out_string_1, std::string* out_string_2, | |
44 base::RunLoop* loop, | |
45 const String& in_string_1, const String& in_string_2) { | |
46 *out_string_1 = in_string_1; | |
47 *out_string_2 = in_string_2; | |
48 loop->Quit(); | |
49 } | |
50 | |
51 void ReceiveConnectionResult(mojom::ConnectResult* out_result, | |
52 Identity* out_target, | |
53 base::RunLoop* loop, | |
54 int32_t in_result, | |
55 mojom::IdentityPtr in_identity) { | |
56 *out_result = static_cast<shell::mojom::ConnectResult>(in_result); | |
57 *out_target = in_identity.To<Identity>(); | |
58 loop->Quit(); | |
59 } | |
60 | |
61 void QuitLoop(base::RunLoop* loop) { | |
62 loop->Quit(); | |
63 } | |
64 | |
65 } // namespace | |
66 | |
67 class ConnectTest : public mojo::test::ShellTest, | |
68 public InterfaceFactory<test::mojom::ExposedInterface>, | |
69 public test::mojom::ExposedInterface { | |
70 public: | |
71 ConnectTest() : ShellTest("mojo:connect_unittests") {} | |
72 ~ConnectTest() override {} | |
73 | |
74 protected: | |
75 scoped_ptr<Connection> ConnectTo(Connector::ConnectParams* params) { | |
76 scoped_ptr<Connection> connection = connector()->Connect(params); | |
77 base::RunLoop loop; | |
78 connection->AddConnectionCompletedClosure(base::Bind(&QuitLoop, &loop)); | |
79 loop.Run(); | |
80 return connection; | |
81 } | |
82 | |
83 void set_ping_callback(const base::Closure& callback) { | |
84 ping_callback_ = callback; | |
85 } | |
86 | |
87 void CompareConnectionState( | |
88 const std::string& connection_local_name, | |
89 const std::string& connection_remote_name, | |
90 const std::string& connection_remote_userid, | |
91 uint32_t connection_remote_id, | |
92 const std::string& initialize_local_name, | |
93 const std::string& initialize_userid, | |
94 uint32_t initialize_id) { | |
95 EXPECT_EQ(connection_local_name, connection_state_->connection_local_name); | |
96 EXPECT_EQ(connection_remote_name, | |
97 connection_state_->connection_remote_name); | |
98 EXPECT_EQ(connection_remote_userid, | |
99 connection_state_->connection_remote_userid); | |
100 EXPECT_EQ(connection_remote_id, connection_state_->connection_remote_id); | |
101 EXPECT_EQ(initialize_local_name, connection_state_->initialize_local_name); | |
102 EXPECT_EQ(initialize_id, connection_state_->initialize_id); | |
103 EXPECT_EQ(initialize_userid, connection_state_->initialize_userid); | |
104 } | |
105 | |
106 private: | |
107 // mojo::test::ShellTest: | |
108 void SetUp() override { | |
109 mojo::test::ShellTest::SetUp(); | |
110 // We need to connect to the package first to force the shell to read the | |
111 // package app's manifest and register aliases for the applications it | |
112 // provides. | |
113 test::mojom::ConnectTestServicePtr root_service; | |
114 scoped_ptr<Connection> connection = connector()->Connect(kTestPackageName); | |
115 connection->GetInterface(&root_service); | |
116 base::RunLoop run_loop; | |
117 std::string root_name; | |
118 root_service->GetTitle( | |
119 base::Bind(&ReceiveOneString, &root_name, &run_loop)); | |
120 run_loop.Run(); | |
121 } | |
122 | |
123 // InterfaceFactory<test::mojom::ExposedInterface>: | |
124 void Create(Connection* connection, | |
125 test::mojom::ExposedInterfaceRequest request) override { | |
126 bindings_.AddBinding(this, std::move(request)); | |
127 } | |
128 void ConnectionAccepted(test::mojom::ConnectionStatePtr state) override { | |
129 connection_state_ = std::move(state); | |
130 ping_callback_.Run(); | |
131 } | |
132 | |
133 base::Closure ping_callback_; | |
134 test::mojom::ConnectionStatePtr connection_state_; | |
135 | |
136 BindingSet<test::mojom::ExposedInterface> bindings_; | |
137 | |
138 DISALLOW_COPY_AND_ASSIGN(ConnectTest); | |
139 }; | |
140 | |
141 // Ensure the connection was properly established and that a round trip | |
142 // method call/response is completed. | |
143 TEST_F(ConnectTest, Connect) { | |
144 scoped_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
145 test::mojom::ConnectTestServicePtr service; | |
146 connection->GetInterface(&service); | |
147 base::RunLoop run_loop; | |
148 std::string title; | |
149 service->GetTitle(base::Bind(&ReceiveOneString, &title, &run_loop)); | |
150 run_loop.Run(); | |
151 EXPECT_EQ("APP", title); | |
152 EXPECT_FALSE(connection->IsPending()); | |
153 EXPECT_NE(mojom::kInvalidInstanceID, connection->GetRemoteInstanceID()); | |
154 EXPECT_EQ(connection->GetRemoteIdentity().name(), kTestAppName); | |
155 } | |
156 | |
157 TEST_F(ConnectTest, Instances) { | |
158 Connector::ConnectParams params_a( | |
159 Identity(kTestAppName, mojom::kInheritUserID, "A")); | |
160 scoped_ptr<Connection> connection_a1 = ConnectTo(¶ms_a); | |
161 scoped_ptr<Connection> connection_a2 = ConnectTo(¶ms_a); | |
162 std::string instance_a1, instance_a2; | |
163 test::mojom::ConnectTestServicePtr service_a1; | |
164 { | |
165 connection_a1->GetInterface(&service_a1); | |
166 base::RunLoop loop; | |
167 service_a1->GetInstance(base::Bind(&ReceiveOneString, &instance_a1, &loop)); | |
168 loop.Run(); | |
169 } | |
170 test::mojom::ConnectTestServicePtr service_a2; | |
171 { | |
172 connection_a2->GetInterface(&service_a2); | |
173 base::RunLoop loop; | |
174 service_a2->GetInstance(base::Bind(&ReceiveOneString, &instance_a2, &loop)); | |
175 loop.Run(); | |
176 } | |
177 EXPECT_EQ(instance_a1, instance_a2); | |
178 | |
179 Connector::ConnectParams params_b( | |
180 Identity(kTestAppName, mojom::kInheritUserID, "B")); | |
181 scoped_ptr<Connection> connection_b = ConnectTo(¶ms_b); | |
182 std::string instance_b; | |
183 test::mojom::ConnectTestServicePtr service_b; | |
184 { | |
185 connection_b->GetInterface(&service_b); | |
186 base::RunLoop loop; | |
187 service_b->GetInstance(base::Bind(&ReceiveOneString, &instance_b, &loop)); | |
188 loop.Run(); | |
189 } | |
190 | |
191 EXPECT_NE(instance_a1, instance_b); | |
192 } | |
193 | |
194 // When both the unresolved and resolved instance names are their default | |
195 // values, the instance name from the unresolved name must be used. | |
196 // (The case where the instance names differ is covered by | |
197 // LifecycleTest.PackagedApp_CrashCrashesOtherProvidedApp). | |
198 TEST_F(ConnectTest, PreferUnresolvedDefaultInstanceName) { | |
199 // Connect to an app with no manifest-supplied instance name provided by a | |
200 // package, the instance name must be derived from the application instance | |
201 // name, not the package. | |
202 scoped_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
203 { | |
204 base::RunLoop loop; | |
205 connection->AddConnectionCompletedClosure(base::Bind(&QuitLoop, &loop)); | |
206 loop.Run(); | |
207 } | |
208 | |
209 std::string instance; | |
210 { | |
211 test::mojom::ConnectTestServicePtr service; | |
212 connection->GetInterface(&service); | |
213 base::RunLoop loop; | |
214 service->GetInstance(base::Bind(&ReceiveOneString, &instance, &loop)); | |
215 loop.Run(); | |
216 } | |
217 EXPECT_EQ(GetNamePath(kTestAppName), instance); | |
218 } | |
219 | |
220 // BlockedInterface should not be exposed to this application because it is not | |
221 // in our CapabilityFilter whitelist. | |
222 TEST_F(ConnectTest, BlockedInterface) { | |
223 scoped_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
224 base::RunLoop run_loop; | |
225 test::mojom::BlockedInterfacePtr blocked; | |
226 connection->GetInterface(&blocked); | |
227 blocked.set_connection_error_handler(base::Bind(&QuitLoop, &run_loop)); | |
228 std::string title = "unchanged"; | |
229 blocked->GetTitleBlocked(base::Bind(&ReceiveOneString, &title, &run_loop)); | |
230 run_loop.Run(); | |
231 EXPECT_EQ("unchanged", title); | |
232 } | |
233 | |
234 // Connects to an app provided by a package. | |
235 TEST_F(ConnectTest, PackagedApp) { | |
236 scoped_ptr<Connection> connection = connector()->Connect(kTestAppAName); | |
237 test::mojom::ConnectTestServicePtr service_a; | |
238 connection->GetInterface(&service_a); | |
239 base::RunLoop run_loop; | |
240 std::string a_name; | |
241 service_a->GetTitle(base::Bind(&ReceiveOneString, &a_name, &run_loop)); | |
242 run_loop.Run(); | |
243 EXPECT_EQ("A", a_name); | |
244 EXPECT_FALSE(connection->IsPending()); | |
245 EXPECT_NE(mojom::kInvalidInstanceID, connection->GetRemoteInstanceID()); | |
246 EXPECT_EQ(connection->GetRemoteIdentity().name(), kTestAppAName); | |
247 } | |
248 | |
249 // Ask the target application to attempt to connect to a third application | |
250 // provided by a package whose id is permitted by the primary target's | |
251 // CapabilityFilter but whose package is not. The connection should be | |
252 // blocked and the returned title should be "uninitialized". | |
253 TEST_F(ConnectTest, BlockedPackage) { | |
254 scoped_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
255 test::mojom::StandaloneAppPtr standalone_app; | |
256 connection->GetInterface(&standalone_app); | |
257 base::RunLoop run_loop; | |
258 std::string title; | |
259 standalone_app->ConnectToAllowedAppInBlockedPackage( | |
260 base::Bind(&ReceiveOneString, &title, &run_loop)); | |
261 run_loop.Run(); | |
262 EXPECT_EQ("uninitialized", title); | |
263 } | |
264 | |
265 // BlockedInterface should not be exposed to this application because it is not | |
266 // in our CapabilityFilter whitelist. | |
267 TEST_F(ConnectTest, PackagedApp_BlockedInterface) { | |
268 scoped_ptr<Connection> connection = connector()->Connect(kTestAppAName); | |
269 base::RunLoop run_loop; | |
270 test::mojom::BlockedInterfacePtr blocked; | |
271 connection->GetInterface(&blocked); | |
272 blocked.set_connection_error_handler(base::Bind(&QuitLoop, &run_loop)); | |
273 run_loop.Run(); | |
274 } | |
275 | |
276 // Connection to another application provided by the same package, blocked | |
277 // because it's not in the capability filter whitelist. | |
278 TEST_F(ConnectTest, BlockedPackagedApplication) { | |
279 scoped_ptr<Connection> connection = connector()->Connect(kTestAppBName); | |
280 test::mojom::ConnectTestServicePtr service_b; | |
281 connection->GetInterface(&service_b); | |
282 base::RunLoop run_loop; | |
283 connection->SetConnectionLostClosure(base::Bind(&QuitLoop, &run_loop)); | |
284 run_loop.Run(); | |
285 EXPECT_FALSE(connection->IsPending()); | |
286 EXPECT_EQ(mojom::ConnectResult::ACCESS_DENIED, connection->GetResult()); | |
287 EXPECT_EQ(mojom::kInvalidInstanceID, connection->GetRemoteInstanceID()); | |
288 } | |
289 | |
290 TEST_F(ConnectTest, CapabilityClasses) { | |
291 scoped_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
292 test::mojom::StandaloneAppPtr standalone_app; | |
293 connection->GetInterface(&standalone_app); | |
294 std::string string1, string2; | |
295 base::RunLoop loop; | |
296 standalone_app->ConnectToClassInterface( | |
297 base::Bind(&ReceiveTwoStrings, &string1, &string2, &loop)); | |
298 loop.Run(); | |
299 EXPECT_EQ("PONG", string1); | |
300 EXPECT_EQ("CLASS APP", string2); | |
301 } | |
302 | |
303 TEST_F(ConnectTest, ConnectAsDifferentUser_Allowed) { | |
304 scoped_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
305 test::mojom::UserIdTestPtr user_id_test; | |
306 connection->GetInterface(&user_id_test); | |
307 shell::mojom::ConnectResult result; | |
308 Identity target(kTestClassAppName, base::GenerateGUID()); | |
309 Identity result_identity; | |
310 { | |
311 base::RunLoop loop; | |
312 user_id_test->ConnectToClassAppAsDifferentUser( | |
313 mojom::Identity::From(target), | |
314 base::Bind(&ReceiveConnectionResult, &result, &result_identity, &loop)); | |
315 loop.Run(); | |
316 } | |
317 EXPECT_EQ(result, shell::mojom::ConnectResult::SUCCEEDED); | |
318 EXPECT_EQ(target, result_identity); | |
319 } | |
320 | |
321 TEST_F(ConnectTest, ConnectAsDifferentUser_Blocked) { | |
322 scoped_ptr<Connection> connection = connector()->Connect(kTestAppAName); | |
323 test::mojom::UserIdTestPtr user_id_test; | |
324 connection->GetInterface(&user_id_test); | |
325 shell::mojom::ConnectResult result; | |
326 Identity target(kTestClassAppName, base::GenerateGUID()); | |
327 Identity result_identity; | |
328 { | |
329 base::RunLoop loop; | |
330 user_id_test->ConnectToClassAppAsDifferentUser( | |
331 mojom::Identity::From(target), | |
332 base::Bind(&ReceiveConnectionResult, &result, &result_identity, &loop)); | |
333 loop.Run(); | |
334 } | |
335 EXPECT_EQ(shell::mojom::ConnectResult::ACCESS_DENIED, result); | |
336 EXPECT_FALSE(target == result_identity); | |
337 } | |
338 | |
339 // There are various other tests (shell, lifecycle) that test valid client | |
340 // process specifications. This is the only one for blocking. | |
341 TEST_F(ConnectTest, ConnectToClientProcess_Blocked) { | |
342 scoped_ptr<Connection> connection = connector()->Connect(kTestDriverName); | |
343 test::mojom::ClientProcessTestPtr client_process_test; | |
344 connection->GetInterface(&client_process_test); | |
345 shell::mojom::ConnectResult result; | |
346 Identity result_identity; | |
347 { | |
348 base::RunLoop loop; | |
349 client_process_test->LaunchAndConnectToProcess( | |
350 base::Bind(&ReceiveConnectionResult, &result, &result_identity, &loop)); | |
351 loop.Run(); | |
352 } | |
353 EXPECT_EQ(shell::mojom::ConnectResult::ACCESS_DENIED, result); | |
354 } | |
355 | |
356 // Verifies that a client with the "all_users" capability class can receive | |
357 // connections from clients run as other users. | |
358 TEST_F(ConnectTest, AllUsersSingleton) { | |
359 // Connect to an instance with an explicitly different user_id. This supplied | |
360 // user id should be ignored by the shell (which will generate its own | |
361 // synthetic user id for all-user singleton instances). | |
362 const std::string singleton_userid = base::GenerateGUID(); | |
363 Connector::ConnectParams params( | |
364 Identity(kTestSingletonAppName, singleton_userid)); | |
365 scoped_ptr<Connection> connection = connector()->Connect(¶ms); | |
366 { | |
367 base::RunLoop loop; | |
368 connection->AddConnectionCompletedClosure(base::Bind(&QuitLoop, &loop)); | |
369 loop.Run(); | |
370 EXPECT_NE(connection->GetRemoteIdentity().user_id(), singleton_userid); | |
371 } | |
372 // This connects using the current client's user_id. It should be bound to the | |
373 // same service started above, with the same shell-generated user id. | |
374 scoped_ptr<Connection> inherit_connection = | |
375 connector()->Connect(kTestSingletonAppName); | |
376 { | |
377 base::RunLoop loop; | |
378 inherit_connection->AddConnectionCompletedClosure( | |
379 base::Bind(&QuitLoop, &loop)); | |
380 loop.Run(); | |
381 EXPECT_EQ(inherit_connection->GetRemoteIdentity().user_id(), | |
382 connection->GetRemoteIdentity().user_id()); | |
383 } | |
384 } | |
385 | |
386 // Tests that we can expose an interface to targets on outbound connections. | |
387 TEST_F(ConnectTest, LocalInterface) { | |
388 // Connect to a standalone application. | |
389 { | |
390 test::mojom::ConnectTestServicePtr service; | |
391 scoped_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
392 connection->GetInterface(&service); | |
393 connection->AddInterface<test::mojom::ExposedInterface>(this); | |
394 | |
395 uint32_t remote_id = shell::mojom::kInvalidInstanceID; | |
396 { | |
397 base::RunLoop run_loop; | |
398 EXPECT_TRUE(connection->IsPending()); | |
399 EXPECT_EQ(mojom::kInvalidInstanceID, connection->GetRemoteInstanceID()); | |
400 connection->AddConnectionCompletedClosure( | |
401 base::Bind(&QuitLoop, &run_loop)); | |
402 run_loop.Run(); | |
403 EXPECT_FALSE(connection->IsPending()); | |
404 remote_id = connection->GetRemoteInstanceID(); | |
405 EXPECT_NE(mojom::kInvalidInstanceID, remote_id); | |
406 } | |
407 | |
408 { | |
409 base::RunLoop run_loop; | |
410 set_ping_callback(base::Bind(&QuitLoop, &run_loop)); | |
411 run_loop.Run(); | |
412 CompareConnectionState( | |
413 kTestAppName, test_name(), test_userid(), test_instance_id(), | |
414 kTestAppName, connection->GetRemoteIdentity().user_id(), remote_id); | |
415 } | |
416 } | |
417 | |
418 // Connect to an application provided by a package. | |
419 { | |
420 test::mojom::ConnectTestServicePtr service_a; | |
421 scoped_ptr<Connection> connection = connector()->Connect(kTestAppAName); | |
422 connection->GetInterface(&service_a); | |
423 connection->AddInterface<test::mojom::ExposedInterface>(this); | |
424 | |
425 uint32_t remote_id = shell::mojom::kInvalidInstanceID; | |
426 { | |
427 base::RunLoop run_loop; | |
428 EXPECT_TRUE(connection->IsPending()); | |
429 EXPECT_EQ(mojom::kInvalidInstanceID, connection->GetRemoteInstanceID()); | |
430 connection->AddConnectionCompletedClosure( | |
431 base::Bind(&QuitLoop, &run_loop)); | |
432 run_loop.Run(); | |
433 EXPECT_FALSE(connection->IsPending()); | |
434 remote_id = connection->GetRemoteInstanceID(); | |
435 EXPECT_NE(mojom::kInvalidInstanceID, remote_id); | |
436 } | |
437 | |
438 { | |
439 base::RunLoop run_loop; | |
440 set_ping_callback(base::Bind(&QuitLoop, &run_loop)); | |
441 run_loop.Run(); | |
442 CompareConnectionState( | |
443 kTestAppAName, test_name(), test_userid(), test_instance_id(), | |
444 kTestAppAName, connection->GetRemoteIdentity().user_id(), remote_id); | |
445 } | |
446 | |
447 } | |
448 } | |
449 | |
450 } // namespace shell | |
451 } // namespace mojo | |
OLD | NEW |