Skip to content

Commit 462612f

Browse files
Merge pull request #15 from triblespace/codex/extend-tests-for-weakbytes-and-weakview
Add weak reference tests and view Kani proofs
2 parents e2f73f2 + 50c907c commit 462612f

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- move weak reference and downcasting examples into module docs
77
- expand module introduction describing use cases
88
- document rationale for separating `ByteSource` and `ByteOwner`
9+
- add tests for weak reference upgrade/downgrade and Kani proofs for view helpers
910
- add examples for quick start and PyBytes usage
1011
- add example showing how to wrap Python `bytes` into `Bytes`
1112
- summarize built-in `ByteSource`s and show how to extend them

src/tests.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,41 @@ fn test_slice_to_bytes_unrelated_slice() {
7676
let slice = &other[1..4];
7777
assert!(bytes.slice_to_bytes(slice).is_none());
7878
}
79+
80+
#[test]
81+
fn test_weakbytes_multiple_upgrades() {
82+
let bytes = Bytes::from(b"hello".to_vec());
83+
let weak = bytes.downgrade();
84+
85+
// Upgrade works while strong reference exists
86+
let strong1 = weak.upgrade().unwrap();
87+
assert_eq!(strong1.as_ref(), bytes.as_ref());
88+
drop(strong1);
89+
90+
// Can upgrade multiple times
91+
let strong2 = weak.upgrade().unwrap();
92+
assert_eq!(strong2.as_ref(), b"hello".as_ref());
93+
94+
drop(bytes);
95+
drop(strong2);
96+
97+
// After all strong refs are gone, upgrade returns None
98+
assert!(weak.upgrade().is_none());
99+
}
100+
101+
#[cfg(feature = "zerocopy")]
102+
#[test]
103+
fn test_weakview_downgrade_upgrade() {
104+
let bytes = Bytes::from(b"abcdef".to_vec());
105+
let view = bytes.clone().view::<[u8]>().unwrap();
106+
107+
let weak = view.downgrade();
108+
let strong = weak.upgrade().unwrap();
109+
assert_eq!(strong.as_ref(), view.as_ref());
110+
111+
drop(bytes);
112+
drop(view);
113+
drop(strong);
114+
115+
assert!(weak.upgrade().is_none());
116+
}

src/view.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,3 +442,66 @@ mod tests {
442442
assert_eq!(&bytes[..], [1u8, 2, 3].as_slice());
443443
}
444444
}
445+
446+
#[cfg(kani)]
447+
mod verification {
448+
use super::*;
449+
use kani::BoundedArbitrary;
450+
451+
#[kani::proof]
452+
#[kani::unwind(16)]
453+
pub fn check_view_prefix_ok() {
454+
let data: Vec<u8> = Vec::bounded_any::<16>();
455+
kani::assume(data.len() >= 4);
456+
let mut bytes = Bytes::from_source(data.clone());
457+
let original = bytes.clone();
458+
let view = bytes.view_prefix::<[u8; 4]>().expect("prefix exists");
459+
let expected: [u8; 4] = original.as_ref()[..4].try_into().unwrap();
460+
assert_eq!(*view, expected);
461+
assert_eq!(bytes.as_ref(), &original.as_ref()[4..]);
462+
}
463+
464+
#[kani::proof]
465+
#[kani::unwind(16)]
466+
pub fn check_view_suffix_ok() {
467+
let data: Vec<u8> = Vec::bounded_any::<16>();
468+
kani::assume(data.len() >= 4);
469+
let mut bytes = Bytes::from_source(data.clone());
470+
let original = bytes.clone();
471+
let view = bytes.view_suffix::<[u8; 4]>().expect("suffix exists");
472+
let start = original.len() - 4;
473+
let expected: [u8; 4] = original.as_ref()[start..].try_into().unwrap();
474+
assert_eq!(*view, expected);
475+
assert_eq!(bytes.as_ref(), &original.as_ref()[..start]);
476+
}
477+
478+
#[derive(
479+
zerocopy::TryFromBytes,
480+
zerocopy::IntoBytes,
481+
zerocopy::KnownLayout,
482+
zerocopy::Immutable,
483+
Clone,
484+
Copy,
485+
)]
486+
#[repr(C)]
487+
struct Pair {
488+
a: u32,
489+
b: u32,
490+
}
491+
492+
#[kani::proof]
493+
#[kani::unwind(8)]
494+
pub fn check_field_to_view_ok() {
495+
let value = Pair {
496+
a: kani::any(),
497+
b: kani::any(),
498+
};
499+
let bytes = Bytes::from_source(Box::new(value));
500+
let view = bytes.view::<Pair>().unwrap();
501+
let field = view.field_to_view(&view.a).expect("field view");
502+
assert_eq!(*field, view.a);
503+
504+
let other: u32 = kani::any();
505+
assert!(view.field_to_view(&other).is_none());
506+
}
507+
}

0 commit comments

Comments
 (0)