use crate::testing_prelude::*;
use std::fs::create_dir_all;
use std::path::PathBuf;
const ANNOUNCE: &str = "https://tracker.invalid/announce";
const PIECE_LENGTH: i64 = 16384;
#[test]
#[ignore = "generates demo sample files, not a test"]
fn generate_audit_samples() {
let directory = samples_directory();
create_dir_all(&directory).expect("should create samples directory");
let samples = samples();
for (file_name, bytes) in &samples {
write(directory.join(file_name), bytes).expect("should write sample torrent");
}
assert_eq!(samples.len(), 27);
}
fn samples_directory() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../samples/audit")
}
fn samples() -> Vec<(String, Vec<u8>)> {
vec![
(
"non-utf8-accents-in-tracks.torrent".to_owned(),
non_utf8_accents_in_tracks(),
),
(
"non-utf8-accent-in-folder.torrent".to_owned(),
non_utf8_accent_in_folder(),
),
(
"non-breaking-space-in-tracks.torrent".to_owned(),
non_breaking_space_in_tracks(),
),
(
"zero-width-space-in-track.torrent".to_owned(),
zero_width_space_in_track(),
),
(
"ltr-mark-in-folder.torrent".to_owned(),
ltr_mark_in_folder(),
),
("ltr-mark-in-track.torrent".to_owned(), ltr_mark_in_track()),
(
"backslash-in-track.torrent".to_owned(),
backslash_in_track(),
),
(
"backslash-in-folder.torrent".to_owned(),
backslash_in_folder(),
),
(
"decomposed-accents-in-tracks.torrent".to_owned(),
decomposed_accents_in_tracks(),
),
(
"decomposed-accent-in-folder.torrent".to_owned(),
decomposed_accent_in_folder(),
),
(
"non-utf8-broken-extension-e.torrent".to_owned(),
non_utf8_broken_extension_e(),
),
(
"non-utf8-broken-extension-a.torrent".to_owned(),
non_utf8_broken_extension_a(),
),
(
"single-file-no-files-list-red.torrent".to_owned(),
single_file_no_files_list_red(),
),
(
"single-file-no-files-list-ops.torrent".to_owned(),
single_file_no_files_list_ops(),
),
(
"forward-slash-in-track.torrent".to_owned(),
forward_slash_in_track(),
),
(
"trailing-slash-in-folder.torrent".to_owned(),
trailing_slash_in_folder(),
),
(
"trailing-backslash-in-folder.torrent".to_owned(),
trailing_backslash_in_folder(),
),
(
"trailing-division-slash-in-folder.torrent".to_owned(),
trailing_division_slash_in_folder(),
),
(
"trailing-fullwidth-slash-in-folder.torrent".to_owned(),
trailing_fullwidth_slash_in_folder(),
),
(
"trailing-overlong-slash-in-folder.torrent".to_owned(),
trailing_overlong_slash_in_folder(),
),
(
"necessary-mark-beside-rtl-in-folder.torrent".to_owned(),
necessary_mark_beside_rtl_in_folder(),
),
("isolate-in-track.torrent".to_owned(), isolate_in_track()),
(
"rtl-override-in-track.torrent".to_owned(),
rtl_override_in_track(),
),
(
"rtl-mark-in-folder.torrent".to_owned(),
rtl_mark_in_folder(),
),
("leading-period.torrent".to_owned(), leading_period()),
("leading-space.torrent".to_owned(), leading_space()),
("trailing-space.torrent".to_owned(), trailing_space()),
]
}
fn leading_period() -> Vec<u8> {
multi(
"RED",
100_025,
".Test Artist 25 - Sample Album 25 [FLAC]",
vec![file("Test Artist 25 - Sample Track.flac")],
)
}
fn leading_space() -> Vec<u8> {
multi(
"OPS",
100_026,
"Test Artist 26 - Sample Album 26 [FLAC]",
vec![
file(" 01 - Sample Track.flac"),
file("02 - Sample Track.flac"),
],
)
}
fn trailing_space() -> Vec<u8> {
multi(
"RED",
100_027,
"Test Artist 27 - Sample Album 27 [FLAC] ",
vec![file("Test Artist 27 - Sample Track.flac")],
)
}
fn non_utf8_accents_in_tracks() -> Vec<u8> {
multi(
"RED",
100_001,
"Test Artist 1 - Sample Album 1 [FLAC]",
vec![
file(splice(b"01 - Sample Track ", 0xE8, b" One.flac")),
file(splice(b"02 - Sample Track ", 0xEE, b" Two.flac")),
],
)
}
fn non_utf8_accent_in_folder() -> Vec<u8> {
multi(
"OPS",
100_002,
splice(b"Test Artist 2 - Sample Album ", 0xE4, b" [FLAC]"),
vec![file("Test Artist 2 - Sample Track.flac")],
)
}
fn non_breaking_space_in_tracks() -> Vec<u8> {
multi(
"RED",
100_003,
"Test Artist 3 - Sample Album 3 [FLAC]",
vec![
file("01 - Sample Track.flac"),
file("02 - Sample Track with inv\u{00A0}isible.flac"),
file("03 - Sample Track with another\u{00A0}.flac"),
],
)
}
fn zero_width_space_in_track() -> Vec<u8> {
multi(
"OPS",
100_004,
"Test Artist 4 - Sample Album 4 [FLAC]",
vec![
file("01 - Sample Track with invisible\u{200B}.flac"),
file("02 - Sample Track.flac"),
],
)
}
fn ltr_mark_in_folder() -> Vec<u8> {
multi(
"RED",
100_005,
"Test Artist 5 - Sample Album \u{200E}[FLAC]",
vec![file("Test Artist 5 - Sample Track.flac")],
)
}
fn ltr_mark_in_track() -> Vec<u8> {
multi(
"OPS",
100_006,
"Test Artist 6 - Sample Album 6 [FLAC]",
vec![
file("00 - Sample Playlist \u{200E}.m3u"),
file("01 - Sample Track.flac"),
],
)
}
fn necessary_mark_beside_rtl_in_folder() -> Vec<u8> {
multi(
"OPS",
100_021,
"Test Artist 21 - \u{0623}\u{0644}\u{0628}\u{0648}\u{0645}\u{200E} [FLAC]",
vec![file("Test Artist 21 - Sample Track.flac")],
)
}
fn isolate_in_track() -> Vec<u8> {
multi(
"RED",
100_022,
"Test Artist 22 - Sample Album 22 [FLAC]",
vec![
file("01 - Sample Track \u{2066}One\u{2069}.flac"),
file("02 - Sample Track.flac"),
],
)
}
fn rtl_override_in_track() -> Vec<u8> {
multi(
"OPS",
100_023,
"Test Artist 23 - Sample Album 23 [FLAC]",
vec![
file("01 - Sample Track \u{202E}One.flac"),
file("02 - Sample Track.flac"),
],
)
}
fn rtl_mark_in_folder() -> Vec<u8> {
multi(
"RED",
100_024,
"Test Artist 24 - Sample Album 24 \u{200F}[FLAC]",
vec![file("Test Artist 24 - Sample Track.flac")],
)
}
fn backslash_in_track() -> Vec<u8> {
multi(
"RED",
100_007,
"Test Artist 7 - Sample Album 7 [FLAC]",
vec![
file("01 - Sample Track\\.flac"),
file("02 - Sample Track.flac"),
],
)
}
fn backslash_in_folder() -> Vec<u8> {
multi(
"RED",
100_008,
"Test Artist 8 - Sample Album \\ [FLAC]",
vec![file("Test Artist 8 - Sample Track.flac")],
)
}
fn forward_slash_in_track() -> Vec<u8> {
multi(
"OPS",
100_015,
"Test Artist 15 - Sample Album 15 [FLAC]",
vec![
file("01 - Sample Track/One.flac"),
file("02 - Sample Track.flac"),
],
)
}
fn trailing_slash_in_folder() -> Vec<u8> {
multi(
"RED",
100_016,
"Test Artist 16 - Sample Album 16 [FLAC]/",
vec![file("Test Artist 16 - Sample Track.flac")],
)
}
fn trailing_backslash_in_folder() -> Vec<u8> {
multi(
"OPS",
100_017,
"Test Artist 17 - Sample Album 17 [FLAC]\\",
vec![file("Test Artist 17 - Sample Track.flac")],
)
}
fn trailing_division_slash_in_folder() -> Vec<u8> {
multi(
"RED",
100_018,
"Test Artist 18 - Sample Album 18 [FLAC]\u{2215}",
vec![file("Test Artist 18 - Sample Track.flac")],
)
}
fn trailing_fullwidth_slash_in_folder() -> Vec<u8> {
multi(
"OPS",
100_019,
"Test Artist 19 - Sample Album 19 [FLAC]\u{FF0F}",
vec![file("Test Artist 19 - Sample Track.flac")],
)
}
fn trailing_overlong_slash_in_folder() -> Vec<u8> {
let mut name = b"Test Artist 20 - Sample Album 20 [FLAC]".to_vec();
name.extend_from_slice(&[0xC0, 0xAF]);
multi(
"RED",
100_020,
name,
vec![file("Test Artist 20 - Sample Track.flac")],
)
}
fn decomposed_accents_in_tracks() -> Vec<u8> {
multi(
"RED",
100_009,
"Test Artist 9 - Sample Album 9 [FLAC]",
vec![
file("01 - Sample Track Sce\u{0301}ne.flac"),
file("02 - Sample Track Re\u{0301}sume.flac"),
],
)
}
fn decomposed_accent_in_folder() -> Vec<u8> {
multi(
"OPS",
100_010,
"Test Artist 10 - Sample Album No\u{0308}rd [FLAC]",
vec![file("Test Artist 10 - Sample Track No\u{0308}rd.flac")],
)
}
fn non_utf8_broken_extension_e() -> Vec<u8> {
multi(
"RED",
100_011,
"Test Artist 11 - Sample Album 11 [FLAC]",
vec![
file(splice(b"01 - Sample Track", 0xE9, b".flac")),
file("02 - Sample Track.flac"),
],
)
}
fn non_utf8_broken_extension_a() -> Vec<u8> {
multi(
"OPS",
100_012,
"Test Artist 12 - Sample Album 12 [FLAC]",
vec![
file(splice(b"01 - Sample Track", 0xE4, b".flac")),
file("02 - Sample Track.flac"),
],
)
}
fn single_file_no_files_list_red() -> Vec<u8> {
single("RED", 100_013, "Test Artist 13 - Sample Single Track.flac")
}
fn single_file_no_files_list_ops() -> Vec<u8> {
single("OPS", 100_014, "Test Artist 14 - Sample Single Track.flac")
}
fn file(component: impl Into<RawString>) -> Vec<RawString> {
vec![component.into()]
}
fn multi(source: &str, id: u32, name: impl Into<RawString>, files: Vec<Vec<RawString>>) -> Vec<u8> {
let files = files.into_iter().map(TorrentBuilder::file).collect();
let info = TorrentBuilder::new()
.with_dictionaries("files", files)
.with_string("name", name)
.with_integer("piece length", PIECE_LENGTH)
.with_string("pieces", vec![0_u8; 20])
.with_string("source", source);
wrap(source, id, info)
}
fn single(source: &str, id: u32, name: impl Into<RawString>) -> Vec<u8> {
let info = TorrentBuilder::new()
.with_integer("length", PIECE_LENGTH)
.with_string("name", name)
.with_integer("piece length", PIECE_LENGTH)
.with_string("pieces", vec![0_u8; 20])
.with_string("source", source);
wrap(source, id, info)
}
fn wrap(source: &str, id: u32, info: TorrentBuilder) -> Vec<u8> {
TorrentBuilder::new()
.with_string("announce", ANNOUNCE)
.with_string("comment", comment(source, id))
.with_dictionary("info", info)
.build()
}
fn comment(source: &str, id: u32) -> String {
let host = if source == "OPS" { OPS_URL } else { RED_URL };
format!("{host}/torrents.php?id=1&torrentid={id}#torrent{id}")
}