xlsynth_test_helpers/
assert_valid_sv.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use tempfile::NamedTempFile;
4
5fn can_use_slang() -> bool {
6    let maybe_slang_path = std::env::var("SLANG_PATH").unwrap_or_default();
7    !maybe_slang_path.is_empty()
8}
9
10/// Helper that asserts that the given SV string is valid.
11pub fn assert_valid_sv(sv: &str) {
12    if !can_use_slang() {
13        log::warn!("Skipping SV validation because Slang is not available.");
14        return;
15    }
16
17    // Write out a named temporary file with `sv` as the contents.
18    let temp_file = NamedTempFile::new().unwrap();
19    std::fs::write(temp_file.path(), sv).unwrap();
20
21    // Now parse those contents we wrote out with slang.
22    let cfg = slang_rs::SlangConfig {
23        sources: &[temp_file.path().to_str().unwrap()],
24        ..Default::default()
25    };
26    slang_rs::run_slang(&cfg).expect("expect single SystemVerilog file contents is valid");
27}
28
29pub struct FlistEntry {
30    pub filename: String,
31    pub contents: String,
32}
33
34pub fn assert_valid_sv_flist(files: &[FlistEntry]) {
35    if !can_use_slang() {
36        log::warn!("Skipping SV validation because Slang is not available.");
37        return;
38    }
39
40    // Make a temporary directory and write out all the files to it.
41    let temp_dir = tempfile::tempdir().unwrap();
42
43    // Create all the temporary files based on the file list entries given.
44    let mut sources = vec![];
45    for entry in files {
46        let path = temp_dir.path().join(&entry.filename);
47        sources.push(path.to_str().unwrap().to_string());
48        std::fs::write(path, &entry.contents).unwrap();
49    }
50
51    // Now run Slang on those files.
52    let sources_strs = sources.iter().map(|s| s.as_str()).collect::<Vec<_>>();
53    let cfg = slang_rs::SlangConfig {
54        sources: &sources_strs,
55        ..Default::default()
56    };
57    let result = slang_rs::run_slang(&cfg);
58    if result.is_err() {
59        panic!(
60            "expect we can parse valid SystemVerilog via whole file list; error:\n{}",
61            result.err().unwrap().to_string().replace("\\n", "\n")
62        );
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_observe_error_if_can_use_slang() {
72        let _ = env_logger::builder().is_test(true).try_init();
73        if !can_use_slang() {
74            log::warn!("Skipping SV validation because Slang is not available.");
75            return;
76        }
77        let invalid_sv = "module foo; garbage; endmodule";
78        // This call should panic on the invalid SV.
79        let result = std::panic::catch_unwind(|| assert_valid_sv(invalid_sv));
80        assert!(result.is_err());
81        let error = result.err().unwrap();
82        let error_message = error.downcast_ref::<String>().unwrap();
83        assert!(error_message.contains("expected a declaration name"));
84    }
85}