Skip to content

Commit 053659a

Browse files
committed
test: add tests to TorrentEntry
1 parent 5870954 commit 053659a

File tree

1 file changed

+269
-0
lines changed

1 file changed

+269
-0
lines changed

src/tracker/torrent.rs

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,272 @@ pub enum TorrentError {
9898
CouldNotSendResponse,
9999
InvalidInfoHash,
100100
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
use std::{
105+
net::{IpAddr, Ipv4Addr, SocketAddr},
106+
ops::Sub,
107+
time::Duration,
108+
};
109+
110+
use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes};
111+
112+
use crate::{
113+
peer::TorrentPeer,
114+
protocol::clock::clock::{DefaultClock, SinceUnixEpoch, StoppedClock, StoppedTime, Time, WorkingClock},
115+
torrent::TorrentEntry,
116+
PeerId,
117+
};
118+
119+
struct TorrentPeerBuilder {
120+
peer: TorrentPeer,
121+
}
122+
123+
impl TorrentPeerBuilder {
124+
pub fn default() -> TorrentPeerBuilder {
125+
let default_peer = TorrentPeer {
126+
peer_id: PeerId([0u8; 20]),
127+
peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080),
128+
updated: DefaultClock::now(),
129+
uploaded: NumberOfBytes(0),
130+
downloaded: NumberOfBytes(0),
131+
left: NumberOfBytes(0),
132+
event: AnnounceEvent::Started,
133+
};
134+
TorrentPeerBuilder { peer: default_peer }
135+
}
136+
137+
pub fn with_event_completed(mut self) -> Self {
138+
self.peer.event = AnnounceEvent::Completed;
139+
self
140+
}
141+
142+
pub fn with_peer_address(mut self, peer_addr: SocketAddr) -> Self {
143+
self.peer.peer_addr = peer_addr;
144+
self
145+
}
146+
147+
pub fn with_peer_id(mut self, peer_id: PeerId) -> Self {
148+
self.peer.peer_id = peer_id;
149+
self
150+
}
151+
152+
pub fn with_number_of_bytes_left(mut self, left: i64) -> Self {
153+
self.peer.left = NumberOfBytes(left);
154+
self
155+
}
156+
157+
pub fn updated_at(mut self, updated: SinceUnixEpoch) -> Self {
158+
self.peer.updated = updated;
159+
self
160+
}
161+
162+
pub fn into(self) -> TorrentPeer {
163+
self.peer
164+
}
165+
}
166+
167+
/// A torrent seeder is a peer with 0 bytes left to download which
168+
/// has not announced it has stopped
169+
fn a_torrent_seeder() -> TorrentPeer {
170+
TorrentPeerBuilder::default()
171+
.with_number_of_bytes_left(0)
172+
.with_event_completed()
173+
.into()
174+
}
175+
176+
/// A torrent leecher is a peer that is not a seeder.
177+
/// Leecher: left > 0 OR event = Stopped
178+
fn a_torrent_leecher() -> TorrentPeer {
179+
TorrentPeerBuilder::default()
180+
.with_number_of_bytes_left(1)
181+
.with_event_completed()
182+
.into()
183+
}
184+
185+
#[test]
186+
fn the_default_torrent_entry_should_contain_an_empty_list_of_peers() {
187+
let torrent_entry = TorrentEntry::new();
188+
189+
assert_eq!(torrent_entry.get_peers(None).len(), 0);
190+
}
191+
192+
#[test]
193+
fn a_new_peer_can_be_added_to_a_torrent_entry() {
194+
let mut torrent_entry = TorrentEntry::new();
195+
let torrent_peer = TorrentPeerBuilder::default().into();
196+
197+
torrent_entry.update_peer(&torrent_peer); // Add the peer
198+
199+
assert_eq!(*torrent_entry.get_peers(None)[0], torrent_peer);
200+
assert_eq!(torrent_entry.get_peers(None).len(), 1);
201+
}
202+
203+
#[test]
204+
fn a_torrent_entry_should_contain_the_list_of_peers_that_were_added_to_the_torrent() {
205+
let mut torrent_entry = TorrentEntry::new();
206+
let torrent_peer = TorrentPeerBuilder::default().into();
207+
208+
torrent_entry.update_peer(&torrent_peer); // Add the peer
209+
210+
assert_eq!(torrent_entry.get_peers(None), vec![&torrent_peer]);
211+
}
212+
213+
#[test]
214+
fn a_peer_can_be_updated_in_a_torrent_entry() {
215+
let mut torrent_entry = TorrentEntry::new();
216+
let mut torrent_peer = TorrentPeerBuilder::default().into();
217+
torrent_entry.update_peer(&torrent_peer); // Add the peer
218+
219+
torrent_peer.event = AnnounceEvent::Completed; // Update the peer
220+
torrent_entry.update_peer(&torrent_peer); // Update the peer in the torrent entry
221+
222+
assert_eq!(torrent_entry.get_peers(None)[0].event, AnnounceEvent::Completed);
223+
}
224+
225+
#[test]
226+
fn a_peer_should_be_removed_from_a_torrent_entry_when_the_peer_announces_it_has_stopped() {
227+
let mut torrent_entry = TorrentEntry::new();
228+
let mut torrent_peer = TorrentPeerBuilder::default().into();
229+
torrent_entry.update_peer(&torrent_peer); // Add the peer
230+
231+
torrent_peer.event = AnnounceEvent::Stopped; // Update the peer
232+
torrent_entry.update_peer(&torrent_peer); // Update the peer in the torrent entry
233+
234+
assert_eq!(torrent_entry.get_peers(None).len(), 0);
235+
}
236+
237+
#[test]
238+
fn torrent_stats_change_when_a_previously_known_peer_announces_it_has_completed_the_torrent() {
239+
let mut torrent_entry = TorrentEntry::new();
240+
let mut torrent_peer = TorrentPeerBuilder::default().into();
241+
242+
torrent_entry.update_peer(&torrent_peer); // Add the peer
243+
244+
torrent_peer.event = AnnounceEvent::Completed; // Update the peer
245+
let stats_have_changed = torrent_entry.update_peer(&torrent_peer); // Update the peer in the torrent entry
246+
247+
assert!(stats_have_changed);
248+
}
249+
250+
#[test]
251+
fn torrent_stats_should_not_change_when_a_peer_announces_it_has_completed_the_torrent_if_it_is_the_first_announce_from_the_peer(
252+
) {
253+
let mut torrent_entry = TorrentEntry::new();
254+
let torrent_peer_announcing_complete_event = TorrentPeerBuilder::default().with_event_completed().into();
255+
256+
// Add a peer that did not exist before in the entry
257+
let torrent_stats_have_not_changed = !torrent_entry.update_peer(&torrent_peer_announcing_complete_event);
258+
259+
assert!(torrent_stats_have_not_changed);
260+
}
261+
262+
#[test]
263+
fn a_torrent_entry_could_filter_out_peers_with_a_given_socket_address() {
264+
let mut torrent_entry = TorrentEntry::new();
265+
let peer_socket_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
266+
let torrent_peer = TorrentPeerBuilder::default().with_peer_address(peer_socket_address).into();
267+
torrent_entry.update_peer(&torrent_peer); // Add peer
268+
269+
// Get peers excluding the one we have just added
270+
let peers = torrent_entry.get_peers(Some(&peer_socket_address));
271+
272+
assert_eq!(peers.len(), 0);
273+
}
274+
275+
fn peer_id_from_i32(number: i32) -> PeerId {
276+
let peer_id = number.to_le_bytes();
277+
PeerId([
278+
0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, peer_id[0], peer_id[1], peer_id[2],
279+
peer_id[3],
280+
])
281+
}
282+
283+
#[test]
284+
fn the_tracker_should_limit_the_list_of_peers_to_74_when_clients_scrape_torrents() {
285+
let mut torrent_entry = TorrentEntry::new();
286+
287+
// We add one more peer than the scrape limit
288+
for peer_number in 1..=74 + 1 {
289+
let torrent_peer = TorrentPeerBuilder::default()
290+
.with_peer_id(peer_id_from_i32(peer_number))
291+
.into();
292+
torrent_entry.update_peer(&torrent_peer);
293+
}
294+
295+
let peers = torrent_entry.get_peers(None);
296+
297+
assert_eq!(peers.len(), 74)
298+
}
299+
300+
#[test]
301+
fn torrent_stats_should_have_the_number_of_seeders_for_a_torrent() {
302+
let mut torrent_entry = TorrentEntry::new();
303+
let torrent_seeder = a_torrent_seeder();
304+
305+
torrent_entry.update_peer(&torrent_seeder); // Add seeder
306+
307+
assert_eq!(torrent_entry.get_stats().0, 1);
308+
}
309+
310+
#[test]
311+
fn torrent_stats_should_have_the_number_of_leechers_for_a_torrent() {
312+
let mut torrent_entry = TorrentEntry::new();
313+
let torrent_leecher = a_torrent_leecher();
314+
315+
torrent_entry.update_peer(&torrent_leecher); // Add leecher
316+
317+
assert_eq!(torrent_entry.get_stats().2, 1);
318+
}
319+
320+
#[test]
321+
fn torrent_stats_should_have_the_number_of_peers_that_having_announced_at_least_two_events_the_latest_one_is_the_completed_event(
322+
) {
323+
let mut torrent_entry = TorrentEntry::new();
324+
let mut torrent_peer = TorrentPeerBuilder::default().into();
325+
torrent_entry.update_peer(&torrent_peer); // Add the peer
326+
327+
// Announce "Completed" torrent download event.
328+
torrent_peer.event = AnnounceEvent::Completed;
329+
torrent_entry.update_peer(&torrent_peer); // Update the peer
330+
331+
let number_of_previously_known_peers_with_completed_torrent = torrent_entry.get_stats().1;
332+
333+
assert_eq!(number_of_previously_known_peers_with_completed_torrent, 1);
334+
}
335+
336+
#[test]
337+
fn torrent_stats_should_not_include_a_peer_in_the_completed_counter_if_the_peer_has_announced_only_one_event() {
338+
let mut torrent_entry = TorrentEntry::new();
339+
let torrent_peer_announcing_complete_event = TorrentPeerBuilder::default().with_event_completed().into();
340+
341+
// Announce "Completed" torrent download event.
342+
// It's the first event announced from this peer.
343+
torrent_entry.update_peer(&torrent_peer_announcing_complete_event); // Add the peer
344+
345+
let number_of_peers_with_completed_torrent = torrent_entry.get_stats().1;
346+
347+
assert_eq!(number_of_peers_with_completed_torrent, 0);
348+
}
349+
350+
#[test]
351+
fn a_torrent_entry_should_remove_a_peer_not_updated_after_a_timeout_in_seconds() {
352+
let mut torrent_entry = TorrentEntry::new();
353+
354+
let timeout = 120u32;
355+
356+
let now = WorkingClock::now();
357+
StoppedClock::local_set(&now);
358+
359+
let timeout_seconds_before_now = now.sub(Duration::from_secs(timeout as u64));
360+
let inactive_peer = TorrentPeerBuilder::default()
361+
.updated_at(timeout_seconds_before_now.sub(Duration::from_secs(1)))
362+
.into();
363+
torrent_entry.update_peer(&inactive_peer); // Add the peer
364+
365+
torrent_entry.remove_inactive_peers(timeout);
366+
367+
assert_eq!(torrent_entry.peers.len(), 0);
368+
}
369+
}

0 commit comments

Comments
 (0)