1use crate::cmd::{cfg_spinner, run_stage};
2use crate::install::Tools;
3use crate::parse::{Opts, ServeOpts, TestOpts};
4use crate::thread::spawn_thread;
5use crate::{errors::*, serve};
6use console::{style, Emoji};
7use indicatif::{MultiProgress, ProgressBar};
8use std::path::PathBuf;
9use std::process::{Command, Stdio};
10
11static TESTING: Emoji<'_, '_> = Emoji("🧪", "");
13
14macro_rules! handle_exit_code {
16 ($code:expr) => {
17 let (_, _, code) = $code;
18 if code != 0 {
19 return ::std::result::Result::Ok(code);
20 }
21 };
22}
23
24pub fn test(
27 dir: PathBuf,
28 test_opts: &TestOpts,
29 tools: &Tools,
30 global_opts: &Opts,
31) -> Result<i32, ExecutionError> {
32 let tools = tools.clone();
34 let Opts {
35 cargo_engine_path,
36 cargo_engine_args,
37 verbose,
38 ..
39 } = global_opts.clone();
40
41 let serve_opts = ServeOpts {
42 no_run: true,
44 no_build: test_opts.no_build,
45 release: false,
46 standalone: false,
47 watch: test_opts.watch,
48 custom_watch: test_opts.custom_watch.clone(),
49 host: test_opts.host.clone(),
50 port: test_opts.port,
51 };
52 let num_steps: u8 = if test_opts.no_build { 2 } else { 4 };
53 let spinners = MultiProgress::new();
56 let (exit_code, server_path) = serve(
57 dir.clone(),
58 &serve_opts,
59 &tools,
60 global_opts,
61 &spinners,
62 true,
63 )?;
64 if exit_code != 0 {
65 return Ok(exit_code);
66 }
67 if let Some(server_path) = server_path {
68 let mut server = Command::new(&server_path)
72 .envs([
73 ("PERSEUS_ENGINE_OPERATION", "serve"),
74 ("PERSEUS_TESTING", "true"),
75 ])
76 .current_dir(&dir)
77 .stdin(Stdio::piped())
78 .stdout(Stdio::piped())
79 .stderr(Stdio::piped())
80 .spawn()
81 .map_err(|err| ExecutionError::CmdExecFailed {
82 cmd: server_path,
83 source: err,
84 })?;
85
86 let test_msg = format!(
88 "{} {} Running tests",
89 style(format!("[{}/{}]", num_steps, num_steps)).bold().dim(),
90 TESTING,
91 );
92 let test_spinner = spinners.insert(num_steps.into(), ProgressBar::new_spinner());
93 let test_spinner = cfg_spinner(test_spinner, &test_msg);
94 let test_dir = dir;
95 let headless = !test_opts.show_browser;
96 let test_thread = spawn_thread(
97 move || {
98 handle_exit_code!(run_stage(
99 vec![&format!(
100 "{} test {} -- --test-threads 1",
103 cargo_engine_path, cargo_engine_args
104 )],
105 &test_dir,
106 &test_spinner,
107 &test_msg,
108 if headless {
109 vec![
110 ("CARGO_TARGET_DIR", "dist/target_engine"),
111 ("RUSTFLAGS", "--cfg=engine"),
112 ("CARGO_TERM_COLOR", "always"),
113 ("PERSEUS_RUN_WASM_TESTS", "true"),
114 ("PERSEUS_RUN_WASM_TESTS_HEADLESS", "true"),
115 ]
116 } else {
117 vec![
118 ("CARGO_TARGET_DIR", "dist/target_engine"),
119 ("RUSTFLAGS", "--cfg=engine"),
120 ("CARGO_TERM_COLOR", "always"),
121 ("PERSEUS_RUN_WASM_TESTS", "true"),
122 ]
123 },
124 verbose,
125 )?);
126
127 Ok(0)
128 },
129 false,
131 );
132
133 let test_res = test_thread
134 .join()
135 .map_err(|_| ExecutionError::ThreadWaitFailed)??;
136
137 let _ = server.kill();
140
141 if test_res != 0 {
142 return Ok(test_res);
143 }
144
145 Ok(0)
147 } else {
148 Err(ExecutionError::GetServerExecutableFailedSimple)
149 }
150}