From 4a5c5cd1162ca3979917a507351e6ca6ed1bf9cc Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Wed, 8 Feb 2023 14:22:56 +0100 Subject: [PATCH] proptesting --- Cargo.toml | 2 +- benches/bencode_bench.rs | 9 ++-- src/{error.rs => errors.rs} | 12 ++--- src/lib.rs | 88 ++++++++++++++++++++++++++++++++++--- 4 files changed, 93 insertions(+), 18 deletions(-) rename src/{error.rs => errors.rs} (80%) diff --git a/Cargo.toml b/Cargo.toml index afb85fc..9b3b095 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nom_bencode" -version = "0.1.4" +version = "0.2.0" authors = ["Edgar "] description = "A bencode parser written with nom." repository = "https://github.com/edg-l/nom-bencode/" diff --git a/benches/bencode_bench.rs b/benches/bencode_bench.rs index 58a6c7f..2ee15cf 100644 --- a/benches/bencode_bench.rs +++ b/benches/bencode_bench.rs @@ -1,10 +1,11 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use nom_bencode::*; -static SOURCE_BYTES_22KB: &'static [u8] = include_bytes!("../test-assets/big-buck-bunny.torrent"); -static SOURCE_BYTES_113KB: &'static [u8] = include_bytes!("../test-assets/private.torrent"); -static SOURCE_BYTES_218KB: &'static [u8] = include_bytes!("../test-assets/multi-file.torrent"); +static SOURCE_BYTES_22KB: &[u8] = include_bytes!("../test-assets/big-buck-bunny.torrent"); +static SOURCE_BYTES_113KB: &[u8] = include_bytes!("../test-assets/private.torrent"); +static SOURCE_BYTES_218KB: &[u8] = include_bytes!("../test-assets/multi-file.torrent"); -fn parse_source(src: &[u8]) -> Result, nom_bencode::Error<&[u8]>> { +fn parse_source(src: &[u8]) -> Result, Err>> { nom_bencode::parse(src) } diff --git a/src/error.rs b/src/errors.rs similarity index 80% rename from src/error.rs rename to src/errors.rs index 1568c74..497f7d1 100644 --- a/src/error.rs +++ b/src/errors.rs @@ -1,11 +1,12 @@ -use nom::{ - error::{ParseError, ErrorKind}, -}; +//! Errors produced by this library. + +use nom::error::{ErrorKind, ParseError}; use std::{fmt::Debug, num::ParseIntError}; /// Parser Errors. #[derive(Debug, thiserror::Error)] pub enum BencodeError { + /// A error from a nom parser. #[error("a nom error: {1:?}")] Nom(I, ErrorKind), /// A integer has an invalid form, e.g -0. @@ -29,12 +30,11 @@ impl ParseError for BencodeError { } } - impl From> for nom::Err> { fn from(value: BencodeError) -> Self { match value { - value @ BencodeError::Nom(_, _) => nom::Err::Error(value), - value => nom::Err::Failure(value), + value @ BencodeError::Nom(_, _) => Self::Error(value), + value => Self::Failure(value), } } } diff --git a/src/lib.rs b/src/lib.rs index c5d7fb3..284cbb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,16 +25,14 @@ //! } //! ``` -/* #![forbid(unsafe_code)] #![deny(missing_docs)] #![deny(warnings)] #![deny(clippy::nursery)] #![deny(clippy::pedantic)] #![deny(clippy::all)] - */ -use error::BencodeError; +pub use errors::BencodeError; use nom::{ branch::alt, bytes::complete::take, @@ -42,11 +40,12 @@ use nom::{ combinator::{eof, recognize}, multi::{many0, many_till}, sequence::{delimited, pair, preceded}, - Err, IResult, + IResult, }; use std::{collections::HashMap, fmt::Debug}; -pub mod error; +pub mod errors; +pub use nom::Err; type BenResult<'a> = IResult<&'a [u8], Value<'a>, BencodeError<&'a [u8]>>; @@ -179,7 +178,7 @@ pub fn parse(source: &[u8]) -> Result, Err>> { mod tests { use crate::{parse, BencodeError, Value}; use assert_matches::assert_matches; - use proptest::prelude::*; + use proptest::{collection::vec, prelude::*}; #[test] fn test_integer() { @@ -379,10 +378,85 @@ mod tests { let _ = parse(include_bytes!("../test-assets/multi-file.torrent")).unwrap(); } + prop_compose! { + fn bencode_bytes()(s in vec(any::(), 1..100)) -> Vec { + let mut data: Vec = Vec::with_capacity(s.len() + 5); + data.extend(format!("{}:", s.len()).as_bytes()); + data.extend(s); + data + } + } + + prop_compose! { + fn bencode_integer()(s in any::()) -> Vec { + format!("i{s}e").as_bytes().to_vec() + } + } + + prop_compose! { + fn bencode_list()(s in vec((bencode_integer(), bencode_bytes()), 1..100)) -> Vec { + let mut data: Vec = Vec::with_capacity(s.len() + 2); + data.extend(b"l"); + for (i, (a, b)) in s.iter().enumerate() { + if i % 2 == 0 { + data.extend(a); + data.extend(b); + } else { + data.extend(b); + data.extend(a); + } + + } + data.extend(b"e"); + data + } + } + + prop_compose! { + fn bencode_dict()(s in vec((bencode_bytes(), bencode_bytes()), 1..100)) -> Vec { + let mut data: Vec = Vec::with_capacity(s.len() + 2); + data.extend(b"d"); + for (i, (a, b)) in s.iter().enumerate() { + if i % 2 == 0 { + data.extend(a); + data.extend(b); + } else { + data.extend(b); + data.extend(a); + } + } + data.extend(b"e"); + data + } + } + proptest! { #[test] - fn doesnt_panic(s in any::>()) { + fn proptest_doesnt_panic_or_overflow(s in any::>()) { parse(&s).ok(); } + + #[test] + fn proptest_parse_integer(s in bencode_integer()) { + prop_assert!(Value::parse_integer(&s).is_ok()); + } + + #[test] + fn proptest_parse_bytes(s in bencode_bytes()) { + let mut data: Vec = Vec::with_capacity(s.len() + 5); + data.extend(format!("{}:", s.len()).as_bytes()); + data.extend(s); + prop_assert!(Value::parse_bytes(&data).is_ok()); + } + + #[test] + fn proptest_parse_list(s in bencode_list()) { + prop_assert!(Value::parse_list(&s).is_ok()); + } + + #[test] + fn proptest_parse_dict(s in bencode_dict()) { + prop_assert!(Value::parse_dict(&s).is_ok()); + } } }