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