2019-05-24 01:25:55 -07:00
|
|
|
use crate::common::*;
|
2020-03-12 22:05:49 -07:00
|
|
|
use create_step::CreateStep;
|
|
|
|
|
|
|
|
mod create_step;
|
2019-05-24 01:25:55 -07:00
|
|
|
|
|
|
|
#[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(
|
|
|
|
long = "announce",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "a",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "URL",
|
|
|
|
help = "Use `URL` as the primary tracker announce URL. To supply multiple announce URLs, also \
|
|
|
|
use `--announce-tier`."
|
2020-01-24 14:17:31 -08:00
|
|
|
)]
|
2020-03-17 03:02:02 -07:00
|
|
|
announce: Option<Url>,
|
2020-02-03 04:39:48 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "allow",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "A",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "LINT",
|
|
|
|
possible_values = Lint::VALUES,
|
2020-03-17 03:02:02 -07:00
|
|
|
set(ArgSettings::CaseInsensitive),
|
2020-03-07 00:52:44 -08:00
|
|
|
help = "Allow `LINT`. Lints check for conditions which, although permitted, are not usually \
|
|
|
|
desirable. For example, piece length can be any non-zero value, but probably \
|
|
|
|
shouldn't be below 16 KiB. The lint `small-piece-size` checks for this, and \
|
|
|
|
`--allow small-piece-size` can be used to disable this check.",
|
2020-02-03 04:39:48 -08:00
|
|
|
)]
|
|
|
|
allowed_lints: Vec<Lint>,
|
2020-01-24 14:17:31 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "announce-tier",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "t",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "URL-LIST",
|
|
|
|
help = "Use `URL-LIST` as a tracker announce tier. Each instance adds a new \
|
2020-03-06 23:25:15 -08:00
|
|
|
tier. To add multiple trackers to a given tier, separate their announce URLs \
|
|
|
|
with commas:\n\
|
|
|
|
\n\
|
|
|
|
`--announce-tier udp://example.com:80/announce,https://example.net:443/announce`
|
|
|
|
\n\
|
|
|
|
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.
|
|
|
|
\n\
|
|
|
|
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"
|
2020-01-24 14:17:31 -08:00
|
|
|
)]
|
|
|
|
announce_tiers: Vec<String>,
|
|
|
|
#[structopt(
|
|
|
|
long = "comment",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "c",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "TEXT",
|
|
|
|
help = "Include `TEXT` as the comment for generated `.torrent` file. Stored under `comment` \
|
|
|
|
key of top-level metainfo dictionary."
|
2020-01-24 14:17:31 -08:00
|
|
|
)]
|
2020-01-20 23:05:39 -08:00
|
|
|
comment: Option<String>,
|
2020-02-14 02:16:19 -08:00
|
|
|
#[structopt(
|
2020-03-09 00:43:12 -07:00
|
|
|
long = "node",
|
|
|
|
short = "n",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "NODE",
|
2020-03-06 23:25:15 -08:00
|
|
|
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:
|
2020-03-09 00:43:12 -07:00
|
|
|
`--node router.example.com:1337`
|
|
|
|
`--node 203.0.113.0:2290`
|
|
|
|
`--node [2001:db8:4275:7920:6269:7463:6f69:6e21]:8832`"
|
2020-02-14 02:16:19 -08:00
|
|
|
)]
|
2020-03-17 03:02:02 -07:00
|
|
|
dht_nodes: Vec<HostPort>,
|
2020-02-05 18:32:09 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "follow-symlinks",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "F",
|
2020-03-05 22:51:00 -08:00
|
|
|
help = "Follow symlinks in torrent input. By default, symlinks to files and directories are \
|
|
|
|
not included in torrent contents."
|
2020-02-05 18:32:09 -08:00
|
|
|
)]
|
|
|
|
follow_symlinks: bool,
|
2020-02-04 21:55:19 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "force",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "f",
|
2020-02-04 21:55:19 -08:00
|
|
|
help = "Overwrite the destination `.torrent` file, if it exists."
|
|
|
|
)]
|
|
|
|
force: bool,
|
2020-02-05 21:47:12 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "glob",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "g",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "GLOB",
|
2020-03-05 22:51:00 -08:00
|
|
|
help = "Include or exclude files that match `GLOB`. Multiple glob may be provided, with the \
|
2020-03-07 00:52:44 -08:00
|
|
|
last one taking precedence. Precede a glob with `!` to exclude it."
|
2020-02-05 21:47:12 -08:00
|
|
|
)]
|
|
|
|
globs: Vec<String>,
|
2020-02-05 18:32:09 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "include-hidden",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "h",
|
2020-03-05 22:51:00 -08:00
|
|
|
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."
|
2020-02-05 18:32:09 -08:00
|
|
|
)]
|
|
|
|
include_hidden: bool,
|
|
|
|
#[structopt(
|
|
|
|
long = "include-junk",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "j",
|
2020-02-05 18:32:09 -08:00
|
|
|
help = "Include junk files that would otherwise be skipped."
|
|
|
|
)]
|
|
|
|
include_junk: bool,
|
2020-01-24 14:17:31 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "input",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "i",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "PATH",
|
|
|
|
help = "Read torrent contents from `PATH`. If `PATH` is a file, torrent will be a single-file \
|
|
|
|
torrent, if `PATH` is a directory, torrent will be a multi-file torrent.",
|
2020-02-04 21:29:53 -08:00
|
|
|
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(
|
2020-03-09 00:43:12 -07:00
|
|
|
long = "md5",
|
|
|
|
short = "M",
|
2020-03-05 22:51:00 -08:00
|
|
|
help = "Include MD5 checksum of each file in the torrent. N.B. MD5 is cryptographically \
|
2020-03-06 23:25:15 -08:00
|
|
|
broken and only suitable for checking for accidental corruption."
|
2020-01-24 14:17:31 -08:00
|
|
|
)]
|
2019-05-24 01:25:55 -07:00
|
|
|
md5sum: bool,
|
2020-01-24 14:17:31 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "name",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "N",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "TEXT",
|
|
|
|
help = "Set name of torrent to `TEXT`. Defaults to the filename of the argument to `--input`."
|
2020-01-24 14:17:31 -08:00
|
|
|
)]
|
2019-05-24 01:25:55 -07:00
|
|
|
name: Option<String>,
|
2020-01-24 14:17:31 -08:00
|
|
|
#[structopt(
|
|
|
|
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(
|
|
|
|
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(
|
|
|
|
long = "open",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "O",
|
2020-03-06 23:25:15 -08:00
|
|
|
help = "Open `.torrent` file after creation. Uses `xdg-open`, `gnome-open`, or `kde-open` on \
|
2020-03-17 06:23:33 -07:00
|
|
|
Linux; `open` on macOS; and `cmd /C start` on Windows"
|
2020-01-30 05:54:08 -08:00
|
|
|
)]
|
|
|
|
open: bool,
|
2020-01-24 14:17:31 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "output",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "o",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "TARGET",
|
|
|
|
help = "Save `.torrent` file to `TARGET`, or print to standard output if `TARGET` is `-`. \
|
|
|
|
Defaults to `$INPUT.torrent`.",
|
2020-02-04 21:29:53 -08:00
|
|
|
parse(from_os_str)
|
2020-01-24 14:17:31 -08:00
|
|
|
)]
|
2020-03-11 22:44:14 -07:00
|
|
|
output: Option<OutputTarget>,
|
2020-01-24 14:17:31 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "piece-length",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "p",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "BYTES",
|
|
|
|
help = "Set piece length to `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(
|
|
|
|
long = "private",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "P",
|
2020-03-06 23:25:15 -08:00
|
|
|
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-24 14:17:31 -08:00
|
|
|
)]
|
2020-01-20 23:05:39 -08:00
|
|
|
private: bool,
|
2020-03-11 22:25:41 -07:00
|
|
|
#[structopt(
|
|
|
|
long = "show",
|
|
|
|
short = "S",
|
|
|
|
help = "Display information about created torrent file."
|
|
|
|
)]
|
|
|
|
show: bool,
|
2020-02-04 08:51:56 -08:00
|
|
|
#[structopt(
|
|
|
|
long = "source",
|
2020-03-09 00:43:12 -07:00
|
|
|
short = "s",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "TEXT",
|
|
|
|
help = "Set torrent source to `TEXT`. Stored under `source` key of info dictionary. This is \
|
|
|
|
useful for keeping statistics from being mis-reported when participating in swarms \
|
|
|
|
with the same contents, but with different trackers. When source is set to a unique \
|
|
|
|
value for torrents with the same contents, torrent clients will treat them as \
|
|
|
|
distinct torrents, and not share peers between them, and will correctly report \
|
|
|
|
download and upload statistics to multiple trackers."
|
2020-02-04 08:51:56 -08:00
|
|
|
)]
|
|
|
|
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-03-17 03:02:02 -07:00
|
|
|
let mut linter = Linter::new();
|
|
|
|
linter.allow(self.allowed_lints.iter().cloned());
|
|
|
|
|
2020-03-11 19:04:22 -04:00
|
|
|
let mut announce_list = Vec::new();
|
|
|
|
for tier in &self.announce_tiers {
|
|
|
|
let tier = tier.split(',').map(str::to_string).collect::<Vec<String>>();
|
|
|
|
|
|
|
|
tier
|
|
|
|
.iter()
|
|
|
|
.map(|announce| announce.parse())
|
|
|
|
.collect::<Result<Vec<Url>, url::ParseError>>()
|
|
|
|
.context(error::AnnounceUrlParse)?;
|
|
|
|
|
|
|
|
announce_list.push(tier);
|
|
|
|
}
|
|
|
|
|
2020-03-17 03:02:02 -07:00
|
|
|
if linter.is_denied(Lint::PrivateTrackerless) && self.private && self.announce.is_none() {
|
|
|
|
return Err(Error::PrivateTrackerless);
|
|
|
|
}
|
|
|
|
|
2020-03-12 22:05:49 -07:00
|
|
|
CreateStep::Searching.print(env)?;
|
2020-03-11 19:04:22 -04:00
|
|
|
|
2020-03-15 22:41:47 -07:00
|
|
|
let spinner = if env.err().is_styled_term() {
|
2020-03-12 01:08:40 -07:00
|
|
|
let style = ProgressStyle::default_spinner()
|
|
|
|
.template("{spinner:.green} {msg:.bold}…")
|
2020-03-12 22:05:49 -07:00
|
|
|
.tick_chars(consts::TICK_CHARS);
|
2020-03-11 19:04:22 -04:00
|
|
|
|
2020-03-12 01:08:40 -07:00
|
|
|
Some(ProgressBar::new_spinner().with_style(style))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2020-03-11 19:04:22 -04: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-03-11 19:04:22 -04:00
|
|
|
.spinner(spinner)
|
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
|
|
|
|
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 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-03-11 22:44:14 -07:00
|
|
|
OutputTarget::File(input.parent().unwrap().join(torrent_name))
|
2019-05-24 01:25:55 -07:00
|
|
|
});
|
|
|
|
|
2020-03-12 01:01:26 -07:00
|
|
|
if let OutputTarget::File(path) = &output {
|
|
|
|
if !self.force && path.exists() {
|
|
|
|
return Err(Error::OutputExists {
|
|
|
|
path: path.to_owned(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-03-12 22:05:49 -07:00
|
|
|
CreateStep::Hashing.print(env)?;
|
2020-03-11 19:04:22 -04:00
|
|
|
|
2020-03-15 22:41:47 -07:00
|
|
|
let progress_bar = if env.err().is_styled_term() {
|
2020-03-12 01:08:40 -07:00
|
|
|
let style = ProgressStyle::default_bar()
|
|
|
|
.template(
|
|
|
|
"{spinner:.green} ⟪{elapsed_precise}⟫ ⟦{bar:40.cyan}⟧ \
|
|
|
|
{binary_bytes}/{binary_total_bytes} ⟨{binary_bytes_per_sec}, {eta}⟩",
|
|
|
|
)
|
2020-03-12 22:05:49 -07:00
|
|
|
.tick_chars(consts::TICK_CHARS)
|
|
|
|
.progress_chars(consts::PROGRESS_CHARS);
|
2020-03-11 22:00:20 -04:00
|
|
|
|
2020-03-12 01:08:40 -07:00
|
|
|
Some(ProgressBar::new(files.total_size().count()).with_style(style))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2020-03-11 22:00:20 -04:00
|
|
|
|
2020-02-14 00:12:49 -08:00
|
|
|
let (mode, pieces) = Hasher::hash(
|
|
|
|
&files,
|
|
|
|
self.md5sum,
|
|
|
|
piece_length.as_piece_length()?.into_usize(),
|
2020-03-11 22:00:20 -04:00
|
|
|
progress_bar,
|
2020-02-14 00:12:49 -08:00
|
|
|
)?;
|
2019-05-24 01:25:55 -07:00
|
|
|
|
2020-03-12 22:05:49 -07:00
|
|
|
CreateStep::Writing { output: &output }.print(env)?;
|
2020-03-11 19:04:22 -04:00
|
|
|
|
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-03-17 03:02:02 -07:00
|
|
|
announce: self.announce.map(|url| url.to_string()),
|
2020-01-24 14:17:31 -08:00
|
|
|
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 {
|
2020-03-11 22:44:14 -07:00
|
|
|
OutputTarget::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-04 21:29:53 -08:00
|
|
|
}
|
2020-03-15 03:22:33 -07:00
|
|
|
OutputTarget::Stdout => env.out_mut().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-03-12 22:05:49 -07:00
|
|
|
let status = metainfo.verify(&input, None)?;
|
2020-02-14 00:12:49 -08:00
|
|
|
|
|
|
|
if !status.good() {
|
2020-03-15 22:41:47 -07:00
|
|
|
return Err(Error::Verify);
|
2020-02-14 00:12:49 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-12 18:00:52 -07:00
|
|
|
errln!(env, "\u{2728}\u{2728} Done! \u{2728}\u{2728}")?;
|
2020-03-11 19:04:22 -04:00
|
|
|
|
2020-03-11 22:25:41 -07:00
|
|
|
if self.show {
|
|
|
|
TorrentSummary::from_metainfo(metainfo)?.write(env)?;
|
|
|
|
}
|
|
|
|
|
2020-03-11 22:44:14 -07:00
|
|
|
if let OutputTarget::File(path) = output {
|
2020-03-11 22:25:41 -07:00
|
|
|
if self.open {
|
2020-03-17 06:23:33 -07:00
|
|
|
Platform::open_file(&path)?;
|
2020-03-11 22:25:41 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-24 01:25:55 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-12 18:00:52 -07:00
|
|
|
}
|
|
|
|
|
2019-05-24 01:25:55 -07:00
|
|
|
#[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 { .. })));
|
|
|
|
}
|
|
|
|
|
2020-03-17 03:02:02 -07:00
|
|
|
#[test]
|
|
|
|
fn announce_is_optional() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: "",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_matches!(env.run(), Ok(()));
|
|
|
|
}
|
|
|
|
|
2019-05-24 01:25:55 -07:00
|
|
|
#[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
|
|
|
}
|
|
|
|
|
2020-03-07 19:23:20 -08:00
|
|
|
#[test]
|
|
|
|
fn input_dot() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
".",
|
|
|
|
"--announce",
|
|
|
|
"https://bar",
|
|
|
|
],
|
|
|
|
cwd: "dir",
|
|
|
|
tree: {
|
|
|
|
dir: {
|
|
|
|
foo: "",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
};
|
|
|
|
env.run().unwrap();
|
|
|
|
let metainfo = env.load_metainfo("../dir.torrent");
|
|
|
|
assert_eq!(metainfo.info.name, "dir");
|
|
|
|
assert_matches!(metainfo.info.mode, Mode::Multiple{files} if files.len() == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn input_dot_dot() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"..",
|
|
|
|
"--announce",
|
|
|
|
"https://bar",
|
|
|
|
],
|
|
|
|
cwd: "a/b",
|
|
|
|
tree: {
|
|
|
|
a: {
|
|
|
|
b: {
|
|
|
|
foo: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
};
|
|
|
|
env.run().unwrap();
|
|
|
|
let metainfo = env.load_metainfo("../../a.torrent");
|
|
|
|
assert_eq!(metainfo.info.name, "a");
|
|
|
|
assert_matches!(metainfo.info.mode, Mode::Multiple{files} if files.len() == 1);
|
|
|
|
}
|
|
|
|
|
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-03-17 03:02:02 -07:00
|
|
|
assert_eq!(metainfo.announce, Some("http://bar/".into()));
|
2020-01-24 14:17:31 -08:00
|
|
|
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!(
|
2020-03-17 03:02:02 -07:00
|
|
|
metainfo.announce.as_deref(),
|
|
|
|
Some("udp://tracker.opentrackr.org:1337/announce")
|
2020-01-30 03:13:26 -08:00
|
|
|
);
|
|
|
|
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-03-17 03:02:02 -07:00
|
|
|
assert_eq!(
|
|
|
|
metainfo.announce.as_deref(),
|
|
|
|
Some("wss://tracker.btorrent.xyz/")
|
|
|
|
);
|
2020-01-31 04:21:55 -08:00
|
|
|
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-03-17 03:02:02 -07:00
|
|
|
assert_eq!(metainfo.announce.as_deref(), Some("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-03-17 03:02:02 -07:00
|
|
|
assert_eq!(metainfo.announce.as_deref(), Some("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 * 2u32.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);
|
|
|
|
}
|
|
|
|
|
2020-03-07 19:45:26 -08:00
|
|
|
#[test]
|
|
|
|
fn uneven_last_piece() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"http://bar",
|
|
|
|
"--allow",
|
|
|
|
"small-piece-length",
|
|
|
|
"--piece-length",
|
|
|
|
"4",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: "123",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
env.run().unwrap();
|
|
|
|
let metainfo = env.load_metainfo("foo.torrent");
|
|
|
|
assert_eq!(metainfo.info.pieces, PieceList::from_pieces(&["123"]));
|
|
|
|
assert_eq!(
|
|
|
|
metainfo.info.mode,
|
|
|
|
Mode::Single {
|
|
|
|
length: Bytes(3),
|
|
|
|
md5sum: None,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn even_last_piece() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"http://bar",
|
|
|
|
"--allow",
|
|
|
|
"small-piece-length",
|
|
|
|
"--piece-length",
|
|
|
|
"4",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: "1234",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
env.run().unwrap();
|
|
|
|
let metainfo = env.load_metainfo("foo.torrent");
|
|
|
|
assert_eq!(metainfo.info.pieces, PieceList::from_pieces(&["1234"]));
|
|
|
|
assert_eq!(
|
|
|
|
metainfo.info.mode,
|
|
|
|
Mode::Single {
|
|
|
|
length: Bytes(4),
|
|
|
|
md5sum: None,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multi_piece_file() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"http://bar",
|
|
|
|
"--allow",
|
|
|
|
"small-piece-length",
|
|
|
|
"--piece-length",
|
|
|
|
"2",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: "1234",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
env.run().unwrap();
|
|
|
|
let metainfo = env.load_metainfo("foo.torrent");
|
|
|
|
assert_eq!(metainfo.info.pieces, PieceList::from_pieces(&["12", "34"]));
|
|
|
|
assert_eq!(
|
|
|
|
metainfo.info.mode,
|
|
|
|
Mode::Single {
|
|
|
|
length: Bytes(4),
|
|
|
|
md5sum: None,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multi_file_piece() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"dir",
|
|
|
|
"--announce",
|
|
|
|
"http://bar",
|
|
|
|
"--allow",
|
|
|
|
"small-piece-length",
|
|
|
|
"--piece-length",
|
|
|
|
"8",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5",
|
2020-03-07 19:45:26 -08:00
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
dir: {
|
|
|
|
foo: "1234",
|
|
|
|
bar: "5678",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
env.run().unwrap();
|
|
|
|
let metainfo = env.load_metainfo("dir.torrent");
|
|
|
|
assert_eq!(metainfo.info.pieces, PieceList::from_pieces(&["56781234"]));
|
|
|
|
assert_eq!(
|
|
|
|
metainfo.info.mode,
|
|
|
|
Mode::Multiple {
|
|
|
|
files: vec![
|
|
|
|
FileInfo {
|
|
|
|
path: FilePath::from_components(&["bar"]),
|
|
|
|
length: Bytes(4),
|
|
|
|
md5sum: Some(Md5Digest::from_data("5678")),
|
|
|
|
},
|
|
|
|
FileInfo {
|
|
|
|
path: FilePath::from_components(&["foo"]),
|
|
|
|
length: Bytes(4),
|
|
|
|
md5sum: Some(Md5Digest::from_data("1234")),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-05-24 01:25:55 -07:00
|
|
|
#[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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5",
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5"
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
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\necho $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-03-11 22:25:41 -07: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();
|
|
|
|
assert_eq!(env.out(), "");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn show() {
|
|
|
|
let mut env = TestEnvBuilder::new()
|
|
|
|
.arg_slice(&[
|
|
|
|
"imdl",
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"http://bar",
|
|
|
|
"--no-creation-date",
|
|
|
|
"--show",
|
|
|
|
])
|
|
|
|
.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-03-05 22:51:00 -08:00
|
|
|
#[rustfmt::skip]
|
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(),
|
2020-03-12 01:01:26 -07:00
|
|
|
Error::OutputExists {path}
|
|
|
|
if path == env.resolve("foo.torrent")
|
2020-02-04 21:55:19 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5",
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5",
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5",
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5",
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--md5"
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--node",
|
2020-03-05 21:44:20 -08:00
|
|
|
"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",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--node",
|
2020-02-14 02:16:19 -08:00
|
|
|
"router.example.com:1337",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--node",
|
2020-02-14 02:16:19 -08:00
|
|
|
"203.0.113.0:2290",
|
2020-03-09 00:43:12 -07:00
|
|
|
"--node",
|
2020-02-14 02:16:19 -08:00
|
|
|
"[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(),
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
}
|
2020-03-11 19:04:22 -04:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn create_progress_messages() {
|
|
|
|
let mut env = TestEnvBuilder::new()
|
|
|
|
.arg_slice(&[
|
|
|
|
"imdl",
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"http://bar",
|
|
|
|
])
|
|
|
|
.build();
|
|
|
|
|
|
|
|
fs::write(env.resolve("foo"), "").unwrap();
|
|
|
|
|
|
|
|
let want = format!(
|
|
|
|
"[1/3] \u{1F9FF} Searching for files…\n[2/3] \u{1F9EE} Hashing pieces…\n[3/3] \u{1F4BE} \
|
|
|
|
Writing metainfo to `{}`…\n\u{2728}\u{2728} Done! \u{2728}\u{2728}\n",
|
|
|
|
env.resolve("foo.torrent").display()
|
|
|
|
);
|
|
|
|
|
|
|
|
env.run().unwrap();
|
|
|
|
|
|
|
|
assert_eq!(env.err(), want);
|
|
|
|
}
|
2020-03-17 03:02:02 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn private_requires_announce() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--private",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: "",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
env.run(),
|
|
|
|
Err(error @ Error::PrivateTrackerless)
|
|
|
|
if error.lint() == Some(Lint::PrivateTrackerless)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn private_trackerless_announce() {
|
|
|
|
let mut env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--private",
|
|
|
|
"--allow",
|
|
|
|
"private-trackerLESS",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: "",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_matches!(env.run(), Ok(()));
|
|
|
|
}
|
2019-05-24 01:25:55 -07:00
|
|
|
}
|