1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License in the LICENSE file at the
6// root of this repository, or online at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
1516//! test macro with auto-initialized logging
1718use proc_macro::TokenStream;
19use proc_macro2::TokenStream as TokenStream2;
20use quote::quote;
21use syn::{AttributeArgs, ItemFn, Meta, NestedMeta, ReturnType, parse_macro_input, parse_quote};
2223/// Based on <https://github.com/d-e-s-o/test-log>
24/// Copyright (C) 2019-2022 Daniel Mueller <deso@posteo.net>
25/// SPDX-License-Identifier: (Apache-2.0 OR MIT)
26///
27/// Implementation for the `test` macro.
28pub fn test_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
29let args = parse_macro_input!(attr as AttributeArgs);
30let input = parse_macro_input!(item as ItemFn);
3132let inner_test = match args.as_slice() {
33 [] => parse_quote! { ::core::prelude::v1::test },
34 [NestedMeta::Meta(Meta::Path(path))] => quote! { #path },
35 [NestedMeta::Meta(Meta::List(list))] => quote! { #list },
36_ => panic!("unsupported attributes supplied: {:?}", args),
37 };
3839 expand_wrapper(&inner_test, &input)
40}
4142fn expand_logging_init() -> TokenStream2 {
43let crate_name = std::env::var("CARGO_PKG_NAME").unwrap();
44if crate_name == "mz-ore" {
45quote! {
46 {
47use crate::test;
48let _ = test::init_logging();
49 }
50 }
51 } else {
52quote! {
53 {
54let _ = ::mz_ore::test::init_logging();
55 }
56 }
57 }
58}
5960/// Emit code for a wrapper function around a test function.
61fn expand_wrapper(inner_test: &TokenStream2, wrappee: &ItemFn) -> TokenStream {
62let attrs = &wrappee.attrs;
63let async_ = &wrappee.sig.asyncness;
64let await_ = if async_.is_some() {
65quote! {.await}
66 } else {
67quote! {}
68 };
69let body = &wrappee.block;
70let test_name = &wrappee.sig.ident;
7172// Note that Rust does not allow us to have a test function with
73 // #[should_panic] that has a non-unit return value.
74let ret = match &wrappee.sig.output {
75 ReturnType::Default => quote! {},
76 ReturnType::Type(_, type_) => quote! {-> #type_},
77 };
7879let logging_init = expand_logging_init();
8081let result = quote! {
82#[#inner_test]
83#(#attrs)*
84 #async_ fn #test_name() #ret {
85 #async_ fn test_impl() #ret {
86 #body
87 }
8889 #logging_init
9091 test_impl()#await_
92 }
93 };
94 result.into()
95}