Metainfo refactoring

- Combine test Metainfo values into a small number of globally available
  values.
- Serialize update URLs as `URL` insted of `String`.
- Add additional `show` tests

type: reform
This commit is contained in:
Casey Rodarmor 2020-05-03 01:24:36 -07:00
parent f4f7a69069
commit 213624cf8e
No known key found for this signature in database
GPG Key ID: 556186B153EC6FE0
6 changed files with 510 additions and 522 deletions

View File

@ -39,7 +39,6 @@ strum_macros = "0.18.0"
syn = "1.0.14"
tempfile = "3.0.0"
unicode-width = "0.1.0"
url = "2.1.1"
[dependencies.bendy]
version = "0.3.0"
@ -57,6 +56,10 @@ features = ["derive"]
version = "0.3.0"
features = ["default", "wrap_help"]
[dependencies.url]
version = "2.1.1"
features = ["serde"]
[dev-dependencies]
claim = "0.3.1"
temptree = "0.0.0"

View File

@ -26,7 +26,7 @@ pub(crate) struct Info {
with = "unwrap_or_skip",
rename = "update-url"
)]
pub(crate) update_url: Option<String>,
pub(crate) update_url: Option<Url>,
}
impl Info {

View File

@ -117,155 +117,69 @@ impl Metainfo {
pub(crate) fn infohash(&self) -> Result<Infohash> {
self.info.infohash()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_single() {
let value = Metainfo {
announce: Some("announce".into()),
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
comment: Some("comment".into()),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
nodes: Some(vec!["x:12".parse().unwrap(), "1.1.1.1:16".parse().unwrap()]),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
let deserialized = bendy::serde::de::from_bytes(&bencode).unwrap();
assert_eq!(value, deserialized);
}
#[test]
fn round_trip_multiple() {
let value = Metainfo {
announce: Some("announce".into()),
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
nodes: Some(vec!["x:12".parse().unwrap(), "1.1.1.1:16".parse().unwrap()]),
comment: Some("comment".into()),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Multiple {
files: vec![FileInfo {
length: Bytes(10),
path: FilePath::from_components(&["foo", "bar"]),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
}],
},
update_url: None,
},
};
let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
let deserialized = bendy::serde::de::from_bytes(&bencode).unwrap();
assert_eq!(value, deserialized);
}
fn representation(value: Metainfo, want: &str) {
let have = value.serialize().unwrap();
if have != want.as_bytes() {
eprintln!("have:");
eprintln!("{}", String::from_utf8_lossy(&have));
eprintln!("want:");
eprintln!("{}", want);
panic!("Unexpected representation...");
}
}
#[test]
fn bencode_representation_single_some() {
let value = Metainfo {
announce: Some("ANNOUNCE".into()),
announce_list: Some(vec![vec!["A".into(), "B".into()], vec!["C".into()]]),
#[cfg(test)]
pub(crate) fn test_value_single() -> Metainfo {
Metainfo {
announce: Some("udp://announce.example:1337".into()),
announce_list: Some(vec![
vec![
"http://a.example:4567".into(),
"https://b.example:77".into(),
],
vec!["udp://c.example:88".into()],
]),
nodes: Some(vec![
"domain:1".parse().unwrap(),
"node.example:12".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[1234:5678:9abc:def0:1234:5678:9abc:def0]:65000"
.parse()
.unwrap(),
"[2001:0db8:85a3::0000:8a2e:0370]:7334".parse().unwrap(),
]),
comment: Some("COMMENT".into()),
created_by: Some("CREATED BY".into()),
creation_date: Some(0),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(1024),
piece_length: Bytes(16 * 1024),
source: Some("SOURCE".into()),
name: "NAME".into(),
pieces: PieceList::from_pieces(&["fae50"]),
pieces: PieceList::from_pieces(&["fae50", "fae50"]),
mode: Mode::Single {
length: Bytes(5),
length: Bytes(32 * 1024),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
},
update_url: None,
update_url: Some("https://update.example".parse().unwrap()),
},
};
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "8:ANNOUNCE",
"13:announce-list", "l",
"l", "1:A", "1:B", "e",
"l", "1:C", "e",
"e",
"7:comment", "7:COMMENT",
"10:created by", "10:CREATED BY",
"13:creation date", "i0e",
"8:encoding", "5:UTF-8",
"4:info", "d",
"6:length", "i5e",
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
"4:name", "4:NAME",
"12:piece length", "i1024e",
"6:pieces", "20:8,OS7d玤{Qk!Mk",
"7:private", "i1e",
"6:source", "6:SOURCE",
"e",
"5:nodes", "l",
"l", "6:domain", "i1e", "e",
"l", "7:1.1.1.1", "i16e", "e",
"l", "39:1234:5678:9abc:def0:1234:5678:9abc:def0", "i65000e", "e",
"e",
"e"
);
representation(value, want);
}
}
#[test]
fn bencode_representation_single_none() {
let value = Metainfo {
announce: Some("ANNOUNCE".into()),
#[cfg(test)]
pub(crate) fn test_value_single_infohash() -> &'static str {
"5d6f53772b4c20536fcce0c4c364d764a6efa39c"
}
#[cfg(test)]
pub(crate) fn test_value_single_torrent_size() -> Bytes {
Bytes(509)
}
#[cfg(test)]
pub(crate) fn test_value_multiple() -> Metainfo {
let mut instance = Self::test_value_single();
instance.info.mode = Mode::Multiple {
files: vec![FileInfo {
length: Bytes(32 * 1024),
path: FilePath::from_components(&["DIR", "FILE"]),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
}],
};
instance
}
#[cfg(test)]
pub(crate) fn test_value_single_unset() -> Metainfo {
Metainfo {
announce: None,
announce_list: None,
nodes: None,
comment: None,
@ -284,77 +198,23 @@ mod tests {
},
update_url: None,
},
};
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "8:ANNOUNCE",
"4:info", "d",
"6:length", "i5e",
"4:name", "4:NAME",
"12:piece length", "i1024e",
"6:pieces", "20:8,OS7d玤{Qk!Mk",
"e",
"e"
);
representation(value, want);
}
}
#[test]
fn bencode_representation_multiple_some() {
let value = Metainfo {
announce: Some("ANNOUNCE".into()),
announce_list: None,
nodes: None,
comment: None,
created_by: None,
creation_date: None,
encoding: None,
info: Info {
private: None,
piece_length: Bytes(1024),
source: None,
name: "NAME".into(),
pieces: PieceList::from_pieces(&["fae50"]),
mode: Mode::Multiple {
files: vec![FileInfo {
length: Bytes(1024),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
path: FilePath::from_components(&["a", "b"]),
}],
},
update_url: None,
},
};
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "8:ANNOUNCE",
"4:info", "d",
"5:files", "l",
"d",
"6:length", "i1024e",
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
"4:path", "l", "1:a", "1:b", "e",
"e",
"e",
"4:name", "4:NAME",
"12:piece length", "i1024e",
"6:pieces", "20:8,OS7d玤{Qk!Mk",
"e",
"e"
);
representation(value, want);
#[cfg(test)]
pub(crate) fn test_value_single_unset_infohash() -> &'static str {
"a9105b0ff5f7cefeee5599ed7831749be21cc04e"
}
#[test]
fn bencode_representation_multiple_none() {
let value = Metainfo {
announce: Some("ANNOUNCE".into()),
#[cfg(test)]
pub(crate) fn test_value_single_unset_torrent_size() -> Bytes {
Bytes(85)
}
#[cfg(test)]
pub(crate) fn test_value_multiple_unset() -> Metainfo {
Metainfo {
announce: None,
announce_list: None,
nodes: None,
comment: None,
@ -376,12 +236,167 @@ mod tests {
},
update_url: None,
},
};
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn round_trip_single() {
let value = Metainfo::test_value_single();
let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
let deserialized = bendy::serde::de::from_bytes(&bencode).unwrap();
assert_eq!(value, deserialized);
}
#[test]
fn round_trip_multiple() {
let value = Metainfo::test_value_multiple();
let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
let deserialized = bendy::serde::de::from_bytes(&bencode).unwrap();
assert_eq!(value, deserialized);
}
fn representation(value: Metainfo, want: &str) {
let have = value.serialize().unwrap();
if have != want.as_bytes() {
let have = String::from_utf8_lossy(&have);
assert_eq!(have, want);
eprintln!("have:");
eprintln!("{}", have);
eprintln!("want:");
eprintln!("{}", want);
panic!("Unexpected representation...");
}
}
#[test]
fn bencode_representation_single_set() {
let value = Metainfo::test_value_single();
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "27:udp://announce.example:1337",
"13:announce-list", "l",
"l",
"21:http://a.example:4567",
"20:https://b.example:77",
"e",
"l",
"18:udp://c.example:88",
"e",
"e",
"7:comment", "7:COMMENT",
"10:created by", "10:CREATED BY",
"13:creation date", "i1e",
"8:encoding", "5:UTF-8",
"4:info", "d",
"6:length", "i32768e",
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
"4:name", "4:NAME",
"12:piece length", "i16384e",
"6:pieces", "40:8,OS7d玤{Qk!Mk8,OS7d玤{Qk!Mk",
"7:private", "i1e",
"6:source", "6:SOURCE",
"10:update-url", "23:https://update.example/",
"e",
"5:nodes", "l",
"l", "12:node.example", "i12e", "e",
"l", "7:1.1.1.1", "i16e", "e",
"l", "23:2001:db8:85a3::8a2e:370", "i7334e", "e",
"e",
"e"
);
representation(value, want);
}
#[test]
fn bencode_representation_single_unset() {
let value = Metainfo::test_value_single_unset();
#[rustfmt::skip]
let want = concat!(
"d",
"4:info", "d",
"6:length", "i5e",
"4:name", "4:NAME",
"12:piece length", "i1024e",
"6:pieces", "20:8,OS7d玤{Qk!Mk",
"e",
"e"
);
representation(value, want);
}
#[test]
fn bencode_representation_multiple_set() {
let value = Metainfo::test_value_multiple();
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "27:udp://announce.example:1337",
"13:announce-list", "l",
"l",
"21:http://a.example:4567",
"20:https://b.example:77",
"e",
"l",
"18:udp://c.example:88",
"e",
"e",
"7:comment", "7:COMMENT",
"10:created by", "10:CREATED BY",
"13:creation date", "i1e",
"8:encoding", "5:UTF-8",
"4:info", "d",
"5:files", "l",
"d",
"6:length", "i32768e",
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
"4:path", "l", "3:DIR", "4:FILE", "e",
"e",
"e",
"4:name", "4:NAME",
"12:piece length", "i16384e",
"6:pieces", "40:8,OS7d玤{Qk!Mk8,OS7d玤{Qk!Mk",
"7:private", "i1e",
"6:source", "6:SOURCE",
"10:update-url", "23:https://update.example/",
"e",
"5:nodes", "l",
"l", "12:node.example", "i12e", "e",
"l", "7:1.1.1.1", "i16e", "e",
"l", "23:2001:db8:85a3::8a2e:370", "i7334e", "e",
"e",
"e"
);
representation(value, want);
}
#[test]
fn bencode_representation_multiple_unset() {
let value = Metainfo::test_value_multiple_unset();
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "8:ANNOUNCE",
"4:info", "d",
"5:files", "l",
"d",
@ -400,85 +415,44 @@ mod tests {
}
#[test]
fn private_false() {
let value = Metainfo {
announce: Some("ANNOUNCE".into()),
announce_list: None,
nodes: None,
comment: None,
created_by: None,
creation_date: None,
encoding: None,
info: Info {
private: Some(false),
piece_length: Bytes(1024),
source: None,
name: "NAME".into(),
pieces: PieceList::from_pieces(&["fae50"]),
mode: Mode::Single {
length: Bytes(5),
md5sum: None,
},
update_url: None,
},
};
fn trackers() {
fn assert_trackers_eq(metainfo: &Metainfo, want: &[&str]) {
let want = want
.iter()
.cloned()
.map(Url::parse)
.collect::<Result<Vec<Url>, url::ParseError>>()
.unwrap();
let have = metainfo.trackers().collect::<Result<Vec<Url>>>().unwrap();
assert_eq!(have, want);
}
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "8:ANNOUNCE",
"4:info", "d",
"6:length", "i5e",
"4:name", "4:NAME",
"12:piece length", "i1024e",
"6:pieces", "20:8,OS7d玤{Qk!Mk",
"7:private", "i0e",
"e",
"e"
let mut metainfo = Metainfo::test_value_single();
assert_trackers_eq(
&metainfo,
&[
"udp://announce.example:1337",
"http://a.example:4567",
"https://b.example:77",
"udp://c.example:88",
],
);
representation(value, want);
}
#[test]
fn trackers() {
let mut metainfo = Metainfo {
announce: Some("http://foo".into()),
announce_list: None,
nodes: None,
comment: None,
created_by: None,
creation_date: None,
encoding: None,
info: Info {
private: Some(false),
piece_length: Bytes(1024),
source: None,
name: "NAME".into(),
pieces: PieceList::from_pieces(&["fae50"]),
mode: Mode::Single {
length: Bytes(5),
md5sum: None,
},
update_url: None,
},
};
let trackers = metainfo.trackers().collect::<Result<Vec<Url>>>().unwrap();
assert_eq!(trackers, &["http://foo".parse().unwrap()]);
metainfo.announce_list = Some(vec![
vec!["http://bar".into(), "http://baz".into()],
vec!["http://foo".into()],
vec![
"udp://announce.example:1337".into(),
"https://b.example:77".into(),
],
vec!["udp://c.example:88".into()],
]);
let trackers = metainfo.trackers().collect::<Result<Vec<Url>>>().unwrap();
assert_eq!(
trackers,
assert_trackers_eq(
&metainfo,
&[
"http://foo".parse().unwrap(),
"http://bar".parse().unwrap(),
"http://baz".parse().unwrap(),
"udp://announce.example:1337",
"https://b.example:77",
"udp://c.example:88",
],
);
}

View File

@ -382,13 +382,13 @@ impl Create {
}
let info = Info {
source: self.source,
piece_length: content.piece_length,
name: content.name,
piece_length: content.piece_length,
source: self.source,
update_url: self.update_url,
mode,
pieces,
private,
update_url: self.update_url.map(|url| url.to_string()),
};
let metainfo = Metainfo {
@ -3187,8 +3187,8 @@ Content Size 9 bytes
env.assert_ok();
let metainfo = env.load_metainfo("foo.torrent");
assert_eq!(
metainfo.info.update_url.as_deref(),
Some("https://www.a_real_url.com/")
metainfo.info.update_url,
Some("https://www.a_real_url.com/".parse().unwrap())
);
}
}

View File

@ -111,31 +111,53 @@ mod tests {
#[test]
fn output() -> Result<()> {
let metainfo = Metainfo {
announce: Some("announce".into()),
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
nodes: Some(vec![
"x:12".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[2001:0db8:85a3::0000:8a2e:0370]:7334".parse().unwrap(),
]),
comment: Some("comment".into()),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["xyz", "abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
let metainfo = Metainfo::test_value_single();
#[rustfmt::skip]
let want_human_readable = format!(
" Name NAME
Comment COMMENT
Creation Date 1970-01-01 00:00:01 UTC
Created By CREATED BY
Source SOURCE
Info Hash {}
Torrent Size {}
Content Size 32 KiB
Private yes
Tracker udp://announce.example:1337
Announce List Tier 1: http://a.example:4567
https://b.example:77
Tier 2: udp://c.example:88
Update URL https://update.example/
DHT Nodes node.example:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 2
File Count 1
Files NAME
", Metainfo::test_value_single_infohash(), Metainfo::test_value_single_torrent_size());
#[rustfmt::skip]
let want_machine_readable = format!("\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC
created by\tCREATED BY
source\tSOURCE
info hash\t{}
torrent size\t{}
content size\t32768
private\tyes
tracker\tudp://announce.example:1337
announce list\thttp://a.example:4567\thttps://b.example:77\tudp://c.example:88
update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384
piece count\t2
file count\t1
files\tNAME
", Metainfo::test_value_single_infohash(), Metainfo::test_value_single_torrent_size().count());
{
let mut env = TestEnvBuilder::new()
@ -150,29 +172,7 @@ mod tests {
env.assert_ok();
let have = env.out();
let want = " Name foo
Comment comment
Creation Date 1970-01-01 00:00:01 UTC
Created By created by
Source source
Info Hash e12253978dc6d50db11d05747abcea1ad03b51c5
Torrent Size 339 bytes
Content Size 20 bytes
Private yes
Tracker announce
Announce List Tier 1: announce
b
Tier 2: c
DHT Nodes x:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 2
File Count 1
Files foo
";
assert_eq!(have, want);
assert_eq!(have, want_human_readable);
}
{
@ -188,29 +188,8 @@ Announce List Tier 1: announce
env.assert_ok();
let have = env.out();
let want = " Name foo
Comment comment
Creation Date 1970-01-01 00:00:01 UTC
Created By created by
Source source
Info Hash e12253978dc6d50db11d05747abcea1ad03b51c5
Torrent Size 339 bytes
Content Size 20 bytes
Private yes
Tracker announce
Announce List Tier 1: announce
b
Tier 2: c
DHT Nodes x:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 2
File Count 1
Files foo
";
assert_eq!(have, want);
assert_eq!(have, want_human_readable);
}
{
@ -225,26 +204,8 @@ Announce List Tier 1: announce
env.assert_ok();
let have = env.out();
let want = "\
name\tfoo
comment\tcomment
creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by
source\tsource
info hash\te12253978dc6d50db11d05747abcea1ad03b51c5
torrent size\t339
content size\t20
private\tyes
tracker\tannounce
announce list\tannounce\tb\tc
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384
piece count\t2
file count\t1
files\tfoo
";
assert_eq!(have, want);
assert_eq!(have, want_machine_readable);
}
Ok(())
@ -252,31 +213,7 @@ files\tfoo
#[test]
fn tier_list_with_main() -> Result<()> {
let metainfo = Metainfo {
announce: Some("a".into()),
announce_list: Some(vec![vec!["x".into()], vec!["y".into()], vec!["z".into()]]),
comment: Some("comment".into()),
created_by: Some("created by".into()),
nodes: Some(vec![
"x:12".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[2001:0db8:85a3::0000:8a2e:0370]:7334".parse().unwrap(),
]),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["xyz", "abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
let metainfo = Metainfo::test_value_single();
{
let mut env = TestEnvBuilder::new()
@ -291,27 +228,34 @@ files\tfoo
env.assert_ok();
let have = env.out();
let want = " Name foo
Comment comment
#[rustfmt::skip]
let want = format!(
" Name NAME
Comment COMMENT
Creation Date 1970-01-01 00:00:01 UTC
Created By created by
Source source
Info Hash e12253978dc6d50db11d05747abcea1ad03b51c5
Torrent Size 327 bytes
Content Size 20 bytes
Created By CREATED BY
Source SOURCE
Info Hash {}
Torrent Size {}
Content Size 32 KiB
Private yes
Tracker a
Announce List Tier 1: x
Tier 2: y
Tier 3: z
DHT Nodes x:12
Tracker udp://announce.example:1337
Announce List Tier 1: http://a.example:4567
https://b.example:77
Tier 2: udp://c.example:88
Update URL https://update.example/
DHT Nodes node.example:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 2
File Count 1
Files foo
";
Files NAME
",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size()
);
assert_eq!(have, want);
}
@ -328,24 +272,31 @@ Announce List Tier 1: x
env.assert_ok();
let have = env.out();
let want = "\
name\tfoo
comment\tcomment
#[rustfmt::skip]
let want = format!(
"\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by
source\tsource
info hash\te12253978dc6d50db11d05747abcea1ad03b51c5
torrent size\t327
content size\t20
created by\tCREATED BY
source\tSOURCE
info hash\t{}
torrent size\t{}
content size\t32768
private\tyes
tracker\ta
announce list\tx\ty\tz
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
tracker\tudp://announce.example:1337
announce list\thttp://a.example:4567\thttps://b.example:77\tudp://c.example:88
update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384
piece count\t2
file count\t1
files\tfoo
";
files\tNAME
",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size().count()
);
assert_eq!(have, want);
}
@ -355,31 +306,13 @@ files\tfoo
#[test]
fn tier_list_without_main() -> Result<()> {
let metainfo = Metainfo {
announce: Some("a".into()),
announce_list: Some(vec![vec!["b".into()], vec!["c".into()], vec!["a".into()]]),
comment: Some("comment".into()),
nodes: Some(vec![
"x:12".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[2001:0db8:85a3::8a2e:0370]:7334".parse().unwrap(),
]),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
let mut metainfo = Metainfo::test_value_single();
metainfo.announce_list = Some(vec![
vec!["B".into()],
vec!["C".into()],
vec!["ANNOUNCE".into()],
]);
{
let mut env = TestEnvBuilder::new()
@ -394,27 +327,34 @@ files\tfoo
env.assert_ok();
let have = env.out();
let want = " Name foo
Comment comment
#[rustfmt::skip]
let want = format!(
" Name NAME
Comment COMMENT
Creation Date 1970-01-01 00:00:01 UTC
Created By created by
Source source
Info Hash b9cd9cae5748518c99d00d8ae86c0162510be4d9
Torrent Size 307 bytes
Content Size 20 bytes
Created By CREATED BY
Source SOURCE
Info Hash {}
Torrent Size {}
Content Size 32 KiB
Private yes
Tracker a
Announce List Tier 1: b
Tier 2: c
Tier 3: a
DHT Nodes x:12
Tracker udp://announce.example:1337
Announce List Tier 1: B
Tier 2: C
Tier 3: ANNOUNCE
Update URL https://update.example/
DHT Nodes node.example:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 1
Piece Count 2
File Count 1
Files foo
";
Files NAME
",
Metainfo::test_value_single_infohash(),
Bytes(Metainfo::test_value_single_torrent_size().count() - 50)
);
assert_eq!(have, want);
}
@ -431,24 +371,30 @@ Announce List Tier 1: b
env.assert_ok();
let have = env.out();
let want = "\
name\tfoo
comment\tcomment
#[rustfmt::skip]
let want = format!("\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by
source\tsource
info hash\tb9cd9cae5748518c99d00d8ae86c0162510be4d9
torrent size\t307
content size\t20
created by\tCREATED BY
source\tSOURCE
info hash\t{}
torrent size\t{}
content size\t32768
private\tyes
tracker\ta
announce list\tb\tc\ta
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
tracker\tudp://announce.example:1337
announce list\tB\tC\tANNOUNCE
update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384
piece count\t1
piece count\t2
file count\t1
files\tfoo
";
files\tNAME
",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size().count() - 50
);
assert_eq!(have, want);
}
@ -458,31 +404,9 @@ files\tfoo
#[test]
fn trackerless() -> Result<()> {
let metainfo = Metainfo {
announce: None,
announce_list: None,
comment: Some("comment".into()),
nodes: Some(vec![
"x:12".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[2001:0db8:85a3::8a2e:0370]:7334".parse().unwrap(),
]),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
let mut metainfo = Metainfo::test_value_single();
metainfo.announce = None;
metainfo.announce_list = None;
{
let mut env = TestEnvBuilder::new()
@ -497,23 +421,29 @@ files\tfoo
env.assert_ok();
let have = env.out();
let want = " Name foo
Comment comment
#[rustfmt::skip]
let want = format!(" Name NAME
Comment COMMENT
Creation Date 1970-01-01 00:00:01 UTC
Created By created by
Source source
Info Hash b9cd9cae5748518c99d00d8ae86c0162510be4d9
Torrent Size 261 bytes
Content Size 20 bytes
Created By CREATED BY
Source SOURCE
Info Hash {}
Torrent Size {}
Content Size 32 KiB
Private yes
DHT Nodes x:12
Update URL https://update.example/
DHT Nodes node.example:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 1
Piece Count 2
File Count 1
Files foo
";
Files NAME
",
Metainfo::test_value_single_infohash(),
Bytes(Metainfo::test_value_single_torrent_size().count() - 130)
);
assert_eq!(have, want);
}
@ -530,22 +460,99 @@ Creation Date 1970-01-01 00:00:01 UTC
env.assert_ok();
let have = env.out();
let want = "\
name\tfoo
comment\tcomment
#[rustfmt::skip]
let want = format!(
"\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by
source\tsource
info hash\tb9cd9cae5748518c99d00d8ae86c0162510be4d9
torrent size\t261
content size\t20
created by\tCREATED BY
source\tSOURCE
info hash\t{}
torrent size\t{}
content size\t32768
private\tyes
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384
piece count\t2
file count\t1
files\tNAME
",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size().count() - 130
);
assert_eq!(have, want);
}
Ok(())
}
#[test]
fn unset() -> Result<()> {
let metainfo = Metainfo::test_value_single_unset();
{
let mut env = TestEnvBuilder::new()
.arg_slice(&["imdl", "torrent", "show", "--input", "foo.torrent"])
.out_is_term()
.build();
let path = env.resolve("foo.torrent")?;
metainfo.dump(path).unwrap();
env.assert_ok();
let have = env.out();
#[rustfmt::skip]
let want = format!(" Name NAME
Info Hash {}
Torrent Size {}
Content Size 5 bytes
Private no
Piece Size 1 KiB
Piece Count 1
File Count 1
Files NAME
",
Metainfo::test_value_single_unset_infohash(),
Metainfo::test_value_single_unset_torrent_size()
);
assert_eq!(have, want);
}
{
let mut env = TestEnvBuilder::new()
.arg_slice(&["imdl", "torrent", "show", "--input", "foo.torrent"])
.build();
let path = env.resolve("foo.torrent")?;
metainfo.dump(path).unwrap();
env.assert_ok();
let have = env.out();
#[rustfmt::skip]
let want = format!(
"\
name\tNAME
info hash\t{}
torrent size\t{}
content size\t5
private\tno
piece size\t1024
piece count\t1
file count\t1
files\tfoo
";
files\tNAME
",
Metainfo::test_value_single_unset_infohash(),
Metainfo::test_value_single_unset_torrent_size().count()
);
assert_eq!(have, want);
}

View File

@ -105,6 +105,10 @@ impl TorrentSummary {
table.tiers("Announce List", value);
}
if let Some(update_url) = &self.metainfo.info.update_url {
table.row("Update URL", update_url);
}
if let Some(nodes) = &self.metainfo.nodes {
table.list(
"DHT Nodes",