Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
#[allow(unused)]
macro_rules! set_insert_deser_assert_macro [
[$set: ident, $data: ident, $($key: expr),*] => [
$($set.insert($key));*
;
let $data = borsh::to_vec(&$set).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!($data);
]
];
#[allow(unused)]
macro_rules! map_insert_deser_assert_macro [
[$map: ident, $data: ident, $($key: expr => $value: expr),*] => [
$($map.insert($key, $value));*
;
let $data = borsh::to_vec(&$map).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!($data);
]
];
#[allow(unused)]
macro_rules! set_wrong_order_test [
[$test_name: ident, $set_type: ty] => [
#[test]
fn $test_name() {
let mut data = vec![];
let arr_key = ["various".to_string(), "foo".to_string(), "many".to_string()];
let len = arr_key.len() as u32;
u32::serialize(&len, &mut data).expect("no error");
for key in &arr_key {
key.serialize(&mut data).expect("no error");
}
let result = from_slice::<$set_type>(&data);
#[cfg(not(feature = "de_strict_order"))]
{
let result = result.unwrap();
assert_eq!(result.len(), arr_key.len());
for key in &arr_key {
assert!(result.contains(key));
}
}
#[cfg(feature = "de_strict_order")]
{
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), ERROR_WRONG_ORDER_OF_KEYS);
}
}
]
];
#[allow(unused)]
macro_rules! map_wrong_order_test [
[$test_name: ident, $map_type: ty] => [
#[test]
fn $test_name() {
let mut data = vec![];
let arr_key = ["various".to_string(), "foo".to_string(), "many".to_string()];
let arr_val = [
"value".to_string(),
"different".to_string(),
"unexp".to_string(),
];
let len = arr_key.len() as u32;
u32::serialize(&len, &mut data).expect("no error");
let entries = IntoIterator::into_iter(arr_key.clone())
.zip(IntoIterator::into_iter(arr_val))
.collect::<Vec<_>>();
for (key, value) in entries.clone() {
key.serialize(&mut data).expect("no error");
value.serialize(&mut data).expect("no error");
}
let result = from_slice::<$map_type>(&data);
#[cfg(not(feature = "de_strict_order"))]
{
let result = result.unwrap();
assert_eq!(result.len(), arr_key.len());
for (key, value) in entries {
assert_eq!(result.get(&key), Some(&value));
}
}
#[cfg(feature = "de_strict_order")]
{
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), ERROR_WRONG_ORDER_OF_KEYS);
}
}
]
];
#[allow(unused)]
macro_rules! schema_map(
() => { BTreeMap::new() };
{ $($key:expr => $value:expr),+ } => {
{
let mut m = BTreeMap::new();
$(
m.insert($key.to_string(), $value);
)+
m
}
};
);
#[allow(unused)]
#[cfg(feature = "unstable__schema")]
pub mod schema_imports {
extern crate alloc;
pub use alloc::{
boxed::Box,
collections::BTreeMap,
format,
string::{String, ToString},
vec,
vec::Vec,
};
pub use borsh::schema::{
add_definition, BorshSchemaContainer, Declaration, Definition, Fields,
SchemaContainerValidateError, SchemaMaxSerializedSizeError,
};
pub use borsh::{schema_container_of, BorshSchema};
}
@@ -0,0 +1,35 @@
#![allow(unused)]
use crate::common_macro::schema_imports::*;
use core::fmt::{Debug, Display};
/// test: Sausage wasn't populated with param Sausage<W>
#[derive(borsh::BorshSchema, Debug)]
enum AWithSkip<C, W> {
Bacon,
Eggs,
Salad(u32, C, u32),
Sausage {
#[borsh(skip)]
wrapper: W,
filling: u32,
},
}
/// test: inner structs in BorshSchema derive don't need any bounds, unrelated to BorshSchema
// #[derive(borsh::BorshSchema)]
// struct SideLeft<A>(
// A,
// )
// where
// A: Display + Debug,
// B: Display + Debug;
#[derive(borsh::BorshSchema)]
enum Side<A, B>
where
A: Display + Debug,
B: Display + Debug,
{
Left(A),
Right(B),
}
@@ -0,0 +1,44 @@
use borsh::{BorshDeserialize, BorshSerialize};
#[allow(unused)]
use alloc::{string::String, vec::Vec};
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(hash_collections)]
use core::{cmp::Eq, hash::Hash};
#[cfg(feature = "std")]
use std::collections::HashMap;
use alloc::collections::BTreeMap;
/// `T: Ord` bound is required for `BorshDeserialize` derive to be successful
#[allow(unused)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
enum E<T: Ord, U, W> {
X { f: BTreeMap<T, U> },
Y(W),
}
#[cfg(hash_collections)]
#[allow(unused)]
#[derive(BorshSerialize, BorshDeserialize, Debug)]
enum I1<K, V, R> {
B {
#[allow(unused)]
#[borsh(skip)]
x: HashMap<K, V>,
y: String,
},
C(K, Vec<R>),
}
#[cfg(hash_collections)]
#[allow(unused)]
#[derive(BorshSerialize, BorshDeserialize, Debug)]
enum I2<K: Ord + Eq + Hash, R, U> {
B { x: HashMap<K, R>, y: String },
C(K, #[borsh(skip)] U),
}
@@ -0,0 +1,98 @@
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(hash_collections)]
use core::{cmp::Eq, hash::Hash};
#[cfg(feature = "std")]
use std::collections::HashMap;
use alloc::{
collections::BTreeMap,
string::String,
};
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize, Debug)]
struct TupleA<W>(W, u32);
#[derive(BorshSerialize, BorshDeserialize, Debug)]
struct NamedA<W> {
a: W,
b: u32,
}
/// `T: PartialOrd` is injected here via field bound to avoid having this restriction on
/// the struct itself
#[cfg(hash_collections)]
#[derive(BorshSerialize)]
struct C1<T, U> {
a: String,
#[borsh(bound(serialize = "T: borsh::ser::BorshSerialize + Ord,
U: borsh::ser::BorshSerialize"))]
b: HashMap<T, U>,
}
/// `T: PartialOrd + Hash + Eq` is injected here via field bound to avoid having this restriction on
/// the struct itself
#[allow(unused)]
#[cfg(hash_collections)]
#[derive(BorshDeserialize)]
struct C2<T, U> {
a: String,
#[borsh(bound(deserialize = "T: Ord + Hash + Eq + borsh::de::BorshDeserialize,
U: borsh::de::BorshDeserialize"))]
b: HashMap<T, U>,
}
/// `T: Ord` bound is required for `BorshDeserialize` derive to be successful
#[derive(BorshSerialize, BorshDeserialize)]
struct D<T: Ord, R> {
a: String,
b: BTreeMap<T, R>,
}
#[cfg(hash_collections)]
#[derive(BorshSerialize)]
struct G<K, V, U>(#[borsh(skip)] HashMap<K, V>, U);
#[cfg(hash_collections)]
#[derive(BorshDeserialize)]
struct G1<K, V, U>(#[borsh(skip)] HashMap<K, V>, U);
#[cfg(hash_collections)]
#[derive(BorshDeserialize)]
struct G2<K: Ord + Hash + Eq, R, U>(HashMap<K, R>, #[borsh(skip)] U);
/// implicit derived `core::default::Default` bounds on `K` and `V` are dropped by empty bound
/// specified, as `HashMap` hash its own `Default` implementation
#[cfg(hash_collections)]
#[derive(BorshDeserialize)]
struct G3<K, V, U>(#[borsh(skip, bound(deserialize = ""))] HashMap<K, V>, U);
#[cfg(hash_collections)]
#[derive(BorshSerialize, BorshDeserialize)]
struct H<K: Ord, V, U> {
x: BTreeMap<K, V>,
#[allow(unused)]
#[borsh(skip)]
y: U,
}
trait TraitName {
type Associated;
fn method(&self);
}
#[allow(unused)]
#[derive(BorshSerialize)]
struct ParametrizedWrongDerive<T, V>
where
T: TraitName,
{
#[borsh(bound(serialize = "<T as TraitName>::Associated: borsh::ser::BorshSerialize"))]
field: <T as TraitName>::Associated,
another: V,
}
@@ -0,0 +1,15 @@
// Borsh macros should not collide with the local modules:
// https://github.com/near/borsh-rs/issues/11
mod std {}
mod core {}
#[allow(unused)]
#[derive(borsh::BorshSerialize, borsh::BorshDeserialize)]
struct A;
#[allow(unused)]
#[derive(borsh::BorshSerialize, borsh::BorshDeserialize)]
enum B {
C,
D,
}
@@ -0,0 +1,38 @@
use borsh::{BorshDeserialize, BorshSerialize};
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(hash_collections)]
use core::{cmp::Eq, hash::Hash};
use alloc::{boxed::Box, string::String};
#[cfg(hash_collections)]
#[allow(unused)]
#[derive(BorshSerialize, BorshDeserialize)]
struct CRec<U: Ord + Hash + Eq> {
a: String,
b: HashMap<U, CRec<U>>,
}
// `impl<T, U> BorshDeserialize for Box<T>` pulls in => `ToOwned`
// => pulls in at least `Clone`
#[allow(unused)]
#[derive(Clone, BorshSerialize, BorshDeserialize)]
struct CRecA {
a: String,
b: Box<CRecA>,
}
#[cfg(hash_collections)]
#[allow(unused)]
#[derive(BorshSerialize, BorshDeserialize)]
struct CRecC {
a: String,
b: HashMap<String, CRecC>,
}
@@ -0,0 +1,143 @@
use borsh::{from_reader, to_vec, BorshDeserialize, BorshSerialize};
use alloc::{
string::{String, ToString},
vec::Vec,
};
const ERROR_NOT_ALL_BYTES_READ: &str = "Not all bytes read";
const ERROR_UNEXPECTED_LENGTH_OF_INPUT: &str = "Unexpected length of input";
#[derive(BorshSerialize, BorshDeserialize, Debug)]
struct Serializable {
item1: i32,
item2: String,
item3: f64,
}
#[test]
fn test_custom_reader() {
let s = Serializable {
item1: 100,
item2: "foo".into(),
item3: 1.2345,
};
let bytes = to_vec(&s).unwrap();
let mut reader = CustomReader {
data: bytes,
read_index: 0,
};
let de: Serializable = BorshDeserialize::deserialize_reader(&mut reader).unwrap();
assert_eq!(de.item1, s.item1);
assert_eq!(de.item2, s.item2);
assert_eq!(de.item3, s.item3);
}
#[test]
fn test_custom_reader_with_insufficient_data() {
let s = Serializable {
item1: 100,
item2: "foo".into(),
item3: 1.2345,
};
let mut bytes = to_vec(&s).unwrap();
bytes.pop().unwrap();
let mut reader = CustomReader {
data: bytes,
read_index: 0,
};
assert_eq!(
<Serializable as BorshDeserialize>::deserialize_reader(&mut reader)
.unwrap_err()
.to_string(),
ERROR_UNEXPECTED_LENGTH_OF_INPUT
);
}
#[test]
fn test_custom_reader_with_too_much_data() {
let s = Serializable {
item1: 100,
item2: "foo".into(),
item3: 1.2345,
};
let mut bytes = to_vec(&s).unwrap();
bytes.push(1);
let mut reader = CustomReader {
data: bytes,
read_index: 0,
};
assert_eq!(
from_reader::<CustomReader, Serializable>(&mut reader)
.unwrap_err()
.to_string(),
ERROR_NOT_ALL_BYTES_READ
);
}
struct CustomReader {
data: Vec<u8>,
read_index: usize,
}
impl borsh::io::Read for CustomReader {
fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result<usize> {
let len = buf.len().min(self.data.len() - self.read_index);
buf[0..len].copy_from_slice(&self.data[self.read_index..self.read_index + len]);
self.read_index += len;
Ok(len)
}
}
#[test]
fn test_custom_reader_that_doesnt_fill_slices() {
let s = Serializable {
item1: 100,
item2: "foo".into(),
item3: 1.2345,
};
let bytes = to_vec(&s).unwrap();
let mut reader = CustomReaderThatDoesntFillSlices {
data: bytes,
read_index: 0,
};
let de: Serializable = BorshDeserialize::deserialize_reader(&mut reader).unwrap();
assert_eq!(de.item1, s.item1);
assert_eq!(de.item2, s.item2);
assert_eq!(de.item3, s.item3);
}
struct CustomReaderThatDoesntFillSlices {
data: Vec<u8>,
read_index: usize,
}
impl borsh::io::Read for CustomReaderThatDoesntFillSlices {
fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result<usize> {
let len = buf.len().min(self.data.len() - self.read_index);
let len = if len <= 1 { len } else { len / 2 };
buf[0..len].copy_from_slice(&self.data[self.read_index..self.read_index + len]);
self.read_index += len;
Ok(len)
}
}
#[test]
fn test_custom_reader_that_fails_preserves_error_information() {
let mut reader = CustomReaderThatFails;
let err = from_reader::<CustomReaderThatFails, Serializable>(&mut reader).unwrap_err();
assert_eq!(err.to_string(), "I don't like to run");
assert_eq!(err.kind(), borsh::io::ErrorKind::ConnectionAborted);
}
struct CustomReaderThatFails;
impl borsh::io::Read for CustomReaderThatFails {
fn read(&mut self, _buf: &mut [u8]) -> borsh::io::Result<usize> {
Err(borsh::io::Error::new(
borsh::io::ErrorKind::ConnectionAborted,
"I don't like to run",
))
}
}
@@ -0,0 +1,28 @@
use alloc::string::ToString;
use borsh::from_slice;
#[test]
fn test_non_ascii() {
let buf = borsh::to_vec(&[0xbf, 0xf3, 0xb3, 0x77][..]).unwrap();
assert_eq!(
from_slice::<ascii::AsciiString>(&buf)
.unwrap_err()
.to_string(),
"the byte at index 0 is not ASCII"
);
let buf = borsh::to_vec("żółw").unwrap();
assert_eq!(
from_slice::<ascii::AsciiString>(&buf)
.unwrap_err()
.to_string(),
"the byte at index 0 is not ASCII"
);
assert_eq!(
from_slice::<ascii::AsciiChar>(&[0xbf])
.unwrap_err()
.to_string(),
"not an ASCII character"
);
}
@@ -0,0 +1,13 @@
use alloc::string::ToString;
#[test]
fn test_ref_cell_try_borrow_error() {
let rcell = core::cell::RefCell::new("str");
let _active_borrow = rcell.try_borrow_mut().unwrap();
assert_eq!(
borsh::to_vec(&rcell).unwrap_err().to_string(),
"already mutably borrowed"
);
}
@@ -0,0 +1,233 @@
use borsh::from_slice;
#[cfg(feature = "derive")]
use borsh::BorshDeserialize;
use alloc::{
format,
string::{String, ToString},
vec,
vec::Vec,
};
#[cfg(feature = "derive")]
#[derive(BorshDeserialize, Debug)]
#[borsh(use_discriminant = true)]
enum A {
X,
Y,
}
#[cfg(feature = "derive")]
#[derive(BorshDeserialize, Debug)]
#[borsh(use_discriminant = false)]
enum AWithUseDiscriminantFalse {
X,
Y,
}
#[cfg(feature = "derive")]
#[derive(BorshDeserialize, Debug)]
struct B {
#[allow(unused)]
x: u64,
#[allow(unused)]
y: u32,
}
const ERROR_UNEXPECTED_LENGTH_OF_INPUT: &str = "Unexpected length of input";
const ERROR_INVALID_ZERO_VALUE: &str = "Expected a non-zero value";
#[cfg(feature = "derive")]
#[test]
fn test_missing_bytes() {
let bytes = vec![1, 0];
assert_eq!(
from_slice::<B>(&bytes).unwrap_err().to_string(),
ERROR_UNEXPECTED_LENGTH_OF_INPUT
);
}
#[cfg(feature = "derive")]
#[test]
fn test_invalid_enum_variant() {
let bytes = vec![123];
assert_eq!(
from_slice::<A>(&bytes).unwrap_err().to_string(),
"Unexpected variant tag: 123"
);
}
#[cfg(feature = "derive")]
#[test]
fn test_invalid_enum_variant_old() {
let bytes = vec![123];
assert_eq!(
from_slice::<AWithUseDiscriminantFalse>(&bytes)
.unwrap_err()
.to_string(),
"Unexpected variant tag: 123"
);
}
#[test]
fn test_extra_bytes() {
let bytes = vec![1, 0, 0, 0, 32, 32];
assert_eq!(
from_slice::<Vec<u8>>(&bytes).unwrap_err().to_string(),
"Not all bytes read"
);
}
#[test]
fn test_invalid_bool() {
for i in 2u8..=255 {
let bytes = [i];
assert_eq!(
from_slice::<bool>(&bytes).unwrap_err().to_string(),
format!("Invalid bool representation: {}", i)
);
}
}
#[test]
fn test_invalid_option() {
for i in 2u8..=255 {
let bytes = [i, 32];
assert_eq!(
from_slice::<Option<u8>>(&bytes).unwrap_err().to_string(),
format!(
"Invalid Option representation: {}. The first byte must be 0 or 1",
i
)
);
}
}
#[test]
fn test_invalid_result() {
for i in 2u8..=255 {
let bytes = [i, 0];
assert_eq!(
from_slice::<Result<u64, String>>(&bytes)
.unwrap_err()
.to_string(),
format!(
"Invalid Result representation: {}. The first byte must be 0 or 1",
i
)
);
}
}
#[test]
fn test_invalid_length() {
let bytes = vec![255u8; 4];
assert_eq!(
from_slice::<Vec<u64>>(&bytes).unwrap_err().to_string(),
ERROR_UNEXPECTED_LENGTH_OF_INPUT
);
}
#[test]
fn test_invalid_length_string() {
let bytes = vec![255u8; 4];
assert_eq!(
from_slice::<String>(&bytes).unwrap_err().to_string(),
ERROR_UNEXPECTED_LENGTH_OF_INPUT
);
}
#[test]
fn test_non_utf_string() {
let bytes = vec![1, 0, 0, 0, 0xC0];
assert_eq!(
from_slice::<String>(&bytes).unwrap_err().to_string(),
"invalid utf-8 sequence of 1 bytes from index 0"
);
}
#[test]
fn test_nan_float() {
let bytes = vec![0, 0, 192, 127];
assert_eq!(
from_slice::<f32>(&bytes).unwrap_err().to_string(),
"For portability reasons we do not allow to deserialize NaNs."
);
}
#[test]
fn test_evil_bytes_vec_with_extra() {
// Should fail to allocate given length
// test takes a really long time if read() is used instead of read_exact()
let bytes = vec![255, 255, 255, 255, 32, 32];
assert_eq!(
from_slice::<Vec<[u8; 32]>>(&bytes).unwrap_err().to_string(),
ERROR_UNEXPECTED_LENGTH_OF_INPUT
);
}
#[test]
fn test_evil_bytes_string_extra() {
// Might fail if reading too much
let bytes = vec![255, 255, 255, 255, 32, 32];
assert_eq!(
from_slice::<String>(&bytes).unwrap_err().to_string(),
ERROR_UNEXPECTED_LENGTH_OF_INPUT
);
}
#[test]
fn test_zero_on_nonzero_integer_u8() {
let bytes = &[0];
assert_eq!(
from_slice::<core::num::NonZeroU8>(bytes)
.unwrap_err()
.to_string(),
ERROR_INVALID_ZERO_VALUE
);
}
#[test]
fn test_zero_on_nonzero_integer_u32() {
let bytes = &[0; 4];
assert_eq!(
from_slice::<core::num::NonZeroU32>(bytes)
.unwrap_err()
.to_string(),
ERROR_INVALID_ZERO_VALUE
);
}
#[test]
fn test_zero_on_nonzero_integer_i64() {
let bytes = &[0; 8];
assert_eq!(
from_slice::<core::num::NonZeroI64>(bytes)
.unwrap_err()
.to_string(),
ERROR_INVALID_ZERO_VALUE
);
}
#[test]
fn test_zero_on_nonzero_integer_usize() {
let bytes = &[0; 8];
assert_eq!(
from_slice::<core::num::NonZeroUsize>(bytes)
.unwrap_err()
.to_string(),
ERROR_INVALID_ZERO_VALUE
);
}
#[test]
fn test_zero_on_nonzero_integer_missing_byte() {
let bytes = &[0; 7];
assert_eq!(
from_slice::<core::num::NonZeroUsize>(bytes)
.unwrap_err()
.to_string(),
ERROR_UNEXPECTED_LENGTH_OF_INPUT
);
}
@@ -0,0 +1,53 @@
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
#[borsh(init=init)]
struct A {
lazy: Option<u64>,
}
impl A {
pub fn init(&mut self) {
if let Some(v) = self.lazy.as_mut() {
*v *= 10;
}
}
}
#[test]
fn test_simple_struct() {
let a = A { lazy: Some(5) };
let encoded_a = to_vec(&a).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded_a);
let decoded_a = from_slice::<A>(&encoded_a).unwrap();
let expected_a = A { lazy: Some(50) };
assert_eq!(expected_a, decoded_a);
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
#[borsh(init=initialization_method)]
enum AEnum {
A,
B,
C,
}
impl AEnum {
pub fn initialization_method(&mut self) {
*self = AEnum::C;
}
}
#[test]
fn test_simple_enum() {
let a = AEnum::B;
let encoded_a = to_vec(&a).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded_a);
let decoded_a = from_slice::<AEnum>(&encoded_a).unwrap();
assert_eq!(AEnum::C, decoded_a);
}
@@ -0,0 +1,21 @@
#![allow(clippy::float_cmp)]
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
use bson::oid::ObjectId;
#[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug)]
struct StructWithObjectId(i32, ObjectId, u8);
#[test]
fn test_object_id() {
let obj = StructWithObjectId(
123,
ObjectId::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
33,
);
let serialized = to_vec(&obj).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(serialized);
let deserialized: StructWithObjectId = from_slice(&serialized).unwrap();
assert_eq!(obj, deserialized);
}
@@ -0,0 +1,193 @@
use alloc::vec;
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
// sequence, no unit enums
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)]
#[borsh(use_discriminant = true)]
#[repr(u16)]
enum XY {
A,
B = 20,
C,
D(u32, u32),
E = 10,
F(u64),
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)]
#[borsh(use_discriminant = false)]
#[repr(u16)]
enum XYNoDiscriminant {
A,
B = 20,
C,
D(u32, u32),
E = 10,
F(u64),
}
#[test]
fn test_discriminant_serde_no_unit_type() {
let values = vec![XY::A, XY::B, XY::C, XY::E, XY::D(12, 14), XY::F(35325423)];
let expected_discriminants = [0u8, 20, 21, 10, 22, 11];
for (ind, value) in values.iter().enumerate() {
let data = to_vec(value).unwrap();
assert_eq!(data[0], expected_discriminants[ind]);
assert_eq!(from_slice::<XY>(&data).unwrap(), values[ind]);
}
}
#[test]
fn test_discriminant_serde_no_unit_type_no_use_discriminant() {
let values = vec![
XYNoDiscriminant::A,
XYNoDiscriminant::B,
XYNoDiscriminant::C,
XYNoDiscriminant::D(12, 14),
XYNoDiscriminant::E,
XYNoDiscriminant::F(35325423),
];
let expected_discriminants = [0u8, 1, 2, 3, 4, 5];
for (ind, value) in values.iter().enumerate() {
let data = to_vec(value).unwrap();
assert_eq!(data[0], expected_discriminants[ind]);
assert_eq!(from_slice::<XYNoDiscriminant>(&data).unwrap(), values[ind]);
}
}
// minimal
#[derive(BorshSerialize)]
#[borsh(use_discriminant = true)]
enum MyDiscriminantEnum {
A = 20,
}
#[derive(BorshSerialize)]
#[borsh(use_discriminant = false)]
enum MyDiscriminantEnumFalse {
A = 20,
}
#[derive(BorshSerialize)]
enum MyEnumNoDiscriminant {
A,
}
#[test]
fn test_discriminant_minimal_true() {
assert_eq!(MyDiscriminantEnum::A as u8, 20);
assert_eq!(to_vec(&MyDiscriminantEnum::A).unwrap(), vec![20]);
}
#[test]
fn test_discriminant_minimal_false() {
assert_eq!(MyDiscriminantEnumFalse::A as u8, 20);
assert_eq!(
to_vec(&MyEnumNoDiscriminant::A).unwrap(),
to_vec(&MyDiscriminantEnumFalse::A).unwrap(),
);
assert_eq!(to_vec(&MyDiscriminantEnumFalse::A).unwrap(), vec![0]);
}
// sequence
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)]
#[borsh(use_discriminant = false)]
enum XNoDiscriminant {
A,
B = 20,
C,
D,
E = 10,
F,
}
#[test]
fn test_discriminant_serde_no_use_discriminant() {
let values = vec![
XNoDiscriminant::A,
XNoDiscriminant::B,
XNoDiscriminant::C,
XNoDiscriminant::D,
XNoDiscriminant::E,
XNoDiscriminant::F,
];
let expected_discriminants = [0u8, 1, 2, 3, 4, 5];
for (index, value) in values.iter().enumerate() {
let data = to_vec(value).unwrap();
assert_eq!(data[0], expected_discriminants[index]);
assert_eq!(from_slice::<XNoDiscriminant>(&data).unwrap(), values[index]);
}
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
struct D {
x: u64,
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
enum C {
C1,
C2(u64),
C3(u64, u64),
C4 { x: u64, y: u64 },
C5(D),
}
#[test]
fn test_enum_tuples() {
let values = vec![
C::C1,
C::C2(u64::MAX),
C::C3(1, 2),
C::C4 { x: 0, y: 100 },
C::C5(D { x: u64::MAX }),
];
for value in values {
assert_eq!(from_slice::<C>(&to_vec(&value).unwrap()).unwrap(), value);
}
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)]
#[borsh(use_discriminant = true)]
enum X {
A,
B = 20,
C,
D,
E = 10,
F,
}
#[test]
fn test_discriminant_serialization() {
let values = vec![X::A, X::B, X::C, X::D, X::E, X::F];
for value in values {
assert_eq!(to_vec(&value).unwrap(), [value as u8]);
}
}
#[test]
fn test_discriminant_deserialization() {
let values = vec![X::A, X::B, X::C, X::D, X::E, X::F];
for value in values {
assert_eq!(from_slice::<X>(&[value as u8]).unwrap(), value,);
}
}
#[test]
#[should_panic = "Unexpected variant tag: 2"]
fn test_deserialize_invalid_discriminant() {
from_slice::<X>(&[2]).unwrap();
}
#[test]
fn test_discriminant_serde() {
let values = vec![X::A, X::B, X::C, X::D, X::E, X::F];
let expected_discriminants = [0u8, 20, 21, 22, 10, 11];
for (index, value) in values.iter().enumerate() {
let data = to_vec(value).unwrap();
assert_eq!(data[0], expected_discriminants[index]);
assert_eq!(from_slice::<X>(&data).unwrap(), values[index]);
}
}
@@ -0,0 +1,29 @@
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
enum B<W, G> {
X { f: Vec<W> },
Y(G),
}
#[test]
fn test_generic_enum() {
let b: B<String, u64> = B::X {
f: vec!["one".to_string(), "two".to_string(), "three".to_string()],
};
let c: B<String, u64> = B::Y(656556u64);
let list = vec![b, c];
let data = to_vec(&list).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_list = from_slice::<Vec<B<String, u64>>>(&data).unwrap();
assert_eq!(list, actual_list);
}
@@ -0,0 +1,110 @@
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
use core::marker::PhantomData;
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(hash_collections)]
use core::{cmp::Eq, hash::Hash};
#[cfg(feature = "std")]
use std::collections::HashMap;
use alloc::{
string::{ToString, String},
vec,
vec::Vec,
};
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
enum B<W, G> {
X { f: Vec<W> },
Y(G),
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
struct A<T, F, G> {
x: Vec<T>,
y: String,
b: B<F, G>,
pd: PhantomData<T>,
c: Result<T, G>,
d: [u64; 5],
}
#[test]
fn test_generic_struct() {
let a = A::<String, u64, String> {
x: vec!["foo".to_string(), "bar".to_string()],
pd: Default::default(),
y: "world".to_string(),
b: B::X { f: vec![1, 2] },
c: Err("error".to_string()),
d: [0, 1, 2, 3, 4],
};
let data = to_vec(&a).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_a = from_slice::<A<String, u64, String>>(&data).unwrap();
assert_eq!(a, actual_a);
}
trait TraitName {
type Associated;
#[allow(unused)]
fn method(&self);
}
impl TraitName for u32 {
type Associated = String;
fn method(&self) {}
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
struct Parametrized<T, V>
where
T: TraitName,
{
field: T::Associated,
another: V,
}
#[test]
fn test_generic_associated_type_field() {
let a = Parametrized::<u32, String> {
field: "value".to_string(),
another: "field".to_string(),
};
let data = to_vec(&a).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_a = from_slice::<Parametrized<u32, String>>(&data).unwrap();
assert_eq!(a, actual_a);
}
/// `T: PartialOrd` bound is required for `BorshSerialize` derive to be successful
/// `T: Hash + Eq` bound is required for `BorshDeserialize` derive to be successful
#[cfg(hash_collections)]
#[derive(BorshSerialize, BorshDeserialize)]
struct C<T: Ord + Hash + Eq, U> {
a: String,
b: HashMap<T, U>,
}
#[cfg(hash_collections)]
#[test]
fn test_generic_struct_hashmap() {
let mut hashmap = HashMap::new();
hashmap.insert(34, "another".to_string());
hashmap.insert(14, "value".to_string());
let a = C::<u32, String> {
a: "field".to_string(),
b: hashmap,
};
let data = to_vec(&a).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_a = from_slice::<C<u32, String>>(&data).unwrap();
assert_eq!(actual_a.b.get(&14), Some("value".to_string()).as_ref());
assert_eq!(actual_a.b.get(&34), Some("another".to_string()).as_ref());
}
@@ -0,0 +1,153 @@
use alloc::{
collections::BTreeMap,
string::{String, ToString},
};
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
// the `BorshSchema` derive expands to code that uses the `format!` macro at the
// struct definition site, so it has to be in scope here too.
#[cfg(feature = "unstable__schema")]
use alloc::format;
#[derive(Debug, PartialEq, Eq)]
struct ThirdParty<K, V>(pub BTreeMap<K, V>);
mod third_party_impl {
use super::ThirdParty;
pub(super) fn serialize_third_party<
K: borsh::ser::BorshSerialize,
V: borsh::ser::BorshSerialize,
W: borsh::io::Write,
>(
obj: &ThirdParty<K, V>,
writer: &mut W,
) -> ::core::result::Result<(), borsh::io::Error> {
borsh::BorshSerialize::serialize(&obj.0, writer)?;
Ok(())
}
pub(super) fn deserialize_third_party<
R: borsh::io::Read,
K: borsh::de::BorshDeserialize + Ord,
V: borsh::de::BorshDeserialize,
>(
reader: &mut R,
) -> ::core::result::Result<ThirdParty<K, V>, borsh::io::Error> {
Ok(ThirdParty(borsh::BorshDeserialize::deserialize_reader(
reader,
)?))
}
#[cfg(feature = "unstable__schema")]
use alloc::{collections::BTreeMap, format, vec};
#[cfg(feature = "unstable__schema")]
pub(super) fn declaration<K: borsh::BorshSchema, V: borsh::BorshSchema>(
) -> borsh::schema::Declaration {
let params = vec![<K>::declaration(), <V>::declaration()];
format!(r#"{}<{}>"#, "ThirdParty", params.join(", "))
}
#[cfg(feature = "unstable__schema")]
pub(super) fn add_definitions_recursively<K: borsh::BorshSchema, V: borsh::BorshSchema>(
definitions: &mut BTreeMap<borsh::schema::Declaration, borsh::schema::Definition>,
) {
let fields = borsh::schema::Fields::UnnamedFields(vec![
<BTreeMap<K, V> as borsh::BorshSchema>::declaration(),
]);
let definition = borsh::schema::Definition::Struct { fields };
let no_recursion_flag = definitions.get(&declaration::<K, V>()).is_none();
borsh::schema::add_definition(declaration::<K, V>(), definition, definitions);
if no_recursion_flag {
<BTreeMap<K, V> as borsh::BorshSchema>::add_definitions_recursively(definitions);
}
}
}
// This mirrors the `near/intents` use case: the common `serialize_with` /
// `deserialize_with` / `bound` part is written once, and the schema-only part
// lives in a separate, `cfg`-gated `#[borsh(...)]` attribute. borsh merges the
// disjoint top-level keys of all `#[borsh(...)]` attributes into one.
#[cfg_attr(feature = "unstable__schema", derive(borsh::BorshSchema))]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug)]
struct A<K, V> {
#[borsh(serialize_with = "third_party_impl::serialize_third_party")]
#[borsh(deserialize_with = "third_party_impl::deserialize_third_party")]
#[borsh(bound(
deserialize = "K: borsh::de::BorshDeserialize + Ord, V: borsh::de::BorshDeserialize",
))]
#[cfg_attr(
feature = "unstable__schema",
borsh(schema(with_funcs(
declaration = "third_party_impl::declaration::<K, V>",
definitions = "third_party_impl::add_definitions_recursively::<K, V>"
)))
)]
x: ThirdParty<K, V>,
y: u64,
}
#[test]
fn test_overridden_struct_multiple_attrs() {
let mut m = BTreeMap::<u64, String>::new();
m.insert(0, "0th element".to_string());
m.insert(1, "1st element".to_string());
let th_p = ThirdParty(m);
let a = A { x: th_p, y: 42 };
let data = to_vec(&a).unwrap();
let actual_a = from_slice::<A<u64, String>>(&data).unwrap();
assert_eq!(a, actual_a);
}
#[cfg(feature = "unstable__schema")]
#[test]
fn test_overridden_struct_multiple_attrs_schema() {
use borsh::BorshSchema;
assert_eq!(
"A<u64, String>".to_string(),
<A<u64, String>>::declaration()
);
let mut defs = Default::default();
<A<u64, String>>::add_definitions_recursively(&mut defs);
// the schema-only `with_funcs` attribute from the separate `#[borsh(...)]`
// block takes effect: `x` resolves to the custom `ThirdParty` declaration.
assert!(defs.contains_key("ThirdParty<u64, String>"));
}
// Item-level merge through the real derive macro: `use_discriminant` lives in
// one `#[borsh(...)]` attribute and `init` in a separate one. Both have to take
// effect for the assertions below to hold.
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug)]
#[borsh(use_discriminant = true)]
#[borsh(init = initialization_method)]
enum WithSplitItemAttrs {
A,
B = 10,
C,
}
impl WithSplitItemAttrs {
fn initialization_method(&mut self) {
*self = WithSplitItemAttrs::C;
}
}
#[test]
fn test_item_level_multiple_attrs() {
let value = WithSplitItemAttrs::B;
let data = to_vec(&value).unwrap();
// `use_discriminant = true` (first attribute) honors the explicit
// discriminant: `B = 10` serializes its tag as the byte `10`, not the
// positional index `1`.
assert_eq!(data, [10]);
// `init = initialization_method` (second attribute) runs after decoding, so
// the deserialized value is rewritten to `C`.
let decoded = from_slice::<WithSplitItemAttrs>(&data).unwrap();
assert_eq!(decoded, WithSplitItemAttrs::C);
}
@@ -0,0 +1,28 @@
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
enum ERecD {
B { x: String, y: i32 },
C(u8, Vec<ERecD>),
}
#[test]
fn test_recursive_enum() {
let one = ERecD::B {
x: "one".to_string(),
y: 3213123,
};
let two = ERecD::C(10, vec![]);
let three = ERecD::C(11, vec![one, two]);
let data = to_vec(&three).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_three = from_slice::<ERecD>(&data).unwrap();
assert_eq!(three, actual_three);
}
@@ -0,0 +1,31 @@
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
use alloc::{string::{String, ToString}, vec::Vec, vec};
#[derive(Debug, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
struct CRecB {
a: String,
b: Vec<CRecB>,
}
#[test]
fn test_recursive_struct() {
let one = CRecB {
a: "one".to_string(),
b: vec![],
};
let two = CRecB {
a: "two".to_string(),
b: vec![],
};
let three = CRecB {
a: "three".to_string(),
b: vec![one, two],
};
let data = to_vec(&three).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_three = from_slice::<CRecB>(&data).unwrap();
assert_eq!(three, actual_three);
}
@@ -0,0 +1,98 @@
use alloc::{
collections::BTreeMap,
string::{String, ToString},
};
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
#[derive(Debug, PartialEq, Eq)]
struct ThirdParty<K, V>(pub BTreeMap<K, V>);
mod third_party_impl {
use super::ThirdParty;
pub(super) fn serialize_third_party<
K: borsh::ser::BorshSerialize,
V: borsh::ser::BorshSerialize,
W: borsh::io::Write,
>(
obj: &ThirdParty<K, V>,
writer: &mut W,
) -> ::core::result::Result<(), borsh::io::Error> {
borsh::BorshSerialize::serialize(&obj.0, writer)?;
Ok(())
}
pub(super) fn deserialize_third_party<
R: borsh::io::Read,
K: borsh::de::BorshDeserialize + Ord,
V: borsh::de::BorshDeserialize,
>(
reader: &mut R,
) -> ::core::result::Result<ThirdParty<K, V>, borsh::io::Error> {
Ok(ThirdParty(borsh::BorshDeserialize::deserialize_reader(
reader,
)?))
}
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug)]
struct A<K, V> {
#[borsh(
deserialize_with = "third_party_impl::deserialize_third_party",
serialize_with = "third_party_impl::serialize_third_party",
bound(
deserialize = "K: borsh::de::BorshDeserialize + Ord, V: borsh::de::BorshDeserialize",
)
)]
x: ThirdParty<K, V>,
y: u64,
}
#[allow(unused)]
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug)]
enum C<K, V> {
C3(u64, u64),
C4(
u64,
#[borsh(
deserialize_with = "third_party_impl::deserialize_third_party",
serialize_with = "third_party_impl::serialize_third_party",
bound(
deserialize = "K: borsh::de::BorshDeserialize + Ord, V: borsh::de::BorshDeserialize",
)
)]
ThirdParty<K, V>,
),
}
#[test]
fn test_overriden_struct() {
let mut m = BTreeMap::<u64, String>::new();
m.insert(0, "0th element".to_string());
m.insert(1, "1st element".to_string());
let th_p = ThirdParty(m);
let a = A { x: th_p, y: 42 };
let data = to_vec(&a).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_a = from_slice::<A<u64, String>>(&data).unwrap();
assert_eq!(a, actual_a);
}
#[test]
fn test_overriden_enum() {
let mut m = BTreeMap::<u64, String>::new();
m.insert(0, "0th element".to_string());
m.insert(1, "1st element".to_string());
let th_p = ThirdParty(m);
let c = C::C4(42, th_p);
let data = to_vec(&c).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let actual_c = from_slice::<C<u64, String>>(&data).unwrap();
assert_eq!(c, actual_c);
}
@@ -0,0 +1,30 @@
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
use alloc::vec;
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
enum MixedWithUnitVariants {
A(u16),
B,
C { x: i32, y: i32 },
D,
}
#[test]
fn test_mixed_enum() {
let vars = vec![
MixedWithUnitVariants::A(13),
MixedWithUnitVariants::B,
MixedWithUnitVariants::C { x: 132, y: -17 },
MixedWithUnitVariants::D,
];
for variant in vars {
let encoded = to_vec(&variant).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = from_slice::<MixedWithUnitVariants>(&encoded).unwrap();
assert_eq!(variant, decoded);
}
}
@@ -0,0 +1,210 @@
use core::{ops, result::Result};
use alloc::{
borrow,
boxed::Box,
collections::{BTreeMap, BTreeSet, LinkedList, VecDeque},
string::{String, ToString},
vec,
vec::Vec,
};
use bytes::{Bytes, BytesMut};
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
struct A<'a> {
x: u64,
b: B,
y: f32,
z: String,
t: (String, u64),
btree_map_string: BTreeMap<String, String>,
btree_set_u64: BTreeSet<u64>,
linked_list_string: LinkedList<String>,
vec_deque_u64: VecDeque<u64>,
bytes: Bytes,
bytes_mut: BytesMut,
v: Vec<String>,
w: Box<[u8]>,
box_str: Box<str>,
i: [u8; 32],
u: Result<String, String>,
lazy: Option<u64>,
c: borrow::Cow<'a, str>,
cow_arr: borrow::Cow<'a, [borrow::Cow<'a, str>]>,
range_u32: ops::Range<u32>,
#[borsh(skip)]
skipped: Option<u64>,
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
struct B {
x: u64,
y: i32,
c: C,
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
enum C {
C1,
C2(u64),
C3(u64, u64),
C4 { x: u64, y: u64 },
C5(D),
}
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
struct D {
x: u64,
}
#[derive(BorshSerialize)]
struct E<'a, 'b> {
a: &'a A<'b>,
}
#[derive(BorshSerialize)]
struct F1<'a, 'b> {
aa: &'a [&'a A<'b>],
}
#[derive(BorshDeserialize)]
struct F2<'b> {
aa: Vec<A<'b>>,
}
#[test]
fn test_ultimate_combined_all_features() {
let mut map: BTreeMap<String, String> = BTreeMap::new();
map.insert("test".into(), "test".into());
let mut set: BTreeSet<u64> = BTreeSet::new();
set.insert(u64::MAX);
let cow_arr = [
borrow::Cow::Borrowed("Hello1"),
borrow::Cow::Owned("Hello2".to_string()),
];
let a = A {
x: 1,
b: B {
x: 2,
y: 3,
c: C::C5(D { x: 1 }),
},
y: 4.0,
z: "123".to_string(),
t: ("Hello".to_string(), 10),
btree_map_string: map.clone(),
btree_set_u64: set.clone(),
linked_list_string: vec!["a".to_string(), "b".to_string()].into_iter().collect(),
vec_deque_u64: vec![1, 2, 3].into_iter().collect(),
bytes: vec![5, 4, 3, 2, 1].into(),
bytes_mut: BytesMut::from(&[1, 2, 3, 4, 5][..]),
v: vec!["qwe".to_string(), "zxc".to_string()],
w: vec![0].into_boxed_slice(),
box_str: Box::from("asd"),
i: [4u8; 32],
u: Ok("Hello".to_string()),
lazy: Some(5),
c: borrow::Cow::Borrowed("Hello"),
cow_arr: borrow::Cow::Borrowed(&cow_arr),
range_u32: 12..71,
skipped: Some(6),
};
let encoded_a = to_vec(&a).unwrap();
let e = E { a: &a };
let encoded_ref_a = to_vec(&e).unwrap();
assert_eq!(encoded_ref_a, encoded_a);
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded_a);
let decoded_a = from_slice::<A>(&encoded_a).unwrap();
let expected_a = A {
x: 1,
b: B {
x: 2,
y: 3,
c: C::C5(D { x: 1 }),
},
y: 4.0,
z: a.z.clone(),
t: ("Hello".to_string(), 10),
btree_map_string: map,
btree_set_u64: set,
linked_list_string: vec!["a".to_string(), "b".to_string()].into_iter().collect(),
vec_deque_u64: vec![1, 2, 3].into_iter().collect(),
bytes: vec![5, 4, 3, 2, 1].into(),
bytes_mut: BytesMut::from(&[1, 2, 3, 4, 5][..]),
v: a.v.clone(),
w: a.w.clone(),
box_str: Box::from("asd"),
i: a.i,
u: Ok("Hello".to_string()),
lazy: Some(5),
c: borrow::Cow::Owned("Hello".to_string()),
cow_arr: borrow::Cow::Owned(vec![
borrow::Cow::Owned("Hello1".to_string()),
borrow::Cow::Owned("Hello2".to_string()),
]),
range_u32: 12..71,
skipped: None,
};
assert_eq!(expected_a, decoded_a);
let f1 = F1 { aa: &[&a, &a] };
let encoded_f1 = to_vec(&f1).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded_f1);
let decoded_f2 = from_slice::<F2>(&encoded_f1).unwrap();
assert_eq!(decoded_f2.aa.len(), 2);
assert!(decoded_f2.aa.iter().all(|f2_a| f2_a == &expected_a));
}
#[test]
fn test_object_length() {
let mut map: BTreeMap<String, String> = BTreeMap::new();
map.insert("test".into(), "test".into());
let mut set: BTreeSet<u64> = BTreeSet::new();
set.insert(u64::MAX);
set.insert(100);
set.insert(103);
set.insert(109);
let cow_arr = [
borrow::Cow::Borrowed("Hello1"),
borrow::Cow::Owned("Hello2".to_string()),
];
let a = A {
x: 1,
b: B {
x: 2,
y: 3,
c: C::C5(D { x: 1 }),
},
y: 4.0,
z: "123".to_string(),
t: ("Hello".to_string(), 10),
btree_map_string: map.clone(),
btree_set_u64: set.clone(),
linked_list_string: vec!["a".to_string(), "b".to_string()].into_iter().collect(),
vec_deque_u64: vec![1, 2, 3].into_iter().collect(),
bytes: vec![5, 4, 3, 2, 1].into(),
bytes_mut: BytesMut::from(&[1, 2, 3, 4, 5][..]),
v: vec!["qwe".to_string(), "zxc".to_string()],
w: vec![0].into_boxed_slice(),
box_str: Box::from("asd"),
i: [4u8; 32],
u: Ok("Hello".to_string()),
lazy: Some(5),
c: borrow::Cow::Borrowed("Hello"),
cow_arr: borrow::Cow::Borrowed(&cow_arr),
range_u32: 12..71,
skipped: Some(6),
};
let encoded_a_len = to_vec(&a).unwrap().len();
let len_helper_result = borsh::object_length(&a).unwrap();
assert_eq!(encoded_a_len, len_helper_result);
}
@@ -0,0 +1,77 @@
#![allow(clippy::float_cmp)]
use borsh::{from_slice, to_vec};
#[cfg(feature = "derive")]
use borsh::{BorshDeserialize, BorshSerialize};
use alloc::string::{String, ToString};
macro_rules! test_array {
($v: expr, $t: ty, $len: expr) => {
let buf = borsh::to_vec(&$v).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(buf);
let actual_v: [$t; $len] = from_slice(&buf).expect("failed to deserialize");
assert_eq!($v.len(), actual_v.len());
#[allow(clippy::reversed_empty_ranges)]
for i in 0..$len {
assert_eq!($v[i], actual_v[i]);
}
};
}
macro_rules! test_arrays {
($test_name: ident, $el: expr, $t: ty) => {
#[test]
fn $test_name() {
test_array!([$el; 0], $t, 0);
test_array!([$el; 1], $t, 1);
test_array!([$el; 2], $t, 2);
test_array!([$el; 3], $t, 3);
test_array!([$el; 4], $t, 4);
test_array!([$el; 8], $t, 8);
test_array!([$el; 16], $t, 16);
test_array!([$el; 32], $t, 32);
test_array!([$el; 64], $t, 64);
test_array!([$el; 65], $t, 65);
}
};
}
test_arrays!(test_array_u8, 100u8, u8);
test_arrays!(test_array_i8, 100i8, i8);
test_arrays!(test_array_u32, 1000000000u32, u32);
test_arrays!(test_array_u64, 1000000000000000000u64, u64);
test_arrays!(
test_array_u128,
1000000000000000000000000000000000000u128,
u128
);
test_arrays!(test_array_f32, 1000000000.0f32, f32);
test_arrays!(test_array_array_u8, [100u8; 32], [u8; 32]);
test_arrays!(test_array_zst, (), ());
#[cfg(feature = "derive")]
#[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug)]
struct CustomStruct(u8);
#[cfg(feature = "derive")]
#[test]
fn test_custom_struct_array() {
let arr = [CustomStruct(0), CustomStruct(1), CustomStruct(2)];
let serialized = to_vec(&arr).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(serialized);
let deserialized: [CustomStruct; 3] = from_slice(&serialized).unwrap();
assert_eq!(arr, deserialized);
}
#[test]
fn test_string_array() {
let arr = ["0".to_string(), "1".to_string(), "2".to_string()];
let serialized = to_vec(&arr).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(serialized);
let deserialized: [String; 3] = from_slice(&serialized).unwrap();
assert_eq!(arr, deserialized);
}
@@ -0,0 +1,53 @@
use borsh::{from_slice, to_vec};
use alloc::{
string::String,
vec::Vec,
};
/// Verifies serialisation and deserialisation of an ASCII string `value`.
fn check_ascii(value: &str) -> Vec<u8> {
// Caller promises value is ASCII.
let ascii_str = ascii::AsciiStr::from_ascii(&value).unwrap();
let buf = to_vec(ascii_str).unwrap();
// AsciiStr and AsciiString serialise the same way String does.
assert_eq!(buf, to_vec(&ascii::AsciiString::from(ascii_str)).unwrap());
// Check round trip.
let got = from_slice::<ascii::AsciiString>(&buf).unwrap();
assert_eq!(ascii_str, got);
buf
}
macro_rules! test_ascii_string {
($test_name: ident, $str: expr, $snap:expr) => {
#[test]
fn $test_name() {
let value = String::from($str);
let _buf = check_ascii(&value);
#[cfg(feature = "std")]
if $snap {
insta::assert_debug_snapshot!(_buf);
}
}
};
}
test_ascii_string!(test_empty_string, "", true);
test_ascii_string!(test_a, "a", true);
test_ascii_string!(test_hello_world, "hello world", true);
test_ascii_string!(test_x_1024, "x".repeat(1024), true);
test_ascii_string!(test_x_4096, "x".repeat(4096), false);
test_ascii_string!(test_x_65535, "x".repeat(65535), false);
test_ascii_string!(test_hello_10, "hello world!".repeat(30), true);
test_ascii_string!(test_hello_1000, "hello Achilles!".repeat(1000), false);
#[test]
fn test_ascii_char() {
use ascii::AsciiChar;
let buf = to_vec(&AsciiChar::Dot).unwrap();
assert_eq!(".".as_bytes(), buf);
assert_eq!(AsciiChar::Dot, from_slice::<AsciiChar>(&buf).unwrap());
from_slice::<AsciiChar>(&[b'\x80']).unwrap_err();
}
@@ -0,0 +1,94 @@
use alloc::{
collections::{BTreeMap, BTreeSet},
string::{String, ToString},
vec,
vec::Vec,
};
use borsh::{from_slice, BorshSerialize};
macro_rules! btreeset_test_template [
[$test_name: ident, $($key: expr),* ] => [
#[allow(unused_mut)]
#[allow(redundant_semicolons)]
#[test]
fn $test_name() {
let mut set = BTreeSet::new();
set_insert_deser_assert_macro!(set, data, $($key),*);
let actual_set = from_slice::<BTreeSet<String>>(&data).unwrap();
assert_eq!(set, actual_set);
}
]
];
macro_rules! btreemap_test_template [
[$test_name: ident, $($key: expr => $value: expr),* ] => [
#[allow(unused_mut)]
#[allow(redundant_semicolons)]
#[test]
fn $test_name() {
let mut map = BTreeMap::new();
map_insert_deser_assert_macro!(map, data, $($key => $value),*);
let actual_map = from_slice::<BTreeMap<String, String>>(&data).unwrap();
assert_eq!(map, actual_map);
}
]
];
btreeset_test_template!(test_empty_btreeset,);
btreeset_test_template!(test_1_element_btreeset, "one".to_string());
btreeset_test_template!(
test_2_element_btreeset,
"one".to_string(),
"different".to_string()
);
btreeset_test_template!(
test_default_btreeset,
"foo".to_string(),
"many".to_string(),
"various".to_string(),
"different".to_string(),
"keys".to_string(),
"one".to_string()
);
btreemap_test_template!(test_default_btreemap,
"foo".to_string() => "bar".to_string(),
"one".to_string() => "two".to_string()
);
btreemap_test_template!(test_empty_btreemap,);
btreemap_test_template!(test_1_element_btreemap,
"one".to_string() => "element".to_string()
);
btreemap_test_template!(test_8_element_btreemap,
"one".to_string() => "element".to_string(),
"key".to_string() => "powers".to_string(),
"more".to_string() => "of".to_string(),
"various".to_string() => "two".to_string(),
"different".to_string() => "are".to_string(),
"keys".to_string() => "always".to_string(),
"where".to_string() => "unpredictable".to_string(),
"nowhere".to_string() => "pile".to_string()
);
#[cfg(feature = "de_strict_order")]
const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order";
set_wrong_order_test!(test_btreeset_deser_err_wrong_order, BTreeSet<String>);
map_wrong_order_test!(test_btreemap_deser_err_wrong_order, BTreeMap<String, String>);
@@ -0,0 +1,23 @@
use alloc::string::{String, ToString};
#[test]
fn test_cell_roundtrip() {
let cell = core::cell::Cell::new(42u32);
let out = borsh::to_vec(&cell).unwrap();
let cell_round: core::cell::Cell<u32> = borsh::from_slice(&out).unwrap();
assert_eq!(cell, cell_round);
}
#[test]
fn test_ref_cell_roundtrip() {
let rcell = core::cell::RefCell::new("str".to_string());
let out = borsh::to_vec(&rcell).unwrap();
let rcell_round: core::cell::RefCell<String> = borsh::from_slice(&out).unwrap();
assert_eq!(rcell, rcell_round);
}
+71
View File
@@ -0,0 +1,71 @@
use borsh::{from_slice, to_vec};
use core::{matches, ops::Deref};
use alloc::string::ToString;
use alloc::{borrow::Cow, vec};
#[test]
fn test_cow_str() {
let input: Cow<'_, str> = Cow::Borrowed("static input");
let encoded = to_vec(&input).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let out: Cow<'_, str> = from_slice(&encoded).unwrap();
assert!(matches!(out, Cow::Owned(..)));
assert_eq!(input, out);
assert_eq!(out, "static input");
}
#[test]
fn test_cow_byte_slice() {
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let input: Cow<'_, [u8]> = Cow::Borrowed(&arr);
let encoded = to_vec(&input).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let out: Cow<'_, [u8]> = from_slice(&encoded).unwrap();
assert!(matches!(out, Cow::Owned(..)));
assert_eq!(input, out);
assert_eq!(out, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
}
#[test]
fn test_cow_slice_of_cow_str() {
let arr = [
Cow::Borrowed("first static input"),
Cow::Owned("second static input".to_string()),
];
let input: Cow<'_, [Cow<'_, str>]> = Cow::Borrowed(&arr);
let encoded = to_vec(&input).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let out: Cow<'_, [Cow<'_, str>]> = from_slice(&encoded).unwrap();
assert!(matches!(out, Cow::Owned(..)));
for element in out.deref() {
assert!(matches!(element, Cow::Owned(..)));
}
assert_eq!(input, out);
assert_eq!(
out,
vec![
Cow::Borrowed("first static input"),
Cow::Borrowed("second static input"),
]
);
}
@@ -0,0 +1,213 @@
#[cfg(feature = "std")]
use core::hash::BuildHasher;
#[cfg(feature = "hashbrown")]
use hashbrown::{HashMap, HashSet};
#[cfg(feature = "std")]
use std::collections::{
hash_map::{DefaultHasher, RandomState},
HashMap, HashSet,
};
#[cfg(not(feature = "std"))]
use core::iter::IntoIterator;
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
use borsh::{from_slice, BorshSerialize};
macro_rules! hashset_test_template [
[$test_name: ident, $($key: expr),* ] => [
#[allow(unused_mut)]
#[allow(redundant_semicolons)]
#[test]
fn $test_name() {
let mut set = HashSet::new();
set_insert_deser_assert_macro!(set, data, $($key),*);
let actual_set = from_slice::<HashSet<String>>(&data).unwrap();
assert_eq!(set, actual_set);
}
]
];
macro_rules! hashmap_test_template [
[$test_name: ident, $($key: expr => $value: expr),* ] => [
#[allow(unused_mut)]
#[allow(redundant_semicolons)]
#[test]
fn $test_name() {
let mut map = HashMap::new();
map_insert_deser_assert_macro!(map, data, $($key => $value),*);
let actual_map = from_slice::<HashMap<String, String>>(&data).unwrap();
assert_eq!(map, actual_map);
}
]
];
#[derive(Default)]
#[cfg(feature = "std")]
struct NewHasher(RandomState);
#[cfg(feature = "std")]
impl BuildHasher for NewHasher {
type Hasher = DefaultHasher;
fn build_hasher(&self) -> DefaultHasher {
self.0.build_hasher()
}
}
#[cfg(feature = "std")]
macro_rules! generic_hashset_test_template [
[$test_name: ident, $($key: expr),* ] => [
#[allow(unused_mut)]
#[allow(redundant_semicolons)]
#[test]
fn $test_name() {
let mut set = HashSet::with_hasher(NewHasher::default());
set_insert_deser_assert_macro!(set, data, $($key),*);
let actual_set = from_slice::<HashSet<String, NewHasher>>(&data).unwrap();
assert_eq!(set, actual_set);
}
]
];
#[cfg(feature = "std")]
macro_rules! generic_hashmap_test_template [
[$test_name: ident, $($key: expr => $value: expr),* ] => [
#[allow(unused_mut)]
#[allow(redundant_semicolons)]
#[test]
fn $test_name() {
let mut map = HashMap::with_hasher(NewHasher::default());
map_insert_deser_assert_macro!(map, data, $($key => $value),*);
let actual_map = from_slice::<HashMap<String, String, NewHasher>>(&data).unwrap();
assert_eq!(map, actual_map);
}
]
];
hashset_test_template!(test_empty_hashset,);
hashset_test_template!(test_1_element_hashset, "one".to_string());
hashset_test_template!(
test_2_element_hashset,
"one".to_string(),
"different".to_string()
);
hashset_test_template!(
test_default_hashset,
"foo".to_string(),
"many".to_string(),
"various".to_string(),
"different".to_string(),
"keys".to_string(),
"one".to_string()
);
#[cfg(feature = "std")]
generic_hashset_test_template!(test_empty_generic_hashset,);
#[cfg(feature = "std")]
generic_hashset_test_template!(test_1_element_generic_hashset, "one".to_string());
#[cfg(feature = "std")]
generic_hashset_test_template!(
test_2_element_generic_hashset,
"one".to_string(),
"different".to_string()
);
#[cfg(feature = "std")]
generic_hashset_test_template!(
test_generic_hashset,
"foo".to_string(),
"many".to_string(),
"various".to_string(),
"different".to_string(),
"keys".to_string(),
"one".to_string()
);
hashmap_test_template!(test_default_hashmap,
"foo".to_string() => "bar".to_string(),
"one".to_string() => "two".to_string()
);
hashmap_test_template!(test_empty_hashmap,);
hashmap_test_template!(test_1_element_hashmap,
"one".to_string() => "element".to_string()
);
hashmap_test_template!(test_8_element_hashmap,
"one".to_string() => "element".to_string(),
"key".to_string() => "powers".to_string(),
"more".to_string() => "of".to_string(),
"various".to_string() => "two".to_string(),
"different".to_string() => "are".to_string(),
"keys".to_string() => "always".to_string(),
"where".to_string() => "unpredictable".to_string(),
"nowhere".to_string() => "pile".to_string()
);
#[cfg(feature = "std")]
generic_hashmap_test_template!(test_generic_hash_hashmap,
"foo".to_string() => "bar".to_string(),
"one".to_string() => "two".to_string()
);
#[cfg(feature = "std")]
generic_hashmap_test_template!(test_empty_generic_hashmap,);
#[cfg(feature = "std")]
generic_hashmap_test_template!(test_1_element_generic_hashmap,
"one".to_string() => "element".to_string()
);
#[cfg(feature = "std")]
generic_hashmap_test_template!(test_8_element_generic_hashmap,
"one".to_string() => "element".to_string(),
"key".to_string() => "powers".to_string(),
"more".to_string() => "of".to_string(),
"various".to_string() => "two".to_string(),
"different".to_string() => "are".to_string(),
"keys".to_string() => "always".to_string(),
"where".to_string() => "unpredictable".to_string(),
"nowhere".to_string() => "pile".to_string()
);
#[cfg(feature = "de_strict_order")]
const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order";
set_wrong_order_test!(test_hashset_deser_err_wrong_order, HashSet<String>);
#[cfg(feature = "std")]
set_wrong_order_test!(test_generic_hashset_deser_err_wrong_order, HashSet<String, NewHasher>);
map_wrong_order_test!(test_hashmap_deser_err_wrong_order, HashMap<String, String>);
#[cfg(feature = "std")]
map_wrong_order_test!(test_generic_hashmap_deser_err_wrong_order, HashMap<String, String, NewHasher>);
@@ -0,0 +1,41 @@
use borsh::BorshDeserialize;
use indexmap::{IndexMap, IndexSet};
#[test]
// Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L100
// license: MIT OR Apache-2.0
fn test_indexmap_roundtrip() {
let original_map: IndexMap<i32, i32> = {
let mut map = IndexMap::new();
map.insert(1, 2);
map.insert(3, 4);
map.insert(5, 6);
map
};
let serialized_map = borsh::to_vec(&original_map).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(serialized_map);
let deserialized_map: IndexMap<i32, i32> =
BorshDeserialize::try_from_slice(&serialized_map).unwrap();
assert_eq!(original_map, deserialized_map);
}
#[test]
// Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L115
// license: MIT OR Apache-2.0
fn test_indexset_roundtrip() {
let mut original_set = IndexSet::new();
[1, 2, 3, 4, 5, 6].iter().for_each(|&i| {
original_set.insert(i);
});
let serialized_set = borsh::to_vec(&original_set).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(serialized_set);
let deserialized_set: IndexSet<i32> =
BorshDeserialize::try_from_slice(&serialized_set).unwrap();
assert_eq!(original_set, deserialized_set);
}
@@ -0,0 +1,56 @@
use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use alloc::{vec, vec::Vec};
#[test]
fn test_ipv4_addr_roundtrip_enum() {
let original = IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1));
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<IpAddr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}
#[test]
fn test_ipv4_addr_roundtrip() {
let original = Ipv4Addr::new(192, 168, 0, 1);
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<Ipv4Addr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}
#[test]
fn test_ipv6_addr_roundtrip_enum() {
let original = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<IpAddr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}
#[test]
fn test_ipv6_addr_roundtrip() {
let original = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<Ipv6Addr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}
#[test]
fn test_ipaddr_vec_roundtrip() {
let original = vec![
IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)),
];
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<Vec<IpAddr>>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}
@@ -0,0 +1,32 @@
use borsh::from_slice;
use core::num::*;
#[test]
fn test_nonzero_integer_u8() {
let bytes = &[1];
assert_eq!(from_slice::<NonZeroU8>(bytes).unwrap().get(), 1);
}
#[test]
fn test_nonzero_integer_u32() {
let bytes = &[255, 0, 0, 0];
assert_eq!(from_slice::<NonZeroU32>(bytes).unwrap().get(), 255);
}
#[test]
fn test_nonzero_integer_usize() {
let bytes = &[1, 1, 0, 0, 0, 0, 0, 0];
assert_eq!(from_slice::<NonZeroUsize>(bytes).unwrap().get(), 257);
}
#[test]
fn test_nonzero_integer_i64() {
let bytes = &[255; 8];
assert_eq!(from_slice::<NonZeroI64>(bytes).unwrap().get(), -1);
}
#[test]
fn test_nonzero_integer_i16b() {
let bytes = &[0, 0b1000_0000];
assert_eq!(from_slice::<NonZeroI16>(bytes).unwrap().get(), i16::MIN);
}
@@ -0,0 +1,25 @@
use borsh::{from_slice, to_vec};
macro_rules! test_primitive {
($test_name: ident, $v: expr, $t: ty) => {
#[test]
fn $test_name() {
let expected: $t = $v;
let buf = to_vec(&expected).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(buf);
let actual = from_slice::<$t>(&buf).expect("failed to deserialize");
assert_eq!(actual, expected);
}
};
}
test_primitive!(test_isize_neg, -100isize, isize);
test_primitive!(test_isize_pos, 100isize, isize);
test_primitive!(test_isize_min, isize::min_value(), isize);
test_primitive!(test_isize_max, isize::max_value(), isize);
test_primitive!(test_usize, 100usize, usize);
test_primitive!(test_usize_min, usize::min_value(), usize);
test_primitive!(test_usize_max, usize::max_value(), usize);
@@ -0,0 +1,11 @@
#[test]
fn test_ranges() {
let want = (1..2, 3..=4, 5.., ..6, ..=7, ..);
let encoded = borsh::to_vec(&want).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let got = borsh::from_slice(&encoded).unwrap();
assert_eq!(want, got);
}
+37
View File
@@ -0,0 +1,37 @@
pub use alloc::{rc, sync};
use borsh::{from_slice, to_vec};
#[test]
fn test_rc_roundtrip() {
let value = rc::Rc::new(8u8);
let serialized = to_vec(&value).unwrap();
let deserialized = from_slice::<rc::Rc<u8>>(&serialized).unwrap();
assert_eq!(value, deserialized);
}
#[test]
fn test_slice_rc() {
let original: &[i32] = &[1, 2, 3, 4, 6, 9, 10];
let shared: rc::Rc<[i32]> = rc::Rc::from(original);
let serialized = to_vec(&shared).unwrap();
let deserialized = from_slice::<rc::Rc<[i32]>>(&serialized).unwrap();
assert_eq!(original, &*deserialized);
}
#[test]
fn test_arc_roundtrip() {
let value = sync::Arc::new(8u8);
let serialized = to_vec(&value).unwrap();
let deserialized = from_slice::<sync::Arc<u8>>(&serialized).unwrap();
assert_eq!(value, deserialized);
}
#[test]
fn test_slice_arc() {
let original: &[i32] = &[1, 2, 3, 4, 6, 9, 10];
let shared: sync::Arc<[i32]> = sync::Arc::from(original);
let serialized = to_vec(&shared).unwrap();
let deserialized = from_slice::<sync::Arc<[i32]>>(&serialized).unwrap();
assert_eq!(original, &*deserialized);
}
@@ -0,0 +1,40 @@
use alloc::string::String;
use borsh::{from_slice, to_vec};
/// Verifies serialisation and deserialisation of the given string.
///
/// Returns serialised representation of the string.
fn check_string(value: &str) -> alloc::vec::Vec<u8> {
// Encoding is the same as Vec<u8> with UTF-8 encoded string.
let buf = to_vec(value.as_bytes()).unwrap();
assert_eq!(buf, to_vec(value).unwrap());
assert_eq!(buf, to_vec(&String::from(value)).unwrap());
// Check round trip.
assert_eq!(value, from_slice::<String>(&buf).unwrap());
buf
}
macro_rules! test_string {
($test_name: ident, $str: expr, $snap:expr) => {
#[test]
fn $test_name() {
let value = String::from($str);
let _buf = check_string(&value);
#[cfg(feature = "std")]
if $snap {
insta::assert_debug_snapshot!(_buf);
}
}
};
}
test_string!(test_empty_string, "", true);
test_string!(test_a, "a", true);
test_string!(test_hello_world, "hello world", true);
test_string!(test_x_1024, "x".repeat(1024), true);
test_string!(test_x_4096, "x".repeat(4096), false);
test_string!(test_x_65535, "x".repeat(65535), false);
test_string!(test_hello_10, "hello world!".repeat(30), true);
test_string!(test_hello_1000, "hello world!".repeat(1000), false);
test_string!(test_non_ascii, "💩", true);
@@ -0,0 +1,11 @@
use borsh::{from_slice, to_vec};
#[test]
fn test_unary_tuple() {
let expected = (true,);
let buf = to_vec(&expected).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(buf);
let actual = from_slice::<(bool,)>(&buf).expect("failed to deserialize");
assert_eq!(actual, expected);
}
+17
View File
@@ -0,0 +1,17 @@
use borsh::BorshDeserialize;
use uuid::Uuid;
#[test]
fn test_uuid_roundtrip() {
let original_uuid = Uuid::from_bytes([
0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
0xd7, 0xd8,
]);
let serialized_uuid = borsh::to_vec(&original_uuid).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(serialized_uuid);
let deserialized_uuid: Uuid =
BorshDeserialize::try_from_slice(&serialized_uuid).unwrap();
assert_eq!(original_uuid, deserialized_uuid);
}
+41
View File
@@ -0,0 +1,41 @@
use borsh::{from_slice, to_vec};
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
macro_rules! test_vec {
($v: expr, $t: ty, $snap: expr) => {
let buf = to_vec(&$v).unwrap();
#[cfg(feature = "std")]
if $snap {
insta::assert_debug_snapshot!(buf);
}
let actual_v: Vec<$t> = from_slice(&buf).expect("failed to deserialize");
assert_eq!(actual_v, $v);
};
}
macro_rules! test_vecs {
($test_name: ident, $el: expr, $t: ty) => {
#[test]
fn $test_name() {
test_vec!(Vec::<$t>::new(), $t, true);
test_vec!(vec![$el], $t, true);
test_vec!(vec![$el; 10], $t, true);
test_vec!(vec![$el; 100], $t, true);
test_vec!(vec![$el; 1000], $t, false); // one assumes that the concept has been proved
test_vec!(vec![$el; 10000], $t, false);
}
};
}
test_vecs!(test_vec_u8, 100u8, u8);
test_vecs!(test_vec_i8, 100i8, i8);
test_vecs!(test_vec_u32, 1000000000u32, u32);
test_vecs!(test_vec_f32, 1000000000.0f32, f32);
test_vecs!(test_vec_string, "a".to_string(), String);
test_vecs!(test_vec_vec_u8, vec![100u8; 10], Vec<u8>);
test_vecs!(test_vec_vec_u32, vec![100u32; 10], Vec<u32>);
@@ -0,0 +1,216 @@
use crate::common_macro::schema_imports::*;
#[track_caller]
fn test_ok<T: BorshSchema>(want: usize) {
let schema = BorshSchemaContainer::for_type::<T>();
assert_eq!(Ok(want), schema.max_serialized_size());
}
#[track_caller]
fn test_err<T: BorshSchema>(err: SchemaMaxSerializedSizeError) {
let schema = BorshSchemaContainer::for_type::<T>();
assert_eq!(Err(err), schema.max_serialized_size());
}
const MAX_LEN: usize = u32::MAX as usize;
#[test]
fn max_serialized_size_primitives() {
test_ok::<()>(0);
test_ok::<bool>(1);
test_ok::<f32>(4);
test_ok::<f64>(8);
test_ok::<i8>(1);
test_ok::<i16>(2);
test_ok::<i32>(4);
test_ok::<i64>(8);
test_ok::<i128>(16);
test_ok::<u8>(1);
test_ok::<u16>(2);
test_ok::<u32>(4);
test_ok::<u64>(8);
test_ok::<u128>(16);
test_ok::<core::num::NonZeroI8>(1);
test_ok::<core::num::NonZeroI16>(2);
test_ok::<core::num::NonZeroI32>(4);
test_ok::<core::num::NonZeroI64>(8);
test_ok::<core::num::NonZeroI128>(16);
test_ok::<core::num::NonZeroU8>(1);
test_ok::<core::num::NonZeroU16>(2);
test_ok::<core::num::NonZeroU32>(4);
test_ok::<core::num::NonZeroU64>(8);
test_ok::<core::num::NonZeroU128>(16);
test_ok::<isize>(8);
test_ok::<usize>(8);
test_ok::<core::num::NonZeroUsize>(8);
}
#[test]
fn max_serialized_size_built_in_types() {
test_ok::<core::ops::RangeFull>(0);
test_ok::<core::ops::RangeInclusive<u8>>(2);
test_ok::<core::ops::RangeToInclusive<u64>>(8);
test_ok::<Option<()>>(1);
test_ok::<Option<u8>>(2);
test_ok::<Result<u8, usize>>(9);
test_ok::<Result<u8, Vec<u8>>>(1 + 4 + MAX_LEN);
test_ok::<()>(0);
test_ok::<(u8,)>(1);
test_ok::<(u8, u32)>(5);
test_ok::<[u8; 0]>(0);
test_ok::<[u8; 16]>(16);
test_ok::<[[u8; 4]; 4]>(16);
test_ok::<[u16; 0]>(0);
test_ok::<[u16; 16]>(32);
test_ok::<[[u16; 4]; 4]>(32);
test_ok::<Vec<u8>>(4 + MAX_LEN);
test_ok::<String>(4 + MAX_LEN);
test_err::<Vec<Vec<u8>>>(SchemaMaxSerializedSizeError::Overflow);
test_ok::<Vec<Vec<()>>>(4 + MAX_LEN * 4);
test_ok::<[[[(); MAX_LEN]; MAX_LEN]; MAX_LEN]>(0);
}
#[test]
fn max_serialized_size_derived_types() {
#[derive(BorshSchema)]
pub struct Empty;
#[derive(BorshSchema)]
pub struct Named {
_foo: usize,
_bar: [u8; 15],
}
#[derive(BorshSchema)]
#[allow(unused)]
pub struct Unnamed(usize, [u8; 15]);
#[derive(BorshSchema)]
struct Multiple {
_usz0: usize,
_usz1: usize,
_usz2: usize,
_vec0: Vec<usize>,
_vec1: Vec<usize>,
}
#[derive(BorshSchema)]
#[allow(unused)]
struct Recursive(Option<Box<Recursive>>);
test_ok::<Empty>(0);
test_ok::<Named>(23);
test_ok::<Unnamed>(23);
test_ok::<Multiple>(3 * 8 + 2 * (4 + MAX_LEN * 8));
test_err::<BorshSchemaContainer>(SchemaMaxSerializedSizeError::Overflow);
test_err::<Recursive>(SchemaMaxSerializedSizeError::Recursive);
}
#[test]
fn max_serialized_size_custom_enum() {
#[allow(dead_code)]
enum Maybe<const N: u8, T> {
Just(T),
Nothing,
}
impl<const N: u8, T: BorshSchema> BorshSchema for Maybe<N, T> {
fn declaration() -> Declaration {
let res = format!(r#"Maybe<{}, {}>"#, N, T::declaration());
res
}
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
let definition = Definition::Enum {
tag_width: N,
variants: vec![
(0, "Just".into(), T::declaration()),
(1, "Nothing".into(), <()>::declaration()),
],
};
add_definition(Self::declaration(), definition, definitions);
T::add_definitions_recursively(definitions);
<()>::add_definitions_recursively(definitions);
}
}
test_ok::<Maybe<0, ()>>(0);
test_ok::<Maybe<0, u16>>(2);
test_ok::<Maybe<0, u64>>(8);
test_ok::<Maybe<1, ()>>(1);
test_ok::<Maybe<1, u16>>(3);
test_ok::<Maybe<1, u64>>(9);
test_ok::<Maybe<4, ()>>(4);
test_ok::<Maybe<4, u16>>(6);
test_ok::<Maybe<4, u64>>(12);
}
#[test]
fn max_serialized_size_bound_vec() {
#[allow(dead_code)]
struct BoundVec<const W: u8, const N: u64>;
impl<const W: u8, const N: u64> BorshSchema for BoundVec<W, N> {
fn declaration() -> Declaration {
format!("BoundVec<{}, {}>", W, N)
}
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
let definition = Definition::Sequence {
length_width: W,
length_range: 0..=N,
elements: "u8".to_string(),
};
add_definition(Self::declaration(), definition, definitions);
u8::add_definitions_recursively(definitions);
}
}
test_ok::<BoundVec<4, 0>>(4);
test_ok::<BoundVec<4, { u16::MAX as u64 }>>(4 + u16::MAX as usize);
test_ok::<BoundVec<4, 20>>(24);
test_ok::<BoundVec<1, 0>>(1);
test_ok::<BoundVec<1, { u16::MAX as u64 }>>(1 + u16::MAX as usize);
test_ok::<BoundVec<1, 20>>(21);
test_ok::<BoundVec<0, 0>>(0);
test_ok::<BoundVec<0, { u16::MAX as u64 }>>(u16::MAX as usize);
test_ok::<BoundVec<0, 20>>(20);
}
#[test]
fn max_serialized_size_small_vec() {
#[allow(dead_code)]
struct SmallVec<T>(core::marker::PhantomData<T>);
impl<T: BorshSchema> BorshSchema for SmallVec<T> {
fn declaration() -> Declaration {
format!(r#"SmallVec<{}>"#, T::declaration())
}
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
let definition = Definition::Sequence {
length_width: 1,
length_range: 0..=u8::MAX as u64,
elements: T::declaration(),
};
add_definition(Self::declaration(), definition, definitions);
T::add_definitions_recursively(definitions);
}
}
test_ok::<SmallVec<u8>>(u8::MAX as usize + 1);
test_ok::<SmallVec<u16>>(u8::MAX as usize * 2 + 1);
}
@@ -0,0 +1,94 @@
use crate::common_macro::schema_imports::*;
use alloc::{boxed::Box, collections::BTreeMap, format, string::ToString, vec::Vec};
#[track_caller]
fn test_ok<T: BorshSchema>() {
let schema = BorshSchemaContainer::for_type::<T>();
assert_eq!(Ok(()), schema.validate());
}
#[track_caller]
fn test_err<T: BorshSchema>(err: SchemaContainerValidateError) {
let schema = BorshSchemaContainer::for_type::<T>();
assert_eq!(Err(err), schema.validate());
}
#[test]
fn validate_for_derived_types() {
#[derive(BorshSchema)]
pub struct Empty;
#[derive(BorshSchema)]
pub struct Named {
_foo: usize,
_bar: [u8; 15],
}
#[derive(BorshSchema)]
#[allow(unused)]
pub struct Unnamed(usize, [u8; 15]);
#[derive(BorshSchema)]
#[allow(unused)]
struct Recursive(Option<Box<Recursive>>);
#[derive(BorshSchema)]
#[allow(unused)]
struct RecursiveSequence(Vec<RecursiveSequence>);
// thankfully, this one cannot be constructed
#[derive(BorshSchema)]
#[allow(unused)]
struct RecursiveArray(Box<[RecursiveArray; 3]>);
test_ok::<Empty>();
test_ok::<Named>();
test_ok::<Unnamed>();
test_ok::<BorshSchemaContainer>();
test_ok::<Recursive>();
test_ok::<RecursiveSequence>();
test_ok::<RecursiveArray>();
test_ok::<[(); 300]>();
}
#[test]
fn validate_for_zst_sequences() {
test_err::<Vec<Vec<()>>>(SchemaContainerValidateError::ZSTSequence(
"Vec<()>".to_string(),
));
test_err::<Vec<core::ops::RangeFull>>(SchemaContainerValidateError::ZSTSequence(
"Vec<RangeFull>".to_string(),
));
}
#[test]
fn validate_bound_vec() {
#[allow(dead_code)]
struct BoundVec<const W: u8, const N: u64>;
impl<const W: u8, const N: u64> BorshSchema for BoundVec<W, N> {
fn declaration() -> Declaration {
format!("BoundVec<{}, {}>", W, N)
}
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
let definition = Definition::Sequence {
length_width: W,
length_range: 0..=N,
elements: "u8".to_string(),
};
add_definition(Self::declaration(), definition, definitions);
u8::add_definitions_recursively(definitions);
}
}
test_ok::<BoundVec<4, { u16::MAX as u64 }>>();
test_err::<BoundVec<1, { u16::MAX as u64 }>>(SchemaContainerValidateError::TagTooNarrow(
"BoundVec<1, 65535>".to_string(),
));
test_ok::<BoundVec<1, { u8::MAX as u64 }>>();
test_ok::<BoundVec<0, { u16::MAX as u64 }>>();
}
@@ -0,0 +1,166 @@
use crate::common_macro::schema_imports::*;
use alloc::{
collections::{BTreeMap, BTreeSet},
format,
string::ToString,
vec::Vec,
};
struct ConflictingSchema;
impl BorshSchema for ConflictingSchema {
#[inline]
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
let fields = Fields::Empty;
let def = Definition::Struct { fields };
add_definition(Self::declaration(), def, definitions);
}
#[inline]
fn declaration() -> Declaration {
"i64".into()
}
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_conflict() {
let mut defs = Default::default();
<Vec<i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<ConflictingSchema as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_vec() {
let mut defs = Default::default();
<Vec<i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<Vec<ConflictingSchema> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_range() {
let mut defs = Default::default();
<core::ops::Range<i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<core::ops::Range<ConflictingSchema> as borsh::BorshSchema>::add_definitions_recursively(
&mut defs,
);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_slice() {
let mut defs = Default::default();
<[i64] as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<[ConflictingSchema] as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_array() {
let mut defs = Default::default();
<[i64; 10] as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<[ConflictingSchema; 10] as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_option() {
let mut defs = Default::default();
<Option<i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<Option<ConflictingSchema> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct GenericStruct<T> {
field: T,
}
#[test]
fn test_implicit_conflict_struct() {
let mut defs = Default::default();
<GenericStruct<i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<GenericStruct<ConflictingSchema> as borsh::BorshSchema>::add_definitions_recursively(
&mut defs,
);
// NOTE: the contents of `defs` depend on the order of 2 above lines
// this loophole is needed to enable derives for recursive structs/enums
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct SelfConflictingStruct {
field_1: i64,
field_2: ConflictingSchema,
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_self_conflicting_struct() {
let mut defs = Default::default();
<SelfConflictingStruct as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum GenericEnum<T> {
A { field: T },
B(u64),
}
#[test]
fn test_implicit_conflict_enum() {
let mut defs = Default::default();
<GenericEnum<i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<GenericEnum<ConflictingSchema> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
// NOTE: the contents of `defs` depend on the order of 2 above lines
// this loophole is needed to enable derives for recursive structs/enums
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum SelfConflictingEnum {
A { field: i64 },
B { field: ConflictingSchema },
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_self_conflicting_enum() {
let mut defs = Default::default();
<SelfConflictingEnum as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_result() {
let mut defs = Default::default();
<Result<u8, i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<Result<u8, ConflictingSchema> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_btreemap() {
let mut defs = Default::default();
<BTreeMap<i64, u8> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<BTreeMap<ConflictingSchema, u8> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_btreeset() {
let mut defs = Default::default();
<BTreeSet<i64> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<BTreeSet<ConflictingSchema> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
#[test]
#[should_panic(expected = "Redefining type schema for i64")]
fn test_implicit_conflict_tuple() {
let mut defs = Default::default();
<(i64, u8) as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
<(ConflictingSchema, u8) as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
}
+48
View File
@@ -0,0 +1,48 @@
use crate::common_macro::schema_imports::*;
#[test]
fn simple_array() {
let actual_name = <[u64; 32]>::declaration();
let mut actual_defs = schema_map!();
<[u64; 32]>::add_definitions_recursively(&mut actual_defs);
assert_eq!("[u64; 32]", actual_name);
assert_eq!(
schema_map! {
"[u64; 32]" => Definition::Sequence {
length_width: Definition::ARRAY_LENGTH_WIDTH,
length_range: 32..=32,
elements: "u64".to_string()
},
"u64" => Definition::Primitive(8)
},
actual_defs
);
}
#[test]
fn nested_array() {
let actual_name = <[[[u64; 9]; 10]; 32]>::declaration();
let mut actual_defs = schema_map!();
<[[[u64; 9]; 10]; 32]>::add_definitions_recursively(&mut actual_defs);
assert_eq!("[[[u64; 9]; 10]; 32]", actual_name);
assert_eq!(
schema_map! {
"[u64; 9]" => Definition::Sequence {
length_width: Definition::ARRAY_LENGTH_WIDTH,
length_range: 9..=9,
elements: "u64".to_string()
},
"[[u64; 9]; 10]" => Definition::Sequence {
length_width: Definition::ARRAY_LENGTH_WIDTH,
length_range: 10..=10,
elements: "[u64; 9]".to_string()
},
"[[[u64; 9]; 10]; 32]" => Definition::Sequence {
length_width: Definition::ARRAY_LENGTH_WIDTH,
length_range: 32..=32,
elements: "[[u64; 9]; 10]".to_string()
},
"u64" => Definition::Primitive(8)
},
actual_defs
);
}
@@ -0,0 +1,32 @@
use crate::common_macro::schema_imports::*;
#[test]
fn test_ascii_strings() {
assert_eq!("AsciiString", ascii::AsciiStr::declaration());
assert_eq!("AsciiString", ascii::AsciiString::declaration());
assert_eq!("AsciiChar", ascii::AsciiChar::declaration());
let want_char = schema_map! {
"AsciiChar" => Definition::Primitive(1)
};
let mut actual_defs = schema_map!();
ascii::AsciiChar::add_definitions_recursively(&mut actual_defs);
assert_eq!(want_char, actual_defs);
let want = schema_map! {
"AsciiString" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "AsciiChar".to_string()
},
"AsciiChar" => Definition::Primitive(1)
};
let mut actual_defs = schema_map!();
ascii::AsciiStr::add_definitions_recursively(&mut actual_defs);
assert_eq!(want, actual_defs);
let mut actual_defs = schema_map!();
ascii::AsciiString::add_definitions_recursively(&mut actual_defs);
assert_eq!(want, actual_defs);
}
+9
View File
@@ -0,0 +1,9 @@
use crate::common_macro::schema_imports::*;
#[test]
fn boxed_schema() {
let boxed_declaration = Box::<str>::declaration();
assert_eq!("String", boxed_declaration);
let boxed_declaration = Box::<[u8]>::declaration();
assert_eq!("Vec<u8>", boxed_declaration);
}
@@ -0,0 +1,53 @@
use crate::common_macro::schema_imports::*;
use alloc::collections::{BTreeMap, BTreeSet};
#[test]
fn b_tree_map() {
let actual_name = BTreeMap::<u64, String>::declaration();
let mut actual_defs = schema_map!();
BTreeMap::<u64, String>::add_definitions_recursively(&mut actual_defs);
assert_eq!("BTreeMap<u64, String>", actual_name);
assert_eq!(
schema_map! {
"BTreeMap<u64, String>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(u64, String)".to_string(),
} ,
"(u64, String)" => Definition::Tuple { elements: vec![ "u64".to_string(), "String".to_string()]},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
#[test]
fn b_tree_set() {
let actual_name = BTreeSet::<String>::declaration();
let mut actual_defs = schema_map!();
BTreeSet::<String>::add_definitions_recursively(&mut actual_defs);
assert_eq!("BTreeSet<String>", actual_name);
assert_eq!(
schema_map! {
"BTreeSet<String>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "String".to_string(),
},
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
+40
View File
@@ -0,0 +1,40 @@
use crate::common_macro::schema_imports::*;
fn common_map_i32() -> BTreeMap<String, Definition> {
schema_map! {
"i32" => Definition::Primitive(4)
}
}
fn common_map_slice_i32() -> BTreeMap<String, Definition> {
schema_map! {
"Vec<i32>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "i32".to_string()
},
"i32" => Definition::Primitive(4)
}
}
#[test]
fn test_cell() {
assert_eq!("i32", <core::cell::Cell<i32> as BorshSchema>::declaration());
let mut actual_defs = schema_map!();
<core::cell::Cell<i32> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_i32(), actual_defs);
}
#[test]
fn test_ref_cell_vec() {
assert_eq!(
"Vec<i32>",
<core::cell::RefCell<Vec<i32>> as BorshSchema>::declaration()
);
let mut actual_defs = schema_map!();
<core::cell::RefCell<Vec<i32>> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_slice_i32(), actual_defs);
}
+68
View File
@@ -0,0 +1,68 @@
use crate::common_macro::schema_imports::*;
use alloc::borrow::Cow;
#[test]
fn test_cow_str() {
assert_eq!("String", <Cow<'_, str> as BorshSchema>::declaration());
let mut actual_defs = schema_map!();
<Cow<'_, str> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(
schema_map! {
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
#[test]
fn test_cow_byte_slice() {
assert_eq!("Vec<u8>", <Cow<'_, [u8]> as BorshSchema>::declaration());
let mut actual_defs = schema_map!();
<Cow<'_, [u8]> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(
schema_map! {
"Vec<u8>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string(),
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
#[test]
fn test_cow_slice_of_cow_str() {
assert_eq!(
"Vec<String>",
<Cow<'_, [Cow<'_, str>]> as BorshSchema>::declaration()
);
let mut actual_defs = schema_map!();
<Cow<'_, [Cow<'_, str>]> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(
schema_map! {
"Vec<String>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "String".to_string(),
},
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
@@ -0,0 +1,102 @@
use crate::common_macro::schema_imports::*;
#[allow(unused)]
#[derive(BorshSchema)]
#[borsh(use_discriminant = true)]
#[repr(i16)]
enum XY {
A,
B = 20,
C,
D(u32, u32),
E = 10,
F(u64),
}
#[test]
fn test_schema_discriminant_no_unit_type() {
assert_eq!("XY".to_string(), XY::declaration());
let mut defs = Default::default();
XY::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"XY" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "A".to_string(), "XY__A".to_string()),
(20, "B".to_string(), "XY__B".to_string()),
(21, "C".to_string(), "XY__C".to_string()),
(22, "D".to_string(), "XY__D".to_string()),
(10, "E".to_string(), "XY__E".to_string()),
(11, "F".to_string(), "XY__F".to_string())
]
},
"XY__A" => Definition::Struct{ fields: Fields::Empty },
"XY__B" => Definition::Struct{ fields: Fields::Empty },
"XY__C" => Definition::Struct{ fields: Fields::Empty },
"XY__D" => Definition::Struct{ fields: Fields::UnnamedFields(
vec!["u32".to_string(), "u32".to_string()]
)},
"XY__E" => Definition::Struct{ fields: Fields::Empty },
"XY__F" => Definition::Struct{ fields: Fields::UnnamedFields(
vec!["u64".to_string()]
)},
"u32" => Definition::Primitive(4),
"u64" => Definition::Primitive(8)
},
defs
);
}
#[allow(unused)]
#[derive(BorshSchema)]
#[borsh(use_discriminant = false)]
#[repr(i16)]
enum XYNoDiscriminant {
A,
B = 20,
C,
D(u32, u32),
E = 10,
F(u64),
}
#[test]
fn test_schema_discriminant_no_unit_type_no_use_discriminant() {
assert_eq!(
"XYNoDiscriminant".to_string(),
XYNoDiscriminant::declaration()
);
let mut defs = Default::default();
XYNoDiscriminant::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"XYNoDiscriminant" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "A".to_string(), "XYNoDiscriminant__A".to_string()),
(1, "B".to_string(), "XYNoDiscriminant__B".to_string()),
(2, "C".to_string(), "XYNoDiscriminant__C".to_string()),
(3, "D".to_string(), "XYNoDiscriminant__D".to_string()),
(4, "E".to_string(), "XYNoDiscriminant__E".to_string()),
(5, "F".to_string(), "XYNoDiscriminant__F".to_string())
]
},
"XYNoDiscriminant__A" => Definition::Struct{ fields: Fields::Empty },
"XYNoDiscriminant__B" => Definition::Struct{ fields: Fields::Empty },
"XYNoDiscriminant__C" => Definition::Struct{ fields: Fields::Empty },
"XYNoDiscriminant__D" => Definition::Struct{ fields: Fields::UnnamedFields(
vec!["u32".to_string(), "u32".to_string()]
)},
"XYNoDiscriminant__E" => Definition::Struct{ fields: Fields::Empty },
"XYNoDiscriminant__F" => Definition::Struct{ fields: Fields::UnnamedFields(
vec!["u64".to_string()]
)},
"u32" => Definition::Primitive(4),
"u64" => Definition::Primitive(8)
},
defs
);
}
@@ -0,0 +1,317 @@
use crate::common_macro::schema_imports::*;
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[test]
pub fn complex_enum_generics() {
#[derive(borsh::BorshSchema)]
struct Tomatoes;
#[derive(borsh::BorshSchema)]
struct Cucumber;
#[derive(borsh::BorshSchema)]
struct Oil;
#[derive(borsh::BorshSchema)]
struct Wrapper;
#[derive(borsh::BorshSchema)]
struct Filling;
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum A<C, W> {
Bacon,
Eggs,
Salad(Tomatoes, C, Oil),
Sausage { wrapper: W, filling: Filling },
}
assert_eq!(
"A<Cucumber, Wrapper>".to_string(),
<A<Cucumber, Wrapper>>::declaration()
);
let mut defs = Default::default();
<A<Cucumber, Wrapper>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Cucumber" => Definition::Struct {fields: Fields::Empty},
"A__Salad<Cucumber>" => Definition::Struct{
fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil".to_string()])
},
"A__Bacon" => Definition::Struct {fields: Fields::Empty},
"Oil" => Definition::Struct {fields: Fields::Empty},
"A<Cucumber, Wrapper>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "Bacon".to_string(), "A__Bacon".to_string()),
(1, "Eggs".to_string(), "A__Eggs".to_string()),
(2, "Salad".to_string(), "A__Salad<Cucumber>".to_string()),
(3, "Sausage".to_string(), "A__Sausage<Wrapper>".to_string())
]
},
"Wrapper" => Definition::Struct {fields: Fields::Empty},
"Tomatoes" => Definition::Struct {fields: Fields::Empty},
"A__Sausage<Wrapper>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("wrapper".to_string(), "Wrapper".to_string()),
("filling".to_string(), "Filling".to_string())
])
},
"A__Eggs" => Definition::Struct {fields: Fields::Empty},
"Filling" => Definition::Struct {fields: Fields::Empty}
},
defs
);
}
// Checks that recursive definitions work. Also checks that re-instantiations of templated types work.
#[cfg(hash_collections)]
#[test]
pub fn complex_enum_generics2() {
#[derive(borsh::BorshSchema)]
struct Tomatoes;
#[derive(borsh::BorshSchema)]
struct Cucumber;
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct Oil<K, V> {
seeds: HashMap<K, V>,
liquid: Option<K>,
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct Wrapper<T> {
foo: Option<T>,
bar: Box<A<T, T>>,
}
#[derive(borsh::BorshSchema)]
struct Filling;
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum A<C, W> {
Bacon,
Eggs,
Salad(Tomatoes, C, Oil<u64, String>),
Sausage { wrapper: W, filling: Filling },
}
assert_eq!(
"A<Cucumber, Wrapper<String>>".to_string(),
<A<Cucumber, Wrapper<String>>>::declaration()
);
let mut defs = Default::default();
<A<Cucumber, Wrapper<String>>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A<Cucumber, Wrapper<String>>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "Bacon".to_string(), "A__Bacon".to_string()),
(1, "Eggs".to_string(), "A__Eggs".to_string()),
(2, "Salad".to_string(), "A__Salad<Cucumber>".to_string()),
(3, "Sausage".to_string(), "A__Sausage<Wrapper<String>>".to_string())
]
},
"A<String, String>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "Bacon".to_string(), "A__Bacon".to_string()),
(1, "Eggs".to_string(), "A__Eggs".to_string()),
(2, "Salad".to_string(), "A__Salad<String>".to_string()),
(3, "Sausage".to_string(), "A__Sausage<String>".to_string())
]
},
"A__Bacon" => Definition::Struct {fields: Fields::Empty},
"A__Eggs" => Definition::Struct {fields: Fields::Empty},
"A__Salad<Cucumber>" => Definition::Struct {fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil<u64, String>".to_string()])},
"A__Salad<String>" => Definition::Struct { fields: Fields::UnnamedFields( vec!["Tomatoes".to_string(), "String".to_string(), "Oil<u64, String>".to_string() ])},
"A__Sausage<Wrapper<String>>" => Definition::Struct {fields: Fields::NamedFields(vec![("wrapper".to_string(), "Wrapper<String>".to_string()), ("filling".to_string(), "Filling".to_string())])},
"A__Sausage<String>" => Definition::Struct{ fields: Fields::NamedFields(vec![("wrapper".to_string(), "String".to_string()), ("filling".to_string(), "Filling".to_string())])},
"Cucumber" => Definition::Struct {fields: Fields::Empty},
"Filling" => Definition::Struct {fields: Fields::Empty},
"HashMap<u64, String>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(u64, String)".to_string(),
},
"Oil<u64, String>" => Definition::Struct { fields: Fields::NamedFields(vec![("seeds".to_string(), "HashMap<u64, String>".to_string()), ("liquid".to_string(), "Option<u64>".to_string())])},
"Option<String>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "None".to_string(), "()".to_string()),
(1, "Some".to_string(), "String".to_string())
]
},
"Option<u64>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "None".to_string(), "()".to_string()),
(1, "Some".to_string(), "u64".to_string())
]
},
"Tomatoes" => Definition::Struct {fields: Fields::Empty},
"(u64, String)" => Definition::Tuple {elements: vec!["u64".to_string(), "String".to_string()]},
"Wrapper<String>" => Definition::Struct{ fields: Fields::NamedFields(vec![("foo".to_string(), "Option<String>".to_string()), ("bar".to_string(), "A<String, String>".to_string())])},
"u64" => Definition::Primitive(8),
"()" => Definition::Primitive(0),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
fn common_map_associated() -> BTreeMap<String, Definition> {
schema_map! {
"EnumParametrized<String, u32, i8, u16>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "B".to_string(), "EnumParametrized__B<u32, i8, u16>".to_string()),
(1, "C".to_string(), "EnumParametrized__C<String>".to_string())
]
},
"EnumParametrized__B<u32, i8, u16>" => Definition::Struct { fields: Fields::NamedFields(vec![
("x".to_string(), "BTreeMap<u32, u16>".to_string()),
("y".to_string(), "String".to_string()),
("z".to_string(), "i8".to_string())
])},
"EnumParametrized__C<String>" => Definition::Struct{ fields: Fields::UnnamedFields(vec!["String".to_string(), "u16".to_string()])},
"BTreeMap<u32, u16>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(u32, u16)".to_string(),
},
"(u32, u16)" => Definition::Tuple { elements: vec!["u32".to_string(), "u16".to_string()]},
"u32" => Definition::Primitive(4),
"i8" => Definition::Primitive(1),
"u16" => Definition::Primitive(2),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
}
}
#[test]
pub fn generic_associated_item1() {
trait TraitName {
type Associated;
#[allow(unused)]
fn method(&self);
}
impl TraitName for u32 {
type Associated = i8;
fn method(&self) {}
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum EnumParametrized<T, K, V>
where
K: TraitName,
K: core::cmp::Ord,
V: core::cmp::Ord,
{
B {
x: BTreeMap<K, V>,
y: String,
z: K::Associated,
},
C(T, u16),
}
assert_eq!(
"EnumParametrized<String, u32, i8, u16>".to_string(),
<EnumParametrized<String, u32, u16>>::declaration()
);
let mut defs = Default::default();
<EnumParametrized<String, u32, u16>>::add_definitions_recursively(&mut defs);
assert_eq!(common_map_associated(), defs);
}
#[test]
pub fn generic_associated_item2() {
trait TraitName {
type Associated;
#[allow(unused)]
fn method(&self);
}
impl TraitName for u32 {
type Associated = i8;
fn method(&self) {}
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum EnumParametrized<T, K, V>
where
K: TraitName,
K: core::cmp::Ord,
V: core::cmp::Ord,
{
B {
x: BTreeMap<K, V>,
y: String,
#[borsh(schema(params = "K => <K as TraitName>::Associated"))]
z: <K as TraitName>::Associated,
},
C(T, u16),
}
assert_eq!(
"EnumParametrized<String, u32, i8, u16>".to_string(),
<EnumParametrized<String, u32, u16>>::declaration()
);
let mut defs = Default::default();
<EnumParametrized<String, u32, u16>>::add_definitions_recursively(&mut defs);
assert_eq!(common_map_associated(), defs);
}
#[test]
pub fn generic_enum_with_predicate_bound_referencing_filtered_param() {
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum Parametrized<T, U>
where
T: From<U>,
{
V1(T),
V2(U),
}
assert_eq!(
"Parametrized<u64, u32>".to_string(),
<Parametrized<u64, u32>>::declaration()
);
let mut defs = Default::default();
<Parametrized<u64, u32>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Parametrized<u64, u32>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "V1".to_string(), "Parametrized__V1<u64>".to_string()),
(1, "V2".to_string(), "Parametrized__V2<u32>".to_string()),
],
},
"Parametrized__V1<u64>" => Definition::Struct {
fields: Fields::UnnamedFields(vec!["u64".to_string()])
},
"Parametrized__V2<u32>" => Definition::Struct {
fields: Fields::UnnamedFields(vec!["u32".to_string()])
},
"u64" => Definition::Primitive(8),
"u32" => Definition::Primitive(4)
},
defs
);
}
@@ -0,0 +1,232 @@
use crate::common_macro::schema_imports::*;
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[test]
pub fn wrapper_struct() {
#[derive(borsh::BorshSchema)]
struct A<T>(T);
assert_eq!("A<u64>".to_string(), <A<u64>>::declaration());
let mut defs = Default::default();
<A<u64>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A<u64>" => Definition::Struct {fields: Fields::UnnamedFields(vec!["u64".to_string()])},
"u64" => Definition::Primitive(8)
},
defs
);
}
#[test]
pub fn tuple_struct_params() {
#[derive(borsh::BorshSchema)]
struct A<K, V>(K, V);
assert_eq!(
"A<u64, String>".to_string(),
<A<u64, String>>::declaration()
);
let mut defs = Default::default();
<A<u64, String>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A<u64, String>" => Definition::Struct { fields: Fields::UnnamedFields(vec![
"u64".to_string(), "String".to_string()
])},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
#[cfg(hash_collections)]
#[test]
pub fn simple_generics() {
#[derive(borsh::BorshSchema)]
struct A<K, V> {
_f1: HashMap<K, V>,
_f2: String,
}
assert_eq!(
"A<u64, String>".to_string(),
<A<u64, String>>::declaration()
);
let mut defs = Default::default();
<A<u64, String>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A<u64, String>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("_f1".to_string(), "HashMap<u64, String>".to_string()),
("_f2".to_string(), "String".to_string())
])
},
"HashMap<u64, String>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(u64, String)".to_string(),
},
"(u64, String)" => Definition::Tuple{elements: vec!["u64".to_string(), "String".to_string()]},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
fn common_map_associated() -> BTreeMap<String, Definition> {
schema_map! {
"Parametrized<String, i8>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("field".to_string(), "i8".to_string()),
("another".to_string(), "String".to_string())
])
},
"i8" => Definition::Primitive(1),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
}
}
#[test]
pub fn generic_associated_item() {
trait TraitName {
type Associated;
#[allow(unused)]
fn method(&self);
}
impl TraitName for u32 {
type Associated = i8;
fn method(&self) {}
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct Parametrized<V, T>
where
T: TraitName,
{
field: T::Associated,
another: V,
}
assert_eq!(
"Parametrized<String, i8>".to_string(),
<Parametrized<String, u32>>::declaration()
);
let mut defs = Default::default();
<Parametrized<String, u32>>::add_definitions_recursively(&mut defs);
assert_eq!(common_map_associated(), defs);
}
#[test]
pub fn generic_associated_item2() {
trait TraitName {
type Associated;
#[allow(unused)]
fn method(&self);
}
impl TraitName for u32 {
type Associated = i8;
fn method(&self) {}
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct Parametrized<V, T>
where
T: TraitName,
{
#[borsh(schema(params = "T => <T as TraitName>::Associated"))]
field: <T as TraitName>::Associated,
another: V,
}
assert_eq!(
"Parametrized<String, i8>".to_string(),
<Parametrized<String, u32>>::declaration()
);
let mut defs = Default::default();
<Parametrized<String, u32>>::add_definitions_recursively(&mut defs);
assert_eq!(common_map_associated(), defs);
}
#[test]
pub fn generic_associated_item3() {
trait TraitName {
type Associated;
#[allow(unused)]
fn method(&self);
}
impl TraitName for u32 {
type Associated = i8;
fn method(&self) {}
}
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct Parametrized<V, T>
where
T: TraitName,
{
#[borsh(schema(params = "T => T, T => <T as TraitName>::Associated"))]
field: (<T as TraitName>::Associated, T),
another: V,
}
assert_eq!(
"Parametrized<String, u32, i8>".to_string(),
<Parametrized<String, u32>>::declaration()
);
let mut defs = Default::default();
<Parametrized<String, u32>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Parametrized<String, u32, i8>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("field".to_string(), "(i8, u32)".to_string()),
("another".to_string(), "String".to_string())
])
},
"(i8, u32)" => Definition::Tuple {
elements: vec!["i8".to_string(), "u32".to_string()]
},
"i8" => Definition::Primitive(1),
"u32" => Definition::Primitive(4),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
@@ -0,0 +1,59 @@
use crate::common_macro::schema_imports::*;
#[cfg(feature = "hashbrown")]
use hashbrown::{HashMap, HashSet};
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};
#[test]
fn simple_map() {
let actual_name = HashMap::<u64, String>::declaration();
let mut actual_defs = schema_map!();
HashMap::<u64, String>::add_definitions_recursively(&mut actual_defs);
assert_eq!("HashMap<u64, String>", actual_name);
assert_eq!(
schema_map! {
"HashMap<u64, String>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(u64, String)".to_string(),
} ,
"(u64, String)" => Definition::Tuple {
elements: vec![ "u64".to_string(), "String".to_string()],
},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
#[test]
fn simple_set() {
let actual_name = HashSet::<String>::declaration();
let mut actual_defs = schema_map!();
HashSet::<String>::add_definitions_recursively(&mut actual_defs);
assert_eq!("HashSet<String>", actual_name);
assert_eq!(
schema_map! {
"HashSet<String>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "String".to_string(),
},
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
+11
View File
@@ -0,0 +1,11 @@
use crate::common_macro::schema_imports::*;
use core::net::IpAddr;
#[test]
fn ip_addr_schema() {
let actual_name = IpAddr::declaration();
assert_eq!("IpAddr", actual_name);
let mut defs = Default::default();
IpAddr::add_definitions_recursively(&mut defs);
insta::assert_snapshot!(format!("{:#?}", defs));
}
+52
View File
@@ -0,0 +1,52 @@
use crate::common_macro::schema_imports::*;
#[test]
fn simple_option() {
let actual_name = Option::<u64>::declaration();
let mut actual_defs = schema_map!();
Option::<u64>::add_definitions_recursively(&mut actual_defs);
assert_eq!("Option<u64>", actual_name);
assert_eq!(
schema_map! {
"Option<u64>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "None".to_string(), "()".to_string()),
(1, "Some".to_string(), "u64".to_string()),
]
},
"u64" => Definition::Primitive(8),
"()" => Definition::Primitive(0)
},
actual_defs
);
}
#[test]
fn nested_option() {
let actual_name = Option::<Option<u64>>::declaration();
let mut actual_defs = schema_map!();
Option::<Option<u64>>::add_definitions_recursively(&mut actual_defs);
assert_eq!("Option<Option<u64>>", actual_name);
assert_eq!(
schema_map! {
"Option<u64>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "None".to_string(), "()".to_string()),
(1, "Some".to_string(), "u64".to_string()),
]
},
"Option<Option<u64>>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "None".to_string(), "()".to_string()),
(1, "Some".to_string(), "Option<u64>".to_string()),
]
},
"u64" => Definition::Primitive(8),
"()" => Definition::Primitive(0)
},
actual_defs
);
}
@@ -0,0 +1,123 @@
use crate::common_macro::schema_imports::*;
use core::marker::PhantomData;
#[test]
fn phantom_data_schema() {
let phantom_declaration = PhantomData::<String>::declaration();
assert_eq!("()", phantom_declaration);
let phantom_declaration = PhantomData::<Vec<u8>>::declaration();
assert_eq!("()", phantom_declaration);
}
#[test]
pub fn generic_struct_with_phantom_data_derived() {
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct Parametrized<K, V> {
field: K,
another: PhantomData<V>,
}
assert_eq!(
"Parametrized<String>".to_string(),
<Parametrized<String, u32>>::declaration()
);
let mut defs = Default::default();
<Parametrized<String, u32>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Parametrized<String>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("field".to_string(), "String".to_string()),
("another".to_string(), "()".to_string())
])
},
"()" => Definition::Primitive(0),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
#[test]
pub fn generic_enum_variant_with_phantom_data_derived() {
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum Parametrized<T> {
Item(PhantomData<T>),
}
struct Marker;
assert_eq!(
"Parametrized".to_string(),
<Parametrized<Marker>>::declaration()
);
let mut defs = Default::default();
<Parametrized<Marker>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Parametrized" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "Item".to_string(), "Parametrized__Item".to_string()),
],
},
"Parametrized__Item" => Definition::Struct {
fields: Fields::UnnamedFields(vec!["()".to_string()])
},
"()" => Definition::Primitive(0)
},
defs
);
}
#[test]
pub fn generic_enum_variant_with_mixed_phantom_data_predicate_derived() {
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum Parametrized<T, U>
where
PhantomData<(T, U)>: Clone,
{
Item(PhantomData<T>),
Other(PhantomData<U>),
}
struct Marker;
assert_eq!(
"Parametrized".to_string(),
<Parametrized<Marker, Marker>>::declaration()
);
let mut defs = Default::default();
<Parametrized<Marker, Marker>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Parametrized" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "Item".to_string(), "Parametrized__Item".to_string()),
(1, "Other".to_string(), "Parametrized__Other".to_string()),
],
},
"Parametrized__Item" => Definition::Struct {
fields: Fields::UnnamedFields(vec!["()".to_string()])
},
"Parametrized__Other" => Definition::Struct {
fields: Fields::UnnamedFields(vec!["()".to_string()])
},
"()" => Definition::Primitive(0)
},
defs
);
}
@@ -0,0 +1,33 @@
use crate::common_macro::schema_imports::*;
#[test]
fn isize_schema() {
let schema = schema_container_of::<isize>();
assert_eq!(
schema,
BorshSchemaContainer::new(
"i64".to_string(),
schema_map! {
"i64" => Definition::Primitive(8)
}
)
)
}
#[test]
fn usize_schema() {
let schema = schema_container_of::<usize>();
assert_eq!(
schema,
BorshSchemaContainer::new(
"u64".to_string(),
schema_map! {
"u64" => Definition::Primitive(8)
}
)
)
}
+49
View File
@@ -0,0 +1,49 @@
use crate::common_macro::schema_imports::*;
#[test]
fn range() {
assert_eq!("RangeFull", <core::ops::RangeFull>::declaration());
let mut actual_defs = schema_map!();
<core::ops::RangeFull>::add_definitions_recursively(&mut actual_defs);
assert_eq!(
schema_map! {
"RangeFull" => Definition::Struct {
fields: Fields::Empty
}
},
actual_defs
);
let actual_name = <core::ops::Range<u64>>::declaration();
let mut actual_defs = schema_map!();
<core::ops::Range<u64>>::add_definitions_recursively(&mut actual_defs);
assert_eq!("Range<u64>", actual_name);
assert_eq!(
schema_map! {
"Range<u64>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("start".into(), "u64".into()),
("end".into(), "u64".into()),
])
},
"u64" => Definition::Primitive(8)
},
actual_defs
);
let actual_name = <core::ops::RangeTo<u64>>::declaration();
let mut actual_defs = schema_map!();
<core::ops::RangeTo<u64>>::add_definitions_recursively(&mut actual_defs);
assert_eq!("RangeTo<u64>", actual_name);
assert_eq!(
schema_map! {
"RangeTo<u64>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("end".into(), "u64".into()),
])
},
"u64" => Definition::Primitive(8)
},
actual_defs
);
}
+53
View File
@@ -0,0 +1,53 @@
use crate::common_macro::schema_imports::*;
use alloc::{rc, sync};
fn common_map_i32() -> BTreeMap<String, Definition> {
schema_map! {
"i32" => Definition::Primitive(4)
}
}
fn common_map_slice_i32() -> BTreeMap<String, Definition> {
schema_map! {
"Vec<i32>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "i32".to_string()
},
"i32" => Definition::Primitive(4)
}
}
#[test]
fn test_rc() {
assert_eq!("i32", <rc::Rc<i32> as BorshSchema>::declaration());
let mut actual_defs = schema_map!();
<rc::Rc<i32> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_i32(), actual_defs);
}
#[test]
fn test_slice_rc() {
assert_eq!("Vec<i32>", <rc::Rc<[i32]> as BorshSchema>::declaration());
let mut actual_defs = schema_map!();
<rc::Rc<[i32]> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_slice_i32(), actual_defs);
}
#[test]
fn test_arc() {
assert_eq!("i32", <sync::Arc<i32> as BorshSchema>::declaration());
let mut actual_defs = schema_map!();
<sync::Arc<i32> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_i32(), actual_defs);
}
#[test]
fn test_slice_arc() {
assert_eq!("Vec<i32>", <sync::Arc<[i32]> as BorshSchema>::declaration());
let mut actual_defs = schema_map!();
<sync::Arc<[i32]> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_slice_i32(), actual_defs);
}
@@ -0,0 +1,52 @@
use crate::common_macro::schema_imports::*;
#[allow(unused)]
#[derive(borsh::BorshSchema)]
enum ERecD {
B { x: String, y: i32 },
C(u8, Vec<ERecD>),
}
#[test]
pub fn recursive_enum_schema() {
let mut defs = Default::default();
ERecD::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"ERecD" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "B".to_string(), "ERecD__B".to_string()),
(1, "C".to_string(), "ERecD__C".to_string()),
]
},
"ERecD__B" => Definition::Struct {
fields: Fields::NamedFields (
vec![
("x".to_string(), "String".to_string()),
("y".to_string(), "i32".to_string()),
]
)
},
"ERecD__C" => Definition::Struct {
fields: Fields::UnnamedFields( vec![
"u8".to_string(),
"Vec<ERecD>".to_string(),
])
},
"Vec<ERecD>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "ERecD".to_string(),
},
"i32" => Definition::Primitive(4),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
@@ -0,0 +1,47 @@
use crate::common_macro::schema_imports::*;
#[allow(unused)]
#[derive(borsh::BorshSchema)]
struct CRecC {
a: String,
b: BTreeMap<String, CRecC>,
}
#[test]
pub fn recursive_struct_schema() {
let mut defs = Default::default();
CRecC::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"CRecC" => Definition::Struct {
fields: Fields::NamedFields(
vec![
(
"a".to_string(),
"String".to_string(),
),
(
"b".to_string(),
"BTreeMap<String, CRecC>".to_string(),
),
]
)
},
"BTreeMap<String, CRecC>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(String, CRecC)".to_string(),
},
"(String, CRecC)" => Definition::Tuple {elements: vec!["String".to_string(), "CRecC".to_string()]},
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
@@ -0,0 +1,132 @@
use crate::common_macro::schema_imports::*;
// use alloc::collections::BTreeMap;
#[allow(unused)]
struct ThirdParty<K, V>(BTreeMap<K, V>);
#[allow(unused)]
mod third_party_impl {
use crate::common_macro::schema_imports::*;
pub(super) fn declaration<K: borsh::BorshSchema, V: borsh::BorshSchema>(
) -> borsh::schema::Declaration {
let params = vec![<K>::declaration(), <V>::declaration()];
format!(r#"{}<{}>"#, "ThirdParty", params.join(", "))
}
pub(super) fn add_definitions_recursively<K: borsh::BorshSchema, V: borsh::BorshSchema>(
definitions: &mut BTreeMap<borsh::schema::Declaration, borsh::schema::Definition>,
) {
let fields = borsh::schema::Fields::UnnamedFields(vec![
<BTreeMap<K, V> as borsh::BorshSchema>::declaration(),
]);
let definition = borsh::schema::Definition::Struct { fields };
let no_recursion_flag = definitions.get(&declaration::<K, V>()).is_none();
borsh::schema::add_definition(declaration::<K, V>(), definition, definitions);
if no_recursion_flag {
<BTreeMap<K, V> as borsh::BorshSchema>::add_definitions_recursively(definitions);
}
}
}
#[allow(unused)]
#[derive(BorshSchema)]
struct A<K, V> {
#[borsh(schema(with_funcs(
declaration = "third_party_impl::declaration::<K, V>",
definitions = "third_party_impl::add_definitions_recursively::<K, V>"
)))]
x: ThirdParty<K, V>,
y: u64,
}
#[allow(unused)]
#[derive(BorshSchema)]
enum C<K, V> {
C3(u64, u64),
C4(
u64,
#[borsh(schema(with_funcs(
declaration = "third_party_impl::declaration::<K, V>",
definitions = "third_party_impl::add_definitions_recursively::<K, V>"
)))]
ThirdParty<K, V>,
),
}
#[test]
pub fn struct_overriden() {
assert_eq!(
"A<u64, String>".to_string(),
<A<u64, String>>::declaration()
);
let mut defs = Default::default();
<A<u64, String>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A<u64, String>" => Definition::Struct { fields: Fields::NamedFields(vec![
("x".to_string(), "ThirdParty<u64, String>".to_string()),
("y".to_string(), "u64".to_string())]
)},
"ThirdParty<u64, String>" => Definition::Struct { fields: Fields::UnnamedFields(vec![
"BTreeMap<u64, String>".to_string(),
]) },
"BTreeMap<u64, String>"=> Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(u64, String)".to_string(),
},
"(u64, String)" => Definition::Tuple { elements: vec!["u64".to_string(), "String".to_string()]},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
#[test]
pub fn enum_overriden() {
assert_eq!(
"C<u64, String>".to_string(),
<C<u64, String>>::declaration()
);
let mut defs = Default::default();
<C<u64, String>>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"C<u64, String>" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "C3".to_string(), "C__C3".to_string()),
(1, "C4".to_string(), "C__C4<u64, String>".to_string())
]
},
"C__C3" => Definition::Struct { fields: Fields::UnnamedFields(vec!["u64".to_string(), "u64".to_string()]) },
"C__C4<u64, String>" => Definition::Struct { fields: Fields::UnnamedFields(vec![
"u64".to_string(), "ThirdParty<u64, String>".to_string()
]) },
"ThirdParty<u64, String>" => Definition::Struct { fields: Fields::UnnamedFields(vec![
"BTreeMap<u64, String>".to_string(),
]) },
"BTreeMap<u64, String>"=> Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "(u64, String)".to_string(),
},
"(u64, String)" => Definition::Tuple { elements: vec!["u64".to_string(), "String".to_string()]},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
@@ -0,0 +1,191 @@
use crate::common_macro::schema_imports::*;
use borsh::{try_from_slice_with_schema, try_to_vec_with_schema};
#[test]
pub fn simple_enum() {
#[allow(dead_code)]
#[derive(borsh::BorshSchema)]
enum A {
Bacon,
Eggs,
}
// https://github.com/near/borsh-rs/issues/112
#[allow(unused)]
impl A {
pub fn declaration() -> usize {
42
}
}
assert_eq!("A".to_string(), <A as borsh::BorshSchema>::declaration());
let mut defs = Default::default();
A::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A__Bacon" => Definition::Struct{ fields: Fields::Empty },
"A__Eggs" => Definition::Struct{ fields: Fields::Empty },
"A" => Definition::Enum {
tag_width: 1,
variants: vec![(0, "Bacon".to_string(), "A__Bacon".to_string()), (1, "Eggs".to_string(), "A__Eggs".to_string())]
}
},
defs
);
}
#[test]
pub fn shadow_enum() {
#[allow(dead_code)]
#[derive(borsh::BorshSchema)]
enum State {
V1(StateV1),
}
#[derive(borsh::BorshSchema)]
struct StateV1;
assert_eq!(
"State".to_string(),
<State as borsh::BorshSchema>::declaration()
);
let mut defs = Default::default();
State::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"State" => Definition::Enum {
tag_width: 1,
variants: vec![(0, "V1".to_string(), "State__V1".to_string())]
},
"State__V1" => Definition::Struct {
fields: Fields::UnnamedFields(["StateV1".to_string()].into())
},
"StateV1" => Definition::Struct{ fields: Fields::Empty }
},
defs
);
}
#[test]
pub fn single_field_enum() {
#[allow(dead_code)]
#[derive(borsh::BorshSchema)]
enum A {
Bacon,
}
assert_eq!("A".to_string(), A::declaration());
let mut defs = Default::default();
A::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A__Bacon" => Definition::Struct {fields: Fields::Empty},
"A" => Definition::Enum {
tag_width: 1,
variants: vec![(0, "Bacon".to_string(), "A__Bacon".to_string())]
}
},
defs
);
}
#[test]
pub fn complex_enum_with_schema() {
#[derive(
borsh::BorshSchema,
Default,
borsh::BorshSerialize,
borsh::BorshDeserialize,
PartialEq,
Debug,
)]
struct Tomatoes;
#[derive(
borsh::BorshSchema,
Default,
borsh::BorshSerialize,
borsh::BorshDeserialize,
PartialEq,
Debug,
)]
struct Cucumber;
#[derive(
borsh::BorshSchema,
Default,
borsh::BorshSerialize,
borsh::BorshDeserialize,
PartialEq,
Debug,
)]
struct Oil;
#[derive(
borsh::BorshSchema,
Default,
borsh::BorshSerialize,
borsh::BorshDeserialize,
PartialEq,
Debug,
)]
struct Wrapper;
#[derive(
borsh::BorshSchema,
Default,
borsh::BorshSerialize,
borsh::BorshDeserialize,
PartialEq,
Debug,
)]
struct Filling;
#[derive(
borsh::BorshSchema, borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Debug,
)]
enum A {
Bacon,
Eggs,
Salad(Tomatoes, Cucumber, Oil),
Sausage { wrapper: Wrapper, filling: Filling },
}
impl Default for A {
fn default() -> Self {
A::Sausage {
wrapper: Default::default(),
filling: Default::default(),
}
}
}
// First check schema.
assert_eq!("A".to_string(), A::declaration());
let mut defs = Default::default();
A::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Cucumber" => Definition::Struct {fields: Fields::Empty},
"A__Salad" => Definition::Struct{ fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil".to_string()])},
"A__Bacon" => Definition::Struct {fields: Fields::Empty},
"Oil" => Definition::Struct {fields: Fields::Empty},
"A" => Definition::Enum {
tag_width: 1,
variants: vec![
(0, "Bacon".to_string(), "A__Bacon".to_string()),
(1, "Eggs".to_string(), "A__Eggs".to_string()),
(2, "Salad".to_string(), "A__Salad".to_string()),
(3, "Sausage".to_string(), "A__Sausage".to_string())
]
},
"Wrapper" => Definition::Struct {fields: Fields::Empty},
"Tomatoes" => Definition::Struct {fields: Fields::Empty},
"A__Sausage" => Definition::Struct { fields: Fields::NamedFields(vec![
("wrapper".to_string(), "Wrapper".to_string()),
("filling".to_string(), "Filling".to_string())
])},
"A__Eggs" => Definition::Struct {fields: Fields::Empty},
"Filling" => Definition::Struct {fields: Fields::Empty}
},
defs
);
// Then check that we serialize and deserialize with schema.
let obj = A::default();
let data = try_to_vec_with_schema(&obj).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(data);
let obj2: A = try_from_slice_with_schema(&data).unwrap();
assert_eq!(obj, obj2);
}
@@ -0,0 +1,112 @@
use crate::common_macro::schema_imports::*;
#[test]
pub fn unit_struct() {
#[derive(borsh::BorshSchema)]
struct A;
// https://github.com/near/borsh-rs/issues/112
#[allow(unused)]
impl A {
pub fn declaration() -> usize {
42
}
}
assert_eq!("A".to_string(), <A as borsh::BorshSchema>::declaration());
let mut defs = Default::default();
A::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A" => Definition::Struct {fields: Fields::Empty}
},
defs
);
}
#[test]
pub fn simple_struct() {
#[derive(borsh::BorshSchema)]
struct A {
_f1: u64,
_f2: String,
}
assert_eq!("A".to_string(), A::declaration());
let mut defs = Default::default();
A::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A" => Definition::Struct{ fields: Fields::NamedFields(vec![
("_f1".to_string(), "u64".to_string()),
("_f2".to_string(), "String".to_string())
])},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
#[test]
pub fn tuple_struct() {
#[derive(borsh::BorshSchema)]
#[allow(unused)]
struct A(u64, String);
assert_eq!("A".to_string(), A::declaration());
let mut defs = Default::default();
A::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"A" => Definition::Struct {fields: Fields::UnnamedFields(vec![
"u64".to_string(), "String".to_string()
])},
"u64" => Definition::Primitive(8),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
defs
);
}
#[test]
pub fn boxed() {
#[derive(borsh::BorshSchema)]
struct A {
_f1: Box<u64>,
_f2: Box<str>,
_f3: Box<[u8]>,
}
assert_eq!("A".to_string(), A::declaration());
let mut defs = Default::default();
A::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"Vec<u8>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string(),
},
"A" => Definition::Struct{ fields: Fields::NamedFields(vec![
("_f1".to_string(), "u64".to_string()),
("_f2".to_string(), "String".to_string()),
("_f3".to_string(), "Vec<u8>".to_string())
])},
"u64" => Definition::Primitive(8),
"u8" => Definition::Primitive(1),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
}
},
defs
);
}
+36
View File
@@ -0,0 +1,36 @@
use crate::common_macro::schema_imports::*;
#[test]
fn test_string() {
let actual_name = str::declaration();
assert_eq!("String", actual_name);
let actual_name = String::declaration();
assert_eq!("String", actual_name);
let mut actual_defs = schema_map!();
String::add_definitions_recursively(&mut actual_defs);
assert_eq!(
schema_map! {
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
let mut actual_defs = schema_map!();
str::add_definitions_recursively(&mut actual_defs);
assert_eq!(
schema_map! {
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
+70
View File
@@ -0,0 +1,70 @@
use crate::common_macro::schema_imports::*;
#[test]
fn test_unary_tuple_schema() {
assert_eq!("(bool,)", <(bool,)>::declaration());
let mut defs = Default::default();
<(bool,)>::add_definitions_recursively(&mut defs);
assert_eq!(
schema_map! {
"(bool,)" => Definition::Tuple { elements: vec!["bool".to_string()] },
"bool" => Definition::Primitive(1)
},
defs
);
}
#[test]
fn simple_tuple() {
let actual_name = <(u64, core::num::NonZeroU16, String)>::declaration();
let mut actual_defs = schema_map!();
<(u64, core::num::NonZeroU16, String)>::add_definitions_recursively(&mut actual_defs);
assert_eq!("(u64, NonZeroU16, String)", actual_name);
assert_eq!(
schema_map! {
"(u64, NonZeroU16, String)" => Definition::Tuple {
elements: vec![
"u64".to_string(),
"NonZeroU16".to_string(),
"String".to_string()
]
},
"u64" => Definition::Primitive(8),
"NonZeroU16" => Definition::Primitive(2),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
},
"u8" => Definition::Primitive(1)
},
actual_defs
);
}
#[test]
fn nested_tuple() {
let actual_name = <(u64, (u8, bool), String)>::declaration();
let mut actual_defs = schema_map!();
<(u64, (u8, bool), String)>::add_definitions_recursively(&mut actual_defs);
assert_eq!("(u64, (u8, bool), String)", actual_name);
assert_eq!(
schema_map! {
"(u64, (u8, bool), String)" => Definition::Tuple { elements: vec![
"u64".to_string(),
"(u8, bool)".to_string(),
"String".to_string(),
]},
"(u8, bool)" => Definition::Tuple { elements: vec![ "u8".to_string(), "bool".to_string()]},
"u64" => Definition::Primitive(8),
"u8" => Definition::Primitive(1),
"bool" => Definition::Primitive(1),
"String" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u8".to_string()
}
},
actual_defs
);
}
+97
View File
@@ -0,0 +1,97 @@
use crate::common_macro::schema_imports::*;
use alloc::collections::{VecDeque, LinkedList};
macro_rules! test_vec_like_collection_schema {
[$test_name: ident, $type: ident] => [
#[test]
fn $test_name() {
let actual_name = $type::<u64>::declaration();
let mut actual_defs = schema_map!();
$type::<u64>::add_definitions_recursively(&mut actual_defs);
assert_eq!(format!("{}<u64>", stringify!($type)), actual_name);
assert_eq!(
schema_map! {
actual_name => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u64".to_string(),
},
"u64" => Definition::Primitive(8)
},
actual_defs
);
}
];
}
test_vec_like_collection_schema!(simple_vec, Vec);
test_vec_like_collection_schema!(vec_deque, VecDeque);
test_vec_like_collection_schema!(linked_list, LinkedList);
#[test]
fn nested_vec() {
let actual_name = Vec::<Vec<u64>>::declaration();
let mut actual_defs = schema_map!();
Vec::<Vec<u64>>::add_definitions_recursively(&mut actual_defs);
assert_eq!("Vec<Vec<u64>>", actual_name);
assert_eq!(
schema_map! {
"Vec<u64>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "u64".to_string(),
},
"Vec<Vec<u64>>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "Vec<u64>".to_string(),
},
"u64" => Definition::Primitive(8)
},
actual_defs
);
}
#[test]
fn slice_schema_container() {
let schema = schema_container_of::<[i64]>();
assert_eq!(
schema,
BorshSchemaContainer::new(
"Vec<i64>".to_string(),
schema_map! {
"Vec<i64>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "i64".to_string(),
},
"i64" => Definition::Primitive(8)
}
)
)
}
#[test]
fn vec_schema_container() {
let schema = schema_container_of::<Vec<i64>>();
assert_eq!(
schema,
BorshSchemaContainer::new(
"Vec<i64>".to_string(),
schema_map! {
"Vec<i64>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "i64".to_string(),
},
"i64" => Definition::Primitive(8)
}
)
)
}
+35
View File
@@ -0,0 +1,35 @@
#![cfg_attr(not(feature = "std"), no_std)]
// Smoke tests that ensure that we don't accidentally remove top-level
// re-exports in a minor release.
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec;
use borsh::{self, from_slice};
#[cfg(feature = "unstable__schema")]
use borsh::{schema_container_of, try_from_slice_with_schema};
#[cfg(feature = "unstable__schema")]
#[test]
fn test_to_vec() {
let value = 42u8;
let seriazeble = (schema_container_of::<u8>(), value);
let serialized = borsh::to_vec(&seriazeble).unwrap();
#[cfg(feature = "std")]
println!("serialized: {:?}", serialized);
let deserialized = try_from_slice_with_schema::<u8>(&serialized).unwrap();
assert_eq!(value, deserialized);
}
#[test]
fn test_to_writer() {
let value = 42u8;
let mut serialized = vec![0; 1];
// serialized: [2, 0, 0, 0, 117, 56, 0, 0, 0, 0, 42]
borsh::to_writer(&mut serialized[..], &value).unwrap();
let deserialized = from_slice::<u8>(&serialized).unwrap();
assert_eq!(value, deserialized);
}
+137
View File
@@ -0,0 +1,137 @@
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
#[macro_use]
mod common_macro;
mod custom_reader {
#[cfg(feature = "derive")]
mod test_custom_reader;
}
/// this module doesn't contain runnable tests;
/// it's included into module tree to ensure derived code doesn't raise compilation
/// errors
#[rustfmt::skip]
#[cfg(feature = "derive")]
mod compile_derives {
mod test_macro_namespace_collisions;
#[allow(unused)]
mod test_generic_structs;
mod test_generic_enums;
mod test_recursive_structs;
#[cfg(feature = "unstable__schema")]
mod schema {
mod test_generic_enums;
}
}
/// These are full roundtrip `BorshSerialize`/`BorshDeserialize` tests
#[rustfmt::skip]
mod roundtrip {
mod test_strings;
#[cfg(feature = "ascii")]
mod test_ascii_strings;
mod test_arrays;
mod test_vecs;
mod test_tuple;
mod test_primitives;
mod test_ip_addr;
mod test_nonzero_integers;
mod test_range;
// mod test_phantom_data; // NOTE: there's nothing corresponding to `schema::test_phantom_data`
// mod test_option; // NOTE: there's nothing corresponding to `schema::test_option`
// mod test_box; // NOTE: there's nothing corresponding to `schema::test_box`
#[cfg(hash_collections)]
mod test_hash_map;
mod test_btree_map;
mod test_cow;
mod test_cells;
#[cfg(feature = "rc")]
mod test_rc;
#[cfg(feature = "indexmap")]
mod test_indexmap;
#[cfg(feature = "uuid")]
mod test_uuid;
#[cfg(feature = "derive")]
mod requires_derive_category {
// mod test_simple_structs; // NOTE: there's nothing corresponding to `schema::test_simple_structs`
mod test_generic_structs;
mod test_simple_enums;
mod test_generic_enums;
mod test_recursive_structs;
mod test_recursive_enums;
mod test_serde_with_third_party;
mod test_multiple_borsh_attrs;
mod test_enum_discriminants;
#[cfg(feature = "bytes")]
mod test_ultimate_many_features_combined;
#[cfg(feature = "bson")]
mod test_bson_object_ids;
}
}
/// These are `BorshSchema` tests for various types
#[cfg(feature = "unstable__schema")]
#[rustfmt::skip]
mod schema {
#[cfg(feature = "ascii")]
mod test_ascii_strings;
mod test_strings;
mod test_arrays;
mod test_vecs;
mod test_tuple;
mod test_primitives;
#[cfg(feature = "std")]
mod test_ip_addr;
// mod test_nonzero_integers; // NOTE: there's nothing corresponding to `roundtrip::test_nonzero_integers`
mod test_range;
mod test_phantom_data;
mod test_option;
mod test_box;
#[cfg(hash_collections)]
mod test_hash_map;
mod test_btree_map;
mod test_cow;
mod test_cells;
#[cfg(feature = "rc")]
mod test_rc;
mod test_simple_structs;
mod test_generic_structs;
mod test_simple_enums;
mod test_generic_enums;
mod test_recursive_structs;
mod test_recursive_enums;
mod test_schema_with_third_party; // NOTE: this test corresponds to `roundtrip::test_serde_with_third_party`
mod test_enum_discriminants;
// mod test_ultimate_many_features_combined; // NOTE: there's nothing corresponding to `roundtrip::test_ultimate_many_features_combined`
// mod test_bson_object_ids; // NOTE: there's nothing corresponding to `roundtrip::test_bson_object_ids`
mod schema_conflict {
mod test_schema_conflict;
}
mod container_extension {
mod test_schema_validate;
mod test_max_size;
}
}
mod deserialization_errors {
#[cfg(feature = "ascii")]
mod test_ascii_strings;
mod test_cells;
mod test_initial;
}
mod init_in_deserialize {
#[cfg(feature = "derive")]
mod test_init_in_deserialize;
}
mod zero_sized_types {
#[cfg(feature = "derive")]
mod test_zero_sized_types_forbidden;
}
@@ -0,0 +1,150 @@
use alloc::{string::ToString, vec, vec::Vec};
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};
#[cfg(feature = "hashbrown")]
use hashbrown::{HashMap, HashSet};
use alloc::collections::{BTreeMap, BTreeSet, LinkedList, VecDeque};
use borsh::from_slice;
use borsh::to_vec;
use borsh::BorshDeserialize;
use borsh::BorshSerialize;
use borsh::error::ERROR_ZST_FORBIDDEN;
#[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug, Eq, PartialOrd, Ord, Hash)]
struct A();
#[test]
fn test_deserialize_vec_of_zst() {
let v = [0u8, 0u8, 0u8, 64u8];
let res = from_slice::<Vec<A>>(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_serialize_vec_of_zst() {
let v = vec![A()];
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_serialize_vec_of_unit_type() {
let v = vec![(), (), ()];
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_serialize_vec_of_vec_of_unit_type() {
let v: Vec<Vec<()>> = vec![vec![(), (), ()]];
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_deserialize_vec_deque_of_zst() {
let v = [0u8, 0u8, 0u8, 64u8];
let res = from_slice::<VecDeque<A>>(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_serialize_vec_deque_of_zst() {
let v: VecDeque<A> = vec![A()].into();
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_deserialize_linked_list_of_zst() {
let v = [0u8, 0u8, 0u8, 64u8];
let res = from_slice::<LinkedList<A>>(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_serialize_linked_list_of_zst() {
let v: LinkedList<A> = vec![A()].into_iter().collect();
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_deserialize_btreeset_of_zst() {
let v = [0u8, 0u8, 0u8, 64u8];
let res = from_slice::<BTreeSet<A>>(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_serialize_btreeset_of_zst() {
let v: BTreeSet<A> = vec![A()].into_iter().collect();
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[cfg(hash_collections)]
#[test]
fn test_deserialize_hashset_of_zst() {
let v = [0u8, 0u8, 0u8, 64u8];
let res = from_slice::<HashSet<A>>(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[cfg(hash_collections)]
#[test]
fn test_serialize_hashset_of_zst() {
let v: HashSet<A> = vec![A()].into_iter().collect();
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_deserialize_btreemap_of_zst() {
let v = [0u8, 0u8, 0u8, 64u8];
let res = from_slice::<BTreeMap<A, u64>>(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[test]
fn test_serialize_btreemap_of_zst() {
let v: BTreeMap<A, u64> = vec![(A(), 42u64)].into_iter().collect();
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[cfg(hash_collections)]
#[test]
fn test_deserialize_hashmap_of_zst() {
let v = [0u8, 0u8, 0u8, 64u8];
let res = from_slice::<HashMap<A, u64>>(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[cfg(hash_collections)]
#[test]
fn test_serialize_hashmap_of_zst() {
let v: HashMap<A, u64> = vec![(A(), 42u64)].into_iter().collect();
let res = to_vec(&v);
assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN);
}
#[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug)]
struct B(u32);
#[test]
fn test_deserialize_non_zst() {
let v = [1, 0, 0, 0, 64, 0, 0, 0];
let res = Vec::<B>::try_from_slice(&v);
assert!(res.is_ok());
}
#[test]
fn test_serialize_non_zst() {
let v = vec![B(1)];
let res = to_vec(&v);
assert!(res.is_ok());
}