wasmer_c_api_test_runner/
lib.rs1#[cfg(test)]
2use std::error::Error;
3#[cfg(test)]
4use std::process::Stdio;
5
6#[cfg(test)]
7static INCLUDE_REGEX: &str = "#include \"(.*)\"";
8
9#[derive(Debug)]
10pub struct Config {
11 pub wasmer_dir: String,
12 pub root_dir: String,
13}
14
15impl Config {
16 pub fn get() -> Config {
17 let mut config = Config {
18 wasmer_dir: std::env::var("WASMER_DIR").unwrap_or_default(),
19 root_dir: std::env::var("ROOT_DIR").unwrap_or_default(),
20 };
21
22 let wasmer_base_dir = find_wasmer_base_dir();
23 let manifest_dir = env!("CARGO_MANIFEST_DIR");
24
25 if config.wasmer_dir.is_empty() {
26 println!("manifest dir = {manifest_dir}, wasmer root dir = {wasmer_base_dir}");
27 config.wasmer_dir = wasmer_base_dir.clone() + "/package";
28 assert!(std::path::Path::new(&config.wasmer_dir).exists());
29 }
30 if config.root_dir.is_empty() {
31 config.root_dir = wasmer_base_dir + "/lib/c-api/tests";
32 }
33
34 config
35 }
36}
37
38fn find_wasmer_base_dir() -> String {
39 let wasmer_base_dir = env!("CARGO_MANIFEST_DIR");
40 let mut path2 = wasmer_base_dir.split("wasmer").collect::<Vec<_>>();
41 path2.pop();
42 let mut wasmer_base_dir = path2.join("wasmer");
43
44 if wasmer_base_dir.contains("wasmer/lib/c-api") {
45 wasmer_base_dir = wasmer_base_dir
46 .split("wasmer/lib/c-api")
47 .next()
48 .unwrap()
49 .to_string()
50 + "wasmer";
51 } else if wasmer_base_dir.contains("wasmer\\lib\\c-api") {
52 wasmer_base_dir = wasmer_base_dir
53 .split("wasmer\\lib\\c-api")
54 .next()
55 .unwrap()
56 .to_string()
57 + "wasmer";
58 }
59
60 wasmer_base_dir
61}
62
63#[derive(Default)]
64pub struct RemoveTestsOnDrop {}
65
66impl Drop for RemoveTestsOnDrop {
67 fn drop(&mut self) {
68 let manifest_dir = env!("CARGO_MANIFEST_DIR");
69 for entry in std::fs::read_dir(manifest_dir).unwrap() {
70 let entry = entry.unwrap();
71 let path = entry.path();
72 let extension = path.extension().and_then(|s| s.to_str());
73 if extension == Some("obj") || extension == Some("exe") || extension == Some("o") {
74 println!("removing {}", path.display());
75 let _ = std::fs::remove_file(&path);
76 }
77 }
78 if let Some(parent) = std::path::Path::new(&manifest_dir).parent() {
79 for entry in std::fs::read_dir(parent).unwrap() {
80 let entry = entry.unwrap();
81 let path = entry.path();
82 let extension = path.extension().and_then(|s| s.to_str());
83 if extension == Some("obj") || extension == Some("exe") || extension == Some("o") {
84 println!("removing {}", path.display());
85 let _ = std::fs::remove_file(&path);
86 }
87 }
88 }
89 }
90}
91
92#[cfg(test)]
93pub const CAPI_BASE_TESTS: &[&str] = &[
94 "wasm-c-api/example/callback",
95 "wasm-c-api/example/memory",
96 "wasm-c-api/example/start",
97 "wasm-c-api/example/global",
98 "wasm-c-api/example/reflect",
99 "wasm-c-api/example/trap",
100 "wasm-c-api/example/hello",
101 "wasm-c-api/example/serialize",
102 "wasm-c-api/example/multi",
103 "wasm-c-api/example/hostref",
104];
105
106#[cfg(test)]
111pub const CAPI_SYS_ONLY_TESTS: &[&str] = &["wasm-c-api/example/hostref"];
112
113#[cfg(test)]
114fn capi_tests_for_backend() -> Vec<&'static str> {
115 let backend = std::env::var("WASMER_CAPI_CONFIG").unwrap_or_default();
116 CAPI_BASE_TESTS
117 .iter()
118 .copied()
119 .filter(|test| {
120 if backend == "v8" && CAPI_SYS_ONLY_TESTS.contains(test) {
121 println!("skipping {test}: not supported by the {backend} backend");
122 false
123 } else {
124 true
125 }
126 })
127 .collect()
128}
129
130#[allow(unused_variables, dead_code)]
131pub const CAPI_BASE_TESTS_NOT_WORKING: &[&str] = &[
132 "wasm-c-api/example/finalize",
133 "wasm-c-api/example/threads",
134 "wasm-c-api/example/table",
140];
141
142#[test]
144fn test_ok() {
145 let _drop = RemoveTestsOnDrop::default();
146 let config = Config::get();
147 println!("config: {config:#?}");
148
149 let manifest_dir = env!("CARGO_MANIFEST_DIR");
150 let host = target_lexicon::HOST.to_string();
151 let target = &host;
152
153 let wasmer_dll_dir = format!("{}/lib", config.wasmer_dir);
154 let libwasmer_so_path = format!("{}/lib/libwasmer.so", config.wasmer_dir);
155 let exe_dir = format!("{manifest_dir}/../wasm-c-api/example");
156 let path = std::env::var("PATH").unwrap_or_default();
157 let newpath = format!("{};{path}", wasmer_dll_dir.replace('/', "\\"));
158
159 let tests = capi_tests_for_backend();
160
161 if target.contains("msvc") {
162 for test in tests.iter() {
163 let mut build = cc::Build::new();
164 let build = build
165 .cargo_metadata(false)
166 .warnings(true)
167 .static_crt(true)
168 .extra_warnings(true)
169 .warnings_into_errors(false)
170 .debug(true)
171 .host(&host)
172 .target(target)
173 .opt_level(1);
174
175 let compiler = build.try_get_compiler().unwrap();
176
177 println!("compiler {compiler:#?}");
178
179 let vcvars_bat_path = find_vcvars64(&compiler).expect("no vcvars64.bat");
181 let mut vcvars = std::process::Command::new("cmd");
182 vcvars.arg("/C");
183 vcvars.arg(vcvars_bat_path);
184 println!("running {vcvars:?}");
185
186 let output = vcvars
188 .output()
189 .expect("could not invoke vcvars64.bat at {vcvars_bat_path}");
190
191 if !output.status.success() {
192 println!();
193 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
194 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
195 panic!("failed to invoke vcvars64.bat {test}");
197 }
198
199 let mut command = compiler.to_command();
200
201 command.arg(format!("{manifest_dir}/../{test}.c"));
202 if !config.wasmer_dir.is_empty() {
203 command.arg("/I");
204 command.arg(format!("{}/wasm-c-api/include/", config.root_dir));
205 command.arg("/I");
206 command.arg(format!("{}/include/", config.wasmer_dir));
207 let mut log = String::new();
208 fixup_symlinks(
209 &[
210 format!("{}/include/", config.wasmer_dir),
211 format!("{}/wasm-c-api/include/", config.root_dir),
212 config.root_dir.to_string(),
213 ],
214 &mut log,
215 &config.root_dir,
216 )
217 .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}"));
218 println!("{log}");
219 }
220 command.arg("/link");
221 if !config.wasmer_dir.is_empty() {
222 command.arg(format!("/LIBPATH:{}/lib", config.wasmer_dir));
223 command.arg(format!("{}/lib/wasmer.dll.lib", config.wasmer_dir));
224 }
225 command.arg(format!("/OUT:{manifest_dir}/../{test}.exe"));
226
227 println!("compiling {test}: {command:?}");
228
229 let output = command
231 .output()
232 .unwrap_or_else(|_| panic!("failed to compile {command:#?}"));
233 if !output.status.success() {
234 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
235 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
236 println!("output: {output:#?}");
237 panic!("failed to compile {test}");
239 }
240
241 if std::path::Path::new(&format!("{manifest_dir}/../{test}.exe")).exists() {
242 println!("exe does not exist");
243 }
244
245 let mut command = std::process::Command::new(format!("{manifest_dir}/../{test}.exe"));
247 println!("newpath: {}", newpath.clone());
248 command.env("PATH", newpath.clone());
249 command.current_dir(exe_dir.clone());
250 println!("executing {test}: {command:?}");
251 println!("setting current dir = {exe_dir}");
252 let output = command
253 .output()
254 .unwrap_or_else(|_| panic!("failed to run {command:#?}"));
255 if !output.status.success() {
256 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
257 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
258 println!("output: {output:#?}");
259 panic!("failed to execute {test}");
261 }
262
263 }
274 } else {
275 for test in tests.iter() {
276 let compiler_cmd = match std::process::Command::new("cc").output() {
277 Ok(_) => "cc",
278 Err(_) => "gcc",
279 };
280 let mut command = std::process::Command::new(compiler_cmd);
281
282 if !config.wasmer_dir.is_empty() {
283 command.arg("-I");
284 command.arg(format!("{}/wasm-c-api/include/", config.root_dir));
285 command.arg("-I");
286 command.arg(format!("{}/include/", config.wasmer_dir));
287 let mut log = String::new();
288 fixup_symlinks(
289 &[
290 format!("{}/include/", config.wasmer_dir),
291 format!("{}/wasm-c-api/include/", config.root_dir),
292 config.root_dir.to_string(),
293 ],
294 &mut log,
295 &config.root_dir,
296 )
297 .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}"));
298 }
299 command.arg(format!("{manifest_dir}/../{test}.c"));
300 if !config.wasmer_dir.is_empty() {
301 command.arg("-L");
302 command.arg(format!("{}/lib/", config.wasmer_dir));
303 command.arg("-lwasmer");
304 command.arg(format!("-Wl,-rpath,{}/lib/", config.wasmer_dir));
305 }
306 command.arg("-o");
307 command.arg(format!("{manifest_dir}/../{test}"));
308
309 println!("compile: {command:#?}");
312 let output = command
314 .stdout(Stdio::inherit())
315 .stderr(Stdio::inherit())
316 .current_dir(find_wasmer_base_dir())
317 .output()
318 .unwrap_or_else(|_| panic!("failed to compile {command:#?}"));
319 if !output.status.success() {
320 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
321 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
322 panic!("failed to compile {test}: {command:#?}");
324 }
325
326 let mut command = std::process::Command::new(format!("{manifest_dir}/../{test}"));
328 command.env("LD_PRELOAD", libwasmer_so_path.clone());
329 command.current_dir(exe_dir.clone());
330 println!("execute: {command:#?}");
331 let output = command
332 .output()
333 .unwrap_or_else(|_| panic!("failed to run {command:#?}"));
334 if !output.status.success() {
335 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
336 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
337 panic!("failed to execute {test}: {command:#?}");
339 }
340 }
341 }
342
343 for test in tests.iter() {
344 let _ = std::fs::remove_file(format!("{manifest_dir}/{test}.obj"));
345 let _ = std::fs::remove_file(format!("{manifest_dir}/../{test}.exe"));
346 let _ = std::fs::remove_file(format!("{manifest_dir}/../{test}"));
347 }
348}
349
350#[cfg(test)]
392fn fixup_symlinks(
393 include_paths: &[String],
394 log: &mut String,
395 root_dir: &str,
396) -> Result<(), Box<dyn Error>> {
397 let source = std::path::Path::new(root_dir)
398 .join("lib")
399 .join("c-api")
400 .join("tests")
401 .join("wasm-c-api")
402 .join("include")
403 .join("wasm.h");
404 let target = std::path::Path::new(root_dir)
405 .join("lib")
406 .join("c-api")
407 .join("tests")
408 .join("wasm.h");
409 println!("copying {} -> {}", source.display(), target.display());
410 let _ = std::fs::copy(source, target);
411
412 log.push_str(&format!("include paths: {include_paths:?}"));
413 for i in include_paths {
414 let i = i.replacen("-I", "", 1);
415 let i = i.replacen("/I", "", 1);
416 let mut paths_headers = Vec::new();
417 let readdir = match std::fs::read_dir(&i) {
418 Ok(o) => o,
419 Err(_) => continue,
420 };
421 for entry in readdir {
422 let entry = entry?;
423 let path = entry.path();
424 let path_display = format!("{}", path.display());
425 if path_display.ends_with('h') {
426 paths_headers.push(path_display);
427 }
428 }
429 fixup_symlinks_inner(&paths_headers, log)?;
430 }
431
432 Ok(())
433}
434
435#[cfg(test)]
436fn fixup_symlinks_inner(include_paths: &[String], log: &mut String) -> Result<(), Box<dyn Error>> {
437 log.push_str(&format!("fixup symlinks: {include_paths:#?}"));
438 let regex = regex::Regex::new(INCLUDE_REGEX).unwrap();
439 for path in include_paths.iter() {
440 let file = match std::fs::read_to_string(path) {
441 Ok(o) => o,
442 _ => continue,
443 };
444 let lines_3 = file.lines().take(3).collect::<Vec<_>>();
445 log.push_str(&format!("first 3 lines of {path:?}: {lines_3:#?}\n"));
446
447 let parent = std::path::Path::new(&path).parent().unwrap();
448 if let Ok(symlink) = std::fs::read_to_string(parent.join(&file)) {
449 log.push_str(&format!("symlinking {path:?}\n"));
450 std::fs::write(path, symlink)?;
451 }
452
453 let filepaths = regex
455 .captures_iter(&file)
456 .map(|c| c[1].to_string())
457 .collect::<Vec<_>>();
458 log.push_str(&format!("regex captures: ({path:?}): {filepaths:#?}\n"));
459 let joined_filepaths = filepaths
460 .iter()
461 .map(|s| {
462 let path = parent.join(s);
463 format!("{}", path.display())
464 })
465 .collect::<Vec<_>>();
466 fixup_symlinks_inner(&joined_filepaths, log)?;
467 }
468 Ok(())
469}
470
471#[cfg(test)]
472fn find_vcvars64(compiler: &cc::Tool) -> Option<String> {
473 if !compiler.is_like_msvc() {
474 return None;
475 }
476
477 let path = compiler.path();
478 let path = format!("{}", path.display());
479 let split = path.split("VC").next()?;
480
481 Some(format!("{split}VC\\Auxiliary\\Build\\vcvars64.bat"))
482}