Skip to content

Commit

Permalink
Merge pull request #88 from Anders429/unit
Browse files Browse the repository at this point in the history
Unit test the bulk of the public API.
  • Loading branch information
Anders429 authored Aug 19, 2022
2 parents 3a0a447 + b77db73 commit 473c1da
Show file tree
Hide file tree
Showing 24 changed files with 3,901 additions and 87 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ serde = {version = "1.0.143", default-features = false, features = ["alloc"], op
[dev-dependencies]
claim = "0.5.0"
rustversion = "1.0.9"
serde_derive = "1.0.143"
serde_test = "1.0.143"
# Temporarily use this fork until https://github.com/dtolnay/trybuild/issues/171 is resolved.
trybuild = {git = "https://github.com/Anders429/trybuild"}
Expand Down
98 changes: 88 additions & 10 deletions src/archetype/identifier/impl_serde.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{archetype::Identifier, registry::Registry};
use alloc::vec::Vec;
use alloc::{format, vec::Vec};
use core::{fmt, marker::PhantomData, mem::ManuallyDrop};
use serde::{
de,
Expand Down Expand Up @@ -66,15 +66,17 @@ where
}

// Check that trailing bits are not set.
// SAFETY: `buffer` is guaranteed to have `(R::LEN + 7) / 8` elements, so this will
// always be within the bounds of `buffer.`
let byte = unsafe { buffer.get_unchecked((R::LEN + 7) / 8 - 1) };
let bit = R::LEN % 8;
if bit != 0 && byte & (255 << bit) != 0 {
return Err(de::Error::invalid_value(
Unexpected::Unsigned(u64::from(*byte)),
&self,
));
if R::LEN != 0 {
// SAFETY: `buffer` is guaranteed to have `(R::LEN + 7) / 8` elements, so this will
// always be within the bounds of `buffer.`
let byte = unsafe { buffer.get_unchecked((R::LEN + 7) / 8 - 1) };
let bit = R::LEN % 8;
if bit != 0 && byte & (255 << bit) != 0 {
return Err(de::Error::invalid_value(
Unexpected::Other(&format!("byte array {:?}", &buffer)),
&self,
));
}
}

let mut buffer = ManuallyDrop::new(buffer);
Expand All @@ -96,3 +98,79 @@ where
)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::registry;
use alloc::vec;
use serde_test::{assert_de_tokens_error, assert_tokens, Token};

macro_rules! create_components {
($( $variants:ident ),*) => {
$(
struct $variants(f32);
)*
};
}

create_components!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
);

type Registry =
registry!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);

#[test]
fn serialize_deserialize() {
let identifier = unsafe { Identifier::<Registry>::new(vec![1, 2, 3, 0]) };

assert_tokens(
&identifier,
&[
Token::Tuple { len: 4 },
Token::U8(1),
Token::U8(2),
Token::U8(3),
Token::U8(0),
Token::TupleEnd,
],
);
}

#[test]
fn serialize_deserialize_empty() {
let identifier = unsafe { Identifier::<registry!()>::new(vec![]) };

assert_tokens(&identifier, &[Token::Tuple { len: 0 }, Token::TupleEnd]);
}

#[test]
fn deserialize_from_too_many_bits() {
assert_de_tokens_error::<Identifier<Registry>>(
&[
Token::Tuple { len: 4 },
Token::U8(1),
Token::U8(2),
Token::U8(3),
Token::U8(255),
Token::TupleEnd,
],
"invalid value: byte array [1, 2, 3, 255], expected 26 bits corresponding to components, with prefixed 0s padded on the last byte to round up to 4 bytes"
);
}

#[test]
fn deserialize_from_too_few_bytes() {
assert_de_tokens_error::<Identifier<Registry>>(
&[
Token::Tuple { len: 3 },
Token::U8(1),
Token::U8(2),
Token::U8(3),
Token::TupleEnd,
],
"invalid length 3, expected 26 bits corresponding to components, with prefixed 0s padded on the last byte to round up to 4 bytes"
);
}
}
38 changes: 38 additions & 0 deletions src/archetype/identifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ where
unsafe { Iter::<R>::new(self.pointer) }
}

/// Returns the number of components identified by this identifier.
///
/// This is not a cheap operation. It is O(N), looping over the bits individually and counting
/// them.
#[must_use]
pub(crate) fn count(&self) -> usize {
// SAFETY: The identifier here will outlive the derived `Iter`.
unsafe { self.iter() }.filter(|b| *b).count()
}

/// Returns the size of the components within the canonical entity represented by this
/// identifier.
///
Expand Down Expand Up @@ -241,6 +251,17 @@ where
unsafe { Iter::<R>::new(self.pointer) }
}

/// Returns the number of components identified by this identifier.
///
/// This is not a cheap operation. It is O(N), looping over the bits individually and counting
/// them.
#[cfg(feature = "serde")]
#[must_use]
pub(crate) fn count(self) -> usize {
// SAFETY: The identifier here will outlive the derived `Iter`.
unsafe { self.iter() }.filter(|b| *b).count()
}

/// Returns a copy of the bytes defining this identifier.
pub(crate) fn as_vec(self) -> Vec<u8> {
// SAFETY: The reference created here will always live longer than the referenced
Expand Down Expand Up @@ -379,6 +400,14 @@ mod tests {
);
}

#[cfg(feature = "serde")]
#[test]
fn buffer_count() {
let buffer = unsafe { Identifier::<Registry>::new(vec![1, 2, 3, 0]) };

assert_eq!(buffer.count(), 4);
}

#[test]
fn buffer_size_of_components() {
let buffer = unsafe { Identifier::<registry!(bool, u64, f32)>::new(vec![7]) };
Expand Down Expand Up @@ -417,6 +446,15 @@ mod tests {
);
}

#[cfg(feature = "serde")]
#[test]
fn identifier_count() {
let buffer = unsafe { Identifier::<Registry>::new(vec![1, 2, 3, 0]) };
let identifier = unsafe { buffer.as_ref() };

assert_eq!(identifier.count(), 4);
}

#[test]
fn identifier_as_vec() {
let buffer = unsafe { Identifier::<Registry>::new(vec![1, 2, 3, 0]) };
Expand Down
Loading

0 comments on commit 473c1da

Please sign in to comment.