Skip to content

Commit

Permalink
Add GhcidNg test harness
Browse files Browse the repository at this point in the history
Add test-harness-macro
  • Loading branch information
9999years committed Aug 28, 2023
1 parent f8fd033 commit cfcc024
Show file tree
Hide file tree
Showing 9 changed files with 487 additions and 31 deletions.
77 changes: 47 additions & 30 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
members = [
"ghcid-ng",
"test-harness",
"test-harness-macro",
]

resolver = "2"
15 changes: 15 additions & 0 deletions test-harness-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "test-harness-macro"
version = "0.1.0"
edition = "2021"

description = "Test attribute for ghcid-ng"

publish = false

[lib]
proc-macro = true

[dependencies]
quote = "1.0.33"
syn = { version = "2.0.29", features = ["full"] }
106 changes: 106 additions & 0 deletions test-harness-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use proc_macro::TokenStream;

use quote::quote;
use quote::ToTokens;
use syn::parse;
use syn::parse::Parse;
use syn::parse::ParseStream;
use syn::Attribute;
use syn::Block;
use syn::Ident;
use syn::ItemFn;

/// GHC versions to run each test under.
///
/// Keep this synced with `../../flake.nix`.
const GHC_VERSIONS: [&str; 4] = ["9.0.2", "9.2.8", "9.4.6", "9.6.2"];

#[proc_macro_attribute]
pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse annotated function
let mut function: ItemFn = parse(item).expect("Could not parse item as function");

function.attrs.extend(
parse::<Attributes>(
quote! {
#[tokio::test]
#[tracing_test::traced_test]
#[allow(non_snake_case)]
}
.into(),
)
.expect("Could not parse quoted attributes #[tokio::test] and #[tracing_test::traced_test]")
.0,
);

// Generate functions for each GHC version we want to test.
let mut ret = TokenStream::new();
for ghc_version in GHC_VERSIONS {
ret.extend::<TokenStream>(
make_test_fn(function.clone(), ghc_version)
.to_token_stream()
.into(),
);
}
ret
}

struct Attributes(Vec<Attribute>);

impl Parse for Attributes {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self(input.call(Attribute::parse_outer)?))
}
}

fn make_test_fn(mut function: ItemFn, ghc_version: &str) -> ItemFn {
let ghc_version_ident = ghc_version.replace('.', "");
let stmts = function.block.stmts;
let test_name_base = function.sig.ident.to_string();
let test_name = format!("{test_name_base}_{ghc_version_ident}");
function.sig.ident = Ident::new(&test_name, function.sig.ident.span());

let new_body = parse::<Block>(
quote! {
{
::test_harness::internal::IN_CUSTOM_TEST_HARNESS.with(|value| {
value.store(true, ::std::sync::atomic::Ordering::SeqCst);
});
::test_harness::internal::CARGO_TARGET_TMPDIR.with(|tmpdir| {
*tmpdir.borrow_mut() = Some(::std::path::PathBuf::from(env!("CARGO_TARGET_TMPDIR")));
});
::test_harness::internal::GHC_VERSION.with(|tmpdir| {
*tmpdir.borrow_mut() = #ghc_version.to_owned();
});

match ::tokio::task::spawn(async {
#(#stmts);*
}).await {
Err(err) => {
// Copy out temp files
::test_harness::internal::save_test_logs(
format!("{}::{}", module_path!(), #test_name)
);
::test_harness::internal::cleanup_tempdir();

if err.is_panic() {
::std::panic::resume_unwind(err.into_panic());
} else {
panic!("Test cancelled? {err:?}");
}
}
Ok(()) => {
::test_harness::internal::cleanup_tempdir();
}
};
}
}
.into(),
)
.expect("Could not parse function body");

// Replace function body
*function.block = new_body;

function
}
4 changes: 4 additions & 0 deletions test-harness/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ publish = false

[dependencies]
backoff = { version = "0.4.0", default-features = false }
fs_extra = "1.3.0"
itertools = "0.11.0"
miette = { version = "5.9.0", features = ["fancy"] }
regex = "1.9.4"
serde = { version = "1.0.186", features = ["derive"] }
serde_json = "1.0.105"
tempfile = "3.8.0"
test-harness-macro = { path = "../test-harness-macro" }
test_bin = "0.4.0"
tokio = { version = "1.28.2", features = ["full", "tracing"] }
tracing = "0.1.37"
Loading

0 comments on commit cfcc024

Please sign in to comment.