Coverage Report

Created: 2026-04-07 13:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-macro/src/lib.rs
Line
Count
Source
1
// Copyright 2024 The NativeLink Authors. All rights reserved.
2
//
3
// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    See LICENSE file for details
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
use proc_macro::TokenStream;
16
use proc_macro2::TokenTree;
17
use quote::{format_ident, quote};
18
use syn::{ItemFn, parse_macro_input};
19
20
// Helper function for debugging. Add prettyplease as dependency
21
//
22
// fn unparse(input: proc_macro2::TokenStream) -> String {
23
//     let item = syn::parse2(input).unwrap();
24
//     let file = syn::File {
25
//         attrs: vec![],
26
//         items: vec![item],
27
//         shebang: None,
28
//     };
29
30
//     prettyplease::unparse(&file)
31
// }
32
33
// Either use this as-is or as `#[nativelink_test("foo")]` where foo is the path for nativelink-util
34
// Mostly used inside nativelink-util as `#[nativelink_test("crate")]`
35
// If you start it with an ident instead, e.g. `#[nativelink_test(flavor = "multi_thread")]` we feed it into tokio::test
36
#[proc_macro_attribute]
37
486
pub fn nativelink_test(attr: TokenStream, item: TokenStream) -> TokenStream {
38
486
    let attr = proc_macro2::TokenStream::from(attr);
39
486
    let input_fn = parse_macro_input!(item as ItemFn);
40
486
    let mut maybe_crate_ident: Option<proc_macro2::TokenStream> = None;
41
486
    let mut maybe_tokio_attrs: Option<proc_macro2::TokenStream> = None;
42
43
486
    for 
a6
in attr.clone() {
44
6
        assert!(maybe_crate_ident.is_none());
45
46
6
        match a {
47
5
            TokenTree::Literal(l) => {
48
5
                let s = format_ident!("{}", l.to_string().replace('"', ""));
49
5
                maybe_crate_ident = Some(quote! {#s});
50
5
            }
51
            TokenTree::Ident(_) => {
52
1
                maybe_tokio_attrs = Some(attr);
53
1
                break;
54
            }
55
            _ => {
56
0
                panic!("unsupported tokentree: {a:?}");
57
            }
58
        }
59
    }
60
61
486
    let fn_name = &input_fn.sig.ident;
62
486
    let fn_block = &input_fn.block;
63
486
    let fn_inputs = &input_fn.sig.inputs;
64
486
    let fn_output = &input_fn.sig.output;
65
486
    let fn_attr = &input_fn.attrs;
66
486
    let crate_ident = maybe_crate_ident.unwrap_or_else(|| 
quote!481
(::nativelink_util));
67
486
    let tokio_attrs = maybe_tokio_attrs.unwrap_or_else(|| 
quote!485
());
68
69
486
    let expanded = quote! {
70
        #(#fn_attr)*
71
        #[expect(
72
            clippy::disallowed_methods,
73
            reason = "`tokio::test` uses `tokio::runtime::Runtime::block_on`"
74
        )]
75
        #[tokio::test(#tokio_attrs)]
76
        #[::tracing_test::traced_test]
77
        async fn #fn_name(#fn_inputs) #fn_output {
78
            #crate_ident::__tracing::error_span!(stringify!(#fn_name))
79
                .in_scope(|| async move {
80
                    #crate_ident::common::reseed_rng_for_test().unwrap();
81
                    let res = #fn_block;
82
                    logs_assert(|lines: &[&str]| {
83
                        for line in lines {
84
                            // Most cases of this are us failing to skip something from debug
85
                            // But aws_runtime also has this, so ignore that
86
                            if line.contains(" data: b") && !line.contains("aws_runtime::content_encoding::body::http_body_1_x") {
87
                                return Err(format!("Non-redacted data in \"{line}\""));
88
                            }
89
                        }
90
                        Ok(())
91
                    });
92
                    res
93
                })
94
                .await
95
        }
96
    };
97
98
486
    TokenStream::from(expanded)
99
486
}