phper_test/
cli.rs

1// Copyright (c) 2022 PHPER Framework Team
2// PHPER is licensed under Mulan PSL v2.
3// You can use this software according to the terms and conditions of the Mulan
4// PSL v2. You may obtain a copy of Mulan PSL v2 at:
5//          http://license.coscl.org.cn/MulanPSL2
6// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
7// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9// See the Mulan PSL v2 for more details.
10
11//! Test tools for php cli program.
12
13use crate::context::Context;
14use std::{
15    panic::{UnwindSafe, catch_unwind, resume_unwind},
16    path::Path,
17    process::{Child, Output},
18};
19
20/// Check your extension by executing the php script, if the all executing
21/// return success, than the test is pass.
22///
23/// - `lib_path` is the path of extension lib.
24///
25/// - `scripts` is the path of your php test scripts.
26///
27/// See [example hello integration test](https://github.com/phper-framework/phper/blob/master/examples/hello/tests/integration.rs).
28pub fn test_php_scripts(lib_path: impl AsRef<Path>, scripts: &[&dyn AsRef<Path>]) {
29    let condition = |output: Output| output.status.success();
30    let scripts = scripts
31        .iter()
32        .map(|s| (*s, &condition as _))
33        .collect::<Vec<_>>();
34    test_php_scripts_with_condition(lib_path, &scripts);
35}
36
37/// Script and condition pair.
38pub type ScriptCondition<'a> = (&'a dyn AsRef<Path>, &'a dyn Fn(Output) -> bool);
39
40/// Check your extension by executing the php script, if the all your specified
41/// checkers are pass, than the test is pass.
42///
43/// - `exec_path` is the path of the make executable, which will be used to
44///   detect the path of extension lib.
45///
46/// - `scripts` is the slice of the tuple, format is `(path of your php test
47///   script, checker function or closure)`.
48///
49/// See [example logging integration test](https://github.com/phper-framework/phper/blob/master/examples/logging/tests/integration.rs).
50pub fn test_php_scripts_with_condition(
51    lib_path: impl AsRef<Path>, scripts: &[ScriptCondition<'_>],
52) {
53    let context = Context::get_global();
54
55    for (script, condition) in scripts {
56        let mut cmd = context.create_command_with_lib(&lib_path, script);
57
58        let output = cmd.output().unwrap();
59        let path = script.as_ref().to_str().unwrap();
60
61        let mut stdout = String::from_utf8(output.stdout.clone()).unwrap();
62        if stdout.is_empty() {
63            stdout.push_str("<empty>");
64        }
65
66        let mut stderr = String::from_utf8(output.stderr.clone()).unwrap();
67        if stderr.is_empty() {
68            stderr.push_str("<empty>");
69        }
70
71        eprintln!(
72            "===== command =====\n{} {}\n===== stdout ======\n{}\n===== stderr ======\n{}",
73            &context.php_bin,
74            cmd.get_args().join(" "),
75            stdout,
76            stderr,
77        );
78        #[cfg(target_os = "linux")]
79        if output.status.code().is_none() {
80            use std::os::unix::process::ExitStatusExt;
81            eprintln!(
82                "===== signal ======\nExitStatusExt is None, the signal is: {:?}",
83                output.status.signal()
84            );
85        }
86
87        if !condition(output) {
88            panic!("test php file `{}` failed", path);
89        }
90    }
91}
92
93/// Check your extension by executing the long term php script such as http
94/// server, if the all your specified checkers are pass, than the test is pass.
95#[allow(clippy::zombie_processes)]
96pub fn test_long_term_php_script_with_condition(
97    lib_path: impl AsRef<Path>, script: impl AsRef<Path>,
98    condition: impl FnOnce(&Child) + UnwindSafe,
99) {
100    let context = Context::get_global();
101    let mut command = context.create_command_with_lib(lib_path, script);
102    let mut child = command.spawn().unwrap();
103    let r = catch_unwind(|| condition(&child));
104    child.kill().unwrap();
105    if let Err(e) = r {
106        resume_unwind(e);
107    }
108}