protocol/wire/test: Define `fn roundtrip` and macro
In many tests we assert that encoding and decoding gives the same object. Let's call that a "roundrip", and also provide a proc macro to generate such tests.
This commit is contained in:
parent
bbd1e2cff6
commit
ded0d19d59
|
|
@ -2403,6 +2403,12 @@ dependencies = [
|
|||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
|
|
@ -2893,6 +2899,7 @@ dependencies = [
|
|||
"localtime",
|
||||
"log",
|
||||
"nonempty 0.9.0",
|
||||
"paste",
|
||||
"qcheck",
|
||||
"qcheck-macros",
|
||||
"radicle",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ serde_json = { workspace = true, features = ["preserve_order"] }
|
|||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
paste = "1.0.15"
|
||||
qcheck = { workspace = true }
|
||||
qcheck-macros = { workspace = true }
|
||||
radicle = { workspace = true, features = ["test"] }
|
||||
|
|
|
|||
|
|
@ -560,9 +560,37 @@ impl Decode for Timestamp {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn roundtrip<T>(value: T)
|
||||
where
|
||||
T: Encode + Decode + PartialEq + Debug,
|
||||
{
|
||||
let encoded = value.encode_to_vec();
|
||||
assert_eq!(T::decode_exact(&encoded).expect("roundtrip"), value);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_export]
|
||||
macro_rules! prop_roundtrip {
|
||||
($t:ty, $name:tt) => {
|
||||
paste::paste! {
|
||||
#[quickcheck]
|
||||
fn [< prop_roundtrip_ $name:lower >](v: $t) {
|
||||
$crate::wire::roundtrip(v);
|
||||
}
|
||||
}
|
||||
};
|
||||
($t:ty) => {
|
||||
paste::paste! {
|
||||
prop_roundtrip!($t, [< $t >]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use qcheck;
|
||||
use qcheck_macros::quickcheck;
|
||||
|
||||
|
|
@ -570,101 +598,36 @@ mod tests {
|
|||
use radicle::crypto::Unverified;
|
||||
use radicle::storage::refs::SignedRefs;
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_u8(input: u8) {
|
||||
assert_eq!(u8::decode_exact(&input.encode_to_vec()).unwrap(), input);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_u16(input: u16) {
|
||||
assert_eq!(u16::decode_exact(&input.encode_to_vec()).unwrap(), input);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_u32(input: u32) {
|
||||
assert_eq!(u32::decode_exact(&input.encode_to_vec()).unwrap(), input);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_u64(input: u64) {
|
||||
assert_eq!(u64::decode_exact(&input.encode_to_vec()).unwrap(), input);
|
||||
}
|
||||
prop_roundtrip!(u16);
|
||||
prop_roundtrip!(u32);
|
||||
prop_roundtrip!(u64);
|
||||
prop_roundtrip!(BoundedVec<u8, 16>, vec);
|
||||
prop_roundtrip!(PublicKey);
|
||||
prop_roundtrip!(filter::Filter, filter);
|
||||
prop_roundtrip!(RepoId);
|
||||
prop_roundtrip!(Refs);
|
||||
prop_roundtrip!((String, String), tuple);
|
||||
prop_roundtrip!(SignedRefs<Unverified>, signed_refs);
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_string(input: String) -> qcheck::TestResult {
|
||||
if input.len() > u8::MAX as usize {
|
||||
return qcheck::TestResult::discard();
|
||||
}
|
||||
assert_eq!(String::decode_exact(&input.encode_to_vec()).unwrap(), input);
|
||||
|
||||
roundtrip(input);
|
||||
|
||||
qcheck::TestResult::passed()
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_vec(input: BoundedVec<String, 16>) {
|
||||
assert_eq!(
|
||||
BoundedVec::<String, 16>::decode_exact(&input.encode_to_vec()).unwrap(),
|
||||
input
|
||||
);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_pubkey(input: PublicKey) {
|
||||
assert_eq!(
|
||||
PublicKey::decode_exact(&input.encode_to_vec()).unwrap(),
|
||||
input
|
||||
);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_filter(input: filter::Filter) {
|
||||
assert_eq!(
|
||||
filter::Filter::decode_exact(&input.encode_to_vec()).unwrap(),
|
||||
input
|
||||
);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_id(input: RepoId) {
|
||||
assert_eq!(RepoId::decode_exact(&input.encode_to_vec()).unwrap(), input);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_refs(input: Refs) {
|
||||
assert_eq!(Refs::decode_exact(&input.encode_to_vec()).unwrap(), input);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_tuple(input: (String, String)) {
|
||||
assert_eq!(
|
||||
<(String, String)>::decode_exact(&input.encode_to_vec()).unwrap(),
|
||||
input
|
||||
);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_signature(input: [u8; 64]) {
|
||||
let signature = Signature::from(input);
|
||||
|
||||
assert_eq!(
|
||||
Signature::decode_exact(&signature.encode_to_vec()).unwrap(),
|
||||
signature
|
||||
);
|
||||
roundtrip(Signature::from(input));
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_oid(input: [u8; 20]) {
|
||||
let oid = git::Oid::try_from(input.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(git::Oid::decode_exact(&oid.encode_to_vec()).unwrap(), oid);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_signed_refs(input: SignedRefs<Unverified>) {
|
||||
assert_eq!(
|
||||
SignedRefs::<Unverified>::decode_exact(&input.encode_to_vec()).unwrap(),
|
||||
input
|
||||
);
|
||||
roundtrip(git::Oid::try_from(input.as_slice()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -425,15 +425,20 @@ impl wire::Decode for ZeroBytes {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use qcheck_macros::quickcheck;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::UserAgent;
|
||||
use radicle::storage::refs::RefsAt;
|
||||
use radicle::test::arbitrary;
|
||||
|
||||
use crate::deserializer::Deserializer;
|
||||
use crate::wire::{self, Decode, Encode};
|
||||
use radicle::test::arbitrary;
|
||||
use crate::prop_roundtrip;
|
||||
use crate::wire::{roundtrip, Encode as _};
|
||||
|
||||
use super::*;
|
||||
|
||||
prop_roundtrip!(Address);
|
||||
prop_roundtrip!(Message);
|
||||
|
||||
#[test]
|
||||
fn test_refs_ann_max_size() {
|
||||
|
|
@ -520,14 +525,6 @@ mod tests {
|
|||
.encode_to_vec();
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_message_encode_decode(message: Message) {
|
||||
let encoded = message.encode_to_vec();
|
||||
let decoded = Message::decode_exact(&encoded).unwrap();
|
||||
|
||||
assert_eq!(message, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prop_message_decoder() {
|
||||
fn property(items: Vec<Message>) {
|
||||
|
|
@ -546,28 +543,14 @@ mod tests {
|
|||
.quickcheck(property as fn(items: Vec<Message>));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prop_zero_bytes_encode_decode() {
|
||||
fn property(zeroes: wire::Size) {
|
||||
if zeroes > Ping::MAX_PING_ZEROES {
|
||||
return;
|
||||
}
|
||||
|
||||
let zeroes = ZeroBytes::new(zeroes);
|
||||
|
||||
assert_eq!(
|
||||
ZeroBytes::decode_exact(&zeroes.encode_to_vec()).unwrap(),
|
||||
zeroes
|
||||
);
|
||||
#[quickcheck]
|
||||
fn prop_zero_bytes_encode_decode(zeroes: wire::Size) -> qcheck::TestResult {
|
||||
if zeroes > Ping::MAX_PING_ZEROES {
|
||||
return qcheck::TestResult::discard();
|
||||
}
|
||||
|
||||
qcheck::QuickCheck::new()
|
||||
.gen(qcheck::Gen::new(16))
|
||||
.quickcheck(property as fn(zeroes: wire::Size));
|
||||
}
|
||||
roundtrip(ZeroBytes::new(zeroes));
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_addr(addr: Address) {
|
||||
assert_eq!(Address::decode_exact(&addr.encode_to_vec()).unwrap(), addr);
|
||||
qcheck::TestResult::passed()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,9 +175,14 @@ pub mod payload {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use qcheck_macros::quickcheck;
|
||||
|
||||
use crate::prop_roundtrip;
|
||||
|
||||
use super::*;
|
||||
|
||||
prop_roundtrip!(VarInt);
|
||||
|
||||
impl qcheck::Arbitrary for VarInt {
|
||||
fn arbitrary(g: &mut qcheck::Gen) -> Self {
|
||||
let a = u16::arbitrary(g) as u64;
|
||||
|
|
@ -211,16 +216,8 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_encode_decode(input: VarInt) {
|
||||
let encoded = input.encode_to_vec();
|
||||
let decoded: VarInt = VarInt::decode_exact(&encoded).unwrap();
|
||||
|
||||
assert_eq!(decoded, input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "overflow")]
|
||||
fn test_encode_overflow() {
|
||||
VarInt(u64::MAX).encode_to_vec();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue