summaryrefslogtreecommitdiff
path: root/tests/t_trackerlogic.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/t_trackerlogic.c')
-rw-r--r--tests/t_trackerlogic.c241
1 files changed, 241 insertions, 0 deletions
diff --git a/tests/t_trackerlogic.c b/tests/t_trackerlogic.c
new file mode 100644
index 0000000..f64977f
--- /dev/null
+++ b/tests/t_trackerlogic.c
@@ -0,0 +1,241 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <arpa/inet.h>
6#include <signal.h>
7#include <pthread.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_mutex.h"
12#include "trackerlogic.h"
13#include "test.h"
14
15/* The swarm itself: what an announce does to a torrent, and what a scrape
16 says about it afterwards. The counts are read back through the udp scrape
17 because that is the one reply the tracker builds as plain numbers. */
18
19static struct ot_workstruct ws;
20
21static void hash_named(ot_hash hash, const char *name) {
22 memset(hash, 0, sizeof(ot_hash));
23 memcpy(hash, name, strlen(name) < sizeof(ot_hash) ? strlen(name) : sizeof(ot_hash));
24}
25
26/* An IPv4 peer arrives as the v4 mapped form the tracker stores */
27static void peer_v4(ot_peer6 peer, uint8_t last, uint16_t port) {
28 ot_ip6 ip = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 10, 0, 0, 0};
29 uint16_t net_port = htons(port);
30
31 ip[15] = last;
32 memset(peer, 0, sizeof(ot_peer6));
33 OT_SETIP(peer, ip);
34 OT_SETPORT(peer, &net_port);
35 OT_PEERFLAG(peer) = 0;
36}
37
38static void peer_v6(ot_peer6 peer, uint8_t last, uint16_t port) {
39 ot_ip6 ip = {0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
40 uint16_t net_port = htons(port);
41
42 ip[15] = last;
43 memset(peer, 0, sizeof(ot_peer6));
44 OT_SETIP(peer, ip);
45 OT_SETPORT(peer, &net_port);
46 OT_PEERFLAG(peer) = 0;
47}
48
49static void announce(ot_hash *hash, ot_peer6 peer) {
50 ws.hash = hash;
51 memcpy(ws.peer, peer, sizeof(ot_peer6));
52 ws.reply = ws.outbuf;
53 add_peer_to_torrent_and_return_peers(FLAG_TCP, &ws, 50);
54}
55
56/* seeders, downloads completed, leechers */
57static void scrape(ot_hash hash, uint32_t counts[3]) {
58 char reply[12];
59
60 return_udp_scrape_for_torrent(hash, reply);
61 counts[0] = ntohl(((uint32_t *)reply)[0]);
62 counts[1] = ntohl(((uint32_t *)reply)[1]);
63 counts[2] = ntohl(((uint32_t *)reply)[2]);
64}
65
66static void case_an_announce_makes_a_torrent(void) {
67 ot_hash hash;
68 ot_peer6 peer;
69 uint32_t counts[3];
70 size_t before = mutex_get_torrent_count();
71
72 test_case("the first announce for a hash creates its torrent");
73 hash_named(hash, "first-announce");
74 peer_v4(peer, 1, 6881);
75 announce(&hash, peer);
76
77 CHECK_INT((int)(mutex_get_torrent_count() - before), 1);
78 scrape(hash, counts);
79 CHECK_INT((int)counts[2], 1); /* one leecher */
80 CHECK_INT((int)counts[0], 0);
81}
82
83static void case_the_same_peer_twice_is_one_peer(void) {
84 ot_hash hash;
85 ot_peer6 peer;
86 uint32_t counts[3];
87
88 test_case("announcing twice renews a peer rather than adding another");
89 hash_named(hash, "renewing");
90 peer_v4(peer, 2, 6881);
91 announce(&hash, peer);
92 announce(&hash, peer);
93
94 scrape(hash, counts);
95 CHECK_INT((int)counts[2], 1);
96}
97
98static void case_a_port_makes_a_different_peer(void) {
99 ot_hash hash;
100 ot_peer6 peer;
101 uint32_t counts[3];
102
103 test_case("the same address on another port is another peer");
104 hash_named(hash, "two-ports");
105 peer_v4(peer, 3, 6881);
106 announce(&hash, peer);
107 peer_v4(peer, 3, 6882);
108 announce(&hash, peer);
109
110 scrape(hash, counts);
111 CHECK_INT((int)counts[2], 2);
112}
113
114static void case_seeding_is_counted_apart(void) {
115 ot_hash hash;
116 ot_peer6 peer;
117 uint32_t counts[3];
118
119 test_case("a seeder counts as complete, a leecher as incomplete");
120 hash_named(hash, "seed-and-leech");
121 peer_v4(peer, 4, 6881);
122 OT_PEERFLAG(peer) |= PEER_FLAG_SEEDING;
123 announce(&hash, peer);
124 peer_v4(peer, 5, 6881);
125 announce(&hash, peer);
126
127 scrape(hash, counts);
128 CHECK_INT((int)counts[0], 1);
129 CHECK_INT((int)counts[2], 1);
130}
131
132static void case_completion_without_seeding_is_not_believed(void) {
133 ot_hash hash;
134 ot_peer6 peer;
135 uint32_t counts[3];
136
137 test_case("a peer that claims to have completed but is still leeching is not counted");
138 hash_named(hash, "claiming");
139 peer_v4(peer, 6, 6881);
140 OT_PEERFLAG(peer) |= PEER_FLAG_COMPLETED;
141 announce(&hash, peer);
142
143 scrape(hash, counts);
144 CHECK_INT((int)counts[1], 0);
145 CHECK_INT((int)counts[2], 1); /* still a leecher */
146}
147
148static void case_completing_is_counted_once(void) {
149 ot_hash hash;
150 ot_peer6 peer;
151 uint32_t counts[3];
152
153 test_case("a seeder reporting completion is counted once, however often it says so");
154 hash_named(hash, "completing");
155 peer_v4(peer, 7, 6881);
156 OT_PEERFLAG(peer) |= PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING;
157 announce(&hash, peer);
158 announce(&hash, peer);
159 announce(&hash, peer);
160
161 scrape(hash, counts);
162 CHECK_INT((int)counts[1], 1);
163 CHECK_INT((int)counts[0], 1);
164}
165
166static void case_a_peer_can_leave(void) {
167 ot_hash hash;
168 ot_peer6 peer;
169 uint32_t counts[3];
170
171 test_case("a peer that stops is gone from the swarm");
172 hash_named(hash, "leaving");
173 peer_v4(peer, 7, 6881);
174 announce(&hash, peer);
175 peer_v4(peer, 8, 6881);
176 announce(&hash, peer);
177 scrape(hash, counts);
178 CHECK_INT((int)counts[2], 2);
179
180 ws.hash = &hash;
181 memcpy(ws.peer, peer, sizeof(ot_peer6));
182 remove_peer_from_torrent(FLAG_TCP, &ws);
183
184 scrape(hash, counts);
185 CHECK_INT((int)counts[2], 1);
186}
187
188static void case_the_families_are_kept_apart(void) {
189 ot_hash hash;
190 ot_peer6 peer;
191 uint32_t counts[3];
192
193 test_case("a v4 and a v6 peer of one swarm are both counted");
194 hash_named(hash, "dual-stack");
195 peer_v4(peer, 9, 6881);
196 announce(&hash, peer);
197 peer_v6(peer, 9, 6881);
198 announce(&hash, peer);
199
200 scrape(hash, counts);
201 CHECK_INT((int)counts[2], 2);
202}
203
204static void case_a_hash_nobody_announced(void) {
205 ot_hash hash;
206 uint32_t counts[3];
207
208 test_case("a hash nobody has announced scrapes as nothing at all");
209 hash_named(hash, "never-seen");
210 scrape(hash, counts);
211 CHECK_INT((int)counts[0], 0);
212 CHECK_INT((int)counts[1], 0);
213 CHECK_INT((int)counts[2], 0);
214}
215
216void test_trackerlogic(void) {
217 sigset_t signal_mask;
218
219 /* accesslist_init starts a thread that waits for SIGHUP and is stopped by
220 being sent one, so the signal has to be blocked here as the tracker
221 blocks it, or stopping the tracker logic ends this process instead */
222 sigemptyset(&signal_mask);
223 sigaddset(&signal_mask, SIGHUP);
224 pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
225
226 trackerlogic_init();
227 CHECK_INT(workstruct_init(&ws), 0);
228
229 case_an_announce_makes_a_torrent();
230 case_the_same_peer_twice_is_one_peer();
231 case_a_port_makes_a_different_peer();
232 case_seeding_is_counted_apart();
233 case_completion_without_seeding_is_not_believed();
234 case_completing_is_counted_once();
235 case_a_peer_can_leave();
236 case_the_families_are_kept_apart();
237 case_a_hash_nobody_announced();
238
239 workstruct_deinit(&ws);
240 trackerlogic_deinit();
241}