use bashkit::Bash;
#[tokio::test]
async fn set_e_and_list_in_function() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
f() {
[[ "a" == "b" ]] && return 0
echo "should reach here"
}
f
echo "after f"
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("should reach here"));
assert!(result.stdout.contains("after f"));
}
#[tokio::test]
async fn set_e_and_list_in_brace_group() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
x=""
{
echo "line1"
[[ -n "$x" ]] && echo "has value"
echo "line2"
} > /tmp/out.txt
cat /tmp/out.txt
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("line1"));
assert!(result.stdout.contains("line2"));
}
#[tokio::test]
async fn set_e_and_list_in_for_loop() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
f() {
for x in a b c; do
[[ "$x" == "b" ]] && return 0
done
}
f
echo "after f"
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("after f"));
}
#[tokio::test]
async fn set_e_and_list_top_level() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
[[ "a" == "b" ]] && echo "match"
echo "reached"
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("reached"));
}
#[tokio::test]
async fn set_e_and_list_in_nested_cfor() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
f() {
for ((i=0; i<2; i++)); do
for ((j=0; j<2; j++)); do
[[ $i -ne $j ]] && echo "$i != $j"
done
done
echo "done"
}
f
echo "after f"
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("0 != 1"), "should print 0 != 1");
assert!(result.stdout.contains("1 != 0"), "should print 1 != 0");
assert!(result.stdout.contains("done"), "should reach done");
assert!(result.stdout.contains("after f"), "should reach after f");
}
#[tokio::test]
async fn set_e_and_list_in_nested_for_in() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
f() {
for i in a b; do
for j in a b; do
[[ "$i" != "$j" ]] && echo "$i != $j"
done
done
echo "done"
}
f
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("a != b"));
assert!(result.stdout.contains("b != a"));
assert!(result.stdout.contains("done"));
}
#[tokio::test]
async fn set_e_and_list_in_nested_while() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
i=0
while [[ $i -lt 2 ]]; do
j=0
while [[ $j -lt 2 ]]; do
[[ $i -ne $j ]] && echo "$i != $j"
((j++)) || true
done
((i++)) || true
done
echo "done"
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("0 != 1"));
assert!(result.stdout.contains("1 != 0"));
assert!(result.stdout.contains("done"));
}
#[tokio::test]
async fn set_e_exits_on_plain_failure_in_nested_loop() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
for ((i=0; i<2; i++)); do
for ((j=0; j<2; j++)); do
false
done
done
echo "SHOULD NOT APPEAR"
"#,
)
.await
.unwrap();
assert!(
!result.stdout.contains("SHOULD NOT APPEAR"),
"unexpected stdout: {:?}",
result.stdout
);
}
#[tokio::test]
async fn set_e_and_chain_at_end_of_for_body() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -euo pipefail
result=""
for src in yes no; do
[[ "${src}" == "yes" ]] && result="${src}"
done
echo "result: ${result}"
"#,
)
.await
.unwrap();
assert!(
result.stdout.contains("result: yes"),
"should print 'result: yes' but got: {:?}",
result.stdout
);
}
#[tokio::test]
async fn set_e_exits_on_plain_failure() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
false
echo "SHOULD NOT APPEAR"
"#,
)
.await
.unwrap();
assert!(!result.stdout.contains("SHOULD NOT APPEAR"));
}
#[tokio::test]
async fn set_e_mixed_and_or_then_plain_failure_exits() {
let mut bash = Bash::new();
let result = bash
.exec(
r#"
set -e
f() {
true && echo "ok"; false
}
f
echo "SHOULD NOT APPEAR"
"#,
)
.await
.unwrap();
assert!(result.stdout.contains("ok"));
assert!(
!result.stdout.contains("SHOULD NOT APPEAR"),
"exit_code={}, stdout={:?}, stderr={:?}",
result.exit_code,
result.stdout,
result.stderr
);
}