bashrs 6.66.0

Rust-to-Shell transpiler for deterministic bootstrap scripts
//! Falsification Probar Testing - Popper Methodology
//!
//! Every valid bash pattern must pass the linter without triggering false positives.
//! These tests use jugar-probar's TUI coverage framework to verify 130 F-code tests.
//!
//! Run: cargo test -p bashrs --test falsification_probar_testing

#![allow(clippy::unwrap_used)]

use bashrs::linter::rules::lint_shell;
use jugar_probar::gui_coverage;

/// Helper: Check if result contains a specific rule code
fn has_rule(result: &bashrs::linter::LintResult, rule: &str) -> bool {
    result.diagnostics.iter().any(|d| d.code == rule)
}

/// Helper: Check if result has any parse errors
fn has_parse_error(result: &bashrs::linter::LintResult) -> bool {
    result
        .diagnostics
        .iter()
        .any(|d| d.message.to_lowercase().contains("parse"))
}

// ============================================================================
// 6.1 SUDO AND PERMISSIONS (F001-F010)
// ============================================================================

#[test]
fn test_falsification_sudo_permissions() {
    let mut gui = gui_coverage! {
        buttons: ["F001", "F002", "F003", "F004", "F005", "F006", "F007", "F008", "F009", "F010"],
        screens: ["pass", "fail"]
    };

    let tests = [
        ("F001", "sudo sh -c 'echo 1 > /f'", "SC2024"),
        ("F002", "echo 1 | sudo tee /f", "SC2024"),
        ("F003", "echo 1 | sudo tee /f >/dev/null", "SC2024"),
        ("F004", "sudo -u user cmd > /tmp/f", "SC2024"),
        ("F005", "sudo -v", "SC2024"),
        ("F006", "sudo -k && sudo -n ls", "SC2024"),
        ("F007", "sudo bash -c \"cmd | pipe\"", "SC2024"),
        ("F008", "pkexec cmd > /f", "SC2024"),
        ("F009", "doas cmd > /f", "SC2024"),
        ("F010", "sudo env PATH=$P cmd", "SC2024"),
    ];

    for (id, code, forbidden) in tests {
        let result = lint_shell(code);
        let passed = !has_rule(&result, forbidden);
        gui.click(id);
        gui.visit(if passed { "pass" } else { "fail" });
        assert!(
            passed,
            "{}: Must NOT trigger {} on: {}",
            id, forbidden, code
        );
    }

    let report = gui.generate_report();
    println!(
        "\n6.1 Sudo/Permissions: {:.1}% elements",
        report.element_coverage * 100.0
    );
    assert!(
        report.element_coverage >= 1.0,
        "All sudo tests must pass (element coverage)"
    );
}

// ============================================================================
// 6.2 REDIRECTION AND PIPES (F011-F020)
// ============================================================================

#[test]
fn test_falsification_redirection_pipes() {
    let mut gui = gui_coverage! {
        buttons: ["F011", "F012", "F013", "F014", "F015", "F016", "F017", "F018", "F019", "F020"],
        screens: ["pass", "fail"]
    };

    let tests = [
        ("F011", "cmd 2>&1 | other", "SC2069"),
        ("F012", "cmd >/dev/null 2>&1", "SC2069"),
        ("F013", "cmd &> file", "SC2069"),
        ("F014", "exec 3>&1", "SC2069"),
        ("F015", "cmd |& other", "SC2069"),
        ("F016", "echo \"x\" >&2", "SC2069"),
        ("F017", "read -r x <<< \"str\"", "SC2069"),
        ("F018", "cmd <(list)", "SC2069"),
        ("F019", "cmd > >(other)", "SC2069"),
        ("F020", "{ cmd; } > file", "SC2024"),
    ];

    for (id, code, forbidden) in tests {
        let result = lint_shell(code);
        let passed = !has_rule(&result, forbidden);
        gui.click(id);
        gui.visit(if passed { "pass" } else { "fail" });
        assert!(
            passed,
            "{}: Must NOT trigger {} on: {}",
            id, forbidden, code
        );
    }

    let report = gui.generate_report();
    println!(
        "\n6.2 Redirection/Pipes: {:.1}% elements",
        report.element_coverage * 100.0
    );
    assert!(
        report.element_coverage >= 1.0,
        "All redirection tests must pass"
    );
}

// ============================================================================
// 6.3 QUOTING AND HEREDOCS (F021-F030)
// ============================================================================

#[test]
fn test_falsification_quoting_heredocs() {
    let mut gui = gui_coverage! {
        buttons: ["F021", "F022", "F023", "F024", "F025", "F026", "F027", "F028", "F029", "F030"],
        screens: ["pass", "fail"]
    };

    let tests = [
        ("F021", "cat << 'EOF'\n$var\nEOF", "SC2016"),
        ("F022", "cat << \"EOF\"\n$var\nEOF", "SC2016"),
        ("F023", "cat <<-'EOF'\n  literal\nEOF", "SC2016"),
        ("F024", "echo \"Don't\"", "SC2016"),
        ("F025", "echo 'Value: \"$var\"'", "SC2016"),
        ("F026", "printf '%s\\n' \"$v\"", "SC2059"),
        ("F027", "echo \"Only $ var\"", "SC2016"),
        ("F028", "echo 'a'\\''b'", "SC2016"),
        ("F029", "find . -name '*.c'", "SC2035"),
        ("F030", "grep -r '*.c' .", "SC2035"),
    ];

    for (id, code, forbidden) in tests {
        let result = lint_shell(code);
        let passed = !has_rule(&result, forbidden);
        gui.click(id);
        gui.visit(if passed { "pass" } else { "fail" });
        assert!(
            passed,
            "{}: Must NOT trigger {} on: {}",
            id, forbidden, code
        );
    }

    let report = gui.generate_report();
    println!(
        "\n6.3 Quoting/Heredocs: {:.1}% elements",
        report.element_coverage * 100.0
    );
    assert!(
        report.element_coverage >= 1.0,
        "All quoting tests must pass"
    );
}

// ============================================================================
// 6.4 VARIABLES AND PARAMETERS (F031-F045)
// ============================================================================

#[test]
fn test_falsification_variables_parameters() {
    let mut gui = gui_coverage! {
        buttons: ["F031", "F032", "F033", "F034", "F035", "F036", "F037", "F038", "F039", "F040", "F041", "F042", "F043", "F044", "F045"],
        screens: ["pass", "fail"]
    };

    let tests = [
        ("F031", "echo \"${var:-default}\"", "SC2086"),
        ("F032", "echo \"${var#*}\"", "SC2086"),
        ("F033", "echo \"${!prefix@}\"", "SC2086"),
        ("F034", "echo \"${arr[@]}\"", "SC2068"),
        ("F035", "echo ${#arr[@]}", "SC2086"),
        ("F036", "(( var++ ))", "SC2086"),
        ("F037", "[[ -n $var ]]", "SC2086"),
        ("F038", "f() { local var; echo $var; }", "SC2034"),
        ("F039", "export VAR=1", "SC2034"),
        ("F040", "readonly VAR=1", "SC2034"),
        ("F041", "_unused_arg=1", "SC2034"),
        ("F042", "typeset -n ref=$1", "SC2034"),
        ("F043", "PS1='prompt'", "SC2034"),
        ("F044", "PROMPT_COMMAND='cmd'", "SC2034"),
        ("F045", "trap 'echo $SIG' SIGINT", "SC2034"),
    ];

    for (id, code, forbidden) in tests {
        let result = lint_shell(code);
        let passed = !has_rule(&result, forbidden);
        gui.click(id);
        gui.visit(if passed { "pass" } else { "fail" });
        assert!(
            passed,
            "{}: Must NOT trigger {} on: {}",
            id, forbidden, code
        );
    }

    let report = gui.generate_report();
    println!(
        "\n6.4 Variables/Parameters: {:.1}% elements",
        report.element_coverage * 100.0
    );
    assert!(
        report.element_coverage >= 1.0,
        "All variable tests must pass"
    );
}

// ============================================================================
// 6.5 CONTROL FLOW (F046-F060)
// ============================================================================

#[test]
fn test_falsification_control_flow() {
    let mut gui = gui_coverage! {
        buttons: ["F046", "F047", "F048", "F049", "F050", "F051", "F052", "F053", "F054", "F055", "F056", "F057", "F058", "F059", "F060"],
        screens: ["pass", "fail"]
    };

    let tests = [
        ("F046", "if true; then echo yes; fi", "Parser"),
        ("F047", "case $x in *) ;; esac", "SC2154"),
        ("F048", "for ((i=0;i<10;i++)); do echo $i; done", "SC2086"),
        ("F049", "select x in list; do echo $x; done", "Parser"),
        ("F050", "while read -r; do echo $REPLY; done < f", "SC2034"),
        ("F051", "until [[ cond ]]; do echo x; done", "Parser"),
        ("F052", "[ \"$a\" ] && [ \"$b\" ]", "SC2015"),
        ("F053", "! command", "Parser"),
        ("F054", "time command", "Parser"),
        ("F055", "coproc command", "Parser"),
        ("F056", "f() { return 0 2>/dev/null; }", "SC2086"),
        ("F057", "break 2", "Parser"),
        ("F058", "continue 2", "Parser"),
        ("F059", "exit 0; echo unreachable", "SC2317"),
        ("F060", "function f { cmd; }", "Parser"),
    ];

    for (id, code, forbidden) in tests {
        let result = lint_shell(code);
        let passed = if forbidden == "Parser" {
            !has_parse_error(&result)
        } else {
            !has_rule(&result, forbidden)
        };
        gui.click(id);
        gui.visit(if passed { "pass" } else { "fail" });
        assert!(
            passed,
            "{}: Must NOT trigger {} on: {}",
            id, forbidden, code
        );
    }

    let report = gui.generate_report();
    println!(
        "\n6.5 Control Flow: {:.1}% elements",
        report.element_coverage * 100.0
    );
    assert!(
        report.element_coverage >= 1.0,
        "All control flow tests must pass"
    );
}

// ============================================================================
// 6.6 BUILTINS AND ENVIRONMENT (F061-F070)
// ============================================================================

#[test]
fn test_falsification_builtins_environment() {
    let mut gui = gui_coverage! {
        buttons: ["F061", "F062", "F063", "F064", "F065", "F066", "F067", "F068", "F069", "F070"],
        screens: ["pass", "fail"]
    };

    let tests = [
        ("F061", "echo $EUID", "SC2154"),
        ("F062", "echo $UID", "SC2154"),
        ("F063", "echo $BASH_VERSION", "SC2154"),
        ("F064", "echo $PIPESTATUS", "SC2154"),
        ("F065", "echo $RANDOM", "SC2154"),
        ("F066", "echo $LINENO", "SC2154"),
        ("F067", "echo $SECONDS", "SC2154"),
        ("F068", "echo $PWD", "SC2154"),
        ("F069", "echo $OLDPWD", "SC2154"),
        ("F070", "echo $SHLVL", "SC2154"),
    ];

    for (id, code, forbidden) in tests {
        let result = lint_shell(code);
        let passed = !has_rule(&result, forbidden);
        gui.click(id);
        gui.visit(if passed { "pass" } else { "fail" });
        assert!(
            passed,
            "{}: Must NOT trigger {} on: {}",
            id, forbidden, code
        );
    }

    let report = gui.generate_report();
    println!(
        "\n6.6 Builtins/Environment: {:.1}% elements",
        report.element_coverage * 100.0
    );
    assert!(
        report.element_coverage >= 1.0,
        "All builtin tests must pass"
    );
}

// ============================================================================
// 6.7 SUBSHELLS AND COMMAND SUBSTITUTION (F071-F080)
// ============================================================================

#[test]
fn test_falsification_subshells_cmdsub() {
    let mut gui = gui_coverage! {
        buttons: ["F071", "F072", "F073", "F074", "F075", "F076", "F077", "F078", "F079", "F080"],
        screens: ["pass", "fail"]
    };

    let tests = [
        ("F071", "( cd dir && cmd )", "SC2034"),
        ("F072", "echo $(command)", "SC2034"),
        ("F073", "var=$(cmd)", "SC2031"),
        ("F074", "var=\"$(cmd)\"", "SC2031"),
        ("F075", "echo $( < file )", "SC2002"),
        ("F076", "diff <(cmd1) <(cmd2)", "Parser"),
        ("F077", "exec > >(logger)", "Parser"),
        ("F078", "x=$( (cmd) )", "Parser"),
        ("F079", "x=$( { cmd; } )", "Parser"),
        ("F080", "x=`cmd`", "SC2006"),
    ];

    for (id, code, forbidden) in tests {
        let result = lint_shell(code);
        let passed = if forbidden == "Parser" {
            !has_parse_error(&result)
        } else {
            !has_rule(&result, forbidden)
        };
        gui.click(id);
        gui.visit(if passed { "pass" } else { "fail" });
        assert!(
            passed,
            "{}: Must NOT trigger {} on: {}",
            id, forbidden, code
        );
    }

    let report = gui.generate_report();
    println!(
        "\n6.7 Subshells/CmdSub: {:.1}% elements",
        report.element_coverage * 100.0
    );
    assert!(
        report.element_coverage >= 1.0,
        "All subshell tests must pass"
    );
}

// ============================================================================
// 6.8 TRAPS AND SIGNALS (F081-F090)
// ============================================================================

#[test]

include!("falsification_probar_testing_tests_falsification.rs");