/work/auto_tests/group_topic_test.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Tests that we can successfully change the group topic, that all peers receive topic changes |
3 | | * and that the topic lock works as intended. |
4 | | */ |
5 | | |
6 | | #include <string.h> |
7 | | #include <stdio.h> |
8 | | #include <stdlib.h> |
9 | | #include <stdint.h> |
10 | | |
11 | | #include "auto_test_support.h" |
12 | | #include "check_compat.h" |
13 | | |
14 | | #include "../toxcore/tox.h" |
15 | | #include "../toxcore/group_chats.h" |
16 | | |
17 | 2.00k | #define NUM_GROUP_TOXES 3 |
18 | | |
19 | 4 | #define TOPIC "They're waiting for you Gordon...in the test chamber" |
20 | 2 | #define TOPIC_LEN (sizeof(TOPIC) - 1) |
21 | | |
22 | 6 | #define TOPIC2 "They're waiting for you Gordon...in the test chamber 2.0" |
23 | 3 | #define TOPIC_LEN2 (sizeof(TOPIC2) - 1) |
24 | | |
25 | 3 | #define GROUP_NAME "The Test Chamber" |
26 | 2 | #define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1) |
27 | | |
28 | 2 | #define PEER0_NICK "Koresh" |
29 | 1 | #define PEER0_NICK_LEN (sizeof(PEER0_NICK) - 1) |
30 | | |
31 | | typedef struct State { |
32 | | uint32_t peer_id; // the id of the peer we set to observer |
33 | | } State; |
34 | | |
35 | | static bool all_group_peers_connected(const AutoTox *autotoxes, uint32_t tox_count, uint32_t groupnumber, |
36 | | size_t name_length, uint32_t peer_limit) |
37 | 1 | { |
38 | 2 | for (uint32_t i = 0; i < tox_count; ++i) { |
39 | | // make sure we got an invite |
40 | 2 | if (tox_group_get_name_size(autotoxes[i].tox, groupnumber, nullptr) != name_length) { |
41 | 1 | return false; |
42 | 1 | } |
43 | | |
44 | | // make sure we got a sync response |
45 | 1 | if (peer_limit != 0 && tox_group_get_peer_limit(autotoxes[i].tox, groupnumber, nullptr) != peer_limit) { |
46 | 0 | return false; |
47 | 0 | } |
48 | | |
49 | | // make sure we're actually connected |
50 | 1 | if (!tox_group_is_connected(autotoxes[i].tox, groupnumber, nullptr)) { |
51 | 0 | return false; |
52 | 0 | } |
53 | 1 | } |
54 | | |
55 | 0 | return true; |
56 | 1 | } |
57 | | |
58 | | static void group_peer_join_handler(Tox *tox, const Tox_Event_Group_Peer_Join *event, void *user_data) |
59 | 151 | { |
60 | | //const uint32_t group_number = tox_event_group_peer_join_get_group_number(event); |
61 | 151 | const uint32_t peer_id = tox_event_group_peer_join_get_peer_id(event); |
62 | | |
63 | 151 | AutoTox *autotox = (AutoTox *)user_data; |
64 | 151 | ck_assert(autotox != nullptr); |
65 | | |
66 | 151 | State *state = (State *)autotox->state; |
67 | | |
68 | 151 | state->peer_id = peer_id; |
69 | 151 | } |
70 | | |
71 | | static void group_topic_handler(Tox *tox, const Tox_Event_Group_Topic *event, void *user_data) |
72 | 12 | { |
73 | 12 | const uint32_t group_number = tox_event_group_topic_get_group_number(event); |
74 | | //const uint32_t peer_id = tox_event_group_topic_get_peer_id(event); |
75 | 12 | const uint8_t *topic = tox_event_group_topic_get_topic(event); |
76 | 12 | const uint32_t topic_length = tox_event_group_topic_get_topic_length(event); |
77 | | |
78 | 12 | ck_assert(topic_length <= TOX_GROUP_MAX_TOPIC_LENGTH); |
79 | | |
80 | 12 | Tox_Err_Group_State_Queries query_err; |
81 | 12 | uint8_t topic2[TOX_GROUP_MAX_TOPIC_LENGTH]; |
82 | 12 | tox_group_get_topic(tox, group_number, topic2, &query_err); |
83 | 12 | ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK); |
84 | | |
85 | 12 | size_t topic_length_getter = tox_group_get_topic_size(tox, group_number, &query_err); |
86 | 12 | ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK); |
87 | 12 | ck_assert_msg(topic_length_getter == topic_length && memcmp(topic, topic2, topic_length) == 0, |
88 | 12 | "topic differs in callback: %s, %s", topic, topic2); |
89 | 12 | } |
90 | | |
91 | | static void group_topic_lock_handler(Tox *tox, const Tox_Event_Group_Topic_Lock *event, void *user_data) |
92 | 8 | { |
93 | 8 | const uint32_t group_number = tox_event_group_topic_lock_get_group_number(event); |
94 | 8 | const Tox_Group_Topic_Lock topic_lock = tox_event_group_topic_lock_get_topic_lock(event); |
95 | | |
96 | 8 | Tox_Err_Group_State_Queries err; |
97 | 8 | Tox_Group_Topic_Lock current_lock = tox_group_get_topic_lock(tox, group_number, &err); |
98 | | |
99 | 8 | ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK); |
100 | 8 | ck_assert_msg(topic_lock == current_lock, "topic locks differ in callback"); |
101 | 8 | } |
102 | | |
103 | | /* Sets group topic. |
104 | | * |
105 | | * Return true on success. |
106 | | */ |
107 | | static bool set_topic(Tox *tox, uint32_t groupnumber, const char *topic, size_t length) |
108 | 10 | { |
109 | 10 | Tox_Err_Group_Topic_Set err; |
110 | 10 | tox_group_set_topic(tox, groupnumber, (const uint8_t *)topic, length, &err); |
111 | | |
112 | 10 | return err == TOX_ERR_GROUP_TOPIC_SET_OK; |
113 | 10 | } |
114 | | |
115 | | /* Returns 0 if group topic matches expected topic. |
116 | | * Returns a value < 0 on failure. |
117 | | */ |
118 | | static int check_topic(const Tox *tox, uint32_t groupnumber, const char *expected_topic, size_t expected_length) |
119 | 342 | { |
120 | 342 | Tox_Err_Group_State_Queries query_err; |
121 | 342 | size_t topic_length = tox_group_get_topic_size(tox, groupnumber, &query_err); |
122 | | |
123 | 342 | if (query_err != TOX_ERR_GROUP_STATE_QUERIES_OK) { |
124 | 0 | return -1; |
125 | 0 | } |
126 | | |
127 | 342 | if (expected_length != topic_length) { |
128 | 14 | return -2; |
129 | 14 | } |
130 | | |
131 | 328 | uint8_t topic[TOX_GROUP_MAX_TOPIC_LENGTH]; |
132 | 328 | tox_group_get_topic(tox, groupnumber, topic, &query_err); |
133 | | |
134 | 328 | if (query_err != TOX_ERR_GROUP_STATE_QUERIES_OK) { |
135 | 0 | return -3; |
136 | 0 | } |
137 | | |
138 | 328 | if (memcmp(expected_topic, (const char *)topic, topic_length) != 0) { |
139 | 97 | return -4; |
140 | 97 | } |
141 | | |
142 | 231 | return 0; |
143 | 328 | } |
144 | | |
145 | | static void wait_topic_lock(AutoTox *autotoxes, uint32_t groupnumber, Tox_Group_Topic_Lock expected_lock) |
146 | 2 | { |
147 | 97 | while (1) { |
148 | 97 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
149 | | |
150 | 97 | uint32_t count = 0; |
151 | | |
152 | 388 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
153 | 291 | Tox_Err_Group_State_Queries err; |
154 | 291 | Tox_Group_Topic_Lock topic_lock = tox_group_get_topic_lock(autotoxes[i].tox, groupnumber, &err); |
155 | 291 | ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK); |
156 | | |
157 | 291 | if (topic_lock == expected_lock) { |
158 | 196 | ++count; |
159 | 196 | } |
160 | 291 | } |
161 | | |
162 | 97 | if (count == NUM_GROUP_TOXES) { |
163 | 2 | break; |
164 | 2 | } |
165 | 97 | } |
166 | 2 | } |
167 | | |
168 | | /* Waits for all peers in group to see the same topic */ |
169 | | static void wait_state_topic(AutoTox *autotoxes, uint32_t groupnumber, const char *topic, size_t length) |
170 | 8 | { |
171 | 114 | while (1) { |
172 | 114 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
173 | | |
174 | 114 | uint32_t count = 0; |
175 | | |
176 | 456 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
177 | 342 | const int c_ret = check_topic(autotoxes[i].tox, groupnumber, topic, length); |
178 | | |
179 | 342 | if (c_ret == 0) { |
180 | 231 | ++count; |
181 | 231 | } |
182 | 342 | } |
183 | | |
184 | 114 | if (count == NUM_GROUP_TOXES) { |
185 | 8 | break; |
186 | 8 | } |
187 | 114 | } |
188 | 8 | } |
189 | | |
190 | | /* All peers attempt to set the topic. |
191 | | * |
192 | | * Returns the number of peers who succeeeded. |
193 | | */ |
194 | | static uint32_t set_topic_all_peers(const Random *rng, AutoTox *autotoxes, size_t num_peers, uint32_t groupnumber) |
195 | 3 | { |
196 | 3 | uint32_t change_count = 0; |
197 | | |
198 | 11 | for (size_t i = 0; i < num_peers; ++i) { |
199 | 8 | char new_topic[TOX_GROUP_MAX_TOPIC_LENGTH]; |
200 | 8 | snprintf(new_topic, sizeof(new_topic), "peer %zu changes topic %u", i, random_u32(rng)); |
201 | 8 | size_t length = strlen(new_topic); |
202 | | |
203 | 8 | if (set_topic(autotoxes[i].tox, groupnumber, new_topic, length)) { |
204 | 5 | wait_state_topic(autotoxes, groupnumber, new_topic, length); |
205 | 5 | ++change_count; |
206 | 5 | } else { |
207 | 3 | fprintf(stderr, "Peer %zu couldn't set the topic\n", i); |
208 | 3 | } |
209 | 8 | } |
210 | | |
211 | 3 | return change_count; |
212 | 3 | } |
213 | | |
214 | | static void group_topic_test(AutoTox *autotoxes) |
215 | 1 | { |
216 | 1 | ck_assert_msg(NUM_GROUP_TOXES >= 3, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES); |
217 | | |
218 | 1 | const Random *rng = os_random(); |
219 | 1 | ck_assert(rng != nullptr); |
220 | | |
221 | 1 | Tox *tox0 = autotoxes[0].tox; |
222 | 1 | Tox_Dispatch *dispatch0 = autotoxes[0].dispatch; |
223 | 1 | const State *state0 = (const State *)autotoxes[0].state; |
224 | | |
225 | 1 | tox_events_callback_group_peer_join(dispatch0, group_peer_join_handler); |
226 | | |
227 | 4 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
228 | 3 | tox_events_callback_group_topic(autotoxes[i].dispatch, group_topic_handler); |
229 | 3 | tox_events_callback_group_topic_lock(autotoxes[i].dispatch, group_topic_lock_handler); |
230 | 3 | } |
231 | | |
232 | | /* Tox1 creates a group and is the founder of a newly created group */ |
233 | 1 | Tox_Err_Group_New new_err; |
234 | 1 | const uint32_t groupnumber = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, |
235 | 1 | GROUP_NAME_LEN, |
236 | 1 | (const uint8_t *)PEER0_NICK, PEER0_NICK_LEN, &new_err); |
237 | | |
238 | 1 | ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err); |
239 | | |
240 | 1 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
241 | | |
242 | | /* Founder sets group topic before anyone else joins */ |
243 | 1 | const bool s_ret = set_topic(tox0, groupnumber, TOPIC, TOPIC_LEN); |
244 | 1 | ck_assert_msg(s_ret, "Founder failed to set topic"); |
245 | | |
246 | | /* Founder gets the Chat ID and implicitly shares it publicly */ |
247 | 1 | Tox_Err_Group_State_Queries id_err; |
248 | 1 | uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE]; |
249 | 1 | tox_group_get_chat_id(tox0, groupnumber, chat_id, &id_err); |
250 | | |
251 | 1 | ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "tox_group_get_chat_id failed %d", id_err); |
252 | | |
253 | | /* All other peers join the group using the Chat ID */ |
254 | 3 | for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) { |
255 | 2 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
256 | | |
257 | 2 | Tox_Err_Group_Join join_err; |
258 | 2 | tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err); |
259 | 2 | ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "tox_group_join failed: %d", join_err); |
260 | | |
261 | 2 | c_sleep(100); |
262 | 2 | } |
263 | | |
264 | 1 | fprintf(stderr, "Peers attempting to join group\n"); |
265 | | |
266 | 1 | all_group_peers_connected(autotoxes, NUM_GROUP_TOXES, groupnumber, GROUP_NAME_LEN, MAX_GC_PEERS_DEFAULT); |
267 | | |
268 | 1 | wait_state_topic(autotoxes, groupnumber, TOPIC, TOPIC_LEN); |
269 | | |
270 | | /* Founder disables topic lock */ |
271 | 1 | Tox_Err_Group_Founder_Set_Topic_Lock lock_set_err; |
272 | 1 | tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_DISABLED, &lock_set_err); |
273 | 1 | ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to disable topic lock: %d", |
274 | 1 | lock_set_err); |
275 | | |
276 | 1 | fprintf(stderr, "Topic lock disabled\n"); |
277 | | |
278 | | /* make sure every peer sees the topic lock state change */ |
279 | 1 | wait_topic_lock(autotoxes, groupnumber, TOX_GROUP_TOPIC_LOCK_DISABLED); |
280 | | |
281 | | /* All peers should be able to change the topic now */ |
282 | 1 | uint32_t change_count = set_topic_all_peers(rng, autotoxes, NUM_GROUP_TOXES, groupnumber); |
283 | | |
284 | 1 | ck_assert_msg(change_count == NUM_GROUP_TOXES, "%u peers changed the topic with topic lock disabled", change_count); |
285 | | |
286 | | /* founder silences the last peer he saw join */ |
287 | 1 | Tox_Err_Group_Mod_Set_Role merr; |
288 | 1 | tox_group_mod_set_role(tox0, groupnumber, state0->peer_id, TOX_GROUP_ROLE_OBSERVER, &merr); |
289 | 1 | ck_assert_msg(merr == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set %u to observer role: %d", state0->peer_id, merr); |
290 | | |
291 | 1 | fprintf(stderr, "Random peer is set to observer\n"); |
292 | | |
293 | 1 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
294 | | |
295 | | /* All peers except one should now be able to change the topic */ |
296 | 1 | change_count = set_topic_all_peers(rng, autotoxes, NUM_GROUP_TOXES, groupnumber); |
297 | | |
298 | 1 | ck_assert_msg(change_count == NUM_GROUP_TOXES - 1, "%u peers changed the topic with a silenced peer", change_count); |
299 | | |
300 | | /* Founder enables topic lock and sets topic back to original */ |
301 | 1 | tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_ENABLED, &lock_set_err); |
302 | 1 | ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to enable topic lock: %d", |
303 | 1 | lock_set_err); |
304 | | |
305 | 1 | fprintf(stderr, "Topic lock enabled\n"); |
306 | | |
307 | | /* Wait for all peers to get topic lock state change */ |
308 | 1 | wait_topic_lock(autotoxes, groupnumber, TOX_GROUP_TOPIC_LOCK_ENABLED); |
309 | | |
310 | 1 | const bool s3_ret = set_topic(tox0, groupnumber, TOPIC2, TOPIC_LEN2); |
311 | 1 | ck_assert_msg(s3_ret, "Founder failed to set topic second time"); |
312 | | |
313 | 1 | wait_state_topic(autotoxes, groupnumber, TOPIC2, TOPIC_LEN2); |
314 | | |
315 | | /* No peer excluding the founder should be able to set the topic */ |
316 | | |
317 | 1 | change_count = set_topic_all_peers(rng, &autotoxes[1], NUM_GROUP_TOXES - 1, groupnumber); |
318 | | |
319 | 1 | ck_assert_msg(change_count == 0, "%u peers changed the topic with topic lock enabled", change_count); |
320 | | |
321 | | /* A final check that the topic is unchanged */ |
322 | 1 | wait_state_topic(autotoxes, groupnumber, TOPIC2, TOPIC_LEN2); |
323 | | |
324 | 4 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
325 | 3 | Tox_Err_Group_Leave err_exit; |
326 | 3 | tox_group_leave(autotoxes[i].tox, groupnumber, nullptr, 0, &err_exit); |
327 | 3 | ck_assert_msg(err_exit == TOX_ERR_GROUP_LEAVE_OK, "%d", err_exit); |
328 | 3 | } |
329 | | |
330 | 1 | fprintf(stderr, "All tests passed!\n"); |
331 | 1 | } |
332 | | |
333 | | int main(void) |
334 | 721 | { |
335 | 721 | setvbuf(stdout, nullptr, _IONBF, 0); |
336 | | |
337 | 721 | Run_Auto_Options autotest_opts = default_run_auto_options(); |
338 | 721 | autotest_opts.graph = GRAPH_COMPLETE; |
339 | | |
340 | 721 | run_auto_test(nullptr, NUM_GROUP_TOXES, group_topic_test, sizeof(State), &autotest_opts); |
341 | | |
342 | 721 | return 0; |
343 | 721 | } |
344 | | |
345 | | #undef TOPIC |
346 | | #undef TOPIC_LEN |
347 | | #undef TOPIC2 |
348 | | #undef TOPIC_LEN2 |
349 | | #undef NUM_GROUP_TOXES |
350 | | #undef GROUP_NAME |
351 | | #undef GROUP_NAME_LEN |
352 | | #undef PEER0_NICK |
353 | | #undef PEER0_NICK_LEN |