2019-05-24 01:25:55 -07:00
use crate ::common ::* ;
#[ derive(StructOpt) ]
2020-01-24 14:17:31 -08:00
#[ structopt(
help_message ( consts ::HELP_MESSAGE ) ,
version_message ( consts ::VERSION_MESSAGE ) ,
about ( " Create a `.torrent` file. " )
) ]
2019-05-24 01:25:55 -07:00
pub ( crate ) struct Create {
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " ANNOUNCE " ,
long = " announce " ,
required ( true ) ,
help = " Use `ANNOUNCE` as the primary tracker announce URL. " ,
long_help = " Use `ANNOUNCE` as the primary tracker announce URL. To supply multiple announce URLs, also use `--announce-tier`. "
) ]
announce : Url ,
2020-02-03 04:39:48 -08:00
#[ structopt(
name = " ALLOW " ,
long = " allow " ,
help = " Use `ANNOUNCE` as the primary tracker announce URL. " ,
long_help = " Use `ANNOUNCE` as the primary tracker announce URL. To supply multiple announce URLs, also use `--announce-tier`. "
) ]
allowed_lints : Vec < Lint > ,
2020-01-24 14:17:31 -08:00
#[ structopt(
long = " announce-tier " ,
name = " ANNOUNCE-TIER " ,
help = " Add `ANNOUNCE-TIER` to list of tracker announce tiers. " ,
long_help = " \
Add ` ANNOUNCE - TIER ` to list of tracker announce tiers . Each instance adds a new tier . To add multiple trackers to a given tier , separate their announce URLs with commas :
` - - announce - tier udp ://example.com:80/announce,https://example.net:443/announce`
Announce tiers are stored in the ` announce - list ` key of the top - level metainfo dictionary as a list of lists of strings , as defined by BEP 12 : Multitracker Metadata Extension .
Note : Many BitTorrent clients do not implement the behavior described in BEP 12. See the discussion here for more details : https ://github.com/bittorrent/bittorrent.org/issues/82"
) ]
announce_tiers : Vec < String > ,
#[ structopt(
name = " COMMENT " ,
long = " comment " ,
help = " Include `COMMENT` in generated `.torrent` file. " ,
long_help = " Include `COMMENT` in generated `.torrent` file. Stored under `comment` key of top-level metainfo dictionary. "
) ]
2020-01-20 23:05:39 -08:00
comment : Option < String > ,
2020-02-14 02:16:19 -08:00
#[ structopt(
name = " NODE " ,
long = " dht-node " ,
help = " Add DHT bootstrap node `NODE` to torrent. `NODE` should be in the form `HOST:PORT`. " ,
long_help = " Add DHT bootstrap node `NODE` to torrent. `NODE` should be in the form `HOST:PORT`, where `HOST` is a domain name, an IPv4 address, or an IPv6 address surrounded by brackets. May be given more than once to add multiple bootstrap nodes. Examples:
` - - dht - node router . example . com :1337 `
` - - dht - node 203. 0.11 3.0 :2290 `
` - - dht - node [ 2001 :db8 :4275 :7920 :6269 :7463 :6 f69 :6e21 ] :8832 ` "
) ]
dht_nodes : Vec < Node > ,
2020-02-05 18:32:09 -08:00
#[ structopt(
name = " FOLLOW-SYMLINKS " ,
long = " follow-symlinks " ,
help = " Follow symlinks in torrent input. By default, symlinks to files and directories are not included in torrent contents. "
) ]
follow_symlinks : bool ,
2020-02-04 21:55:19 -08:00
#[ structopt(
name = " FORCE " ,
long = " force " ,
help = " Overwrite the destination `.torrent` file, if it exists. "
) ]
force : bool ,
2020-02-05 21:47:12 -08:00
#[ structopt(
name = " GLOB " ,
long = " glob " ,
help = " Include or exclude files that match `GLOB`. Multiple glob may be provided, with the last one taking precedence. Precede a glob with a ! to exclude it. "
) ]
globs : Vec < String > ,
2020-02-05 18:32:09 -08:00
#[ structopt(
name = " INCLUDE-HIDDEN " ,
long = " include-hidden " ,
help = " Include hidden files that would otherwise be skipped, such as files that start with a `.`, and files hidden by file attributes on macOS and Windows. "
) ]
include_hidden : bool ,
#[ structopt(
name = " INCLUDE-JUNK " ,
long = " include-junk " ,
help = " Include junk files that would otherwise be skipped. "
) ]
include_junk : bool ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " INPUT " ,
long = " input " ,
help = " Read torrent contents from `INPUT`. " ,
2020-02-04 21:29:53 -08:00
long_help = " Read torrent contents from `INPUT`. If `INPUT` is a file, torrent will be a single-file torrent, otherwise if `INPUT` is a directory, torrent will be a multi-file torrent. " ,
parse ( from_os_str )
2020-01-24 14:17:31 -08:00
) ]
2020-01-20 23:05:39 -08:00
input : PathBuf ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " MD5SUM " ,
long = " md5sum " ,
help = " Include MD5 checksum of each file in the torrent. N.B. MD5 is cryptographically broken and only suitable for safeguarding against accidental corruption. " ,
long_help = " Include MD5 checksum of each file in the torrent. N.B. MD5 is cryptographically broken and only suitable for checking for accidental corruption. "
) ]
2019-05-24 01:25:55 -07:00
md5sum : bool ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " NAME " ,
long = " name " ,
help = " Set name of torrent to `NAME`. Defaults to the filename of `--input`. "
) ]
2019-05-24 01:25:55 -07:00
name : Option < String > ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " NO-CREATED-BY " ,
long = " no-created-by " ,
help = " Do not populate `created by` key of generated torrent with imdl version information. "
) ]
2020-01-20 23:05:39 -08:00
no_created_by : bool ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " NO-CREATION-DATE " ,
long = " no-creation-date " ,
help = " Do not populate `creation date` key of generated torrent with current time. "
) ]
2020-01-20 23:05:39 -08:00
no_creation_date : bool ,
2020-01-30 05:54:08 -08:00
#[ structopt(
name = " OPEN " ,
long = " open " ,
help = " Open `.torrent` file after creation " ,
long_help = " Open `.torrent` file after creation. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start on Windows "
) ]
open : bool ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " OUTPUT " ,
long = " output " ,
2020-02-04 21:29:53 -08:00
help = " Save `.torrent` file to `OUTPUT`, or `-` for standard output. Defaults to `$INPUT.torrent`. " ,
parse ( from_os_str )
2020-01-24 14:17:31 -08:00
) ]
2020-02-04 21:29:53 -08:00
output : Option < Target > ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " PIECE-LENGTH " ,
long = " piece-length " ,
2020-02-01 12:30:35 -08:00
help = " Set piece length to `PIECE-LENGTH` bytes. " ,
long_help = " Set piece length to `PIECE-LENGTH` bytes. Accepts SI units, e.g. kib, mib, and gib. "
2020-01-24 14:17:31 -08:00
) ]
2020-02-04 19:59:06 -08:00
piece_length : Option < Bytes > ,
2020-01-24 14:17:31 -08:00
#[ structopt(
name = " PRIVATE " ,
long = " private " ,
help = " Set the `private` flag. " ,
long_help = " Set the `private` flag. Torrent clients that understand the flag and participate in the swarm of a torrent with the flag set will only announce themselves to the announce URLs included in the torrent, and will not use other peer discovery mechanisms, such as the DHT or local peer discovery. See BEP 27: Private Torrents for more information. "
) ]
2020-01-20 23:05:39 -08:00
private : bool ,
2020-02-04 08:51:56 -08:00
#[ structopt(
name = " SOURCE " ,
long = " source " ,
help = " Include `SOURCE` in generated `.torrent` file. " ,
long_help = " Include `SOURCe` in generated `.torrent` file. Stored under `info.source` key of metainfo dictionary. "
) ]
source : Option < String > ,
2019-05-24 01:25:55 -07:00
}
2020-02-04 19:59:06 -08:00
impl Create {
pub ( crate ) fn run ( self , env : & mut Env ) -> Result < ( ) , Error > {
let input = env . resolve ( & self . input ) ;
2020-02-03 04:39:48 -08:00
2020-02-05 18:32:09 -08:00
let files = Walker ::new ( & input )
. include_junk ( self . include_junk )
. include_hidden ( self . include_hidden )
. follow_symlinks ( self . follow_symlinks )
2020-02-05 21:47:12 -08:00
. globs ( & self . globs ) ?
2020-02-05 18:32:09 -08:00
. files ( ) ? ;
2020-02-03 04:39:48 -08:00
2020-02-04 19:59:06 -08:00
let piece_length = self
. piece_length
. unwrap_or_else ( | | PieceLengthPicker ::from_content_size ( files . total_size ( ) ) ) ;
2020-02-03 04:39:48 -08:00
let mut linter = Linter ::new ( ) ;
linter . allow ( self . allowed_lints . iter ( ) . cloned ( ) ) ;
2020-02-14 00:12:49 -08:00
if piece_length . count ( ) = = 0 {
return Err ( Error ::PieceLengthZero ) ;
2020-02-03 04:39:48 -08:00
}
2020-02-14 00:12:49 -08:00
if linter . is_denied ( Lint ::UnevenPieceLength ) & & ! piece_length . count ( ) . is_power_of_two ( ) {
return Err ( Error ::PieceLengthUneven {
2020-02-04 19:59:06 -08:00
bytes : piece_length ,
2020-02-14 00:12:49 -08:00
} ) ;
2020-02-03 04:39:48 -08:00
}
2020-02-14 00:12:49 -08:00
if linter . is_denied ( Lint ::SmallPieceLength ) & & piece_length . count ( ) < 16 * 1024 {
2020-02-03 04:39:48 -08:00
return Err ( Error ::PieceLengthSmall ) ;
}
2020-02-01 12:30:35 -08:00
2019-05-24 01:25:55 -07:00
let mut announce_list = Vec ::new ( ) ;
2020-01-24 14:17:31 -08:00
for tier in & self . announce_tiers {
let tier = tier . split ( ',' ) . map ( str ::to_string ) . collect ::< Vec < String > > ( ) ;
2019-05-24 01:25:55 -07:00
tier
. iter ( )
. map ( | announce | announce . parse ( ) )
. collect ::< Result < Vec < Url > , url ::ParseError > > ( )
. context ( error ::AnnounceUrlParse ) ? ;
announce_list . push ( tier ) ;
}
let filename = input . file_name ( ) . ok_or_else ( | | Error ::FilenameExtract {
path : input . clone ( ) ,
} ) ? ;
let name = match & self . name {
Some ( name ) = > name . clone ( ) ,
None = > filename
. to_str ( )
. ok_or_else ( | | Error ::FilenameDecode {
2020-02-14 02:16:19 -08:00
filename : PathBuf ::from ( filename ) ,
2019-05-24 01:25:55 -07:00
} ) ?
. to_owned ( ) ,
} ;
let output = self
. output
. as_ref ( )
2020-02-04 21:29:53 -08:00
. map ( | output | output . resolve ( env ) )
2019-05-24 01:25:55 -07:00
. unwrap_or_else ( | | {
let mut torrent_name = name . to_owned ( ) ;
torrent_name . push_str ( " .torrent " ) ;
2020-02-04 21:29:53 -08:00
Target ::File ( input . parent ( ) . unwrap ( ) . join ( torrent_name ) )
2019-05-24 01:25:55 -07:00
} ) ;
2020-02-14 00:12:49 -08:00
let private = if self . private { Some ( true ) } else { None } ;
2019-05-24 01:25:55 -07:00
let creation_date = if self . no_creation_date {
None
} else {
Some (
SystemTime ::now ( )
. duration_since ( SystemTime ::UNIX_EPOCH ) ?
. as_secs ( ) ,
)
} ;
let created_by = if self . no_created_by {
None
} else {
Some ( String ::from ( consts ::CREATED_BY_DEFAULT ) )
} ;
2020-02-14 00:12:49 -08:00
let ( mode , pieces ) = Hasher ::hash (
& files ,
self . md5sum ,
piece_length . as_piece_length ( ) ? . into_usize ( ) ,
) ? ;
2019-05-24 01:25:55 -07:00
let info = Info {
2020-02-04 08:51:56 -08:00
source : self . source ,
2020-02-01 12:30:35 -08:00
piece_length ,
2019-05-24 01:25:55 -07:00
mode ,
pieces ,
name ,
private ,
} ;
let metainfo = Metainfo {
2020-01-04 18:58:42 -08:00
comment : self . comment ,
2020-02-04 07:55:50 -08:00
encoding : Some ( consts ::ENCODING_UTF8 . to_string ( ) ) ,
2020-01-24 14:17:31 -08:00
announce : self . announce . to_string ( ) ,
announce_list : if announce_list . is_empty ( ) {
None
} else {
Some ( announce_list )
} ,
2020-02-14 02:16:19 -08:00
nodes : if self . dht_nodes . is_empty ( ) {
None
} else {
Some ( self . dht_nodes )
} ,
2019-05-24 01:25:55 -07:00
creation_date ,
created_by ,
info ,
} ;
2020-02-04 08:36:00 -08:00
let bytes = metainfo . serialize ( ) ? ;
2020-02-04 21:29:53 -08:00
match & output {
Target ::File ( path ) = > {
2020-02-04 21:55:19 -08:00
let mut open_options = fs ::OpenOptions ::new ( ) ;
if self . force {
open_options . write ( true ) . create ( true ) . truncate ( true ) ;
} else {
open_options . write ( true ) . create_new ( true ) ;
}
open_options
. open ( path )
. and_then ( | mut file | file . write_all ( & bytes ) )
. context ( error ::Filesystem { path } ) ? ;
2020-02-14 00:12:49 -08:00
#[ cfg(test) ]
TorrentSummary ::from_metainfo ( metainfo . clone ( ) ) ? . write ( env ) ? ;
#[ cfg(not(test)) ]
2020-02-04 21:29:53 -08:00
TorrentSummary ::from_metainfo ( metainfo ) ? . write ( env ) ? ;
2020-02-14 00:12:49 -08:00
2020-02-04 21:29:53 -08:00
if self . open {
Platform ::open ( & path ) ? ;
}
}
Target ::Stdio = > env . out . write_all ( & bytes ) . context ( error ::Stdout ) ? ,
2020-01-30 05:54:08 -08:00
}
2020-02-14 00:12:49 -08:00
#[ cfg(test) ]
{
2020-02-15 18:08:36 -08:00
let deserialized = bendy ::serde ::de ::from_bytes ::< Metainfo > ( & bytes ) . unwrap ( ) ;
assert_eq! ( deserialized , metainfo ) ;
2020-02-14 00:12:49 -08:00
let status = metainfo . verify ( & input ) ? ;
if ! status . good ( ) {
return Err ( Error ::Verify { status } ) ;
}
}
2019-05-24 01:25:55 -07:00
Ok ( ( ) )
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2020-02-04 08:36:00 -08:00
use pretty_assertions ::assert_eq ;
2019-05-24 01:25:55 -07:00
#[ test ]
fn require_input_argument ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! { args : [ ] , tree : { } } ;
2019-05-24 01:25:55 -07:00
assert! ( matches! ( env . run ( ) , Err ( Error ::Clap { .. } ) ) ) ;
}
#[ test ]
fn require_input_present ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : { } ,
} ;
2019-05-24 01:25:55 -07:00
assert! ( matches! ( env . run ( ) , Err ( Error ::Filesystem { .. } ) ) ) ;
}
#[ test ]
fn torrent_file_is_bencode_dict ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" https://bar " ,
] ,
2020-02-14 00:12:49 -08:00
tree : {
foo : " " ,
}
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
let torrent = env . resolve ( " foo.torrent " ) ;
let bytes = fs ::read ( torrent ) . unwrap ( ) ;
2020-02-11 03:08:57 -08:00
let value = Value ::from_bencode ( & bytes ) . unwrap ( ) ;
assert! ( matches! ( value , Value ::Dict ( _ ) ) ) ;
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn privacy_defaults_to_false ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [ " torrent " , " create " , " --input " , " foo " , " --announce " , " https://bar " ] ,
2020-02-14 00:12:49 -08:00
tree : {
foo : " " ,
}
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-04 07:55:50 -08:00
assert_eq! ( metainfo . info . private , None ) ;
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn privacy_flag_sets_privacy ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [ " torrent " , " create " , " --input " , " foo " , " --announce " , " https://bar " , " --private " ] ,
2020-02-14 00:12:49 -08:00
tree : {
foo : " " ,
}
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-14 00:12:49 -08:00
assert_eq! ( metainfo . info . private , Some ( true ) ) ;
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn tracker_flag_must_be_url ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [ " torrent " , " create " , " --input " , " foo " , " --announce " , " bar " ] ,
2020-02-14 00:12:49 -08:00
tree : {
foo : " " ,
}
} ;
2020-01-24 14:17:31 -08:00
assert_matches! ( env . run ( ) , Err ( Error ::Clap { .. } ) ) ;
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn announce_single ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [ " torrent " , " create " , " --input " , " foo " , " --announce " , " http://bar " ] ,
2020-02-14 00:12:49 -08:00
tree : {
foo : " " ,
}
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-01-24 14:17:31 -08:00
assert_eq! ( metainfo . announce , " http://bar/ " ) ;
assert! ( metainfo . announce_list . is_none ( ) ) ;
2019-05-24 01:25:55 -07:00
}
2020-01-30 03:13:26 -08:00
#[ test ]
fn announce_udp ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
2020-02-14 00:12:49 -08:00
args : [
2020-03-05 21:44:20 -08:00
" torrent " ,
" create " ,
2020-02-14 00:12:49 -08:00
" --input " ,
" foo " ,
" --announce " ,
" udp://tracker.opentrackr.org:1337/announce " ,
] ,
tree : {
foo : " " ,
}
} ;
2020-01-30 03:13:26 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-01-30 03:13:26 -08:00
assert_eq! (
metainfo . announce ,
" udp://tracker.opentrackr.org:1337/announce "
) ;
assert! ( metainfo . announce_list . is_none ( ) ) ;
}
2020-01-31 04:21:55 -08:00
#[ test ]
fn announce_wss_tracker ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" wss://tracker.btorrent.xyz " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2020-01-31 04:21:55 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-01-31 04:21:55 -08:00
assert_eq! ( metainfo . announce , " wss://tracker.btorrent.xyz/ " ) ;
assert! ( metainfo . announce_list . is_none ( ) ) ;
}
2019-05-24 01:25:55 -07:00
#[ test ]
fn announce_single_tier ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --announce-tier " ,
" http://bar,http://baz " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-01-24 14:17:31 -08:00
assert_eq! ( metainfo . announce , " http://bar/ " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! (
metainfo . announce_list ,
2020-01-24 14:17:31 -08:00
Some ( vec! [ vec! [ " http://bar " . into ( ) , " http://baz " . into ( ) ] ] ) ,
2019-05-24 01:25:55 -07:00
) ;
}
#[ test ]
fn announce_multiple_tiers ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --announce-tier " ,
" http://bar,http://baz " ,
" --announce-tier " ,
" http://abc,http://xyz " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-01-24 14:17:31 -08:00
assert_eq! ( metainfo . announce , " http://bar/ " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! (
metainfo . announce_list ,
2020-01-24 14:17:31 -08:00
Some ( vec! [
vec! [ " http://bar " . into ( ) , " http://baz " . into ( ) ] ,
vec! [ " http://abc " . into ( ) , " http://xyz " . into ( ) ] ,
] )
2019-05-24 01:25:55 -07:00
) ;
}
#[ test ]
fn comment_default ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . comment , None ) ;
}
#[ test ]
fn comment_set ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --comment " ,
" Hello, world! " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . comment . unwrap ( ) , " Hello, world! " ) ;
}
#[ test ]
fn piece_length_default ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-14 00:12:49 -08:00
assert_eq! ( metainfo . info . piece_length , Bytes ::from ( 16 * 2 u32 . pow ( 10 ) ) ) ;
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn piece_length_override ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 64KiB " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-14 00:12:49 -08:00
assert_eq! ( metainfo . info . piece_length , Bytes ( 64 * 1024 ) ) ;
2019-05-24 01:25:55 -07:00
}
2020-02-01 12:30:35 -08:00
#[ test ]
fn si_piece_size ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 0.5MiB " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2020-02-01 12:30:35 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-14 00:12:49 -08:00
assert_eq! ( metainfo . info . piece_length , Bytes ( 512 * 1024 ) ) ;
2020-02-01 12:30:35 -08:00
}
2019-05-24 01:25:55 -07:00
#[ test ]
fn name ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 16KiB " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . info . name , " foo " ) ;
}
#[ test ]
fn name_subdir ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo/bar " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 32KiB " ,
] ,
tree : {
foo : {
bar : " " ,
} ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo/bar.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . info . name , " bar " ) ;
}
#[ test ]
fn destination_override ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --output " ,
" x.torrent " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
env . load_metainfo ( " x.torrent " ) ;
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn created_by_default ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . created_by . unwrap ( ) , consts ::CREATED_BY_DEFAULT ) ;
}
#[ test ]
fn created_by_unset ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --no-created-by " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . created_by , None ) ;
}
#[ test ]
fn encoding ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-04 07:55:50 -08:00
assert_eq! ( metainfo . encoding , Some ( " UTF-8 " . into ( ) ) ) ;
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn created_date_default ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
let now = SystemTime ::now ( )
. duration_since ( SystemTime ::UNIX_EPOCH )
. unwrap ( )
. as_secs ( ) ;
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert! ( metainfo . creation_date . unwrap ( ) < now + 10 ) ;
assert! ( metainfo . creation_date . unwrap ( ) > now - 10 ) ;
}
#[ test ]
fn created_date_unset ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --no-creation-date " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . creation_date , None ) ;
}
#[ test ]
fn single_small ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
2020-02-15 18:08:36 -08:00
tree : {
foo : " bar " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::from_pieces ( & [ " bar " ] ) ) ;
2019-05-24 01:25:55 -07:00
assert_eq! (
metainfo . info . mode ,
Mode ::Single {
2020-02-15 18:08:36 -08:00
length : Bytes ( 3 ) ,
2019-05-24 01:25:55 -07:00
md5sum : None ,
}
)
}
#[ test ]
fn single_one_byte_piece ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 1 " ,
" --allow " ,
" small-piece-length " ,
] ,
tree : {
foo : " bar " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
assert_eq! (
metainfo . info . pieces ,
PieceList ::from_pieces ( & [ " b " , " a " , " r " ] )
) ;
2019-05-24 01:25:55 -07:00
assert_eq! (
metainfo . info . mode ,
Mode ::Single {
2020-03-05 21:44:20 -08:00
length : Bytes ( 3 ) ,
2019-05-24 01:25:55 -07:00
md5sum : None ,
}
)
}
#[ test ]
fn single_empty ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces . count ( ) , 0 ) ;
2019-05-24 01:25:55 -07:00
assert_eq! (
metainfo . info . mode ,
Mode ::Single {
2020-02-14 00:12:49 -08:00
length : Bytes ( 0 ) ,
2019-05-24 01:25:55 -07:00
md5sum : None ,
}
)
}
#[ test ]
fn multiple_no_files ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : { } ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces . count ( ) , 0 ) ;
2019-05-24 01:25:55 -07:00
assert_eq! ( metainfo . info . mode , Mode ::Multiple { files : Vec ::new ( ) } )
}
#[ test ]
2020-02-05 16:01:44 -08:00
fn multiple_one_file_md5 ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --md5sum " ,
] ,
2020-02-15 18:08:36 -08:00
tree : {
foo : {
bar : " bar " ,
} ,
} ,
} ;
2020-02-05 16:01:44 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::from_pieces ( & [ " bar " ] ) ) ;
2020-02-05 16:01:44 -08:00
match metainfo . info . mode {
Mode ::Multiple { files } = > {
assert_eq! (
files ,
& [ FileInfo {
2020-02-14 00:12:49 -08:00
length : Bytes ( 3 ) ,
md5sum : Some ( Md5Digest ::from_hex ( " 37b51d194a7513e45b56f6524f2d51f2 " ) ) ,
2020-02-05 16:01:44 -08:00
path : FilePath ::from_components ( & [ " bar " ] ) ,
} , ]
) ;
}
_ = > panic! ( " Expected multi-file torrent " ) ,
}
}
#[ test ]
fn multiple_one_file_md5_off ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
2020-02-15 18:08:36 -08:00
tree : {
foo : {
bar : " bar " ,
} ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::from_pieces ( & [ " bar " ] ) ) ;
2020-02-05 16:01:44 -08:00
match metainfo . info . mode {
Mode ::Multiple { files } = > {
assert_eq! (
files ,
& [ FileInfo {
2020-02-14 00:12:49 -08:00
length : Bytes ( 3 ) ,
2020-02-05 16:01:44 -08:00
md5sum : None ,
path : FilePath ::from_components ( & [ " bar " ] ) ,
} , ]
) ;
}
_ = > panic! ( " Expected multi-file torrent " ) ,
}
2019-05-24 01:25:55 -07:00
}
#[ test ]
fn multiple_three_files ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --md5sum "
] ,
tree : {
foo : {
a : " abc " ,
x : " xyz " ,
h : " hij " ,
} ,
} ,
} ;
2019-05-24 01:25:55 -07:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::from_pieces ( & [ " abchijxyz " ] ) ) ;
2020-02-05 16:01:44 -08:00
match metainfo . info . mode {
Mode ::Multiple { files } = > {
assert_eq! (
files ,
& [
FileInfo {
2020-02-14 00:12:49 -08:00
length : Bytes ( 3 ) ,
md5sum : Some ( Md5Digest ::from_hex ( " 900150983cd24fb0d6963f7d28e17f72 " ) ) ,
2020-02-05 16:01:44 -08:00
path : FilePath ::from_components ( & [ " a " ] ) ,
} ,
FileInfo {
2020-02-14 00:12:49 -08:00
length : Bytes ( 3 ) ,
md5sum : Some ( Md5Digest ::from_hex ( " 857c4402ad934005eae4638a93812bf7 " ) ) ,
2020-02-05 16:01:44 -08:00
path : FilePath ::from_components ( & [ " h " ] ) ,
} ,
FileInfo {
2020-02-14 00:12:49 -08:00
length : Bytes ( 3 ) ,
md5sum : Some ( Md5Digest ::from_hex ( " d16fb36f0911f878998c136191af705e " ) ) ,
2020-02-05 16:01:44 -08:00
path : FilePath ::from_components ( & [ " x " ] ) ,
} ,
]
) ;
}
_ = > panic! ( " Expected multi-file torrent " ) ,
}
2019-05-24 01:25:55 -07:00
}
2020-01-30 05:54:08 -08:00
#[ test ]
fn open ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --open " ,
] ,
tree : { } ,
} ;
2020-01-30 05:54:08 -08:00
let opened = env . resolve ( " opened.txt " ) ;
let torrent = env . resolve ( " foo.torrent " ) ;
let expected = if cfg! ( target_os = " windows " ) {
let script = env . resolve ( " open.bat " ) ;
fs ::write ( & script , format! ( " echo %3 > {} " , opened . display ( ) ) ) . unwrap ( ) ;
format! ( " {} \r \n " , torrent . display ( ) )
} else {
let script = env . resolve ( & Platform ::opener ( ) . unwrap ( ) [ 0 ] ) ;
fs ::write (
& script ,
format! ( " #!/usr/bin/env sh \n echo $1 > {} " , opened . display ( ) ) ,
)
. unwrap ( ) ;
Command ::new ( " chmod " )
. arg ( " +x " )
. arg ( & script )
. status ( )
. unwrap ( ) ;
format! ( " {} \n " , torrent . display ( ) )
} ;
const KEY : & str = " PATH " ;
let path = env ::var_os ( KEY ) . unwrap ( ) ;
let mut split = env ::split_paths ( & path )
. into_iter ( )
. collect ::< Vec < PathBuf > > ( ) ;
split . insert ( 0 , env . dir ( ) . to_owned ( ) ) ;
let new = env ::join_paths ( split ) . unwrap ( ) ;
env ::set_var ( KEY , new ) ;
fs ::write ( env . resolve ( " foo " ) , " " ) . unwrap ( ) ;
env . run ( ) . unwrap ( ) ;
let start = Instant ::now ( ) ;
while start . elapsed ( ) < Duration ::new ( 2 , 0 ) {
if let Ok ( text ) = fs ::read_to_string ( & opened ) {
assert_eq! ( text , expected ) ;
return ;
}
}
panic! ( " Failed to read `opened.txt`. " ) ;
}
2020-02-03 04:39:48 -08:00
#[ test ]
fn uneven_piece_length ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 17KiB " ,
] ,
tree : {
foo : { } ,
} ,
} ;
2020-02-03 04:39:48 -08:00
assert_matches! (
env . run ( ) ,
Err ( Error ::PieceLengthUneven { bytes } ) if bytes . 0 = = 17 * 1024
) ;
}
#[ test ]
fn uneven_piece_length_allow ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 17KiB " ,
" --allow " ,
" uneven-piece-length " ,
] ,
tree : {
foo : { } ,
} ,
} ;
2020-02-03 04:39:48 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
env . load_metainfo ( " foo.torrent " ) ;
2020-02-03 04:39:48 -08:00
}
#[ test ]
fn zero_piece_length ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 0 " ,
] ,
tree : {
foo : { } ,
} ,
} ;
2020-02-03 04:39:48 -08:00
assert_matches! ( env . run ( ) , Err ( Error ::PieceLengthZero ) ) ;
}
#[ test ]
fn small_piece_length ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 8KiB " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2020-02-03 04:39:48 -08:00
assert_matches! ( env . run ( ) , Err ( Error ::PieceLengthSmall ) ) ;
}
#[ test ]
fn small_piece_length_allow ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --piece-length " ,
" 8KiB " ,
" --allow " ,
" small-piece-length " ,
] ,
tree : {
foo : { } ,
}
} ;
2020-02-03 04:39:48 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
env . load_metainfo ( " foo.torrent " ) ;
2020-02-03 04:39:48 -08:00
}
2020-02-04 08:36:00 -08:00
#[ test ]
fn output ( ) {
2020-02-04 10:54:41 -08:00
let mut env = TestEnvBuilder ::new ( )
. arg_slice ( & [
" imdl " ,
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --no-creation-date " ,
] )
. out_is_term ( )
. build ( ) ;
2020-02-04 08:36:00 -08:00
let dir = env . resolve ( " foo " ) ;
fs ::create_dir ( & dir ) . unwrap ( ) ;
fs ::write ( dir . join ( " a " ) , " abc " ) . unwrap ( ) ;
fs ::write ( dir . join ( " x " ) , " xyz " ) . unwrap ( ) ;
fs ::write ( dir . join ( " h " ) , " hij " ) . unwrap ( ) ;
env . run ( ) . unwrap ( ) ;
let have = env . out ( ) ;
2020-02-05 23:57:35 -08:00
let want = format! (
" Name foo
Created By { }
2020-02-05 16:01:44 -08:00
Info Hash d3432a4b9d18baa413095a70f1e417021ceaca5b
Torrent Size 237 bytes
Content Size 9 bytes
2020-02-04 08:36:00 -08:00
Private no
Tracker http ://bar/
2020-02-04 19:59:06 -08:00
Piece Size 16 KiB
2020-02-04 08:36:00 -08:00
Piece Count 1
2020-02-05 16:01:44 -08:00
File Count 3
2020-02-05 23:57:35 -08:00
Files foo
├ ─ a
├ ─ h
└ ─ x
" ,
consts ::CREATED_BY_DEFAULT
) ;
2020-02-04 08:36:00 -08:00
assert_eq! ( have , want ) ;
}
2020-02-04 21:29:53 -08:00
#[ test ]
fn write_to_stdout ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --output " ,
" - " ,
] ,
tree : {
foo : " " ,
} ,
} ;
2020-02-04 21:29:53 -08:00
env . run ( ) . unwrap ( ) ;
let bytes = env . out_bytes ( ) ;
2020-02-11 03:08:57 -08:00
Metainfo ::from_bytes ( & bytes ) ;
2020-02-04 21:29:53 -08:00
}
2020-02-04 21:55:19 -08:00
#[ test ]
fn force_default ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar "
] ,
tree : {
foo : " " ,
" foo.torrent " : " foo " ,
} ,
} ;
2020-02-04 21:55:19 -08:00
assert_matches! (
env . run ( ) . unwrap_err ( ) ,
Error ::Filesystem { source , path }
if path = = env . resolve ( " foo.torrent " ) & & source . kind ( ) = = io ::ErrorKind ::AlreadyExists
)
}
#[ test ]
fn force_true ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --force " ,
] ,
tree : {
foo : " " ,
" foo.torrent " : " foo " ,
} ,
} ;
2020-02-04 21:55:19 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
env . load_metainfo ( " foo.torrent " ) ;
2020-02-04 21:55:19 -08:00
}
2020-02-05 18:32:09 -08:00
#[ test ]
fn exclude_junk ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : {
" Thumbs.db " : " abc " ,
" Desktop.ini " : " abc " ,
} ,
} ,
} ;
2020-02-05 18:32:09 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 18:32:09 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . is_empty ( )
) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::new ( ) ) ;
2020-02-05 18:32:09 -08:00
}
#[ test ]
fn include_junk ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --include-junk " ,
] ,
tree : {
foo : {
" Thumbs.db " : " abc " ,
" Desktop.ini " : " abc " ,
} ,
} ,
} ;
2020-02-05 18:32:09 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 18:32:09 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . len ( ) = = 2
) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::from_pieces ( & [ " abcabc " ] ) ) ;
2020-02-05 18:32:09 -08:00
}
#[ test ]
fn skip_hidden ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
tree : {
foo : {
" .hidden " : " abc " ,
hidden : " abc " ,
} ,
}
} ;
if cfg! ( target_os = " windows " ) {
2020-02-05 18:32:09 -08:00
Command ::new ( " attrib " )
. arg ( " +h " )
2020-03-05 21:44:20 -08:00
. arg ( env . resolve ( " foo/hidden " ) )
2020-02-05 18:32:09 -08:00
. status ( )
. unwrap ( ) ;
2020-03-05 21:44:20 -08:00
} else if cfg! ( target_os = " macos " ) {
2020-02-05 18:32:09 -08:00
Command ::new ( " chflags " )
. arg ( " hidden " )
2020-03-05 21:44:20 -08:00
. arg ( env . resolve ( " foo/hidden " ) )
2020-02-05 18:32:09 -08:00
. status ( )
. unwrap ( ) ;
2020-03-05 21:44:20 -08:00
} else {
fs ::remove_file ( env . resolve ( " foo/hidden " ) ) . unwrap ( ) ;
2020-02-05 18:32:09 -08:00
}
2020-03-05 21:44:20 -08:00
2020-02-05 18:32:09 -08:00
env . run ( ) . unwrap ( ) ;
2020-03-05 21:44:20 -08:00
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-03-05 21:44:20 -08:00
2020-02-05 18:32:09 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . len ( ) = = 0
) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::new ( ) ) ;
2020-02-05 18:32:09 -08:00
}
#[ test ]
fn include_hidden ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --include-hidden " ,
] ,
tree : {
foo : {
" .hidden " : " abc " ,
hidden : " abc " ,
} ,
}
} ;
if cfg! ( target_os = " windows " ) {
Command ::new ( " attrib " )
. arg ( " +h " )
. arg ( env . resolve ( " foo/hidden " ) )
. status ( )
. unwrap ( ) ;
} else if cfg! ( target_os = " macos " ) {
Command ::new ( " chflags " )
. arg ( " hidden " )
. arg ( env . resolve ( " foo/hidden " ) )
. status ( )
. unwrap ( ) ;
}
2020-02-05 18:32:09 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 18:32:09 -08:00
assert_matches! (
metainfo . info . mode ,
2020-03-05 21:44:20 -08:00
Mode ::Multiple { files } if files . len ( ) = = 2
2020-02-05 18:32:09 -08:00
) ;
2020-03-05 21:44:20 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::from_pieces ( & [ " abcabc " ] ) ) ;
2020-02-05 18:32:09 -08:00
}
fn populate_symlinks ( env : & Env ) {
let dir = env . resolve ( " foo " ) ;
let file_src = env . resolve ( " bar " ) ;
let file_link = env . resolve ( " foo/bar " ) ;
let dir_src = env . resolve ( " dir-src " ) ;
let dir_contents = dir_src . join ( " baz " ) ;
let dir_link = env . resolve ( " foo/dir " ) ;
fs ::create_dir ( & dir_src ) . unwrap ( ) ;
fs ::write ( dir_contents , " baz " ) . unwrap ( ) ;
fs ::create_dir ( & dir ) . unwrap ( ) ;
fs ::write ( file_src , " bar " ) . unwrap ( ) ;
#[ cfg(unix) ]
{
Command ::new ( " ln " )
. arg ( " -s " )
. arg ( " ../bar " )
. arg ( file_link )
. status ( )
. unwrap ( ) ;
Command ::new ( " ln " )
. arg ( " -s " )
. arg ( " ../dir-src " )
. arg ( dir_link )
. status ( )
. unwrap ( ) ;
}
}
#[ test ]
fn skip_symlinks ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --md5sum " ,
] ,
tree : { } ,
} ;
2020-02-05 18:32:09 -08:00
populate_symlinks ( & env ) ;
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 18:32:09 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . is_empty ( )
) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::new ( ) ) ;
2020-02-05 18:32:09 -08:00
}
#[ test ]
#[ cfg(unix) ]
fn follow_symlinks ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --follow-symlinks " ,
" --md5sum " ,
] ,
tree : { } ,
} ;
2020-02-05 18:32:09 -08:00
populate_symlinks ( & env ) ;
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-15 18:08:36 -08:00
let mut pieces = PieceList ::new ( ) ;
pieces . push ( Sha1 ::from ( " barbaz " ) . digest ( ) . into ( ) ) ;
assert_eq! ( metainfo . info . pieces , pieces ) ;
2020-02-05 18:32:09 -08:00
match metainfo . info . mode {
Mode ::Multiple { files } = > {
assert_eq! (
files ,
& [
FileInfo {
2020-02-14 00:12:49 -08:00
length : Bytes ( 3 ) ,
md5sum : Some ( Md5Digest ::from_hex ( " 37b51d194a7513e45b56f6524f2d51f2 " ) ) ,
2020-02-05 18:32:09 -08:00
path : FilePath ::from_components ( & [ " bar " ] ) ,
} ,
FileInfo {
2020-02-14 00:12:49 -08:00
length : Bytes ( 3 ) ,
md5sum : Some ( Md5Digest ::from_hex ( " 73feffa4b7f6bb68e44cf984c85f6e88 " ) ) ,
2020-02-05 18:32:09 -08:00
path : FilePath ::from_components ( & [ " dir " , " baz " ] ) ,
} ,
]
) ;
}
_ = > panic! ( " Expected multi-file torrent " ) ,
}
}
#[ test ]
#[ cfg(unix) ]
fn symlink_root ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --md5sum " ,
] ,
tree : { } ,
} ;
2020-02-05 18:32:09 -08:00
let file_src = env . resolve ( " bar " ) ;
let file_link = env . resolve ( " foo " ) ;
Command ::new ( " ln " )
. arg ( " -s " )
. arg ( & file_src )
. arg ( & file_link )
. status ( )
. unwrap ( ) ;
assert_matches! ( env . run ( ) . unwrap_err ( ) , Error ::SymlinkRoot { root } if root = = file_link ) ;
}
#[ test ]
fn skip_dot_dir_contents ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --md5sum " ,
] ,
tree : {
foo : {
" .bar " : {
baz : " baz " ,
} ,
} ,
}
} ;
2020-02-05 18:32:09 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 18:32:09 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . is_empty ( )
) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::new ( ) ) ;
2020-02-05 18:32:09 -08:00
}
#[ test ]
fn skip_hidden_attribute_dir_contents ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --md5sum "
] ,
tree : {
foo : {
bar : { } ,
} ,
} ,
} ;
2020-02-05 18:32:09 -08:00
#[ cfg(target_os = " windows " ) ]
{
2020-03-05 21:44:20 -08:00
env . write ( " foo/bar/baz " , " baz " ) ;
2020-02-05 18:32:09 -08:00
let path = env . resolve ( " foo/bar " ) ;
Command ::new ( " attrib " )
. arg ( " +h " )
. arg ( & path )
. status ( )
. unwrap ( ) ;
}
2020-03-05 21:44:20 -08:00
2020-02-05 18:32:09 -08:00
#[ cfg(target_os = " macos " ) ]
{
2020-03-05 21:44:20 -08:00
env . write ( " foo/bar/baz " , " baz " ) ;
2020-02-05 18:32:09 -08:00
let path = env . resolve ( " foo/bar " ) ;
Command ::new ( " chflags " )
. arg ( " hidden " )
. arg ( & path )
. status ( )
. unwrap ( ) ;
}
2020-03-05 21:44:20 -08:00
2020-02-05 18:32:09 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 18:32:09 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . is_empty ( )
) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::new ( ) ) ;
2020-02-05 18:32:09 -08:00
}
2020-02-05 21:47:12 -08:00
#[ test ]
fn glob_exclude ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --glob " ,
" !a "
] ,
tree : {
foo : {
a : " a " ,
b : " b " ,
c : " c " ,
} ,
}
} ;
2020-02-05 21:47:12 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 21:47:12 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . len ( ) = = 2
) ;
2020-02-15 18:08:36 -08:00
let mut pieces = PieceList ::new ( ) ;
pieces . push ( Sha1 ::from ( " bc " ) . digest ( ) . into ( ) ) ;
assert_eq! ( metainfo . info . pieces , pieces ) ;
2020-02-05 21:47:12 -08:00
}
#[ test ]
fn glob_exclude_nomatch ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --glob " ,
" !x "
] ,
tree : {
foo : {
a : " a " ,
b : " b " ,
c : " c " ,
} ,
}
} ;
2020-02-05 21:47:12 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 21:47:12 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . len ( ) = = 3
) ;
2020-02-15 18:08:36 -08:00
let mut pieces = PieceList ::new ( ) ;
pieces . push ( Sha1 ::from ( " abc " ) . digest ( ) . into ( ) ) ;
assert_eq! ( metainfo . info . pieces , pieces ) ;
2020-02-05 21:47:12 -08:00
}
#[ test ]
fn glob_include ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --glob " ,
" [bc] " ,
] ,
tree : {
foo : {
a : " a " ,
b : " b " ,
c : " c " ,
} ,
}
} ;
2020-02-05 21:47:12 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 21:47:12 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . len ( ) = = 2
) ;
2020-02-15 18:08:36 -08:00
let mut pieces = PieceList ::new ( ) ;
pieces . push ( Sha1 ::from ( " bc " ) . digest ( ) . into ( ) ) ;
assert_eq! ( metainfo . info . pieces , pieces ) ;
2020-02-05 21:47:12 -08:00
}
#[ test ]
fn glob_include_nomatch ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --glob " ,
" x " ,
] ,
tree : {
foo : {
a : " a " ,
b : " b " ,
c : " c " ,
} ,
}
} ;
2020-02-05 21:47:12 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 21:47:12 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . is_empty ( )
) ;
2020-02-15 18:08:36 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::new ( ) ) ;
2020-02-05 21:47:12 -08:00
}
#[ test ]
fn glob_precedence ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --glob " ,
" !* " ,
" --glob " ,
" [ab] " ,
" --glob " ,
" !b " ,
] ,
tree : {
foo : {
a : " a " ,
b : " b " ,
c : " c " ,
} ,
}
} ;
2020-02-05 21:47:12 -08:00
env . run ( ) . unwrap ( ) ;
2020-02-14 02:16:19 -08:00
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
2020-02-05 21:47:12 -08:00
assert_matches! (
metainfo . info . mode ,
Mode ::Multiple { files } if files . len ( ) = = 1
) ;
2020-03-05 21:44:20 -08:00
assert_eq! ( metainfo . info . pieces , PieceList ::from_pieces ( & [ " a " ] ) ) ;
2020-02-05 21:47:12 -08:00
}
2020-02-14 02:16:19 -08:00
#[ test ]
fn nodes_default ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
] ,
2020-02-14 02:16:19 -08:00
tree : {
foo : " " ,
}
} ;
env . run ( ) . unwrap ( ) ;
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
assert! ( metainfo . nodes . is_none ( ) ) ;
}
#[ test ]
fn nodes_invalid ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
args : [
" torrent " ,
" create " ,
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --dht-node " ,
" blah " ,
] ,
2020-02-14 02:16:19 -08:00
tree : {
foo : " " ,
} ,
} ;
assert_matches! ( env . run ( ) , Err ( Error ::Clap { .. } ) ) ;
}
#[ test ]
fn nodes_valid ( ) {
2020-03-05 21:44:20 -08:00
let mut env = test_env! {
2020-02-14 02:16:19 -08:00
args : [
2020-03-05 21:44:20 -08:00
" torrent " ,
" create " ,
2020-02-14 02:16:19 -08:00
" --input " ,
" foo " ,
" --announce " ,
" http://bar " ,
" --dht-node " ,
" router.example.com:1337 " ,
" --dht-node " ,
" 203.0.113.0:2290 " ,
" --dht-node " ,
" [2001:db8:4275:7920:6269:7463:6f69:6e21]:8832 " ,
] ,
tree : {
foo : " " ,
} ,
} ;
env . run ( ) . unwrap ( ) ;
let metainfo = env . load_metainfo ( " foo.torrent " ) ;
assert_eq! (
metainfo . nodes ,
Some ( vec! [
" router.example.com:1337 " . parse ( ) . unwrap ( ) ,
" 203.0.113.0:2290 " . parse ( ) . unwrap ( ) ,
" [2001:db8:4275:7920:6269:7463:6f69:6e21]:8832 "
. parse ( )
. unwrap ( ) ,
] ) ,
) ;
}
2019-05-24 01:25:55 -07:00
}