Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions glib-macros/src/async_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Take a look at the license at the top of the repository in the LICENSE file.

use proc_macro::TokenStream;
use quote::ToTokens;

pub(crate) fn async_test(_args: TokenStream, mut item: TokenStream) -> TokenStream {
let mut item_fn: syn::ItemFn = match syn::parse(item.clone()) {
Ok(it) => it,
Err(e) => {
item.extend(TokenStream::from(e.into_compile_error()));
return item;
}
};

if item_fn.sig.asyncness.is_none() {
item.extend(TokenStream::from(
syn::Error::new_spanned(
item_fn.sig.ident,
"The 'async' keyword is missing from the test function declaration",
)
.into_compile_error(),
));
return item;
}

item_fn.sig.asyncness = None;

let gen_attr = quote::quote! {
#[::core::prelude::v1::test]
};

let body = &item_fn.block;

item_fn.block = syn::parse2(quote::quote! {
{
let main_ctx = glib::MainContext::new();
main_ctx.block_on(async #body)
}
})
.expect("Body parsing failure");

let mut tokens = TokenStream::new();
tokens.extend(TokenStream::from(gen_attr.to_token_stream()));
tokens.extend(TokenStream::from(item_fn.into_token_stream()));

tokens
}
20 changes: 20 additions & 0 deletions glib-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Take a look at the license at the top of the repository in the LICENSE file.

mod async_test;
mod boxed_derive;
mod clone;
mod closure;
Expand Down Expand Up @@ -1567,3 +1568,22 @@ pub fn derive_value_delegate(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as value_delegate_derive::ValueDelegateInput);
value_delegate_derive::impl_value_delegate(input).unwrap()
}

/// An attribute macro for writing asynchronous test functions.
///
/// This macro is designed to wrap an asynchronous test function and ensure that
/// it runs within a `glib::MainContext`. It helps in writing async tests that
/// require the use of an event loop for the asynchronous execution.
///
/// # Example
///
/// ```
/// #[glib::async_test]
/// async fn my_async_test() {
/// // Test code that runs asynchronously
/// }
/// ```
#[proc_macro_attribute]
pub fn async_test(args: TokenStream, item: TokenStream) -> TokenStream {
async_test::async_test(args, item)
}
5 changes: 3 additions & 2 deletions glib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ pub use bitflags;
#[doc(hidden)]
pub use glib_macros::cstr_bytes;
pub use glib_macros::{
clone, closure, closure_local, derived_properties, flags, object_interface, object_subclass,
Boxed, Downgrade, Enum, ErrorDomain, Properties, SharedBoxed, ValueDelegate, Variant,
async_test, clone, closure, closure_local, derived_properties, flags, object_interface,
object_subclass, Boxed, Downgrade, Enum, ErrorDomain, Properties, SharedBoxed, ValueDelegate,
Variant,
};
pub use glib_sys as ffi;
pub use gobject_sys as gobject_ffi;
Expand Down
Loading