/work/auto_tests/scenarios/scenario_group_topic_test.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "framework/framework.h" |
2 | | #include <stdio.h> |
3 | | #include <string.h> |
4 | | #include <stdlib.h> |
5 | | |
6 | 160 | #define NUM_PEERS 3 |
7 | 2 | #define GROUP_NAME "The Test Chamber" |
8 | 1 | #define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1) |
9 | 2 | #define TOPIC1 "Topic One" |
10 | 2 | #define TOPIC2 "Topic Two" |
11 | | |
12 | | typedef struct { |
13 | | uint32_t group_number; |
14 | | uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE]; |
15 | | bool chat_id_ready; |
16 | | bool connected; |
17 | | uint32_t peer_ids[NUM_PEERS]; |
18 | | uint8_t last_topic[TOX_GROUP_MAX_TOPIC_LENGTH]; |
19 | | size_t last_topic_len; |
20 | | Tox_Group_Topic_Lock topic_lock; |
21 | | Tox_Group_Role self_role; |
22 | | } TopicState; |
23 | | |
24 | | static void on_group_self_join(const Tox_Event_Group_Self_Join *event, void *user_data) |
25 | 13 | { |
26 | 13 | ToxNode *self = (ToxNode *)user_data; |
27 | 13 | TopicState *state = (TopicState *)tox_node_get_script_ctx(self); |
28 | 13 | state->connected = true; |
29 | 13 | state->group_number = tox_event_group_self_join_get_group_number(event); |
30 | 13 | state->self_role = tox_group_self_get_role(tox_node_get_tox(self), state->group_number, nullptr); |
31 | 13 | } |
32 | | |
33 | | static void on_group_peer_join(const Tox_Event_Group_Peer_Join *event, void *user_data) |
34 | 6 | { |
35 | 6 | ToxNode *self = (ToxNode *)user_data; |
36 | 6 | TopicState *state = (TopicState *)tox_node_get_script_ctx(self); |
37 | 6 | uint32_t peer_id = tox_event_group_peer_join_get_peer_id(event); |
38 | 6 | uint32_t group_number = tox_event_group_peer_join_get_group_number(event); |
39 | | |
40 | 6 | Tox_Err_Group_Peer_Query q_err; |
41 | 6 | size_t length = tox_group_peer_get_name_size(tox_node_get_tox(self), group_number, peer_id, &q_err); |
42 | 6 | if (q_err == TOX_ERR_GROUP_PEER_QUERY_OK && length > 0) { |
43 | 6 | uint8_t name[TOX_MAX_NAME_LENGTH + 1]; |
44 | 6 | tox_group_peer_get_name(tox_node_get_tox(self), group_number, peer_id, name, &q_err); |
45 | 6 | if (q_err == TOX_ERR_GROUP_PEER_QUERY_OK) { |
46 | 6 | name[length] = 0; |
47 | 6 | if (length >= 4 && memcmp(name, "Peer", 4) == 0) { |
48 | 6 | uint32_t idx = (uint32_t)atoi((const char *)name + 4); |
49 | 6 | if (idx < NUM_PEERS) { |
50 | 6 | state->peer_ids[idx] = peer_id; |
51 | 6 | } |
52 | 6 | } |
53 | 6 | } |
54 | 6 | } |
55 | 6 | } |
56 | | |
57 | | static void on_group_topic(const Tox_Event_Group_Topic *event, void *user_data) |
58 | 31 | { |
59 | 31 | ToxNode *self = (ToxNode *)user_data; |
60 | 31 | TopicState *state = (TopicState *)tox_node_get_script_ctx(self); |
61 | 31 | state->last_topic_len = tox_event_group_topic_get_topic_length(event); |
62 | 31 | memcpy(state->last_topic, tox_event_group_topic_get_topic(event), state->last_topic_len); |
63 | 31 | } |
64 | | |
65 | | static void on_group_topic_lock(const Tox_Event_Group_Topic_Lock *event, void *user_data) |
66 | 8 | { |
67 | 8 | ToxNode *self = (ToxNode *)user_data; |
68 | 8 | TopicState *state = (TopicState *)tox_node_get_script_ctx(self); |
69 | 8 | state->topic_lock = tox_event_group_topic_lock_get_topic_lock(event); |
70 | 8 | } |
71 | | |
72 | | static void on_group_moderation(const Tox_Event_Group_Moderation *event, void *user_data) |
73 | 6 | { |
74 | 6 | ToxNode *self = (ToxNode *)user_data; |
75 | 6 | TopicState *state = (TopicState *)tox_node_get_script_ctx(self); |
76 | 6 | Tox_Err_Group_Self_Query err; |
77 | 6 | state->self_role = tox_group_self_get_role(tox_node_get_tox(self), state->group_number, &err); |
78 | 6 | } |
79 | | |
80 | | static void common_init(ToxNode *self, TopicState *state) |
81 | 8 | { |
82 | 8 | Tox_Dispatch *dispatch = tox_node_get_dispatch(self); |
83 | 8 | tox_events_callback_group_self_join(dispatch, on_group_self_join); |
84 | 8 | tox_events_callback_group_peer_join(dispatch, on_group_peer_join); |
85 | 8 | tox_events_callback_group_topic(dispatch, on_group_topic); |
86 | 8 | tox_events_callback_group_topic_lock(dispatch, on_group_topic_lock); |
87 | 8 | tox_events_callback_group_moderation(dispatch, on_group_moderation); |
88 | | |
89 | 42 | for (uint32_t i = 0; i < NUM_PEERS; ++i) { |
90 | 34 | state->peer_ids[i] = UINT32_MAX; |
91 | 34 | } |
92 | 8 | state->topic_lock = TOX_GROUP_TOPIC_LOCK_ENABLED; |
93 | | |
94 | 8 | tox_node_wait_for_self_connected(self); |
95 | 8 | } |
96 | | |
97 | | static bool topic_is(ToxNode *self, const char *topic) |
98 | 21 | { |
99 | 21 | TopicState *state = (TopicState *)tox_node_get_script_ctx(self); |
100 | 21 | size_t len = strlen(topic); |
101 | 21 | if (state->last_topic_len == len && memcmp(state->last_topic, topic, len) == 0) { |
102 | 10 | return true; |
103 | 10 | } |
104 | | |
105 | 11 | Tox_Err_Group_State_Query err; |
106 | 11 | size_t current_len = tox_group_get_topic_size(tox_node_get_tox(self), state->group_number, &err); |
107 | 11 | if (err == TOX_ERR_GROUP_STATE_QUERY_OK && current_len == len) { |
108 | 7 | uint8_t current_topic[TOX_GROUP_MAX_TOPIC_LENGTH]; |
109 | 7 | tox_group_get_topic(tox_node_get_tox(self), state->group_number, current_topic, &err); |
110 | 7 | if (err == TOX_ERR_GROUP_STATE_QUERY_OK && memcmp(current_topic, topic, len) == 0) { |
111 | 5 | state->last_topic_len = current_len; |
112 | 5 | memcpy(state->last_topic, current_topic, current_len); |
113 | 5 | return true; |
114 | 5 | } |
115 | 7 | } |
116 | 6 | return false; |
117 | 11 | } |
118 | | |
119 | | static bool topic_lock_is(ToxNode *self, Tox_Group_Topic_Lock lock) |
120 | 13 | { |
121 | 13 | TopicState *state = (TopicState *)tox_node_get_script_ctx(self); |
122 | 13 | if (state->topic_lock == lock) { |
123 | 4 | return true; |
124 | 4 | } |
125 | | |
126 | 9 | Tox_Err_Group_State_Query err; |
127 | 9 | Tox_Group_Topic_Lock current_lock = tox_group_get_topic_lock(tox_node_get_tox(self), state->group_number, &err); |
128 | 9 | if (err == TOX_ERR_GROUP_STATE_QUERY_OK && current_lock == lock) { |
129 | 2 | state->topic_lock = current_lock; |
130 | 2 | return true; |
131 | 2 | } |
132 | 7 | return false; |
133 | 9 | } |
134 | | |
135 | | static void founder_script(ToxNode *self, void *ctx) |
136 | 1 | { |
137 | 1 | TopicState *state = (TopicState *)ctx; |
138 | 1 | Tox *tox = tox_node_get_tox(self); |
139 | 1 | common_init(self, state); |
140 | | |
141 | 1 | Tox_Err_Group_New err_new; |
142 | 1 | state->group_number = tox_group_new(tox, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, GROUP_NAME_LEN, (const uint8_t *)"Peer0", 5, &err_new); |
143 | 1 | ck_assert(err_new == TOX_ERR_GROUP_NEW_OK); |
144 | | |
145 | 1 | state->peer_ids[0] = tox_group_self_get_peer_id(tox, state->group_number, nullptr); |
146 | | |
147 | 1 | tox_group_get_chat_id(tox, state->group_number, state->chat_id, nullptr); |
148 | 1 | state->chat_id_ready = true; |
149 | | |
150 | 1 | tox_scenario_barrier_wait(self); // 1: Peers joined |
151 | 3 | for (uint32_t i = 1; i < NUM_PEERS; ++i) { |
152 | 2 | WAIT_UNTIL(state->peer_ids[i] != UINT32_MAX); |
153 | 2 | } |
154 | 1 | tox_scenario_barrier_wait(self); // 2: Sync IDs |
155 | | |
156 | | // 3: Initial Topic |
157 | 1 | tox_group_set_topic(tox, state->group_number, (const uint8_t *)TOPIC1, strlen(TOPIC1), nullptr); |
158 | 1 | WAIT_UNTIL(topic_is(self, TOPIC1)); |
159 | 1 | tox_scenario_barrier_wait(self); |
160 | | |
161 | | // 4: Disable topic lock |
162 | 1 | tox_group_set_topic_lock(tox, state->group_number, TOX_GROUP_TOPIC_LOCK_DISABLED, nullptr); |
163 | 1 | WAIT_UNTIL(topic_lock_is(self, TOX_GROUP_TOPIC_LOCK_DISABLED)); |
164 | 1 | tox_scenario_barrier_wait(self); |
165 | | |
166 | | // 5: Peers changing topic |
167 | 1 | tox_scenario_barrier_wait(self); // Peer 1 turn |
168 | 1 | WAIT_UNTIL(topic_is(self, "Topic from Peer1")); |
169 | 1 | tox_scenario_barrier_wait(self); // Peer 2 turn |
170 | 1 | WAIT_UNTIL(topic_is(self, "Topic from Peer2")); |
171 | | |
172 | | // 6: Set Peer 2 to Observer |
173 | 1 | tox_group_set_role(tox, state->group_number, state->peer_ids[2], TOX_GROUP_ROLE_OBSERVER, nullptr); |
174 | 1 | tox_scenario_barrier_wait(self); |
175 | | |
176 | | // 7: Peer 1 changes topic, Peer 2 should fail |
177 | 1 | tox_scenario_barrier_wait(self); // Peer 1 turn |
178 | 1 | WAIT_UNTIL(topic_is(self, "Topic again from Peer1")); |
179 | 1 | tox_scenario_barrier_wait(self); // Peer 2 turn (fails) |
180 | | |
181 | | // 8: Enable topic lock |
182 | 1 | tox_group_set_topic_lock(tox, state->group_number, TOX_GROUP_TOPIC_LOCK_ENABLED, nullptr); |
183 | 1 | WAIT_UNTIL(topic_lock_is(self, TOX_GROUP_TOPIC_LOCK_ENABLED)); |
184 | 1 | tox_scenario_barrier_wait(self); |
185 | | |
186 | | // 9: Peer 1 attempts to change topic (fails) |
187 | 1 | tox_scenario_barrier_wait(self); |
188 | | |
189 | | // 10: Founder changes topic |
190 | 1 | tox_group_set_topic(tox, state->group_number, (const uint8_t *)TOPIC2, strlen(TOPIC2), nullptr); |
191 | 1 | WAIT_UNTIL(topic_is(self, TOPIC2)); |
192 | 1 | tox_scenario_barrier_wait(self); |
193 | 1 | } |
194 | | |
195 | | static void peer_script(ToxNode *self, void *ctx) |
196 | 2 | { |
197 | 2 | TopicState *state = (TopicState *)ctx; |
198 | 2 | Tox *tox = tox_node_get_tox(self); |
199 | 2 | common_init(self, state); |
200 | | |
201 | 2 | ToxNode *founder = tox_scenario_get_node(tox_node_get_scenario(self), 0); |
202 | 2 | const TopicState *founder_view = (const TopicState *)tox_node_get_peer_ctx(founder); |
203 | | |
204 | 2 | WAIT_UNTIL(founder_view->chat_id_ready); |
205 | | |
206 | 2 | char name[16]; |
207 | 2 | snprintf(name, sizeof(name), "Peer%u", tox_node_get_index(self)); |
208 | 2 | tox_group_join(tox, founder_view->chat_id, (const uint8_t *)name, strlen(name), nullptr, 0, nullptr); |
209 | | |
210 | 2 | WAIT_UNTIL(state->connected); |
211 | 2 | state->peer_ids[tox_node_get_index(self)] = tox_group_self_get_peer_id(tox, state->group_number, nullptr); |
212 | | |
213 | 2 | tox_scenario_barrier_wait(self); // 1: Joined |
214 | 8 | for (uint32_t i = 0; i < NUM_PEERS; ++i) if (i != tox_node_get_index(self)) { |
215 | 4 | WAIT_UNTIL(state->peer_ids[i] != UINT32_MAX); |
216 | 4 | } |
217 | 2 | tox_scenario_barrier_wait(self); // 2: Sync IDs |
218 | | |
219 | | // 3: Topic check |
220 | 2 | WAIT_UNTIL(topic_is(self, TOPIC1)); |
221 | 2 | tox_scenario_barrier_wait(self); |
222 | | |
223 | | // 4: Disable topic lock |
224 | 2 | WAIT_UNTIL(topic_lock_is(self, TOX_GROUP_TOPIC_LOCK_DISABLED)); |
225 | 2 | tox_scenario_barrier_wait(self); |
226 | | |
227 | | // 5: Peers changing topic |
228 | 2 | if (tox_node_get_index(self) == 1) { |
229 | 1 | tox_group_set_topic(tox, state->group_number, (const uint8_t *)"Topic from Peer1", strlen("Topic from Peer1"), nullptr); |
230 | 1 | } |
231 | 2 | tox_scenario_barrier_wait(self); // Peer 1 turn |
232 | 2 | WAIT_UNTIL(topic_is(self, "Topic from Peer1")); |
233 | | |
234 | 2 | if (tox_node_get_index(self) == 2) { |
235 | 1 | tox_group_set_topic(tox, state->group_number, (const uint8_t *)"Topic from Peer2", strlen("Topic from Peer2"), nullptr); |
236 | 1 | } |
237 | 2 | tox_scenario_barrier_wait(self); // Peer 2 turn |
238 | 2 | WAIT_UNTIL(topic_is(self, "Topic from Peer2")); |
239 | | |
240 | | // 6: Set Peer 2 to Observer |
241 | 2 | tox_scenario_barrier_wait(self); |
242 | 2 | if (tox_node_get_index(self) == 2) { |
243 | 1 | WAIT_UNTIL(state->self_role == TOX_GROUP_ROLE_OBSERVER); |
244 | 1 | } |
245 | | |
246 | | // 7: Peer 1 changes topic, Peer 2 should fail |
247 | 2 | if (tox_node_get_index(self) == 1) { |
248 | 1 | tox_group_set_topic(tox, state->group_number, (const uint8_t *)"Topic again from Peer1", strlen("Topic again from Peer1"), nullptr); |
249 | 1 | } |
250 | 2 | tox_scenario_barrier_wait(self); // Peer 1 turn |
251 | 2 | WAIT_UNTIL(topic_is(self, "Topic again from Peer1")); |
252 | | |
253 | 2 | if (tox_node_get_index(self) == 2) { |
254 | 1 | Tox_Err_Group_Topic_Set err; |
255 | 1 | tox_group_set_topic(tox, state->group_number, (const uint8_t *)"Fail topic", 10, &err); |
256 | 1 | ck_assert(err == TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS); |
257 | 1 | } |
258 | 2 | tox_scenario_barrier_wait(self); // Peer 2 turn (fails) |
259 | | |
260 | | // 8: Enable topic lock |
261 | 2 | WAIT_UNTIL(topic_lock_is(self, TOX_GROUP_TOPIC_LOCK_ENABLED)); |
262 | 2 | tox_scenario_barrier_wait(self); |
263 | | |
264 | | // 9: Peer 1 attempts to change topic (fails) |
265 | 2 | if (tox_node_get_index(self) == 1) { |
266 | 1 | Tox_Err_Group_Topic_Set err; |
267 | 1 | tox_group_set_topic(tox, state->group_number, (const uint8_t *)"Fail topic 2", 12, &err); |
268 | 1 | ck_assert(err == TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS); |
269 | 1 | } |
270 | 2 | tox_scenario_barrier_wait(self); |
271 | | |
272 | | // 10: Founder changes topic |
273 | 2 | tox_scenario_barrier_wait(self); |
274 | 2 | WAIT_UNTIL(topic_is(self, TOPIC2)); |
275 | 2 | } |
276 | | |
277 | | int main(int argc, char *argv[]) |
278 | 3 | { |
279 | 3 | ToxScenario *s = tox_scenario_new(argc, argv, 300000); |
280 | 3 | TopicState states[NUM_PEERS] = {0}; |
281 | 3 | ToxNode *nodes[NUM_PEERS]; |
282 | | |
283 | 3 | nodes[0] = tox_scenario_add_node(s, "Founder", founder_script, &states[0], sizeof(TopicState)); |
284 | 13 | for (uint32_t i = 1; i < NUM_PEERS; ++i) { |
285 | 10 | char alias[16]; |
286 | 10 | snprintf(alias, sizeof(alias), "Peer%u", i); |
287 | 10 | nodes[i] = tox_scenario_add_node(s, alias, peer_script, &states[i], sizeof(TopicState)); |
288 | 10 | } |
289 | | |
290 | 16 | for (uint32_t i = 0; i < NUM_PEERS; ++i) { |
291 | 72 | for (uint32_t j = 0; j < NUM_PEERS; ++j) { |
292 | 59 | if (i != j) { |
293 | 46 | tox_node_bootstrap(nodes[i], nodes[j]); |
294 | 46 | tox_node_friend_add(nodes[i], nodes[j]); |
295 | 46 | } |
296 | 59 | } |
297 | 13 | } |
298 | | |
299 | 3 | ToxScenarioStatus res = tox_scenario_run(s); |
300 | 3 | if (res != TOX_SCENARIO_DONE) { |
301 | 0 | return 1; |
302 | 0 | } |
303 | | |
304 | 3 | tox_scenario_free(s); |
305 | 3 | return 0; |
306 | 3 | } |
307 | | |
308 | | #undef TOPIC2 |
309 | | #undef TOPIC1 |
310 | | #undef GROUP_NAME_LEN |
311 | | #undef GROUP_NAME |
312 | | #undef NUM_PEERS |