Merge pull request #117 from Lochnair/test/proto-properties
test(proto): add codec property tests
This commit was merged in pull request #117.
This commit is contained in:
4 files changed
+536
-4
No files matched your search
Generated
+158
-4
@@ -109,7 +109,16 @@ version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"bit-vec 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -118,6 +127,12 @@ version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -516,10 +531,16 @@ version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-set 0.5.3",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||
|
||||
[[package]]
|
||||
name = "filedescriptor"
|
||||
version = "0.8.3"
|
||||
@@ -735,6 +756,7 @@ version = "0.5.0"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"md-5",
|
||||
"proptest",
|
||||
"subtle",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
@@ -1161,7 +1183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
"rand 0.8.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1204,6 +1226,15 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
@@ -1213,6 +1244,31 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
|
||||
dependencies = [
|
||||
"bit-set 0.8.0",
|
||||
"bit-vec 0.8.0",
|
||||
"bitflags 2.13.1",
|
||||
"num-traits",
|
||||
"rand 0.9.5",
|
||||
"rand_chacha",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
"rusty-fork",
|
||||
"tempfile",
|
||||
"unarray",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
@@ -1240,7 +1296,27 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1249,6 +1325,24 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||
dependencies = [
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.30.2"
|
||||
@@ -1416,6 +1510,18 @@ version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||
|
||||
[[package]]
|
||||
name = "rusty-fork"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"quick-error",
|
||||
"tempfile",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
@@ -1606,6 +1712,19 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termina"
|
||||
version = "0.3.3"
|
||||
@@ -1780,6 +1899,12 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
@@ -1842,6 +1967,15 @@ dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
@@ -2016,3 +2150,23 @@ name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
@@ -16,3 +16,6 @@ hmac = "0.13"
|
||||
md-5 = "0.11"
|
||||
subtle = "2.6.1"
|
||||
thiserror = "2.0.20"
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "1.11.0"
|
||||
@@ -764,4 +764,340 @@ mod tests {
|
||||
let encoded = [99];
|
||||
assert_eq!(Params::decode(&encoded), Err(ProtoError::TruncatedVarint));
|
||||
}
|
||||
|
||||
mod properties {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
fn received_stats_strategy() -> impl Strategy<Value = ReceivedStats> {
|
||||
prop_oneof![
|
||||
Just(ReceivedStats::None),
|
||||
Just(ReceivedStats::Count),
|
||||
Just(ReceivedStats::Window),
|
||||
Just(ReceivedStats::Both),
|
||||
]
|
||||
}
|
||||
|
||||
fn stamp_at_strategy() -> impl Strategy<Value = StampAt> {
|
||||
prop_oneof![
|
||||
Just(StampAt::None),
|
||||
Just(StampAt::Send),
|
||||
Just(StampAt::Receive),
|
||||
Just(StampAt::Both),
|
||||
Just(StampAt::Midpoint),
|
||||
]
|
||||
}
|
||||
|
||||
/// A `Clock` valid for direct construction, including `Unspecified`
|
||||
/// (wire omission). Never produces an explicit zero on the wire; see
|
||||
/// `explicit_clock_strategy` for that.
|
||||
fn clock_strategy() -> impl Strategy<Value = Clock> {
|
||||
prop_oneof![
|
||||
Just(Clock::Unspecified),
|
||||
Just(Clock::Wall),
|
||||
Just(Clock::Monotonic),
|
||||
Just(Clock::Both),
|
||||
]
|
||||
}
|
||||
|
||||
/// A `Clock` value valid to encode *explicitly* on the wire (tag 7).
|
||||
/// Excludes `Unspecified`/zero, which is only ever reached by omission.
|
||||
fn explicit_clock_value_strategy() -> impl Strategy<Value = i64> {
|
||||
1_i64..=3
|
||||
}
|
||||
|
||||
fn server_fill_strategy() -> impl Strategy<Value = Option<ServerFill>> {
|
||||
prop_oneof![
|
||||
Just(None),
|
||||
proptest::collection::vec(proptest::char::any(), 0..=8).prop_map(|chars| Some(
|
||||
ServerFill {
|
||||
value: chars.into_iter().collect(),
|
||||
}
|
||||
)),
|
||||
]
|
||||
}
|
||||
|
||||
fn params_strategy() -> impl Strategy<Value = Params> {
|
||||
(
|
||||
any::<i64>(),
|
||||
any::<i64>(),
|
||||
any::<i64>(),
|
||||
any::<i64>(),
|
||||
any::<i64>(),
|
||||
received_stats_strategy(),
|
||||
stamp_at_strategy(),
|
||||
clock_strategy(),
|
||||
server_fill_strategy(),
|
||||
)
|
||||
.prop_map(
|
||||
|(
|
||||
protocol_version,
|
||||
duration_ns,
|
||||
interval_ns,
|
||||
length,
|
||||
dscp,
|
||||
received_stats,
|
||||
stamp_at,
|
||||
clock,
|
||||
server_fill,
|
||||
)| Params {
|
||||
protocol_version,
|
||||
duration_ns,
|
||||
interval_ns,
|
||||
length,
|
||||
received_stats,
|
||||
stamp_at,
|
||||
clock,
|
||||
dscp,
|
||||
server_fill,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn encode_tagged_int(tag: u64, value: i64, out: &mut Vec<u8>) {
|
||||
varint::encode_uvarint(tag, out);
|
||||
varint::encode_varint(value, out);
|
||||
}
|
||||
|
||||
fn encode_tagged_server_fill(value: &str, out: &mut Vec<u8>) {
|
||||
varint::encode_uvarint(9, out);
|
||||
varint::encode_uvarint(value.len() as u64, out);
|
||||
out.extend_from_slice(value.as_bytes());
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn valid_params_round_trip(params in params_strategy()) {
|
||||
let encoded = params.encode();
|
||||
let decoded = Params::decode(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded, params);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoded_param_presence_matches_omission_rules(params in params_strategy()) {
|
||||
let encoded = params.encode();
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(&decoded.params, ¶ms);
|
||||
|
||||
prop_assert_eq!(decoded.presence.protocol_version, params.protocol_version != 0);
|
||||
prop_assert_eq!(decoded.presence.duration_ns, params.duration_ns != 0);
|
||||
prop_assert_eq!(decoded.presence.interval_ns, params.interval_ns != 0);
|
||||
prop_assert_eq!(decoded.presence.length, params.length != 0);
|
||||
prop_assert_eq!(decoded.presence.dscp, params.dscp != 0);
|
||||
prop_assert_eq!(
|
||||
decoded.presence.received_stats,
|
||||
params.received_stats != ReceivedStats::None
|
||||
);
|
||||
prop_assert_eq!(decoded.presence.stamp_at, params.stamp_at != StampAt::None);
|
||||
prop_assert_eq!(decoded.presence.clock, params.clock != Clock::Unspecified);
|
||||
prop_assert_eq!(decoded.presence.server_fill, params.server_fill.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_protocol_version_is_last_wins(first: i64, second: i64) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(1, first, &mut encoded);
|
||||
encode_tagged_int(1, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.protocol_version, second);
|
||||
prop_assert!(decoded.presence.protocol_version);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_duration_ns_is_last_wins(first: i64, second: i64) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(2, first, &mut encoded);
|
||||
encode_tagged_int(2, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.duration_ns, second);
|
||||
prop_assert!(decoded.presence.duration_ns);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_interval_ns_is_last_wins(first: i64, second: i64) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(3, first, &mut encoded);
|
||||
encode_tagged_int(3, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.interval_ns, second);
|
||||
prop_assert!(decoded.presence.interval_ns);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_length_is_last_wins(first: i64, second: i64) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(4, first, &mut encoded);
|
||||
encode_tagged_int(4, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.length, second);
|
||||
prop_assert!(decoded.presence.length);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_received_stats_is_last_wins(first in 0_i64..=3, second in 0_i64..=3) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(5, first, &mut encoded);
|
||||
encode_tagged_int(5, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.received_stats, ReceivedStats::try_from(second).unwrap());
|
||||
prop_assert!(decoded.presence.received_stats);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_stamp_at_is_last_wins(first in 0_i64..=4, second in 0_i64..=4) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(6, first, &mut encoded);
|
||||
encode_tagged_int(6, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.stamp_at, StampAt::try_from(second).unwrap());
|
||||
prop_assert!(decoded.presence.stamp_at);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_clock_is_last_wins(
|
||||
first in explicit_clock_value_strategy(),
|
||||
second in explicit_clock_value_strategy(),
|
||||
) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(7, first, &mut encoded);
|
||||
encode_tagged_int(7, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.clock, Clock::try_from(second).unwrap());
|
||||
prop_assert!(decoded.presence.clock);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_dscp_is_last_wins(first: i64, second: i64) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_int(8, first, &mut encoded);
|
||||
encode_tagged_int(8, second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded.params.dscp, second);
|
||||
prop_assert!(decoded.presence.dscp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_server_fill_is_last_wins(
|
||||
first in proptest::collection::vec(proptest::char::any(), 0..=8)
|
||||
.prop_map(|chars| chars.into_iter().collect::<String>()),
|
||||
second in proptest::collection::vec(proptest::char::any(), 0..=8)
|
||||
.prop_map(|chars| chars.into_iter().collect::<String>()),
|
||||
) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_tagged_server_fill(&first, &mut encoded);
|
||||
encode_tagged_server_fill(&second, &mut encoded);
|
||||
let decoded = Params::decode_with_presence(&encoded).unwrap();
|
||||
prop_assert_eq!(
|
||||
decoded.params.server_fill,
|
||||
Some(ServerFill { value: second })
|
||||
);
|
||||
prop_assert!(decoded.presence.server_fill);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_scalar_tag_prefix_does_not_disturb_known_params(
|
||||
params in params_strategy(),
|
||||
unknown_tag in 10_u64..=u16::MAX as u64,
|
||||
unknown_value: u64,
|
||||
) {
|
||||
let mut prefixed = Vec::new();
|
||||
varint::encode_uvarint(unknown_tag, &mut prefixed);
|
||||
varint::encode_uvarint(unknown_value, &mut prefixed);
|
||||
prefixed.extend_from_slice(¶ms.encode());
|
||||
|
||||
let decoded = Params::decode(&prefixed).unwrap();
|
||||
prop_assert_eq!(decoded, params.clone());
|
||||
|
||||
let decoded_with_presence = Params::decode_with_presence(&prefixed).unwrap();
|
||||
let expected_presence = Params::decode_with_presence(¶ms.encode()).unwrap().presence;
|
||||
prop_assert_eq!(decoded_with_presence.presence, expected_presence);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_scalar_tag_suffix_does_not_disturb_known_params(
|
||||
params in params_strategy(),
|
||||
unknown_tag in 10_u64..=u16::MAX as u64,
|
||||
unknown_value: u64,
|
||||
) {
|
||||
let mut suffixed = params.encode();
|
||||
varint::encode_uvarint(unknown_tag, &mut suffixed);
|
||||
varint::encode_uvarint(unknown_value, &mut suffixed);
|
||||
|
||||
let decoded = Params::decode(&suffixed).unwrap();
|
||||
prop_assert_eq!(decoded, params.clone());
|
||||
|
||||
let decoded_with_presence = Params::decode_with_presence(&suffixed).unwrap();
|
||||
let expected_presence = Params::decode_with_presence(¶ms.encode()).unwrap().presence;
|
||||
prop_assert_eq!(decoded_with_presence.presence, expected_presence);
|
||||
}
|
||||
}
|
||||
|
||||
/// Omitted-vs-explicit-zero presence, for the fields where explicit
|
||||
/// zero is valid on the wire. `Clock` is deliberately excluded: an
|
||||
/// explicit zero there is rejected outright, not merely "present with
|
||||
/// the default value" (see `explicit_clock_zero_is_rejected`).
|
||||
#[test]
|
||||
fn omitted_vs_explicit_zero_presence_for_zero_valid_fields() {
|
||||
let scalar_tags = [1_u64, 2, 3, 4, 8];
|
||||
for tag in scalar_tags {
|
||||
let omitted = Params::decode_with_presence(&[]).unwrap();
|
||||
let mut explicit_zero = Vec::new();
|
||||
encode_tagged_int(tag, 0, &mut explicit_zero);
|
||||
let explicit = Params::decode_with_presence(&explicit_zero).unwrap();
|
||||
|
||||
assert_eq!(omitted.params, explicit.params);
|
||||
assert_eq!(explicit.params, Params::default());
|
||||
|
||||
let (omitted_presence, explicit_presence) = match tag {
|
||||
1 => (
|
||||
omitted.presence.protocol_version,
|
||||
explicit.presence.protocol_version,
|
||||
),
|
||||
2 => (omitted.presence.duration_ns, explicit.presence.duration_ns),
|
||||
3 => (omitted.presence.interval_ns, explicit.presence.interval_ns),
|
||||
4 => (omitted.presence.length, explicit.presence.length),
|
||||
8 => (omitted.presence.dscp, explicit.presence.dscp),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert!(!omitted_presence);
|
||||
assert!(explicit_presence);
|
||||
}
|
||||
|
||||
let enum_tags = [5_u64, 6];
|
||||
for tag in enum_tags {
|
||||
let omitted = Params::decode_with_presence(&[]).unwrap();
|
||||
let mut explicit_zero = Vec::new();
|
||||
encode_tagged_int(tag, 0, &mut explicit_zero);
|
||||
let explicit = Params::decode_with_presence(&explicit_zero).unwrap();
|
||||
|
||||
assert_eq!(omitted.params, explicit.params);
|
||||
|
||||
let (omitted_presence, explicit_presence) = match tag {
|
||||
5 => (
|
||||
omitted.presence.received_stats,
|
||||
explicit.presence.received_stats,
|
||||
),
|
||||
6 => (omitted.presence.stamp_at, explicit.presence.stamp_at),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert!(!omitted_presence);
|
||||
assert!(explicit_presence);
|
||||
}
|
||||
|
||||
// `Clock` deliberately breaks the pattern above: explicit zero is
|
||||
// rejected rather than accepted-but-present.
|
||||
let omitted = Params::decode_with_presence(&[]).unwrap();
|
||||
assert_eq!(omitted.params.clock, Clock::Unspecified);
|
||||
assert!(!omitted.presence.clock);
|
||||
let mut explicit_clock_zero = Vec::new();
|
||||
encode_tagged_int(7, 0, &mut explicit_clock_zero);
|
||||
assert_eq!(
|
||||
Params::decode_with_presence(&explicit_clock_zero),
|
||||
Err(ProtoError::InvalidEnum {
|
||||
name: "Clock",
|
||||
value: 0,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,4 +112,43 @@ mod tests {
|
||||
encode_uvarint(24, &mut tag);
|
||||
assert_eq!(tag, [0x18]);
|
||||
}
|
||||
|
||||
mod properties {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn uvarint_round_trips(value: u64) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_uvarint(value, &mut encoded);
|
||||
prop_assert!(!encoded.is_empty());
|
||||
prop_assert!(encoded.len() <= 10);
|
||||
let (decoded, used) = decode_uvarint(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded, value);
|
||||
prop_assert_eq!(used, encoded.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_varint_round_trips(value: i64) {
|
||||
let mut encoded = Vec::new();
|
||||
encode_varint(value, &mut encoded);
|
||||
prop_assert!(!encoded.is_empty());
|
||||
prop_assert!(encoded.len() <= 10);
|
||||
let (decoded, used) = decode_varint(&encoded).unwrap();
|
||||
prop_assert_eq!(decoded, value);
|
||||
prop_assert_eq!(used, encoded.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zigzag_round_trips_from_signed(value: i64) {
|
||||
prop_assert_eq!(zigzag_decode(zigzag_encode(value)), value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zigzag_round_trips_from_unsigned(value: u64) {
|
||||
prop_assert_eq!(zigzag_encode(zigzag_decode(value)), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user