1
use pretty_assertions::assert_eq;
2
use std::borrow::Cow;
3
use std::collections::BTreeSet;
4
use std::error::Error;
5
use std::fs;
6
use std::path::Path;
7
use std::path::PathBuf;
8
use std::process::Command;
9
use std::time::Instant;
10
use test_suite::AcceptMode;
11
use test_suite::TestConfig;
12
use test_suite::golden_files::assert_no_redundant_expected_files;
13
use test_suite::test_config::{round_expected_time, upsert_expected_time_config};
14

            
15
81
pub fn custom_test(test_dir: &str) -> Result<(), Box<dyn Error>> {
16
81
    let accept_mode = AcceptMode::from_env();
17
81
    let accept = accept_mode.accepts_outputs();
18
81
    let started_at = Instant::now();
19

            
20
    // Convert test directory to a PathBuf
21
81
    let test_path = PathBuf::from(test_dir);
22
81
    assert!(
23
81
        test_path.exists(),
24
        "Test directory not found: {test_path:?}"
25
    );
26

            
27
    // Get paths
28
81
    let script_path = test_path.join("run.sh");
29
81
    assert!(
30
81
        script_path.exists(),
31
        "Test script not found: {script_path:?}"
32
    );
33
81
    let expected_output_path = test_path.join("stdout.expected");
34
81
    let expected_error_path = test_path.join("stderr.expected");
35
81
    let config_path = test_path.join("config.toml");
36
81
    let file_config: TestConfig = if let Ok(config_contents) = fs::read_to_string(&config_path) {
37
41
        toml::from_str(&config_contents).unwrap()
38
    } else {
39
40
        Default::default()
40
    };
41

            
42
    // Execute the test script in the correct directory
43
81
    let output = Command::new("sh")
44
81
        .arg("run.sh")
45
81
        .current_dir(&test_path)
46
81
        .output()?;
47

            
48
    // Convert captured output/error to string
49
81
    let actual_output = String::from_utf8_lossy(&output.stdout);
50
81
    let actual_error = String::from_utf8_lossy(&output.stderr);
51

            
52
81
    if accept {
53
        // Overwrite expected files
54
        update_file(expected_output_path, &actual_output)?;
55
        update_file(expected_error_path, &actual_error)?;
56
    } else {
57
        // Compare results
58
81
        let expected_output = if expected_output_path.exists() {
59
74
            fs::read_to_string(&expected_output_path)?
60
        } else {
61
7
            String::new()
62
        };
63
81
        let expected_error = if expected_error_path.exists() {
64
21
            fs::read_to_string(&expected_error_path)?
65
        } else {
66
60
            String::new()
67
        };
68

            
69
81
        assert_eq!(expected_error, actual_error, "Standard error mismatch");
70
81
        assert_eq!(expected_output, actual_output, "Standard output mismatch");
71
    }
72

            
73
81
    let allowed_expected_files = expected_custom_files_for_case(&actual_output, &actual_error);
74
81
    assert_no_redundant_expected_files(Path::new(&test_path), &allowed_expected_files, None)?;
75

            
76
81
    if accept_mode.records_expected_time() {
77
        let observed_expected_time = round_expected_time(started_at.elapsed());
78
        if let Some(expected_time) =
79
            accept_mode.expected_time_to_record(file_config.expected_time, observed_expected_time)
80
        {
81
            upsert_expected_time_config(&config_path, expected_time)?;
82
        }
83
81
    }
84

            
85
81
    Ok(())
86
81
}
87

            
88
fn update_file(
89
    expected_file_path: PathBuf,
90
    actual_output: &Cow<'_, str>,
91
) -> Result<(), Box<dyn Error>> {
92
    if expected_file_path.exists() {
93
        fs::remove_file(&expected_file_path)?;
94
    }
95
    if !actual_output.trim().is_empty() {
96
        fs::File::create(&expected_file_path)?;
97
        fs::write(&expected_file_path, actual_output.as_bytes())?;
98
    }
99
    Ok(())
100
}
101

            
102
/// Returns the expected snapshot files for the observed custom test output.
103
81
fn expected_custom_files_for_case(
104
81
    stdout: &Cow<'_, str>,
105
81
    stderr: &Cow<'_, str>,
106
81
) -> BTreeSet<String> {
107
81
    let mut expected_files = BTreeSet::new();
108
81
    if !stdout.trim().is_empty() {
109
74
        expected_files.insert("stdout.expected".to_string());
110
74
    }
111
81
    if !stderr.trim().is_empty() {
112
21
        expected_files.insert("stderr.expected".to_string());
113
60
    }
114
81
    expected_files
115
81
}
116

            
117
#[test]
118
1
fn assert_conjure_present() {
119
1
    conjure_cp_cli::find_conjure::conjure_executable().unwrap();
120
1
}
121

            
122
include!(concat!(env!("OUT_DIR"), "/gen_tests_custom.rs"));