Compare commits

...

4 commits

Author SHA1 Message Date
Victor Martinez Montané
af490931f2 chore: release-plz update (#6)
This is an automated PR generated by [release-plz](https://github.com/MarcoIeni/release-plz) via Woodpecker CI.

Co-authored-by: release-plz-bot <bot@codeberg.org>
Reviewed-on: https://codeberg.org/JasterV/test-context/pulls/6
2026-04-24 14:32:50 +02:00
JasterV
a76038297a chore: update deps 2026-04-23 01:34:39 +02:00
Victor Martinez Montané
a44b045c8f chore: release-plz update (#5)
This is an automated PR generated by [release-plz](https://github.com/MarcoIeni/release-plz) via Woodpecker CI.

Co-authored-by: release-plz-bot <bot@codeberg.org>
Co-authored-by: JasterV <49537445+JasterV@users.noreply.github.com>
Reviewed-on: https://codeberg.org/JasterV/test-context/pulls/5
2026-04-22 17:30:49 +02:00
Victor Martinez Montané
dc77d78608 fix/pattern-matching-breaks-compilation (#4)
This PR solves the issue identified at https://codeberg.org/JasterV/test-context/issues/2.

Previously, when defining the function argument that implements the `TestContext` trait, only "Ident" patterns were accepted (That is, only names).

So, the following was valid:

```rust
#[test]
fn my_test(context: MyContext) {}
```

But the following would throw an error:

```rust
#[test]
fn my_test(MyContext { n }: MyContext {}
```

With this PR, we are now able to accept any kind of "pattern" such as struct pattern matching, enum pattern matching... etc.

We only really care about the "type", and not the "pattern", so this PR makes sure that the "pattern" part of the binding is left untouched.

Tests have been added to ensure that destructuring a struct compiles

Co-authored-by: JasterV <49537445+JasterV@users.noreply.github.com>
Reviewed-on: https://codeberg.org/JasterV/test-context/pulls/4
2026-04-22 17:26:08 +02:00
7 changed files with 43 additions and 40 deletions

View file

@ -7,12 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.5.8](https://codeberg.org/JasterV/test-context/compare/v0.5.7...v0.5.8) - 2026-04-22
### Other
- update deps
## [0.5.7](https://codeberg.org/JasterV/test-context/compare/v0.5.6...v0.5.7) - 2026-04-22
### Fixed
- fix/pattern-matching-breaks-compilation ([#4](https://codeberg.org/JasterV/test-context/pulls/4))
## [0.5.6](https://codeberg.org/JasterV/test-context/compare/v0.5.5...v0.5.6) - 2026-04-22
### Fix
### Other
- Fixed an issue identified in [#2](https://codeberg.org/JasterV/test-context/issues/2) where pattern matching in
test function arguments caused the macro to error. PR [#3](https://codeberg.org/JasterV/test-context/pulls/3)
- Updated docs
## [0.5.5](https://codeberg.org/JasterV/test-context/compare/v0.5.4...v0.5.5) - 2026-03-07

View file

@ -4,7 +4,7 @@ members = ["test-context", "test-context-macros"]
[workspace.package]
edition = "2024"
version = "0.5.6"
version = "0.5.8"
rust-version = "1.91.0"
homepage = "https://codeberg.org/JasterV/test-context"
repository = "https://codeberg.org/JasterV/test-context"

View file

@ -14,6 +14,6 @@ license.workspace = true
proc-macro = true
[dependencies]
quote = "1.0.3"
quote = "1.0.45"
syn = { version = "^2", features = ["full"] }
proc-macro2 = "1.0.78"
proc-macro2 = "1.0.106"

View file

@ -93,13 +93,12 @@ fn refactor_input_body(
let result_name = format_ident!("wrapped_result");
let body = &input.block;
let is_async = input.sig.asyncness.is_some();
let context_arg_name = context_arg.name;
let context_pattern = context_arg.pattern;
let context_binding = match context_arg.mode {
ContextArgMode::Owned => quote! { let #context_arg_name = __context; },
ContextArgMode::OwnedMut => quote! { let mut #context_arg_name = __context; },
ContextArgMode::Reference => quote! { let #context_arg_name = &__context; },
ContextArgMode::MutableReference => quote! { let #context_arg_name = &mut __context; },
ContextArgMode::Owned => quote! { let #context_pattern = __context; },
ContextArgMode::Reference => quote! { let #context_pattern = &__context; },
ContextArgMode::MutableReference => quote! { let #context_pattern = &mut __context; },
};
let body = if args.skip_teardown && is_async {

View file

@ -3,8 +3,8 @@ use syn::FnArg;
#[derive(Clone)]
pub struct ContextArg {
/// The identifier name used for the context argument.
pub name: syn::Ident,
/// The original pattern (left side of a `pattern: type` expression).
pub pattern: syn::Pat,
/// The mode in which the context was passed to the test function.
pub mode: ContextArgMode,
}
@ -13,8 +13,6 @@ pub struct ContextArg {
pub enum ContextArgMode {
/// The argument was passed as an owned value (`ContextType`). Only valid with `skip_teardown`.
Owned,
/// The argument was passed as an owned value (mut `ContextType`). Only valid with `skip_teardown`.
OwnedMut,
/// The argument was passed as an immutable reference (`&ContextType`).
Reference,
/// The argument was passed as a mutable reference (`&mut ContextType`).
@ -25,7 +23,6 @@ impl ContextArgMode {
pub fn is_owned(&self) -> bool {
match self {
ContextArgMode::Owned => true,
ContextArgMode::OwnedMut => true,
ContextArgMode::Reference => false,
ContextArgMode::MutableReference => false,
}
@ -40,15 +37,14 @@ pub enum TestArg {
impl TestArg {
pub fn parse_arg_with_expected_context(arg: FnArg, expected_context_type: &syn::Type) -> Self {
let syn::FnArg::Typed(pat_type) = &arg else {
return Self::Any(arg);
};
let syn::Pat::Ident(pat_ident) = &*pat_type.pat else {
return Self::Any(arg);
let pat_type = match arg {
FnArg::Typed(pat_type) => pat_type,
FnArg::Receiver(_) => return TestArg::Any(arg),
};
// fn example(pattern: arg_type)
let arg_type = &*pat_type.ty;
let pattern = &*pat_type.pat;
// Check for mutable/immutable reference
if let syn::Type::Reference(type_ref) = arg_type
@ -61,28 +57,19 @@ impl TestArg {
};
return TestArg::Context(ContextArg {
name: pat_ident.ident.clone(),
pattern: pattern.to_owned(),
mode,
});
}
if !types_equal(arg_type, expected_context_type) {
return TestArg::Any(arg);
if types_equal(arg_type, expected_context_type) {
return TestArg::Context(ContextArg {
pattern: pattern.to_owned(),
mode: ContextArgMode::Owned,
});
}
// To determine mutability for an owned type, we check the identifier pattern.
let mode = if pat_ident.mutability.is_some() {
// This catches signatures like: `mut my_ctx: ContextType`
ContextArgMode::OwnedMut
} else {
// This catches signatures like: `my_ctx: ContextType`
ContextArgMode::Owned
};
TestArg::Context(ContextArg {
name: pat_ident.ident.clone(),
mode,
})
TestArg::Any(FnArg::Typed(pat_type))
}
}

View file

@ -13,9 +13,9 @@ authors.workspace = true
license.workspace = true
[dependencies]
test-context-macros = { version = "0.5.6", path = "../test-context-macros/" }
test-context-macros = { version = "0.5.8", path = "../test-context-macros/" }
futures = "0.3"
[dev-dependencies]
rstest = "0.26.1"
tokio = { version = "1.0", features = ["macros", "rt"] }
tokio = { version = "1.52", features = ["macros", "rt"] }

View file

@ -25,6 +25,12 @@ fn test_sync_setup(ctx: &mut Context) {
assert_eq!(ctx.n, 1);
}
#[test_context(Context)]
#[test]
fn test_pattern_match_setup(Context { n }: &mut Context) {
assert_eq!(*n, 1);
}
#[test_context(Context)]
#[test]
#[should_panic(expected = "Number changed")]