Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Line coverage mismatch on trybuild-based proc-macros tests #358

Open
dbarbosapn opened this issue Mar 20, 2024 · 4 comments
Open

Line coverage mismatch on trybuild-based proc-macros tests #358

dbarbosapn opened this issue Mar 20, 2024 · 4 comments
Labels
S-needs-repro Status: This issue has no reproduction and needs a reproduction to make progress.

Comments

@dbarbosapn
Copy link

Hello,

I'm seeing some big discrepancies in code coverage when testing proc macros with trybuild.

Crate structure:
├── src
│ ├── lib.rs
├── tests
│ ├── testcases
│ │ ├── fail
│ │ │ ├── will_fail.rs
│ │ │ ├── will_fail.stderr
│ │ ├── pass
│ │ │ ├── will_pass.rs
│ ├── tests.rs

The content of tests.rs is:

#[cfg(test)]
mod macro_tests {
    #[test]
    fn macro_failures() {
        let t = trybuild::TestCases::new();
        t.compile_fail("tests/testcases/fail/*.rs");
    }

    #[test]
    fn macro_successes() {
        let t = trybuild::TestCases::new();
        t.pass("tests/testcases/pass/*.rs");
    }
}

The content of lib.rs is:

// Copyright (c) Microsoft Corporation. All Rights Reserved.

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

/// Marks async function to be executed by the mycrate runtime.
///
/// ## Usage
///
/// ```ignore
/// #[mycrate::rt::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
#[proc_macro_attribute]
pub fn rt_main(_: TokenStream, item: TokenStream) -> TokenStream {
    let mut input = syn::parse_macro_input!(item as syn::ItemFn);
    let attrs = &input.attrs;
    let vis = &input.vis;
    let sig = &mut input.sig;
    let body = &input.block;
    let name = &sig.ident;

    if sig.asyncness.is_none() {
        return syn::Error::new_spanned(
            sig.fn_token,
            "function must be async to use the attribute",
        )
        .to_compile_error()
        .into();
    }

    sig.asyncness = None;

    (quote! {
        #(#attrs)*
        #vis #sig {
            mycrate::rt::System::new(stringify!(#name))
                .block_on(async move { #body })
        }
    })
    .into()
}

/// Marks async test function to be executed by the mycrate runtime.
///
/// ## Usage
///
/// ```ignore
/// #[mycrate::rt::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
#[proc_macro_attribute]
pub fn rt_test(_: TokenStream, item: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(item as syn::ItemFn);

    let ret = &input.sig.output;
    let name = &input.sig.ident;
    let body = &input.block;
    let attrs = &input.attrs;
    let mut has_test_attr = false;

    for attr in attrs {
        if attr.path().is_ident("test") {
            has_test_attr = true;
        }
    }

    if input.sig.asyncness.is_none() {
        return syn::Error::new_spanned(
            input.sig.fn_token,
            format!(
                "function must be async to use the attribute, {}",
                input.sig.ident
            ),
        )
        .to_compile_error()
        .into();
    }

    let result = if has_test_attr {
        quote! {
            #(#attrs)*
            fn #name() #ret {
                mycrate::rt::System::new("test")
                    .block_on(async { #body })
            }
        }
    } else {
        quote! {
            #[test]
            #(#attrs)*
            fn #name() #ret {
                mycrate::rt::System::new("test")
                    .block_on(async { #body })
            }
        }
    };

    result.into()
}

This is what workspace coverage is reporting:
image
But, when the file is clicked, all lines and regions are covered.

This is affecting our CI gates so we'll have to exclude macros from coverage, but would love to get some help on this.

Additional details:

  • We're using latest cargo-llvm-cov.
  • When running with --show-missing-lines, it reports the same coverage issue, but no lines are reported as uncovered.
  • After adding the macros tests, we noticed "warning: 3 functions have mismatched data" popped up.

Thanks in advance!

@taiki-e
Copy link
Owner

taiki-e commented Mar 20, 2024

But, when the file is clicked, all lines and regions are covered.

I have not yet checked the reproduction locally, but I suspect this is the same problem as #324.

EDIT: I will not be able to reproduce the problem locally as a complete reproduction is not provided in the first place.

@taiki-e
Copy link
Owner

taiki-e commented Mar 20, 2024

I suspect this is the same problem as #324.

Ah, nah, maybe another issue is involved because the line coverage is also affected.

In any case, I think we need a complete reproduction, including what is tested in trybuild.

@taiki-e taiki-e added the needs-mcve Call for participation: This issue needs a Minimal Complete and Verifiable Example label Mar 20, 2024
@taiki-e taiki-e added S-needs-repro Status: This issue has no reproduction and needs a reproduction to make progress. and removed needs-mcve Call for participation: This issue needs a Minimal Complete and Verifiable Example labels Apr 23, 2024
@PollRobots
Copy link

I uploaded https://github.com/PollRobots/min-repo

If I run

cargo llvm-cov --open

I can see that all tests run and succeed, including trybuild tests that verify each error branch in the macro.
But in the coverage report the error branches are clearly labeled as not taken

@PollRobots
Copy link

@taiki-e hi, is this something that you will have time to look at?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-needs-repro Status: This issue has no reproduction and needs a reproduction to make progress.
Projects
None yet
Development

No branches or pull requests

3 participants