From 213624cf8ecaccb832e14499165767e779a2799c Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 3 May 2020 01:24:36 -0700 Subject: [PATCH] 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 --- Cargo.toml | 5 +- src/info.rs | 2 +- src/metainfo.rs | 436 +++++++++++++-------------- src/subcommand/torrent/create.rs | 10 +- src/subcommand/torrent/show.rs | 487 ++++++++++++++++--------------- src/torrent_summary.rs | 4 + 6 files changed, 466 insertions(+), 478 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bc4d2cc1..f10af489 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/info.rs b/src/info.rs index 9038e4ed..3c8fda8a 100644 --- a/src/info.rs +++ b/src/info.rs @@ -26,7 +26,7 @@ pub(crate) struct Info { with = "unwrap_or_skip", rename = "update-url" )] - pub(crate) update_url: Option, + pub(crate) update_url: Option, } impl Info { diff --git a/src/metainfo.rs b/src/metainfo.rs index 65fb79f6..0b515b15 100644 --- a/src/metainfo.rs +++ b/src/metainfo.rs @@ -117,69 +117,149 @@ impl Metainfo { pub(crate) fn infohash(&self) -> Result { 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()), + #[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![ + "node.example: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()), - 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"]), + source: Some("SOURCE".into()), + name: "NAME".into(), + pieces: PieceList::from_pieces(&["fae50", "fae50"]), mode: Mode::Single { - length: Bytes(20), - md5sum: None, + length: Bytes(32 * 1024), + md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")), }, - update_url: None, + update_url: Some("https://update.example".parse().unwrap()), }, + } + } + + #[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 + } - let bencode = bendy::serde::ser::to_bytes(&value).unwrap(); + #[cfg(test)] + pub(crate) fn test_value_single_unset() -> Metainfo { + Metainfo { + announce: None, + 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::Single { + length: Bytes(5), + md5sum: None, + }, + update_url: None, + }, + } + } - let deserialized = bendy::serde::de::from_bytes(&bencode).unwrap(); + #[cfg(test)] + pub(crate) fn test_value_single_unset_infohash() -> &'static str { + "a9105b0ff5f7cefeee5599ed7831749be21cc04e" + } - assert_eq!(value, deserialized); + #[cfg(test)] + pub(crate) fn test_value_single_unset_torrent_size() -> Bytes { + Bytes(85) } - #[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()), + #[cfg(test)] + pub(crate) fn test_value_multiple_unset() -> Metainfo { + Metainfo { + announce: None, + announce_list: None, + nodes: None, + comment: None, + created_by: None, + creation_date: None, + encoding: None, info: Info { - private: Some(true), - piece_length: Bytes(16 * 1024), - source: Some("source".into()), - name: "foo".into(), - pieces: PieceList::from_pieces(&["abc"]), + private: None, + piece_length: Bytes(1024), + source: None, + name: "NAME".into(), + pieces: PieceList::from_pieces(&["fae50"]), mode: Mode::Multiple { files: vec![FileInfo { - length: Bytes(10), - path: FilePath::from_components(&["foo", "bar"]), - md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")), + length: Bytes(1024), + md5sum: None, + path: FilePath::from_components(&["a", "b"]), }], }, 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(); @@ -192,8 +272,10 @@ mod tests { let have = value.serialize().unwrap(); if have != want.as_bytes() { + let have = String::from_utf8_lossy(&have); + assert_eq!(have, want); eprintln!("have:"); - eprintln!("{}", String::from_utf8_lossy(&have)); + eprintln!("{}", have); eprintln!("want:"); eprintln!("{}", want); panic!("Unexpected representation..."); @@ -201,60 +283,40 @@ mod tests { } #[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()]]), - nodes: Some(vec![ - "domain:1".parse().unwrap(), - "1.1.1.1:16".parse().unwrap(), - "[1234:5678:9abc:def0:1234:5678:9abc:def0]:65000" - .parse() - .unwrap(), - ]), - comment: Some("COMMENT".into()), - created_by: Some("CREATED BY".into()), - creation_date: Some(0), - encoding: Some("UTF-8".into()), - info: Info { - private: Some(true), - piece_length: Bytes(1024), - source: Some("SOURCE".into()), - name: "NAME".into(), - pieces: PieceList::from_pieces(&["fae50"]), - mode: Mode::Single { - length: Bytes(5), - md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")), - }, - update_url: None, - }, - }; + fn bencode_representation_single_set() { + let value = Metainfo::test_value_single(); #[rustfmt::skip] let want = concat!( "d", - "8:announce", "8:ANNOUNCE", - "13:announce-list", "l", - "l", "1:A", "1:B", "e", - "l", "1:C", "e", + "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", "i0e", + "13:creation date", "i1e", "8:encoding", "5:UTF-8", "4:info", "d", - "6:length", "i5e", + "6:length", "i32768e", "6:md5sum", "32:000102030405060708090a0b0c0d0e0f", "4:name", "4:NAME", - "12:piece length", "i1024e", - "6:pieces", "20:8,OS7d玤{Qk!Mk", + "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", "6:domain", "i1e", "e", + "l", "12:node.example", "i12e", "e", "l", "7:1.1.1.1", "i16e", "e", - "l", "39:1234:5678:9abc:def0:1234:5678:9abc:def0", "i65000e", "e", + "l", "23:2001:db8:85a3::8a2e:370", "i7334e", "e", "e", "e" ); @@ -263,33 +325,12 @@ mod tests { } #[test] - fn bencode_representation_single_none() { - 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::Single { - length: Bytes(5), - md5sum: None, - }, - update_url: None, - }, - }; + fn bencode_representation_single_unset() { + let value = Metainfo::test_value_single_unset(); #[rustfmt::skip] let want = concat!( "d", - "8:announce", "8:ANNOUNCE", "4:info", "d", "6:length", "i5e", "4:name", "4:NAME", @@ -303,47 +344,45 @@ mod tests { } #[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, - }, - }; + fn bencode_representation_multiple_set() { + let value = Metainfo::test_value_multiple(); #[rustfmt::skip] let want = concat!( "d", - "8:announce", "8:ANNOUNCE", + "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", "i1024e", + "6:length", "i32768e", "6:md5sum", "32:000102030405060708090a0b0c0d0e0f", - "4:path", "l", "1:a", "1:b", "e", + "4:path", "l", "3:DIR", "4:FILE", "e", "e", "e", "4:name", "4:NAME", - "12:piece length", "i1024e", - "6:pieces", "20:8,OS7d玤{Qk!Mk", + "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" ); @@ -352,36 +391,12 @@ mod tests { } #[test] - fn bencode_representation_multiple_none() { - 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: None, - path: FilePath::from_components(&["a", "b"]), - }], - }, - update_url: None, - }, - }; + 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", @@ -399,86 +414,45 @@ mod tests { representation(value, want); } - #[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, - }, - }; - - #[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" - ); - - 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, - }, - }; + fn assert_trackers_eq(metainfo: &Metainfo, want: &[&str]) { + let want = want + .iter() + .cloned() + .map(Url::parse) + .collect::, url::ParseError>>() + .unwrap(); + let have = metainfo.trackers().collect::>>().unwrap(); + assert_eq!(have, want); + } + + let mut metainfo = Metainfo::test_value_single(); - let trackers = metainfo.trackers().collect::>>().unwrap(); - assert_eq!(trackers, &["http://foo".parse().unwrap()]); + assert_trackers_eq( + &metainfo, + &[ + "udp://announce.example:1337", + "http://a.example:4567", + "https://b.example:77", + "udp://c.example:88", + ], + ); 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::>>().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", ], ); } diff --git a/src/subcommand/torrent/create.rs b/src/subcommand/torrent/create.rs index bc370a4d..ef6a6f88 100644 --- a/src/subcommand/torrent/create.rs +++ b/src/subcommand/torrent/create.rs @@ -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()) ); } } diff --git a/src/subcommand/torrent/show.rs b/src/subcommand/torrent/show.rs index 7181914c..346180ba 100644 --- a/src/subcommand/torrent/show.rs +++ b/src/subcommand/torrent/show.rs @@ -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,27 +188,74 @@ Announce List Tier 1: announce env.assert_ok(); let have = env.out(); - let want = " Name foo - Comment comment + + assert_eq!(have, want_human_readable); + } + + { + 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(); + + assert_eq!(have, want_machine_readable); + } + + Ok(()) + } + + #[test] + fn tier_list_with_main() -> Result<()> { + let metainfo = Metainfo::test_value_single(); + + { + 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 + 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 + Created By CREATED BY + Source SOURCE + Info Hash {} + Torrent Size {} + Content Size 32 KiB Private yes - Tracker announce -Announce List Tier 1: announce - b - Tier 2: c - 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); } @@ -225,24 +272,31 @@ Announce List Tier 1: announce 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\t339 -content size\t20 +created by\tCREATED BY +source\tSOURCE +info hash\t{} +torrent size\t{} +content size\t32768 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 +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); } @@ -251,32 +305,14 @@ 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, - }, - }; + fn tier_list_without_main() -> Result<()> { + 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() @@ -291,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 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: 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 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); } @@ -328,24 +371,30 @@ 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\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\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); } @@ -354,32 +403,10 @@ 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, - }, - }; + fn trackerless() -> Result<()> { + let mut metainfo = Metainfo::test_value_single(); + metainfo.announce = None; + metainfo.announce_list = None; { let mut env = TestEnvBuilder::new() @@ -394,27 +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 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 + 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); } @@ -431,24 +460,28 @@ 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 +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() - 130 + ); assert_eq!(have, want); } @@ -457,32 +490,8 @@ 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, - }, - }; + fn unset() -> Result<()> { + let metainfo = Metainfo::test_value_single_unset(); { let mut env = TestEnvBuilder::new() @@ -497,23 +506,21 @@ files\tfoo 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 b9cd9cae5748518c99d00d8ae86c0162510be4d9 - Torrent Size 261 bytes - Content Size 20 bytes - Private yes - DHT Nodes x:12 - 1.1.1.1:16 - [2001:db8:85a3::8a2e:370]:7334 - Piece Size 16 KiB - Piece Count 1 - File Count 1 - Files foo -"; + + #[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); } @@ -530,22 +537,22 @@ Creation Date 1970-01-01 00:00:01 UTC 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\tb9cd9cae5748518c99d00d8ae86c0162510be4d9 -torrent size\t261 -content size\t20 -private\tyes -dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334 -piece size\t16384 + #[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); } diff --git a/src/torrent_summary.rs b/src/torrent_summary.rs index ec79d35d..3c3281e7 100644 --- a/src/torrent_summary.rs +++ b/src/torrent_summary.rs @@ -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",