Refactor tests and improve verification
Improve verification somewhat, but mostly refactor tests to use test tree, and be nicer to read and write. type: changed
This commit is contained in:
parent
2fb5bdb933
commit
d71bdffda1
96
src/bytes.rs
96
src/bytes.rs
|
@ -1,5 +1,53 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
|
impl Div<Bytes> for Bytes {
|
||||||
|
type Output = u64;
|
||||||
|
|
||||||
|
fn div(self, rhs: Bytes) -> u64 {
|
||||||
|
self.0 / rhs.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<u64> for Bytes {
|
||||||
|
type Output = Bytes;
|
||||||
|
|
||||||
|
fn div(self, rhs: u64) -> Bytes {
|
||||||
|
Bytes::from(self.0 / rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<u64> for Bytes {
|
||||||
|
type Output = Bytes;
|
||||||
|
|
||||||
|
fn mul(self, rhs: u64) -> Self {
|
||||||
|
Bytes::from(self.0 * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DivAssign<u64> for Bytes {
|
||||||
|
fn div_assign(&mut self, rhs: u64) {
|
||||||
|
self.0 /= rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MulAssign<u64> for Bytes {
|
||||||
|
fn mul_assign(&mut self, rhs: u64) {
|
||||||
|
self.0 *= rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign<Bytes> for Bytes {
|
||||||
|
fn add_assign(&mut self, rhs: Bytes) {
|
||||||
|
self.0 += rhs.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<u64> for Bytes {
|
||||||
|
fn sub_assign(&mut self, rhs: u64) {
|
||||||
|
self.0 -= rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const KI: u64 = 1 << 10;
|
const KI: u64 = 1 << 10;
|
||||||
const MI: u64 = KI << 10;
|
const MI: u64 = KI << 10;
|
||||||
const GI: u64 = MI << 10;
|
const GI: u64 = MI << 10;
|
||||||
|
@ -52,54 +100,6 @@ impl<I: Into<u64>> From<I> for Bytes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Div<Bytes> for Bytes {
|
|
||||||
type Output = u64;
|
|
||||||
|
|
||||||
fn div(self, rhs: Bytes) -> u64 {
|
|
||||||
self.0 / rhs.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<u64> for Bytes {
|
|
||||||
type Output = Bytes;
|
|
||||||
|
|
||||||
fn div(self, rhs: u64) -> Bytes {
|
|
||||||
Bytes::from(self.0 / rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DivAssign<u64> for Bytes {
|
|
||||||
fn div_assign(&mut self, rhs: u64) {
|
|
||||||
self.0 /= rhs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<u64> for Bytes {
|
|
||||||
type Output = Bytes;
|
|
||||||
|
|
||||||
fn mul(self, rhs: u64) -> Self {
|
|
||||||
Bytes::from(self.0 * rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MulAssign<u64> for Bytes {
|
|
||||||
fn mul_assign(&mut self, rhs: u64) {
|
|
||||||
self.0 *= rhs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign<Bytes> for Bytes {
|
|
||||||
fn add_assign(&mut self, rhs: Bytes) {
|
|
||||||
self.0 += rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubAssign<u64> for Bytes {
|
|
||||||
fn sub_assign(&mut self, rhs: u64) {
|
|
||||||
self.0 -= rhs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Bytes {
|
impl FromStr for Bytes {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
|
|
|
@ -77,9 +77,6 @@ mod test {
|
||||||
pub(crate) use tempfile::TempDir;
|
pub(crate) use tempfile::TempDir;
|
||||||
pub(crate) use temptree::temptree;
|
pub(crate) use temptree::temptree;
|
||||||
|
|
||||||
// test modules
|
|
||||||
pub(crate) use crate::testing;
|
|
||||||
|
|
||||||
// test structs and enums
|
// test structs and enums
|
||||||
pub(crate) use crate::{capture::Capture, test_env::TestEnv, test_env_builder::TestEnvBuilder};
|
pub(crate) use crate::{capture::Capture, test_env::TestEnv, test_env_builder::TestEnvBuilder};
|
||||||
}
|
}
|
||||||
|
|
18
src/env.rs
18
src/env.rs
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct Env {
|
pub(crate) struct Env {
|
||||||
args: Vec<String>,
|
args: Vec<OsString>,
|
||||||
dir: Box<dyn AsRef<Path>>,
|
dir: Box<dyn AsRef<Path>>,
|
||||||
pub(crate) err: Box<dyn Write>,
|
pub(crate) err: Box<dyn Write>,
|
||||||
pub(crate) out: Box<dyn Write>,
|
pub(crate) out: Box<dyn Write>,
|
||||||
|
@ -76,7 +76,7 @@ impl Env {
|
||||||
D: AsRef<Path> + 'static,
|
D: AsRef<Path> + 'static,
|
||||||
O: Write + 'static,
|
O: Write + 'static,
|
||||||
E: Write + 'static,
|
E: Write + 'static,
|
||||||
S: Into<String>,
|
S: Into<OsString>,
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
|
@ -155,8 +155,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_message_on_stdout() {
|
fn error_message_on_stdout() {
|
||||||
let mut env = testing::env(
|
let mut env = test_env! {
|
||||||
[
|
args: [
|
||||||
"torrent",
|
"torrent",
|
||||||
"create",
|
"create",
|
||||||
"--input",
|
"--input",
|
||||||
|
@ -165,11 +165,11 @@ mod tests {
|
||||||
"udp:bar.com",
|
"udp:bar.com",
|
||||||
"--announce-tier",
|
"--announce-tier",
|
||||||
"foo",
|
"foo",
|
||||||
]
|
],
|
||||||
.iter()
|
tree: {
|
||||||
.cloned(),
|
foo: "",
|
||||||
);
|
}
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
};
|
||||||
env.status().ok();
|
env.status().ok();
|
||||||
let err = env.err();
|
let err = env.err();
|
||||||
if !err.starts_with("error: Failed to parse announce URL:") {
|
if !err.starts_with("error: Failed to parse announce URL:") {
|
||||||
|
|
|
@ -63,36 +63,6 @@ impl FileStatus {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn icon(&self) -> char {
|
|
||||||
if self.error.is_some() {
|
|
||||||
return '!';
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.present {
|
|
||||||
return '?';
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.file {
|
|
||||||
return '¿';
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.md5() {
|
|
||||||
return 'x';
|
|
||||||
}
|
|
||||||
|
|
||||||
let length = self.length_actual.unwrap();
|
|
||||||
|
|
||||||
if length > self.length_expected {
|
|
||||||
return '+';
|
|
||||||
}
|
|
||||||
|
|
||||||
if length < self.length_expected {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
'♡'
|
|
||||||
}
|
|
||||||
|
|
||||||
fn md5(&self) -> bool {
|
fn md5(&self) -> bool {
|
||||||
match (self.md5_actual, self.md5_expected) {
|
match (self.md5_actual, self.md5_expected) {
|
||||||
(Some(actual), Some(expected)) => actual == expected,
|
(Some(actual), Some(expected)) => actual == expected,
|
||||||
|
@ -108,7 +78,4 @@ impl FileStatus {
|
||||||
pub(crate) fn bad(&self) -> bool {
|
pub(crate) fn bad(&self) -> bool {
|
||||||
!self.good()
|
!self.good()
|
||||||
}
|
}
|
||||||
pub(crate) fn path(&self) -> &Path {
|
|
||||||
&self.path
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,7 @@ mod err;
|
||||||
mod outln;
|
mod outln;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod testing;
|
#[macro_use]
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_env;
|
mod test_env;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -52,6 +52,16 @@ impl Metainfo {
|
||||||
Self::deserialize(path, &bytes)
|
Self::deserialize(path, &bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize(path: impl AsRef<Path>, bytes: &[u8]) -> Result<Metainfo, Error> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let metainfo = bendy::serde::de::from_bytes(&bytes).context(error::MetainfoLoad { path })?;
|
||||||
|
Ok(metainfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn serialize(&self) -> Result<Vec<u8>, Error> {
|
||||||
|
bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
@ -60,18 +70,9 @@ impl Metainfo {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn deserialize(path: impl AsRef<Path>, bytes: &[u8]) -> Result<Metainfo, Error> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
bendy::serde::de::from_bytes(&bytes).context(error::MetainfoLoad { path })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn serialize(&self) -> Result<Vec<u8>, Error> {
|
|
||||||
bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn from_bytes(bytes: &[u8]) -> Metainfo {
|
pub(crate) fn from_bytes(bytes: &[u8]) -> Metainfo {
|
||||||
bendy::serde::de::from_bytes(bytes).unwrap()
|
Self::deserialize("<TEST>", bytes).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn files<'a>(
|
pub(crate) fn files<'a>(
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,7 @@ pub(crate) struct Verify {
|
||||||
#[structopt(
|
#[structopt(
|
||||||
name = "TORRENT",
|
name = "TORRENT",
|
||||||
long = "metainfo",
|
long = "metainfo",
|
||||||
help = "Verify input data against `TORRENT` metainfo file.",
|
help = "Verify input data against torrent metainfo in `TORRENT`.",
|
||||||
parse(from_os_str)
|
parse(from_os_str)
|
||||||
)]
|
)]
|
||||||
metainfo: PathBuf,
|
metainfo: PathBuf,
|
||||||
|
@ -36,9 +36,8 @@ impl Verify {
|
||||||
|
|
||||||
let status = metainfo.verify(&base)?;
|
let status = metainfo.verify(&base)?;
|
||||||
|
|
||||||
status.write(env)?;
|
|
||||||
|
|
||||||
if status.good() {
|
if status.good() {
|
||||||
|
errln!(env, "Verification succeeded.");
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Verify { status })
|
Err(Error::Verify { status })
|
||||||
|
@ -50,13 +49,96 @@ impl Verify {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn environment(args: &[&str]) -> TestEnv {
|
#[test]
|
||||||
testing::env(["torrent", "create"].iter().chain(args).cloned())
|
fn require_metainfo_argument() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn require_metainfo_argument() {
|
fn pass() -> Result<()> {
|
||||||
let mut env = environment(&[]);
|
let mut create_env = test_env! {
|
||||||
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"foo",
|
||||||
|
"--announce",
|
||||||
|
"https://bar",
|
||||||
|
],
|
||||||
|
tree: {
|
||||||
|
foo: {
|
||||||
|
a: "abc",
|
||||||
|
d: "efg",
|
||||||
|
h: "ijk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
create_env.run()?;
|
||||||
|
|
||||||
|
let torrent = create_env.resolve("foo.torrent");
|
||||||
|
|
||||||
|
let mut verify_env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"verify",
|
||||||
|
"--metainfo",
|
||||||
|
torrent,
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_matches!(verify_env.run(), Ok(()));
|
||||||
|
|
||||||
|
assert_eq!(verify_env.err(), "Verification succeeded.\n");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail() -> Result<()> {
|
||||||
|
let mut create_env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"foo",
|
||||||
|
"--announce",
|
||||||
|
"https://bar",
|
||||||
|
],
|
||||||
|
tree: {
|
||||||
|
foo: {
|
||||||
|
a: "abc",
|
||||||
|
d: "efg",
|
||||||
|
h: "ijk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
create_env.run()?;
|
||||||
|
|
||||||
|
create_env.write("foo/a", "xyz");
|
||||||
|
|
||||||
|
let torrent = create_env.resolve("foo.torrent");
|
||||||
|
|
||||||
|
let mut verify_env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"verify",
|
||||||
|
"--metainfo",
|
||||||
|
torrent,
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_matches!(verify_env.run(), Err(Error::Verify { .. }));
|
||||||
|
|
||||||
|
assert_eq!(verify_env.err(), "");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,20 +15,13 @@ impl Status {
|
||||||
self.pieces
|
self.pieces
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn good(&self) -> bool {
|
#[cfg(test)]
|
||||||
self.pieces && self.files.iter().all(FileStatus::good)
|
pub(crate) fn files(&self) -> &[FileStatus] {
|
||||||
|
&self.files
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn write(&self, out: &mut Env) -> Result<()> {
|
pub(crate) fn good(&self) -> bool {
|
||||||
for file in &self.files {
|
self.pieces && self.files.iter().all(FileStatus::good)
|
||||||
errln!(out, "{} {}", file.icon(), file.path().display());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.pieces() {
|
|
||||||
errln!(out, "Piece hashes incorrect");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,24 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
|
macro_rules! test_env {
|
||||||
|
{
|
||||||
|
args: [$($arg:expr),* $(,)?],
|
||||||
|
tree: {
|
||||||
|
$($tree:tt)*
|
||||||
|
} $(,)?
|
||||||
|
} => {
|
||||||
|
{
|
||||||
|
let tempdir = temptree! { $($tree)* };
|
||||||
|
|
||||||
|
TestEnvBuilder::new()
|
||||||
|
.tempdir(tempdir)
|
||||||
|
.arg("imdl")
|
||||||
|
$(.arg($arg))*
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct TestEnv {
|
pub(crate) struct TestEnv {
|
||||||
env: Env,
|
env: Env,
|
||||||
err: Capture,
|
err: Capture,
|
||||||
|
@ -23,11 +42,7 @@ impl TestEnv {
|
||||||
self.out.bytes()
|
self.out.bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn create_dir(&self, path: impl AsRef<Path>) {
|
pub(crate) fn write(&self, path: impl AsRef<Path>, bytes: impl AsRef<[u8]>) {
|
||||||
fs::create_dir_all(self.env.resolve(path.as_ref())).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_file(&self, path: impl AsRef<Path>, bytes: impl AsRef<[u8]>) {
|
|
||||||
fs::write(self.env.resolve(path), bytes.as_ref()).unwrap();
|
fs::write(self.env.resolve(path), bytes.as_ref()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct TestEnvBuilder {
|
pub(crate) struct TestEnvBuilder {
|
||||||
args: Vec<String>,
|
args: Vec<OsString>,
|
||||||
out_is_term: bool,
|
out_is_term: bool,
|
||||||
use_color: bool,
|
use_color: bool,
|
||||||
tempdir: Option<TempDir>,
|
tempdir: Option<TempDir>,
|
||||||
|
@ -22,21 +22,14 @@ impl TestEnvBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn arg(mut self, arg: impl Into<String>) -> Self {
|
pub(crate) fn arg(mut self, arg: impl Into<OsString>) -> Self {
|
||||||
self.args.push(arg.into());
|
self.args.push(arg.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
|
|
||||||
for arg in args {
|
|
||||||
self.args.push(arg.into());
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn arg_slice(mut self, args: &[&str]) -> Self {
|
pub(crate) fn arg_slice(mut self, args: &[&str]) -> Self {
|
||||||
for arg in args.iter().cloned() {
|
for arg in args.iter().cloned() {
|
||||||
self.args.push(arg.to_owned());
|
self.args.push(arg.into());
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
use crate::common::*;
|
|
||||||
|
|
||||||
pub(crate) fn env(iter: impl IntoIterator<Item = impl Into<String>>) -> TestEnv {
|
|
||||||
TestEnvBuilder::new().arg("imdl").args(iter).build()
|
|
||||||
}
|
|
111
src/verifier.rs
111
src/verifier.rs
|
@ -1,6 +1,8 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct Verifier {
|
pub(crate) struct Verifier<'a> {
|
||||||
|
metainfo: &'a Metainfo,
|
||||||
|
base: &'a Path,
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
piece_length: usize,
|
piece_length: usize,
|
||||||
pieces: PieceList,
|
pieces: PieceList,
|
||||||
|
@ -8,38 +10,40 @@ pub(crate) struct Verifier {
|
||||||
piece_bytes_hashed: usize,
|
piece_bytes_hashed: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Verifier {
|
impl<'a> Verifier<'a> {
|
||||||
pub(crate) fn new(piece_length: usize) -> Verifier {
|
fn new(metainfo: &'a Metainfo, base: &'a Path) -> Result<Verifier<'a>> {
|
||||||
Verifier {
|
let piece_length = metainfo.info.piece_length.as_piece_length()?.into_usize();
|
||||||
|
|
||||||
|
Ok(Verifier {
|
||||||
buffer: vec![0; piece_length],
|
buffer: vec![0; piece_length],
|
||||||
piece_bytes_hashed: 0,
|
piece_bytes_hashed: 0,
|
||||||
sha1: Sha1::new(),
|
|
||||||
pieces: PieceList::new(),
|
pieces: PieceList::new(),
|
||||||
|
sha1: Sha1::new(),
|
||||||
|
base,
|
||||||
|
metainfo,
|
||||||
piece_length,
|
piece_length,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify(metainfo: &Metainfo, base: &Path) -> Result<Status> {
|
pub(crate) fn verify(metainfo: &'a Metainfo, base: &'a Path) -> Result<Status> {
|
||||||
let piece_length = metainfo.info.piece_length.as_piece_length()?;
|
Self::new(metainfo, base)?.verify_metainfo()
|
||||||
|
}
|
||||||
let piece_length = piece_length.into_usize();
|
|
||||||
|
|
||||||
|
fn verify_metainfo(mut self) -> Result<Status> {
|
||||||
let mut status = Vec::new();
|
let mut status = Vec::new();
|
||||||
|
|
||||||
let mut hasher = Self::new(piece_length);
|
for (path, len, md5sum) in self.metainfo.files(&self.base) {
|
||||||
|
|
||||||
for (path, len, md5sum) in metainfo.files(&base) {
|
|
||||||
status.push(FileStatus::status(&path, len, md5sum));
|
status.push(FileStatus::status(&path, len, md5sum));
|
||||||
hasher.hash(&path).ok();
|
self.hash(&path).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasher.piece_bytes_hashed > 0 {
|
if self.piece_bytes_hashed > 0 {
|
||||||
hasher.pieces.push(hasher.sha1.digest().into());
|
self.pieces.push(self.sha1.digest().into());
|
||||||
hasher.sha1.reset();
|
self.sha1.reset();
|
||||||
hasher.piece_bytes_hashed = 0;
|
self.piece_bytes_hashed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pieces = hasher.pieces == metainfo.info.pieces;
|
let pieces = self.pieces == self.metainfo.info.pieces;
|
||||||
|
|
||||||
Ok(Status::new(pieces, status))
|
Ok(Status::new(pieces, status))
|
||||||
}
|
}
|
||||||
|
@ -77,3 +81,72 @@ impl Verifier {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn good() -> Result<()> {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"foo",
|
||||||
|
"--announce",
|
||||||
|
"https://bar",
|
||||||
|
],
|
||||||
|
tree: {
|
||||||
|
foo: {
|
||||||
|
a: "abc",
|
||||||
|
d: "efg",
|
||||||
|
h: "ijk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
env.run()?;
|
||||||
|
|
||||||
|
let metainfo = env.load_metainfo("foo.torrent");
|
||||||
|
|
||||||
|
assert!(metainfo.verify(&env.resolve("foo"))?.good());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn piece_mismatch() -> Result<()> {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"foo",
|
||||||
|
"--announce",
|
||||||
|
"https://bar",
|
||||||
|
],
|
||||||
|
tree: {
|
||||||
|
foo: {
|
||||||
|
a: "abc",
|
||||||
|
d: "efg",
|
||||||
|
h: "ijk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
env.run()?;
|
||||||
|
|
||||||
|
env.write("foo/a", "xyz");
|
||||||
|
|
||||||
|
let metainfo = env.load_metainfo("foo.torrent");
|
||||||
|
|
||||||
|
let status = metainfo.verify(&env.resolve("foo"))?;
|
||||||
|
|
||||||
|
assert!(status.files().iter().all(FileStatus::good));
|
||||||
|
|
||||||
|
assert!(!status.pieces());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user