1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
#![warn(rust_2018_idioms, clippy::dbg_macro, clippy::print_stdout)]

/*!
Integration test tool for [phper](https://crates.io/crates/phper).

The `php-config` is needed. You can set environment `PHP_CONFIG` to specify the path.

## License

[Unlicense](https://github.com/jmjoy/phper/blob/master/LICENSE).
!*/

use crate::context::Context;
use std::{
    path::Path,
    process::{Child, Output},
};

mod context;
mod utils;

/// Check your extension by executing the php script, if the all executing return success, than the test is pass.
///
/// - `exec_path` is the path of the make executable, which will be used to detect the path of
/// extension lib.
///
/// - `scripts` is the path of your php test scripts.
///
/// See [example hello integration test](https://github.com/jmjoy/phper/blob/master/examples/hello/tests/integration.rs).
pub fn test_php_scripts(exe_path: impl AsRef<Path>, scripts: &[&dyn AsRef<Path>]) {
    let condition = |output: Output| output.status.success();
    let scripts = scripts
        .into_iter()
        .map(|s| (*s, &condition as _))
        .collect::<Vec<_>>();
    test_php_scripts_with_condition(exe_path, &*scripts);
}

/// Check your extension by executing the php script, if the all your specified checkers are pass, than the test is pass.
///
/// - `exec_path` is the path of the make executable, which will be used to detect the path of
/// extension lib.
///
/// - `scripts` is the slice of the tuple, format is `(path of your php test script, checker function or closure)`.
///
/// See [example logging integration test](https://github.com/jmjoy/phper/blob/master/examples/logging/tests/integration.rs).
pub fn test_php_scripts_with_condition(
    exe_path: impl AsRef<Path>,
    scripts: &[(&dyn AsRef<Path>, &dyn Fn(Output) -> bool)],
) {
    let context = Context::get_global();
    let lib_path = utils::get_lib_path(exe_path);
    let tmp_php_ini_file = context.create_tmp_php_ini_file(&lib_path);

    for (script, condition) in scripts {
        let mut cmd = context.create_command_with_tmp_php_ini_args(&tmp_php_ini_file, script);

        let output = cmd.output().unwrap();
        let path = script.as_ref().to_str().unwrap();

        let mut stdout = String::from_utf8(output.stdout.clone()).unwrap();
        if stdout.is_empty() {
            stdout.push_str("<empty>");
        }

        let mut stderr = String::from_utf8(output.stderr.clone()).unwrap();
        if stderr.is_empty() {
            stderr.push_str("<empty>");
        }

        println!(
            "===== command =====\n{} {}\n===== stdout ======\n{}\n===== stderr ======\n{}",
            &context.php_bin,
            cmd.get_args().join(" "),
            stdout,
            stderr,
        );
        #[cfg(target_os = "linux")]
        if output.status.code().is_none() {
            use std::os::unix::process::ExitStatusExt;
            println!(
                "===== signal ======\nExitStatusExt is None, the signal is: {:?}",
                output.status.signal()
            );
        }

        if !condition(output) {
            panic!("test php file `{}` failed", path);
        }
    }
}

pub fn test_long_term_php_script_with_condition(
    exe_path: impl AsRef<Path>,
    script: impl AsRef<Path>,
    condition: impl FnOnce(&Child),
) {
    let context = Context::get_global();
    let lib_path = utils::get_lib_path(exe_path);
    let tmp_php_ini_file = context.create_tmp_php_ini_file(lib_path);
    let mut command = context.create_command_with_tmp_php_ini_args(&tmp_php_ini_file, script);
    let mut child = command.spawn().unwrap();
    condition(&child);
    child.kill().unwrap();
}