Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/io/test_uds_server.cpp
45997 views
1
/**************************************************************************/
2
/* test_uds_server.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "tests/test_macros.h"
32
33
TEST_FORCE_LINK(test_uds_server)
34
35
#ifdef UNIX_ENABLED
36
37
#include "core/io/dir_access.h"
38
#include "core/io/file_access.h"
39
#include "core/io/stream_peer_uds.h"
40
#include "core/io/uds_server.h"
41
#include "core/os/os.h"
42
43
#include <functional>
44
45
namespace TestUDSServer {
46
47
const String SOCKET_PATH = "/tmp/godot_test_uds_socket";
48
const uint32_t SLEEP_DURATION = 1000;
49
const uint64_t MAX_WAIT_USEC = 2000000;
50
51
void wait_for_condition(std::function<bool()> f_test) {
52
const uint64_t time = OS::get_singleton()->get_ticks_usec();
53
while (!f_test() && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) {
54
OS::get_singleton()->delay_usec(SLEEP_DURATION);
55
}
56
}
57
58
void cleanup_socket_file() {
59
// Remove socket file if it exists
60
if (FileAccess::exists(SOCKET_PATH)) {
61
DirAccess::remove_absolute(SOCKET_PATH);
62
}
63
}
64
65
Ref<UDSServer> create_server(const String &p_path) {
66
cleanup_socket_file();
67
68
Ref<UDSServer> server;
69
server.instantiate();
70
71
REQUIRE_EQ(server->listen(p_path), Error::OK);
72
REQUIRE(server->is_listening());
73
CHECK_FALSE(server->is_connection_available());
74
75
return server;
76
}
77
78
Ref<StreamPeerUDS> create_client(const String &p_path) {
79
Ref<StreamPeerUDS> client;
80
client.instantiate();
81
82
Error err = client->connect_to_host(p_path);
83
REQUIRE_EQ(err, Error::OK);
84
85
// UDS connections may be immediately connected or in connecting state
86
StreamPeerUDS::Status status = client->get_status();
87
REQUIRE((status == StreamPeerUDS::STATUS_CONNECTED || status == StreamPeerUDS::STATUS_CONNECTING));
88
89
if (status == StreamPeerUDS::STATUS_CONNECTED) {
90
CHECK_EQ(client->get_connected_path(), p_path);
91
}
92
93
return client;
94
}
95
96
Ref<StreamPeerUDS> accept_connection(Ref<UDSServer> &p_server) {
97
wait_for_condition([&]() {
98
return p_server->is_connection_available();
99
});
100
101
REQUIRE(p_server->is_connection_available());
102
Ref<StreamPeerUDS> client_from_server = p_server->take_connection();
103
REQUIRE(client_from_server.is_valid());
104
CHECK_EQ(client_from_server->get_status(), StreamPeerUDS::STATUS_CONNECTED);
105
106
return client_from_server;
107
}
108
109
TEST_CASE("[UDSServer] Instantiation") {
110
Ref<UDSServer> server;
111
server.instantiate();
112
113
REQUIRE(server.is_valid());
114
CHECK_FALSE(server->is_listening());
115
}
116
117
TEST_CASE("[UDSServer] Accept a connection and receive/send data") {
118
Ref<UDSServer> server = create_server(SOCKET_PATH);
119
Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
120
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
121
122
wait_for_condition([&]() {
123
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
124
});
125
126
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
127
128
// Sending data from client to server.
129
const String hello_world = "Hello World!";
130
client->put_string(hello_world);
131
CHECK_EQ(client_from_server->get_string(), hello_world);
132
133
// Sending data from server to client.
134
const float pi = 3.1415;
135
client_from_server->put_float(pi);
136
CHECK_EQ(client->get_float(), pi);
137
138
client->disconnect_from_host();
139
server->stop();
140
CHECK_FALSE(server->is_listening());
141
142
cleanup_socket_file();
143
}
144
145
TEST_CASE("[UDSServer] Handle multiple clients at the same time") {
146
Ref<UDSServer> server = create_server(SOCKET_PATH);
147
148
Vector<Ref<StreamPeerUDS>> clients;
149
for (int i = 0; i < 5; i++) {
150
clients.push_back(create_client(SOCKET_PATH));
151
}
152
153
Vector<Ref<StreamPeerUDS>> clients_from_server;
154
for (int i = 0; i < clients.size(); i++) {
155
clients_from_server.push_back(accept_connection(server));
156
}
157
158
wait_for_condition([&]() {
159
bool should_exit = true;
160
for (Ref<StreamPeerUDS> &c : clients) {
161
if (c->poll() != Error::OK) {
162
return true;
163
}
164
StreamPeerUDS::Status status = c->get_status();
165
if (status != StreamPeerUDS::STATUS_CONNECTED && status != StreamPeerUDS::STATUS_CONNECTING) {
166
return true;
167
}
168
if (status != StreamPeerUDS::STATUS_CONNECTED) {
169
should_exit = false;
170
}
171
}
172
return should_exit;
173
});
174
175
for (Ref<StreamPeerUDS> &c : clients) {
176
REQUIRE_EQ(c->get_status(), StreamPeerUDS::STATUS_CONNECTED);
177
}
178
179
// Sending data from each client to server.
180
for (int i = 0; i < clients.size(); i++) {
181
String hello_client = "Hello " + itos(i);
182
clients[i]->put_string(hello_client);
183
CHECK_EQ(clients_from_server[i]->get_string(), hello_client);
184
}
185
186
for (Ref<StreamPeerUDS> &c : clients) {
187
c->disconnect_from_host();
188
}
189
server->stop();
190
191
cleanup_socket_file();
192
}
193
194
TEST_CASE("[UDSServer] When stopped shouldn't accept new connections") {
195
Ref<UDSServer> server = create_server(SOCKET_PATH);
196
Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
197
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
198
199
wait_for_condition([&]() {
200
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
201
});
202
203
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
204
205
// Sending data from client to server.
206
const String hello_world = "Hello World!";
207
client->put_string(hello_world);
208
CHECK_EQ(client_from_server->get_string(), hello_world);
209
210
client->disconnect_from_host();
211
server->stop();
212
CHECK_FALSE(server->is_listening());
213
214
// Clean up the socket file after server stops
215
cleanup_socket_file();
216
217
// Try to connect to non-existent socket
218
Ref<StreamPeerUDS> new_client;
219
new_client.instantiate();
220
Error err = new_client->connect_to_host(SOCKET_PATH);
221
222
// Connection should fail since socket doesn't exist
223
CHECK_NE(err, Error::OK);
224
CHECK_FALSE(server->is_connection_available());
225
226
cleanup_socket_file();
227
}
228
229
TEST_CASE("[UDSServer] Should disconnect client") {
230
Ref<UDSServer> server = create_server(SOCKET_PATH);
231
Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
232
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
233
234
wait_for_condition([&]() {
235
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
236
});
237
238
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
239
240
// Sending data from client to server.
241
const String hello_world = "Hello World!";
242
client->put_string(hello_world);
243
CHECK_EQ(client_from_server->get_string(), hello_world);
244
245
client_from_server->disconnect_from_host();
246
server->stop();
247
CHECK_FALSE(server->is_listening());
248
249
// Wait for disconnection
250
wait_for_condition([&]() {
251
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_NONE;
252
});
253
254
// Wait for disconnection
255
wait_for_condition([&]() {
256
return client_from_server->poll() != Error::OK || client_from_server->get_status() == StreamPeerUDS::STATUS_NONE;
257
});
258
259
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_NONE);
260
CHECK_EQ(client_from_server->get_status(), StreamPeerUDS::STATUS_NONE);
261
262
ERR_PRINT_OFF;
263
CHECK_EQ(client->get_string(), String());
264
CHECK_EQ(client_from_server->get_string(), String());
265
ERR_PRINT_ON;
266
267
cleanup_socket_file();
268
}
269
270
TEST_CASE("[UDSServer] Test with different socket paths") {
271
// Test with a different socket path
272
const String alt_socket_path = "/tmp/godot_test_uds_socket_alt";
273
274
// Clean up before test
275
if (FileAccess::exists(alt_socket_path)) {
276
DirAccess::remove_absolute(alt_socket_path);
277
}
278
279
Ref<UDSServer> server = create_server(alt_socket_path);
280
Ref<StreamPeerUDS> client = create_client(alt_socket_path);
281
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
282
283
wait_for_condition([&]() {
284
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
285
});
286
287
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
288
289
// Test data exchange
290
const int test_number = 42;
291
client->put_32(test_number);
292
CHECK_EQ(client_from_server->get_32(), test_number);
293
294
client->disconnect_from_host();
295
server->stop();
296
297
// Clean up
298
if (FileAccess::exists(alt_socket_path)) {
299
DirAccess::remove_absolute(alt_socket_path);
300
}
301
}
302
303
} // namespace TestUDSServer
304
305
#endif // UNIX_ENABLED
306
307