• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
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/fuchsia/scoped_service_binding.h"
6 
7 #include <lib/sys/cpp/component_context.h>
8 #include <lib/sys/cpp/outgoing_directory.h>
9 #include <lib/sys/cpp/service_directory.h>
10 
11 #include "base/fuchsia/process_context.h"
12 #include "base/fuchsia/test_component_context_for_process.h"
13 #include "base/fuchsia/test_interface_impl.h"
14 #include "base/run_loop.h"
15 #include "base/test/bind.h"
16 #include "base/test/task_environment.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 
19 namespace base {
20 
21 class ScopedServiceBindingTest : public testing::Test {
22  protected:
23   ScopedServiceBindingTest() = default;
24   ~ScopedServiceBindingTest() override = default;
25 
26   const base::test::SingleThreadTaskEnvironment task_environment_{
27       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
28 
29   TestComponentContextForProcess test_context_;
30   TestInterfaceImpl test_service_;
31 };
32 
33 // Verifies that ScopedServiceBinding allows connection more than once.
TEST_F(ScopedServiceBindingTest,ConnectTwice)34 TEST_F(ScopedServiceBindingTest, ConnectTwice) {
35   ScopedServiceBinding<testfidl::TestInterface> binding(
36       ComponentContextForProcess()->outgoing().get(), &test_service_);
37 
38   auto stub =
39       test_context_.published_services()->Connect<testfidl::TestInterface>();
40   auto stub2 =
41       test_context_.published_services()->Connect<testfidl::TestInterface>();
42   EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
43   EXPECT_EQ(VerifyTestInterface(stub2), ZX_OK);
44 }
45 
46 // Verifies that ScopedServiceBinding allows connection more than once.
TEST_F(ScopedServiceBindingTest,ConnectTwiceNewName)47 TEST_F(ScopedServiceBindingTest, ConnectTwiceNewName) {
48   const char kInterfaceName[] = "fuchsia.TestInterface2";
49 
50   ScopedServiceBinding<testfidl::TestInterface> new_service_binding(
51       ComponentContextForProcess()->outgoing().get(), &test_service_,
52       kInterfaceName);
53 
54   testfidl::TestInterfacePtr stub, stub2;
55   test_context_.published_services()->Connect(kInterfaceName,
56                                               stub.NewRequest().TakeChannel());
57   test_context_.published_services()->Connect(kInterfaceName,
58                                               stub2.NewRequest().TakeChannel());
59   EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
60   EXPECT_EQ(VerifyTestInterface(stub2), ZX_OK);
61 }
62 
63 // Verify that we can publish a debug service.
TEST_F(ScopedServiceBindingTest,ConnectDebugService)64 TEST_F(ScopedServiceBindingTest, ConnectDebugService) {
65   vfs::PseudoDir* const debug_dir =
66       ComponentContextForProcess()->outgoing()->debug_dir();
67 
68   // Publish the test service to the "debug" directory.
69   ScopedServiceBinding<testfidl::TestInterface> debug_service_binding(
70       debug_dir, &test_service_);
71 
72   // Connect a ServiceDirectory to the "debug" subdirectory.
73   fidl::InterfaceHandle<fuchsia::io::Directory> debug_handle;
74   debug_dir->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE |
75                        fuchsia::io::OpenFlags::RIGHT_WRITABLE,
76                    debug_handle.NewRequest().TakeChannel());
77   sys::ServiceDirectory debug_directory(std::move(debug_handle));
78 
79   // Attempt to connect via the "debug" directory.
80   auto debug_stub = debug_directory.Connect<testfidl::TestInterface>();
81   EXPECT_EQ(VerifyTestInterface(debug_stub), ZX_OK);
82 
83   // Verify that the service does not appear in the outgoing service directory.
84   auto release_stub =
85       test_context_.published_services()->Connect<testfidl::TestInterface>();
86   EXPECT_EQ(VerifyTestInterface(release_stub), ZX_ERR_NOT_FOUND);
87 }
88 
89 // Verifies that ScopedSingleClientServiceBinding allows a different name.
TEST_F(ScopedServiceBindingTest,SingleClientConnectNewName)90 TEST_F(ScopedServiceBindingTest, SingleClientConnectNewName) {
91   const char kInterfaceName[] = "fuchsia.TestInterface2";
92 
93   ScopedSingleClientServiceBinding<testfidl::TestInterface> binding(
94       ComponentContextForProcess()->outgoing().get(), &test_service_,
95       kInterfaceName);
96 
97   testfidl::TestInterfacePtr stub;
98   test_context_.published_services()->Connect(kInterfaceName,
99                                               stub.NewRequest().TakeChannel());
100   EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
101 }
102 
103 // Verify that if we connect twice to a prefer-new bound service, the existing
104 // connection gets closed.
TEST_F(ScopedServiceBindingTest,SingleClientPreferNew)105 TEST_F(ScopedServiceBindingTest, SingleClientPreferNew) {
106   ScopedSingleClientServiceBinding<testfidl::TestInterface,
107                                    ScopedServiceBindingPolicy::kPreferNew>
108       binding(ComponentContextForProcess()->outgoing().get(), &test_service_);
109 
110   // Connect the first client, and verify that it is functional.
111   auto existing_client =
112       test_context_.published_services()->Connect<testfidl::TestInterface>();
113   EXPECT_EQ(VerifyTestInterface(existing_client), ZX_OK);
114 
115   // Connect the second client, so the existing one should be disconnected and
116   // the new should be functional.
117   auto new_client =
118       test_context_.published_services()->Connect<testfidl::TestInterface>();
119   RunLoop().RunUntilIdle();
120   EXPECT_FALSE(existing_client);
121   EXPECT_EQ(VerifyTestInterface(new_client), ZX_OK);
122 }
123 
124 // Verify that if we connect twice to a prefer-existing bound service, the new
125 // connection gets closed.
TEST_F(ScopedServiceBindingTest,SingleClientPreferExisting)126 TEST_F(ScopedServiceBindingTest, SingleClientPreferExisting) {
127   ScopedSingleClientServiceBinding<testfidl::TestInterface,
128                                    ScopedServiceBindingPolicy::kPreferExisting>
129       binding(ComponentContextForProcess()->outgoing().get(), &test_service_);
130 
131   // Connect the first client, and verify that it is functional.
132   auto existing_client =
133       test_context_.published_services()->Connect<testfidl::TestInterface>();
134   EXPECT_EQ(VerifyTestInterface(existing_client), ZX_OK);
135 
136   // Connect the second client, then verify that the it gets closed and the
137   // existing one remains functional.
138   auto new_client =
139       test_context_.published_services()->Connect<testfidl::TestInterface>();
140   RunLoop().RunUntilIdle();
141   EXPECT_FALSE(new_client);
142   EXPECT_EQ(VerifyTestInterface(existing_client), ZX_OK);
143 }
144 
145 // Verify that the default single-client binding policy is prefer-new.
TEST_F(ScopedServiceBindingTest,SingleClientDefaultIsPreferNew)146 TEST_F(ScopedServiceBindingTest, SingleClientDefaultIsPreferNew) {
147   ScopedSingleClientServiceBinding<testfidl::TestInterface> binding(
148       ComponentContextForProcess()->outgoing().get(), &test_service_);
149 
150   // Connect the first client, and verify that it is functional.
151   auto existing_client =
152       test_context_.published_services()->Connect<testfidl::TestInterface>();
153   EXPECT_EQ(VerifyTestInterface(existing_client), ZX_OK);
154 
155   // Connect the second client, so the existing one should be disconnected and
156   // the new should be functional.
157   auto new_client =
158       test_context_.published_services()->Connect<testfidl::TestInterface>();
159   RunLoop().RunUntilIdle();
160   EXPECT_FALSE(existing_client);
161   EXPECT_EQ(VerifyTestInterface(new_client), ZX_OK);
162 }
163 
164 // Verify that single-client bindings support publishing to a PseudoDir.
TEST_F(ScopedServiceBindingTest,SingleClientPublishToPseudoDir)165 TEST_F(ScopedServiceBindingTest, SingleClientPublishToPseudoDir) {
166   vfs::PseudoDir* const debug_dir =
167       ComponentContextForProcess()->outgoing()->debug_dir();
168 
169   ScopedSingleClientServiceBinding<testfidl::TestInterface> binding(
170       debug_dir, &test_service_);
171 
172   // Connect a ServiceDirectory to the "debug" subdirectory.
173   fidl::InterfaceHandle<fuchsia::io::Directory> debug_handle;
174   debug_dir->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE |
175                        fuchsia::io::OpenFlags::RIGHT_WRITABLE,
176                    debug_handle.NewRequest().TakeChannel());
177   sys::ServiceDirectory debug_directory(std::move(debug_handle));
178 
179   // Attempt to connect via the "debug" directory.
180   auto debug_stub = debug_directory.Connect<testfidl::TestInterface>();
181   EXPECT_EQ(VerifyTestInterface(debug_stub), ZX_OK);
182 
183   // Verify that the service does not appear in the outgoing service directory.
184   auto release_stub =
185       test_context_.published_services()->Connect<testfidl::TestInterface>();
186   EXPECT_EQ(VerifyTestInterface(release_stub), ZX_ERR_NOT_FOUND);
187 }
188 
TEST_F(ScopedServiceBindingTest,SingleBindingSetOnLastClientCallback)189 TEST_F(ScopedServiceBindingTest, SingleBindingSetOnLastClientCallback) {
190   ScopedSingleClientServiceBinding<testfidl::TestInterface>
191       single_service_binding(ComponentContextForProcess()->outgoing().get(),
192                              &test_service_);
193 
194   base::RunLoop run_loop;
195   single_service_binding.SetOnLastClientCallback(run_loop.QuitClosure());
196 
197   auto current_client =
198       test_context_.published_services()->Connect<testfidl::TestInterface>();
199   EXPECT_EQ(VerifyTestInterface(current_client), ZX_OK);
200   current_client = nullptr;
201 
202   run_loop.Run();
203 }
204 
205 // Test the kConnectOnce option for ScopedSingleClientServiceBinding properly
206 // stops publishing the service after a first disconnect.
TEST_F(ScopedServiceBindingTest,ConnectOnce_OnlyFirstConnectionSucceeds)207 TEST_F(ScopedServiceBindingTest, ConnectOnce_OnlyFirstConnectionSucceeds) {
208   ScopedSingleClientServiceBinding<testfidl::TestInterface,
209                                    ScopedServiceBindingPolicy::kConnectOnce>
210       binding(ComponentContextForProcess()->outgoing().get(), &test_service_);
211 
212   // Connect the first client, and verify that it is functional.
213   auto existing_client =
214       test_context_.published_services()->Connect<testfidl::TestInterface>();
215   EXPECT_EQ(VerifyTestInterface(existing_client), ZX_OK);
216 
217   // Connect the second client, then verify that it gets closed and the existing
218   // one remains functional.
219   auto new_client =
220       test_context_.published_services()->Connect<testfidl::TestInterface>();
221   RunLoop().RunUntilIdle();
222   EXPECT_FALSE(new_client);
223   EXPECT_EQ(VerifyTestInterface(existing_client), ZX_OK);
224 
225   // Disconnect the first client.
226   existing_client.Unbind().TakeChannel().reset();
227   RunLoop().RunUntilIdle();
228 
229   // Re-connect the second client, then verify that it gets closed.
230   new_client =
231       test_context_.published_services()->Connect<testfidl::TestInterface>();
232   RunLoop().RunUntilIdle();
233   EXPECT_FALSE(new_client);
234 }
235 
236 // Test the last client callback is called every time the number of active
237 // clients reaches 0.
TEST_F(ScopedServiceBindingTest,MultipleLastClientCallback)238 TEST_F(ScopedServiceBindingTest, MultipleLastClientCallback) {
239   ScopedServiceBinding<testfidl::TestInterface> binding(
240       ComponentContextForProcess()->outgoing().get(), &test_service_);
241   int disconnect_count = 0;
242   binding.SetOnLastClientCallback(
243       BindLambdaForTesting([&disconnect_count] { ++disconnect_count; }));
244 
245   // Connect a client, verify it is functional.
246   auto stub =
247       test_context_.published_services()->Connect<testfidl::TestInterface>();
248   EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
249 
250   // Disconnect the client, the callback should have been called once.
251   stub = nullptr;
252   RunLoop().RunUntilIdle();
253   EXPECT_EQ(disconnect_count, 1);
254 
255   // Re-connect the client, verify it is functional.
256   stub = test_context_.published_services()->Connect<testfidl::TestInterface>();
257   EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
258 
259   // Disconnect the client, the callback should have been called a second time.
260   stub = nullptr;
261   RunLoop().RunUntilIdle();
262   EXPECT_EQ(disconnect_count, 2);
263 }
264 
265 }  // namespace base
266