From 1b2b4f0eb5114636ed01b366f349cc976a1d6dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Sun, 9 Aug 2020 01:19:47 +0200 Subject: [PATCH] aquatic_http_protocol: add test for Request serde, fix Request.write bug --- aquatic_http_protocol/src/common.rs | 36 +++++++++++++-- aquatic_http_protocol/src/request.rs | 69 +++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 6 deletions(-) diff --git a/aquatic_http_protocol/src/common.rs b/aquatic_http_protocol/src/common.rs index 125ee46..a0d5d4a 100644 --- a/aquatic_http_protocol/src/common.rs +++ b/aquatic_http_protocol/src/common.rs @@ -61,13 +61,39 @@ impl FromStr for AnnounceEvent { #[cfg(test)] impl quickcheck::Arbitrary for InfoHash { fn arbitrary(g: &mut G) -> Self { - let mut arr = [b'x'; 20]; + let mut arr = [b'0'; 20]; - arr[0] = u8::arbitrary(g); - arr[1] = u8::arbitrary(g); - arr[18] = u8::arbitrary(g); - arr[19] = u8::arbitrary(g); + for byte in arr.iter_mut(){ + *byte = u8::arbitrary(g); + } Self(arr) } +} + + +#[cfg(test)] +impl quickcheck::Arbitrary for PeerId { + fn arbitrary(g: &mut G) -> Self { + let mut arr = [b'0'; 20]; + + for byte in arr.iter_mut(){ + *byte = u8::arbitrary(g); + } + + Self(arr) + } +} + + +#[cfg(test)] +impl quickcheck::Arbitrary for AnnounceEvent { + fn arbitrary(g: &mut G) -> Self { + match (bool::arbitrary(g), bool::arbitrary(g)){ + (false, false) => Self::Started, + (true, false) => Self::Started, + (false, true) => Self::Completed, + (true, true) => Self::Empty, + } + } } \ No newline at end of file diff --git a/aquatic_http_protocol/src/request.rs b/aquatic_http_protocol/src/request.rs index edbbaab..c7e51cb 100644 --- a/aquatic_http_protocol/src/request.rs +++ b/aquatic_http_protocol/src/request.rs @@ -27,7 +27,7 @@ impl AnnounceRequest { urlencode_20_bytes(self.info_hash.0, output)?; output.write_all(b"&peer_id=")?; - urlencode_20_bytes(self.info_hash.0, output)?; + urlencode_20_bytes(self.peer_id.0, output)?; output.write_all(b"&port=")?; output.write_all(itoa::Buffer::new().format(self.port).as_bytes())?; @@ -269,6 +269,8 @@ impl Request { #[cfg(test)] mod tests { + use quickcheck::{Arbitrary, Gen, TestResult, quickcheck}; + use super::*; static ANNOUNCE_REQUEST_PATH: &str = "/announce?info_hash=%04%0bkV%3f%5cr%14%a6%b7%98%adC%c3%c9.%40%24%00%b9&peer_id=-ABC940-5ert69muw5t8&port=12345&uploaded=0&downloaded=0&left=1&numwant=0&key=4ab4b877&compact=1&supportcrypto=1&event=started"; @@ -318,4 +320,69 @@ mod tests { assert_eq!(parsed_request, reference_request); } + + impl Arbitrary for AnnounceRequest { + fn arbitrary(g: &mut G) -> Self { + let key: Option = Arbitrary::arbitrary(g); + + AnnounceRequest { + info_hash: Arbitrary::arbitrary(g), + peer_id: Arbitrary::arbitrary(g), + port: Arbitrary::arbitrary(g), + bytes_left: Arbitrary::arbitrary(g), + event: Arbitrary::arbitrary(g), + compact: true, + numwant: Arbitrary::arbitrary(g), + key: key.map(|key| key.into()), + } + } + } + + impl Arbitrary for ScrapeRequest { + fn arbitrary(g: &mut G) -> Self { + ScrapeRequest { + info_hashes: Arbitrary::arbitrary(g), + } + } + } + + impl Arbitrary for Request { + fn arbitrary(g: &mut G) -> Self { + if Arbitrary::arbitrary(g){ + Self::Announce(Arbitrary::arbitrary(g)) + } else { + Self::Scrape(Arbitrary::arbitrary(g)) + } + } + } + + #[test] + fn quickcheck_serde_identity_request(){ + fn prop(request: Request) -> TestResult { + if let Request::Announce(AnnounceRequest { key: Some(ref key), ..}) = request { + if !key.is_ascii(){ + // Only ascii allowed in headers + return TestResult::discard(); + } + } + + let mut bytes = Vec::new(); + + request.write(&mut bytes).unwrap(); + + let parsed_request = Request::from_bytes(&bytes[..]).unwrap(); + + let success = request == parsed_request; + + if !success { + println!("request: {:?}", request); + println!("parsed request: {:?}", parsed_request); + println!("bytes as str: {}", String::from_utf8_lossy(&bytes)); + } + + TestResult::from_bool(success) + } + + quickcheck(prop as fn(Request) -> TestResult); + } } \ No newline at end of file