diff libpam-sys/libpam-sys-test/build.rs @ 110:2346fd501b7a

Add tests for constants and do other macro niceties. - Adds tests for all the constants. Pretty sweet. - Moves documentation for cfg-pam-impl macro to `libpam-sys`. - Renames `Illumos` to `Sun`. - other stuff
author Paul Fisher <paul@pfish.zone>
date Sun, 29 Jun 2025 02:15:46 -0400
parents
children 04105e9a7de8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpam-sys/libpam-sys-test/build.rs	Sun Jun 29 02:15:46 2025 -0400
@@ -0,0 +1,103 @@
+use bindgen::MacroTypeVariation;
+use libpam_sys_impls::cfg_pam_impl;
+use quote::ToTokens;
+use std::path::PathBuf;
+use std::{env, fs};
+use syn::{Item, ItemConst};
+
+fn main() {
+    generate_const_test();
+}
+
+#[cfg_pam_impl("LinuxPam")]
+fn test_config() -> TestConfig {
+    TestConfig {
+        headers: vec![
+            "security/_pam_types.h".into(),
+            "security/pam_appl.h".into(),
+            "security/pam_ext.h".into(),
+            "security/pam_modules.h".into(),
+        ],
+        ignore_consts: vec!["__LINUX_PAM__".into(), "__LINUX_PAM_MINOR__".into()],
+    }
+}
+
+#[cfg_pam_impl("OpenPam")]
+fn test_config() -> TestConfig {
+    TestConfig {
+        headers: vec![
+            "security/pam_types.h",
+            "security/openpam.h",
+            "security/pam_appl.h",
+            "security/pam_constants.h",
+        ],
+        ignore_consts: vec![],
+    }
+}
+
+#[cfg_pam_impl(not(any("LinuxPam", "OpenPam")))]
+fn test_config() -> TestConfig {
+    panic!("This PAM implementation is not yet tested.")
+}
+
+fn generate_const_test() {
+    let config = test_config();
+    let builder = bindgen::Builder::default()
+        .header_contents("_.h", &config.header_contents())
+        .merge_extern_blocks(true)
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+        .blocklist_type(".*")
+        .blocklist_function(".*")
+        .allowlist_var(".*")
+        .default_macro_constant_type(MacroTypeVariation::Unsigned);
+
+    let generated = builder.generate().unwrap().to_string();
+    let file = syn::parse_file(&generated).unwrap();
+    let mut tests = vec![];
+    tests.push("{".into());
+    tests.extend(
+        file.items
+            .iter()
+            .filter_map(|item| {
+                if let Item::Const(item) = item {
+                    Some(item)
+                } else {
+                    None
+                }
+            })
+            .filter(|item| config.should_check_const(item))
+            .map(|item| {
+                let tokens = item.expr.to_token_stream();
+                format!(
+                    "assert_eq!({tokens}, libpam_sys::{name});",
+                    name = item.ident
+                )
+            }),
+    );
+    tests.push("}".into());
+    fs::write(
+        PathBuf::from(env::var("OUT_DIR").unwrap()).join("constant_test.rs"),
+        tests.join("\n"),
+    )
+    .unwrap();
+}
+
+struct TestConfig {
+    headers: Vec<String>,
+    ignore_consts: Vec<String>,
+}
+
+impl TestConfig {
+    fn header_contents(&self) -> String {
+        let vec: Vec<_> = self
+            .headers
+            .iter()
+            .map(|h| format!("#include <{h}>\n"))
+            .collect();
+        vec.join("")
+    }
+
+    fn should_check_const(&self, item: &ItemConst) -> bool {
+        !self.ignore_consts.contains(&item.ident.to_string())
+    }
+}