shader_sense/
lib.rs

1pub mod include;
2pub mod position;
3pub mod shader;
4pub mod shader_error;
5pub mod symbols;
6pub mod validator;
7
8#[cfg(test)]
9mod tests {
10    use std::{
11        cell::RefCell,
12        collections::HashMap,
13        path::{Path, PathBuf},
14        rc::Rc,
15    };
16
17    use crate::{
18        include::{canonicalize, IncludeHandler},
19        shader::{ShaderParams, ShadingLanguage},
20        symbols::{shader_module_parser::ShaderModuleParser, symbol_provider::SymbolProvider},
21        validator::validator::Validator,
22    };
23
24    fn validate_include(path: &Path) -> bool {
25        let file_path = Path::new("./test/hlsl/dontcare.hlsl");
26        let mut include_handler = IncludeHandler::main(
27            file_path,
28            vec![],
29            HashMap::from([
30                (
31                    PathBuf::from("/Packages"),
32                    PathBuf::from("./test/hlsl/inc0/inc1"),
33                ),
34                (
35                    PathBuf::from("Packages"),
36                    PathBuf::from("./test/hlsl/inc0/inc1"),
37                ),
38                (
39                    PathBuf::from("Using\\Backslashes"),
40                    PathBuf::from("./test/hlsl/inc0/inc1"),
41                ),
42            ]),
43        );
44        include_handler.search_path_in_includes(path).is_some()
45    }
46
47    #[test]
48    fn test_virtual_path() {
49        assert!(
50            validate_include(Path::new("/Packages/level1.hlsl")),
51            "Virtual path with prefix failed."
52        );
53        assert!(
54            validate_include(Path::new("Packages/level1.hlsl")),
55            "Virtual path without prefix failed."
56        );
57        #[cfg(target_os = "windows")] // Only windows support backslashes.
58        assert!(
59            validate_include(Path::new("Using/Backslashes/level1.hlsl")),
60            "Virtual path with backslash failed."
61        );
62    }
63
64    #[test]
65    fn test_directory_stack() {
66        let file_path = Path::new("./test/hlsl/include-level.hlsl");
67        let mut include_handler = IncludeHandler::main(file_path, vec![], HashMap::new());
68        let absolute_level0 =
69            include_handler.search_path_in_includes(Path::new("./inc0/level0.hlsl"));
70        assert!(absolute_level0.is_some());
71        include_handler.push_directory_stack(&absolute_level0.unwrap());
72        let absolute_level1 =
73            include_handler.search_path_in_includes(Path::new("./inc1/level1.hlsl"));
74        assert!(absolute_level1.is_some());
75    }
76
77    #[test]
78    fn test_stack_overflow() {
79        // Should handle include stack overflow gracefully.
80        let file_path = Path::new("./test/hlsl/stack-overflow.hlsl");
81        let mut shader_module_parser =
82            ShaderModuleParser::from_shading_language(ShadingLanguage::Hlsl);
83        let symbol_provider = SymbolProvider::from_shading_language(ShadingLanguage::Hlsl);
84        let shader_module = shader_module_parser
85            .create_module(file_path, &std::fs::read_to_string(file_path).unwrap())
86            .unwrap();
87        println!("Testing symbol overflow");
88        let mut depth = 0;
89        match symbol_provider.query_symbols(
90            &shader_module,
91            ShaderParams::default(),
92            &mut |include| {
93                depth += 1;
94                println!(
95                    "Including {} (depth {})",
96                    include.get_absolute_path().display(),
97                    depth
98                );
99                Ok(Some(Rc::new(RefCell::new(
100                    shader_module_parser
101                        .create_module(
102                            &include.get_absolute_path(),
103                            &std::fs::read_to_string(&include.get_absolute_path()).unwrap(),
104                        )
105                        .unwrap(),
106                ))))
107            },
108            None,
109        ) {
110            Ok(_) => {}
111            Err(err) => panic!("Failed to query symbols: {}", err),
112        }
113        println!("Testing validation overflow");
114        let validator = Validator::from_shading_language(ShadingLanguage::Hlsl);
115        match validator.validate_shader(
116            &shader_module.content,
117            file_path,
118            &ShaderParams::default(),
119            &mut |path| Some(std::fs::read_to_string(path).unwrap()),
120        ) {
121            Ok(diagnostics) => assert!(
122                !diagnostics.is_empty(),
123                "Diagnostics are empty but should not be."
124            ),
125            Err(err) => panic!("Failed to validate shader: {}", err),
126        }
127    }
128    #[test]
129    fn test_canonicalize_parent() {
130        if cfg!(target_os = "windows") {
131            let path = canonicalize(Path::new("D:\\test\\data")).unwrap();
132            assert!(path == Path::new("D:\\test\\data"));
133            assert!(path.parent().unwrap() == Path::new("D:\\test"));
134        } else {
135            let path = canonicalize(Path::new("/test/data")).unwrap();
136            assert!(path == Path::new("/test/data"));
137            assert!(path.parent().unwrap() == Path::new("/test"));
138        }
139    }
140    #[test]
141    fn test_canonicalize_join() {
142        if cfg!(target_os = "windows") {
143            let path = canonicalize(Path::new("D:\\test")).unwrap();
144            assert!(path == Path::new("D:\\test"));
145            assert!(path.join("data") == Path::new("D:\\test\\data"));
146        } else {
147            let path = canonicalize(Path::new("/test")).unwrap();
148            assert!(path == Path::new("/test"));
149            assert!(path.join("data") == Path::new("/test/data"));
150        }
151    }
152}