shimforge-macros 0.1.2

Typed mock generation for shimforge
Documentation
use super::*;

fn expanded(input: Tokens) -> String {
    let tokens = expand(syn::parse2(input));
    syn::parse2::<Expr>(tokens.clone()).unwrap();
    tokens.to_string()
}

#[test]
fn owned_outputs_have_value_helpers() {
    let tokens = expanded(quote!(
        ::renamed,
        session,
        source,
        fn(u32, String) -> String,
    ));
    assert!(tokens.contains("pub fn returns_default"));
    assert!(tokens.contains("pub fn return_once"));
    assert!(tokens.contains(":: renamed :: __install"));
}

#[test]
fn unit_output_and_empty_arguments() {
    let tokens = expanded(quote!(shimforge, session, source, fn()));
    assert!(tokens.contains("__ShimforgeBuilder < () >"));
}

#[test]
fn borrowed_outputs_keep_their_lifetimes() {
    for signature in [
        quote!(fn(&str) -> &str),
        quote!(for<'a> fn(&'a mut [u8]) -> &'a mut [u8]),
        quote!(for<'a> fn(&'a str) -> std::borrow::Cow<'a, str>),
    ] {
        let tokens = expanded(quote!(shimforge, session, source, #signature));
        assert!(!tokens.contains("pub fn returns_default"));
        assert!(tokens.contains("pub fn returning_once"));
    }
}

#[test]
fn static_output_has_value_helpers() {
    let tokens = expanded(quote!(shimforge, session, source, fn() -> &'static str));
    assert!(tokens.contains("pub fn returns"));
}

#[test]
fn unsupported_signatures_and_invalid_input_report_errors() {
    for input in [
        quote!(shimforge, session, source, fn(...)),
        quote!(shimforge),
        quote!(shimforge, session, source, fn(), extra),
    ] {
        assert!(
            expand(syn::parse2(input))
                .to_string()
                .contains("compile_error")
        );
    }
}

#[test]
fn native_and_unsafe_signatures_keep_their_abi() {
    for signature in [
        quote!(unsafe fn(i32) -> i32),
        quote!(extern "C" fn(i32) -> i32),
        quote!(unsafe extern "system" fn(i32) -> i32),
        quote!(extern "C-unwind" fn(i32) -> i32),
    ] {
        let tokens = expanded(quote!(shimforge, session, source, #signature));
        assert!(tokens.contains("pub fn with"));
        assert!(tokens.contains("pub fn times"));
    }
}

#[test]
fn replacement_checks_parse_the_source_target_and_signature() {
    let checked = expand_check(syn::parse2(quote!(source, saved, target, fn(&str) -> &str)));
    assert!(checked.to_string().contains("__same_output"));
    let invalid = expand_check(syn::parse2(quote!(source)));
    assert!(invalid.to_string().contains("compile_error"));
}

#[test]
fn local_replacements_infer_generic_argument_types() {
    for signature in [
        quote!(fn()),
        quote!(for<'a> fn(&'a str) -> &'a str),
        quote!(unsafe extern "system" fn(u32) -> u64),
    ] {
        let tokens = expand_replacement(syn::parse2(
            quote!(shimforge, session, source, target, #signature),
        ));
        syn::parse2::<Expr>(tokens.clone()).unwrap();
        assert!(tokens.to_string().contains("__dispatch"));
    }
    assert!(
        expand_replacement(syn::parse2(quote!(shimforge)))
            .to_string()
            .contains("compile_error")
    );
}

#[test]
fn source_paths_keep_their_generic_lifetimes() {
    let saved = parse_quote!(saved);
    for source in [parse_quote!(source), parse_quote!((source))] {
        let checked = source_item(&source, &saved);
        assert_eq!(quote!(#checked).to_string(), "source");
    }
    let grouped = Expr::Group(syn::ExprGroup {
        attrs: Vec::new(),
        group_token: Default::default(),
        expr: Box::new(parse_quote!(source)),
    });
    assert!(matches!(source_item(&grouped, &saved), Expr::Path(_)));
    let expression = parse_quote!(choose()?);
    assert!(std::ptr::eq(source_item(&expression, &saved), &saved));
}