use std::{panic, sync::Mutex};
use syn::parse_quote;
#[doc(hidden)]
pub mod __private {
pub use regex;
}
#[macro_export]
macro_rules! assert_parse_error_matches {
($expr:expr, $reg:literal) => {
match $expr {
Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"),
Err(e) => {
let error_message = e.to_string();
let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg)
.expect("Invalid regex pattern");
assert!(
re.is_match(&error_message),
"Error message \"{}\" does not match the pattern \"{}\"",
error_message,
$reg
);
},
}
};
}
#[macro_export]
macro_rules! assert_pallet_parses {
(
#[manifest_dir($manifest_dir:literal)]
$($tokens:tt)*
) => {
{
let mut pallet: Option<$crate::pallet::parse::Def> = None;
$crate::pallet::parse::tests::simulate_manifest_dir($manifest_dir, core::panic::AssertUnwindSafe(|| {
pallet = Some($crate::pallet::parse::Def::try_from(syn::parse_quote! {
$($tokens)*
}, false).unwrap());
}));
pallet.unwrap()
}
}
}
#[macro_export]
macro_rules! assert_pallet_parse_error {
(
#[manifest_dir($manifest_dir:literal)]
#[error_regex($reg:literal)]
$($tokens:tt)*
) => {
$crate::pallet::parse::tests::simulate_manifest_dir($manifest_dir, || {
$crate::assert_parse_error_matches!(
$crate::pallet::parse::Def::try_from(
parse_quote! {
$($tokens)*
},
false
),
$reg
);
});
}
}
pub fn simulate_manifest_dir<P: AsRef<std::path::Path>, F: FnOnce() + std::panic::UnwindSafe>(
path: P,
closure: F,
) {
use std::{env::*, path::*};
static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(());
let guard = MANIFEST_DIR_LOCK.lock().unwrap();
let orig = PathBuf::from(
var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"),
);
set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref()));
let result = panic::catch_unwind(closure);
set_var("CARGO_MANIFEST_DIR", &orig);
drop(guard);
result.unwrap();
}
mod tasks;
#[test]
fn test_parse_minimal_pallet() {
assert_pallet_parses! {
#[manifest_dir("../../examples/basic")]
#[frame_support::pallet]
pub mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
};
}
#[test]
fn test_parse_pallet_missing_pallet() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("Missing `\\#\\[pallet::pallet\\]`")]
#[frame_support::pallet]
pub mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {}
}
}
}
#[test]
fn test_parse_pallet_missing_config() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("Missing `\\#\\[pallet::config\\]`")]
#[frame_support::pallet]
pub mod pallet {
#[pallet::pallet]
pub struct Pallet<T>(_);
}
}
}
#[test]
fn test_parse_pallet_deprecated_attribute_on_error_enum() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("The `\\#\\[deprecated\\]` attribute should be applied to individual variants, not the enum as a whole\\.")]
#[frame_support::pallet]
pub mod pallet {
#[pallet::error]
#[deprecated(note = "This enum is deprecated and should not be used.")]
pub enum Error<T> {
Foo
}
}
}
}
#[test]
fn test_parse_pallet_deprecated_attribute_on_event_enum() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("The `\\#\\[deprecated\\]` attribute should be applied to individual variants, not the enum as a whole\\.")]
#[frame_support::pallet]
pub mod pallet {
#[pallet::event]
#[deprecated(note = "This enum is deprecated and should not be used.")]
pub enum Event<T> {
Foo
}
}
}
}