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