Skip to content

Commit

Permalink
🔨 Rework subtest! syntax
Browse files Browse the repository at this point in the history
The existing `subtest!` syntax was a little foreign to work with due
to the custom `|<ident>|` syntax, which feels a little unnatural for
normal rust. This effectively formed a DSL that does not exist in
other parts of the language in any capacity, and it also posed
challenges for things like introducing attributes for subtests, and
even for IDE intellisense to provide better completion-syntax.

As an alternative, a new syntax of `subtest!(<ident>, <block>)` has been
put forward for testing in this change. This drops the custom `|<ident>|`
naming convention in favor of an explicit ident first-argument, followed
by a true `block` input.

Passing a block here will provide better context for parsing and
general intellisense, and enables more room for future-growth for
supporting things like attributes in subtests as defined in #14.

Existing tests/examples have been updated to reflect the new syntax.
  • Loading branch information
bitwizeshift committed May 22, 2023
1 parent 287110d commit b091f5b
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 95 deletions.
12 changes: 4 additions & 8 deletions neotest-macros/src/input/subtest_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,15 @@ use syn::Token;

pub struct SubtestInput {
pub ident: syn::Ident,
pub stmts: Vec<syn::Stmt>,
pub block: syn::Block,
}

impl Parse for SubtestInput {
fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse::<Token![|]>()?;
let ident: syn::Ident = input.parse()?;
input.parse::<Token![|]>()?;
input.parse::<Token![,]>()?;
let block: syn::Block = input.parse()?;

let mut stmts: Vec<syn::Stmt> = Default::default();
while !input.is_empty() {
stmts.push(input.parse()?);
}
Ok(Self { ident, stmts })
Ok(Self { ident, block })
}
}
8 changes: 4 additions & 4 deletions neotest-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,18 @@ pub fn neotest_fixture(attribute: TokenStream, item: TokenStream) -> TokenStream
}

struct Subtest {
stmts: Vec<syn::Stmt>,
block: syn::Block,
}

impl quote::ToTokens for Subtest {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let context = common::ident::context();
let inner = Inner(&self.stmts);
let block = &self.block;
let stmt: syn::Stmt = syn::parse_quote! {
if #context.can_execute_subtest() {
#[allow(unused)]
let mut #context = #context.subtest();
#inner
#block
};
};
stmt.to_tokens(tokens);
Expand All @@ -105,7 +105,7 @@ impl quote::ToTokens for Subtest {
#[proc_macro]
pub fn subtest(item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as SubtestInput);
let subtest = Subtest { stmts: input.stmts };
let subtest = Subtest { block: input.block };
subtest.to_token_stream().into()
}

Expand Down
2 changes: 1 addition & 1 deletion neotest-macros/src/suite/section_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ fn examine_macro(sections: &mut Vec<Section>, mac: &Macro, fail_on_macro: bool)

let input: SubtestInput = syn::parse(tokens)?;
define_subsection(sections, input.ident, |sections| -> syn::Result<()> {
examine_stmts(sections, &input.stmts, fail_on_macro)
examine_stmts(sections, &input.block.stmts, fail_on_macro)
})?;
}
Ok(())
Expand Down
20 changes: 10 additions & 10 deletions neotest/examples/subtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@ mod test {
fn test_string_default() {
let sut = String::default();

subtest! { |string_is_empty|
subtest!(string_is_empty, {
assert!(sut.is_empty());
}
subtest! { |string_has_zero_len|
});
subtest!(string_has_zero_len, {
assert_eq!(sut.len(), 0);
}
})
}

#[neotest(parameter = cap as [10, 42, 64])]
fn test_vec_with_capacity(cap: usize) {
let sut: Vec<i32> = Vec::with_capacity(cap);

subtest! { |capacity_is_set_to_input|
subtest!(capacity_is_set_to_input, {
assert_eq!(sut.capacity(), cap);
}
subtest! { |vec_is_empty|
});
subtest!(vec_is_empty, {
assert!(sut.is_empty());
}
subtest! { |vec_size_is_zero|
});
subtest!(vec_size_is_zero, {
assert_eq!(sut.len(), 0);
}
})
}
}

Expand Down
144 changes: 72 additions & 72 deletions neotest/src/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,149 +163,149 @@ mod test {
const VALUE: u32 = 5;
let matcher = Le(VALUE);

subtest! {|matches_less|
subtest!(matches_less, {
assert!(matcher.matches(VALUE - 1));
}
subtest! {|matches_equal|
});
subtest!(matches_equal, {
assert!(matcher.matches(VALUE));
}
subtest! {|does_not_match_greater|
});
subtest!(does_not_match_greater, {
assert!(!matcher.matches(VALUE + 1));
}
});
}

#[crate::neotest]
fn test_ge() {
const VALUE: u32 = 5;
let matcher = Ge(VALUE);

subtest! {|matches_greater|
subtest!(matches_greater, {
assert!(matcher.matches(VALUE + 1));
}
subtest! {|matches_equal|
});
subtest!(matches_equal, {
assert!(matcher.matches(VALUE));
}
subtest! {|does_not_match_less|
});
subtest!(does_not_match_less, {
assert!(!matcher.matches(VALUE - 1));
}
});
}

#[crate::neotest]
fn test_lt() {
const VALUE: u32 = 5;
let matcher = Lt(VALUE);

subtest! {|matches_less|
subtest!(matches_less, {
assert!(matcher.matches(VALUE - 1));
}
subtest! {|does_not_match_equal|
});
subtest!(does_not_match_equal, {
assert!(!matcher.matches(VALUE));
}
subtest! {|does_not_match_greater|
});
subtest!(does_not_match_greater, {
assert!(!matcher.matches(VALUE + 1));
}
});
}

#[crate::neotest]
fn test_gt() {
const VALUE: u32 = 5;
let matcher = Gt(VALUE);

subtest! {|matches_greater|
subtest!(matches_greater, {
assert!(matcher.matches(VALUE + 1));
}
subtest! {|does_not_match_equal|
});
subtest!(does_not_match_equal, {
assert!(!matcher.matches(VALUE));
}
subtest! {|does_not_match_less|
});
subtest!(does_not_match_less, {
assert!(!matcher.matches(VALUE - 1));
}
});
}

#[crate::neotest]
fn test_eq() {
const VALUE: u32 = 5;
let matcher = Eq(VALUE);

subtest! {|does_not_match_greater|
subtest!(does_not_match_greater, {
assert!(!matcher.matches(VALUE + 1));
}
subtest! {|matches_equal|
});
subtest!(matches_equal, {
assert!(matcher.matches(VALUE));
}
subtest! {|does_not_match_less|
});
subtest!(does_not_match_less, {
assert!(!matcher.matches(VALUE - 1));
}
});
}

#[crate::neotest]
fn test_ne() {
const VALUE: u32 = 5;
let matcher = Ne(VALUE);

subtest! {|matches_greater|
subtest!(matches_greater, {
assert!(matcher.matches(VALUE + 1));
}
subtest! {|does_not_match_equal|
});
subtest!(does_not_match_equal, {
assert!(!matcher.matches(VALUE));
}
subtest! {|matches_less|
});
subtest!(matches_less, {
assert!(matcher.matches(VALUE - 1));
}
});
}

#[crate::neotest]
fn test_any() {
let matcher = Any;

subtest! {|matches_int|
subtest!(matches_int, {
assert!(matcher.matches(5));
}
subtest! {|matches_string|
});
subtest!(matches_string, {
assert!(matcher.matches("hello world"));
}
});
}

#[crate::neotest]
fn test_not() {
const VALUE: u32 = 5;
let matcher = Not(Ge(VALUE)); // lt

subtest! {|negates_input|
subtest! {|does_not_match_greater|
subtest!(negates_input, {
subtest!(does_not_match_greater, {
assert!(!matcher.matches(VALUE + 1));
}
subtest! {|does_not_match_equal|
});
subtest!(does_not_match_equal, {
assert!(!matcher.matches(VALUE));
}
subtest! {|matches_less|
});
subtest!(matches_less, {
assert!(matcher.matches(VALUE - 1));
}
}
});
});
}

#[crate::neotest]
fn test_is_true() {
let matcher = IsTrue;

subtest! {|matches_true|
subtest!(matches_true, {
assert!(matcher.matches(true));
}
subtest! {|does_not_match_false|
});
subtest!(does_not_match_false, {
assert!(!matcher.matches(false));
}
});
}

#[crate::neotest]
fn test_is_false() {
let matcher = IsFalse;

subtest! {|matches_false|
subtest!(matches_false, {
assert!(matcher.matches(false));
}
subtest! {|does_not_match_true|
});
subtest!(does_not_match_true, {
assert!(!matcher.matches(true));
}
});
}

struct BoolLike(bool);
Expand All @@ -320,47 +320,47 @@ mod test {
fn test_is_truthy() {
let matcher = IsTruthy;

subtest! {|matches_truthy_value|
subtest!(matches_truthy_value, {
assert!(matcher.matches(BoolLike(true)));
}
subtest! {|does_not_match_falsey_value|
});
subtest!(does_not_match_falsey_value, {
assert!(!matcher.matches(BoolLike(false)));
}
});
}

#[crate::neotest]
fn test_is_falsey() {
let matcher = IsFalsey;

subtest! {|matches_falsey_value|
subtest!(matches_falsey_value, {
assert!(matcher.matches(BoolLike(false)));
}
subtest! {|does_not_match_truthy_value|
});
subtest!(does_not_match_truthy_value, {
assert!(!matcher.matches(BoolLike(true)));
}
});
}

#[crate::neotest]
fn test_is_none() {
let matcher = IsNone;

subtest! {|matches_none|
subtest!(matches_none, {
assert!(matcher.matches(None::<()>));
}
subtest! {|does_not_match_some|
});
subtest!(does_not_match_some, {
assert!(!matcher.matches(Some(42)));
}
});
}

#[crate::neotest]
fn test_is_some() {
let matcher = IsSome;

subtest! {|matches_some|
subtest!(matches_some, {
assert!(matcher.matches(Some(42)));
}
subtest! {|does_not_match_none|
});
subtest!(does_not_match_none, {
assert!(!matcher.matches(None::<()>));
}
});
}
}

0 comments on commit b091f5b

Please sign in to comment.