use std::cell::Cell;
use crate::test_support::check_func_space_only;
use crate::*;
#[cfg(feature = "bash")]
type Decisions = [u64; 3];
#[cfg(feature = "bash")]
fn measure(body: &str) -> Decisions {
let out = Cell::new([0; 3]);
check_func_space_only::<BashParser, _>(
&format!("f() {{\n {body}\n}}\n"),
"foo.sh",
&[Metric::Cyclomatic, Metric::Abc, Metric::Cognitive],
|space| {
let m = &space.metrics;
out.set([
m.cyclomatic.cyclomatic_sum(),
m.abc.conditions_sum(),
m.cognitive.cognitive_sum(),
]);
},
);
out.get()
}
#[cfg(feature = "bash")]
#[test]
fn bash_test_connectives_score_like_their_twins_1536() {
let rows: [(&str, &str, &str, Decisions); 8] = [
(
r#"if [ "$a" = 1 -a "$b" = 2 ]; then :; fi"#,
r#"if [ "$a" = 1 ] && [ "$b" = 2 ]; then :; fi"#,
r#"if [[ "$a" = 1 && "$b" = 2 ]]; then :; fi"#,
[4, 3, 2],
),
(
r#"if [ "$a" == 1 -a "$b" == 2 ]; then :; fi"#,
r#"if [ "$a" == 1 ] && [ "$b" == 2 ]; then :; fi"#,
r#"if [[ "$a" == 1 && "$b" == 2 ]]; then :; fi"#,
[4, 3, 2],
),
(
r#"if [ "$a" -eq 1 -a "$b" -eq 2 ]; then :; fi"#,
r#"if [ "$a" -eq 1 ] && [ "$b" -eq 2 ]; then :; fi"#,
r#"if [[ "$a" -eq 1 && "$b" -eq 2 ]]; then :; fi"#,
[4, 3, 2],
),
(
r#"if [ "$a" = 1 -o "$b" = 2 ]; then :; fi"#,
r#"if [ "$a" = 1 ] || [ "$b" = 2 ]; then :; fi"#,
r#"if [[ "$a" = 1 || "$b" = 2 ]]; then :; fi"#,
[4, 3, 2],
),
(
r#"if [ "$a" = 1 -a "$b" = 2 -o "$c" = 3 ]; then :; fi"#,
r#"if [ "$a" = 1 ] && [ "$b" = 2 ] || [ "$c" = 3 ]; then :; fi"#,
r#"if [[ "$a" = 1 && "$b" = 2 || "$c" = 3 ]]; then :; fi"#,
[5, 4, 3],
),
(
r#"if [ "$a" = 1 -o "$b" = 2 -o "$c" = 3 ]; then :; fi"#,
r#"if [ "$a" = 1 ] || [ "$b" = 2 ] || [ "$c" = 3 ]; then :; fi"#,
r#"if [[ "$a" = 1 || "$b" = 2 || "$c" = 3 ]]; then :; fi"#,
[5, 4, 2],
),
(
r#"if [ "$a" == 1 -o "$b" == 2 -o "$c" == 3 ]; then :; fi"#,
r#"if [ "$a" == 1 ] || [ "$b" == 2 ] || [ "$c" == 3 ]; then :; fi"#,
r#"if [[ "$a" == 1 || "$b" == 2 || "$c" == 3 ]]; then :; fi"#,
[5, 4, 2],
),
(
r#"if [ "$a" != 1 -o "$b" != 2 -o "$c" != 3 ]; then :; fi"#,
r#"if [ "$a" != 1 ] || [ "$b" != 2 ] || [ "$c" != 3 ]; then :; fi"#,
r#"if [[ "$a" != 1 || "$b" != 2 || "$c" != 3 ]]; then :; fi"#,
[5, 4, 2],
),
];
for (single, chained, double, decisions) in rows {
assert!(
single.contains(" -a ") || single.contains(" -o "),
"`{single}` lost its connective"
);
for body in [single, chained, double] {
assert_eq!(measure(body), decisions, "`{body}`");
}
}
}
#[cfg(feature = "bash")]
#[test]
fn bash_test_connective_gates_keep_their_controls_1536() {
let rows: [(&str, &str, Decisions); 6] = [
(
"if [ -a x ]; then :; fi",
"if [ -f x ]; then :; fi",
[3, 2, 1],
),
(
r#"if [ "$a" = 1 ]; then :; fi"#,
r#"if [ "$a" == 1 ]; then :; fi"#,
[3, 2, 1],
),
(
r#"if [[ ( "$a" = 1 ) ]]; then :; fi"#,
r#"if [[ ( "$a" == 1 ) ]]; then :; fi"#,
[3, 2, 1],
),
(
r#"if [[ ! ( "$a" = 1 ) ]]; then :; fi"#,
r#"if [[ ! ( "$a" == 1 ) ]]; then :; fi"#,
[3, 2, 1],
),
("(( x = 1 ))", "x=1", [2, 0, 0]),
(
r#"if [[ ( "$a" = 1 || "$b" = 2 ) && ( "$c" = 3 || "$d" = 4 ) ]]; then :; fi"#,
r#"if [[ ( "$a" == 1 || "$b" == 2 ) && ( "$c" == 3 || "$d" == 4 ) ]]; then :; fi"#,
[6, 5, 4],
),
];
for (body, twin, decisions) in rows {
assert_eq!(
measure(twin),
decisions,
"`{twin}` moved; re-derive the row"
);
assert_eq!(
measure(body),
decisions,
"`{body}` must score like `{twin}`"
);
}
}
#[cfg(feature = "bash")]
#[test]
fn bash_comparison_run_resumes_after_a_nested_run_1536() {
assert_eq!(
measure(
r#"if [[ "$a" = 1 || ( "$x" = 5 && "$y" = 6 ) || "$b" = 2 || "$c" = 3 || "$d" = 4 ]]; then :; fi"#
),
[8, 7, 4]
);
}
#[cfg(feature = "bash")]
#[test]
fn bash_comparison_runs_score_like_their_twins_1536() {
let rows: [(&str, &str, Decisions); 3] = [
(
r#"if [[ "$a" =~ $re || "$b" =~ $re || "$c" =~ $re ]]; then :; fi"#,
r#"if [[ "$a" == $re || "$b" == $re || "$c" == $re ]]; then :; fi"#,
[5, 4, 2],
),
(
"if [[ ( $a = 1 || $b = 2 ) && $c = 3 || $d = 4 ]]; then :; fi",
"if [[ ( $a == 1 || $b == 2 ) && $c == 3 || $d == 4 ]]; then :; fi",
[6, 5, 4],
),
(
"if [[ $(( x = 1 )) -eq 1 ]]; then :; fi",
"if [[ $(( x + 1 )) -eq 1 ]]; then :; fi",
[3, 2, 1],
),
];
for (body, twin, decisions) in rows {
assert_eq!(
measure(twin),
decisions,
"`{twin}` moved; re-derive the row"
);
assert_eq!(
measure(body),
decisions,
"`{body}` must score like `{twin}`"
);
}
}
#[cfg(feature = "bash")]
#[test]
fn bash_unary_test_preserves_the_comparison_sequence() {
for (operator, connective) in [("-a", "&&"), ("-o", "||")] {
for unary in [r#"-n "$b""#, r#"! -z "$b""#, r#"-f "$b""#] {
for comparison in ["=", "==", "!="] {
let single = format!(
r#"if [ {unary} {operator} "$a" {comparison} 1 {operator} "$c" {comparison} 2 ]; then :; fi"#
);
let twin = format!(
r#"if [ {unary} ] {connective} [ "$a" {comparison} 1 ] {connective} [ "$c" {comparison} 2 ]; then :; fi"#
);
assert_eq!(measure(&twin), [5, 4, 2], "{twin}");
assert_eq!(measure(&single), [5, 4, 2], "{single}");
}
}
}
}