Mercurial > crates > nonstick
diff libpam-sys/libpam-sys-test/build.rs @ 127:c77846f3a979
GET CTEST WORKING.
This will verify that the functions we're exporting are correct.
It has been a nightmare.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Mon, 30 Jun 2025 22:56:26 -0400 |
parents | 2b255c92417b |
children |
line wrap: on
line diff
--- a/libpam-sys/libpam-sys-test/build.rs Mon Jun 30 21:52:10 2025 -0400 +++ b/libpam-sys/libpam-sys-test/build.rs Mon Jun 30 22:56:26 2025 -0400 @@ -1,7 +1,10 @@ use bindgen::MacroTypeVariation; use libpam_sys_impls::__pam_impl_enum__; +use proc_macro2::{Group, TokenStream, TokenTree}; use quote::{format_ident, ToTokens}; -use std::path::PathBuf; +use std::path::Path; +use std::process::Command; +use std::str::FromStr; use std::{env, fs}; use syn::{Item, ItemConst, Type, TypePath}; @@ -46,6 +49,7 @@ }, }; generate_const_test(&config); + generate_ctest(&config); } fn generate_const_test(config: &TestConfig) { @@ -63,8 +67,15 @@ let generated = builder.generate().unwrap().to_string(); let file = syn::parse_file(&generated).unwrap(); - let mut tests = vec![]; - tests.push("{".into()); + let mut tests = vec![ + "use libpam_sys::*;".into(), + "#[allow(deprecated, overflowing_literals)]".into(), + "fn main() {".into(), + format!( + "assert_eq!(PamImpl::CURRENT, PamImpl::{:?});", + PamImpl::CURRENT + ), + ]; tests.extend( file.items .iter() @@ -83,18 +94,97 @@ path: format_ident!("i32").into(), })); format!( - "assert_eq!({tokens}, libpam_sys::{name});", + "assert_eq!({tokens}, {name});", tokens = item.expr.to_token_stream(), name = item.ident ) }), ); tests.push("}".into()); - fs::write( - PathBuf::from(env::var("OUT_DIR").unwrap()).join("constant_test.rs"), - tests.join("\n"), + let const_test = test_file("constant_test.rs"); + fs::write(&const_test, tests.join("\n")).unwrap(); + rustfmt(&const_test); +} + +fn generate_ctest(config: &TestConfig) { + let mut test = ctest::TestGenerator::new(); + + for header in config.headers.iter() { + if header.starts_with('"') { + test.include(env::var("CARGO_MANIFEST_DIR").unwrap()); + } + test.header(&header[1..header.len() - 1]); + } + // These are opaque structs. + test.skip_struct(|name| matches!(name, "pam_handle" | "AppData")); + test.skip_type(|name| matches!(name, "ConversationCallback" | "CleanupCallback")); + test.type_name(|name, _is_struct, is_union| { + assert!(!is_union); // we scabbin' + match name { + "pam_handle" => "struct pam_handle", + "pam_conv" => "struct pam_conv", + "pam_message" => "struct pam_message", + "pam_response" => "struct pam_response", + "AppData" => "void", + other => other, + } + .into() + }); + + // + // Welcome to THE HACK ZONE. + // + + // Define away constness because the various PAM implementations + // have different const annotations and this will surely drive you crazy. + test.define("const", Some("")); + + // Also replace all the `const`s with `mut`s in the ffi.rs file. + let file_contents = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../src/ffi.rs")); + let deconsted_file = test_file("ffi.rs"); + remove_consts(file_contents, &deconsted_file); + + test.generate(&deconsted_file, "ctest.rs"); +} + +fn remove_consts(file_contents: &str, out_file: impl AsRef<Path>) { + let deconstified = deconstify( + TokenStream::from_str(file_contents).unwrap(), + &TokenStream::from_str("mut") + .unwrap() + .into_iter() + .next() + .unwrap(), ) - .unwrap(); + .to_string(); + let out_file = out_file.as_ref(); + fs::write(out_file, deconstified).unwrap(); + rustfmt(out_file) +} + +fn rustfmt(file: impl AsRef<Path>) { + let status = Command::new(env!("CARGO")) + .args(["fmt", "--", file.as_ref().to_str().unwrap()]) + .status() + .unwrap(); + assert!(status.success(), "rustfmt exited with code {status}"); +} + +fn deconstify(stream: TokenStream, mut_token: &TokenTree) -> TokenStream { + TokenStream::from_iter(stream.into_iter().map(|token| { + match token { + TokenTree::Group(g) => { + TokenTree::Group(Group::new(g.delimiter(), deconstify(g.stream(), mut_token))) + .into_token_stream() + } + TokenTree::Ident(id) if id == "const" => mut_token.into_token_stream(), + other => other.into_token_stream(), + } + })) +} + +fn test_file(name: impl AsRef<str>) -> String { + format!("{}/{}", env::var("OUT_DIR").unwrap(), name.as_ref()) } #[derive(Default)]