1 // Copyright 2023 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/async/default.h>
8 #include <lib/sys/cpp/component_context.h>
9
10 #include "base/fuchsia/process_context.h"
11 #include "base/fuchsia/test_component_context_for_process.h"
12 #include "base/fuchsia/test_interface_natural_impl.h"
13 #include "base/run_loop.h"
14 #include "base/test/bind.h"
15 #include "base/test/task_environment.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19
20 class ScopedNaturalServiceBindingTest : public testing::Test {
21 protected:
22 ScopedNaturalServiceBindingTest() = default;
23 ~ScopedNaturalServiceBindingTest() override = default;
24
25 const base::test::SingleThreadTaskEnvironment task_environment_{
26 base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
27
28 TestComponentContextForProcess test_context_;
29 TestInterfaceNaturalImpl test_service_;
30 };
31
32 // Verifies that ScopedNaturalServiceBinding allows more than one simultaneous
33 // client.
TEST_F(ScopedNaturalServiceBindingTest,ConnectTwice)34 TEST_F(ScopedNaturalServiceBindingTest, ConnectTwice) {
35 ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
36 ComponentContextForProcess()->outgoing().get(), &test_service_);
37
38 auto stub =
39 CreateTestInterfaceClient(test_context_.published_services_natural());
40 auto stub2 =
41 CreateTestInterfaceClient(test_context_.published_services_natural());
42 EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
43 EXPECT_EQ(VerifyTestInterface(stub2), ZX_OK);
44 }
45
46 // Verifies that ScopedNaturalServiceBinding allows more than one simultaneous
47 // client with a non-default discovery name.
TEST_F(ScopedNaturalServiceBindingTest,ConnectTwiceNameOverride)48 TEST_F(ScopedNaturalServiceBindingTest, ConnectTwiceNameOverride) {
49 const char kInterfaceName[] = "fuchsia.TestInterface2";
50
51 ScopedNaturalServiceBinding<base_testfidl::TestInterface> new_service_binding(
52 ComponentContextForProcess()->outgoing().get(), &test_service_,
53 kInterfaceName);
54
55 auto stub = CreateTestInterfaceClient(
56 test_context_.published_services_natural(), kInterfaceName);
57 auto stub2 = CreateTestInterfaceClient(
58 test_context_.published_services_natural(), kInterfaceName);
59 EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
60 EXPECT_EQ(VerifyTestInterface(stub2), ZX_OK);
61 }
62
63 // Verify that we can publish a debug `TestInterface` service.
TEST_F(ScopedNaturalServiceBindingTest,ConnectDebugService)64 TEST_F(ScopedNaturalServiceBindingTest, ConnectDebugService) {
65 vfs::PseudoDir* const debug_dir =
66 ComponentContextForProcess()->outgoing()->debug_dir();
67
68 // Publish the test service to the "debug" directory.
69 ScopedNaturalServiceBinding<base_testfidl::TestInterface>
70 debug_service_binding(debug_dir, &test_service_);
71
72 // Connect a `ClientEnd` to the "debug" subdirectory.
73 auto debug_directory_endpoints =
74 fidl::CreateEndpoints<fuchsia_io::Directory>();
75 ASSERT_TRUE(debug_directory_endpoints.is_ok())
76 << debug_directory_endpoints.status_string();
77 debug_dir->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE |
78 fuchsia::io::OpenFlags::RIGHT_WRITABLE,
79 debug_directory_endpoints->server.TakeChannel());
80
81 // Attempt to connect via the "debug" directory.
82 auto debug_stub =
83 CreateTestInterfaceClient(std::move(debug_directory_endpoints->client));
84 EXPECT_EQ(VerifyTestInterface(debug_stub), ZX_OK);
85
86 // Verify that the `TestInterface` service does not appear in the outgoing
87 // service directory.
88 auto release_stub =
89 CreateTestInterfaceClient(test_context_.published_services_natural());
90 EXPECT_EQ(VerifyTestInterface(release_stub), ZX_ERR_NOT_FOUND);
91 }
92
93 // Test the last client callback is called every time the number of active
94 // clients reaches 0.
TEST_F(ScopedNaturalServiceBindingTest,MultipleLastClientCallback)95 TEST_F(ScopedNaturalServiceBindingTest, MultipleLastClientCallback) {
96 ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
97 ComponentContextForProcess()->outgoing().get(), &test_service_);
98 int disconnect_count = 0;
99 binding.SetOnLastClientCallback(
100 BindLambdaForTesting([&disconnect_count] { ++disconnect_count; }));
101
102 // Connect a client, verify it is functional.
103 {
104 auto stub =
105 CreateTestInterfaceClient(test_context_.published_services_natural());
106 EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
107 }
108
109 // Client disconnected on going out of scope, the callback should have been
110 // called once.
111 RunLoop().RunUntilIdle();
112 EXPECT_EQ(disconnect_count, 1);
113
114 // Connect another client, verify it is functional.
115 {
116 auto stub =
117 CreateTestInterfaceClient(test_context_.published_services_natural());
118 EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
119 }
120
121 // Client disconnected on going out of scope, the callback should have been
122 // called a second time.
123 RunLoop().RunUntilIdle();
124 EXPECT_EQ(disconnect_count, 2);
125 }
126
127 // Test the last client callback is called every time the number of active
128 // clients reaches 0.
TEST_F(ScopedNaturalServiceBindingTest,LastClientCallbackOnlyForLastClient)129 TEST_F(ScopedNaturalServiceBindingTest, LastClientCallbackOnlyForLastClient) {
130 ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
131 ComponentContextForProcess()->outgoing().get(), &test_service_);
132 int disconnect_count = 0;
133 binding.SetOnLastClientCallback(
134 BindLambdaForTesting([&disconnect_count] { ++disconnect_count; }));
135
136 {
137 // Connect a long lived client, verify it is functional.
138 auto long_lived_stub =
139 CreateTestInterfaceClient(test_context_.published_services_natural());
140 EXPECT_EQ(VerifyTestInterface(long_lived_stub), ZX_OK);
141
142 // Connect a client, verify it is functional.
143 {
144 auto stub =
145 CreateTestInterfaceClient(test_context_.published_services_natural());
146 EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
147 }
148
149 // Client disconnected on going out of scope, the callback should not have
150 // been called because the long-lived client is still connected.
151 RunLoop().RunUntilIdle();
152 EXPECT_EQ(disconnect_count, 0);
153
154 // Connect another client, verify it is functional.
155 {
156 auto stub =
157 CreateTestInterfaceClient(test_context_.published_services_natural());
158 EXPECT_EQ(VerifyTestInterface(stub), ZX_OK);
159 }
160
161 // Client disconnected on going out of scope, the callback should not have
162 // been called because the long-lived client is still connected.
163 RunLoop().RunUntilIdle();
164 EXPECT_EQ(disconnect_count, 0);
165 }
166
167 // Long lived client disconnected on going out of scope, the callback should
168 // have been called a third time.
169 RunLoop().RunUntilIdle();
170 EXPECT_EQ(disconnect_count, 1);
171 }
172
173 } // namespace base
174