#![allow(clippy::unwrap_used, clippy::expect_used)]
use kaish_kernel::{Kernel, KernelConfig};
fn kernel() -> Kernel {
Kernel::new(KernelConfig::transient().with_skip_validation(true)).expect("kernel")
}
#[tokio::test]
async fn printf_plus_flag_positive() {
let k = kernel();
let result = k.execute("printf '%+d' 5").await.expect("execute");
assert_eq!(result.text_out(), "+5", "printf '%+d' 5 should produce +5");
}
#[tokio::test]
async fn printf_plus_flag_negative() {
let k = kernel();
let result = k.execute("printf '%+d' -- -5").await.expect("execute");
assert_eq!(result.text_out(), "-5", "printf '%+d' -5 should produce -5");
}
#[tokio::test]
async fn printf_space_flag_positive() {
let k = kernel();
let result = k.execute("printf '% d' 5").await.expect("execute");
assert_eq!(result.text_out(), " 5", "printf '% d' 5 should produce ' 5'");
}
#[tokio::test]
async fn printf_space_flag_negative() {
let k = kernel();
let result = k.execute("printf '% d' -- -5").await.expect("execute");
assert_eq!(result.text_out(), "-5", "printf '% d' -5 should produce -5");
}
#[tokio::test]
async fn printf_plus_beats_space() {
let k = kernel();
let result = k.execute("printf '% +d' 5").await.expect("execute");
assert_eq!(result.text_out(), "+5", "printf '% +d' 5: + flag should win over space");
}
#[tokio::test]
async fn printf_hash_hex_lower() {
let k = kernel();
let result = k.execute("printf '%#x' 255").await.expect("execute");
assert_eq!(result.text_out(), "0xff", "printf '%#x' 255 should produce 0xff");
}
#[tokio::test]
async fn printf_hash_hex_upper() {
let k = kernel();
let result = k.execute("printf '%#X' 255").await.expect("execute");
assert_eq!(result.text_out(), "0XFF", "printf '%#X' 255 should produce 0XFF");
}
#[tokio::test]
async fn printf_hash_octal() {
let k = kernel();
let result = k.execute("printf '%#o' 8").await.expect("execute");
assert_eq!(result.text_out(), "010", "printf '%#o' 8 should produce 010");
}
#[tokio::test]
async fn printf_hash_zero_is_zero() {
let k = kernel();
let result = k.execute("printf '%#x' 0").await.expect("execute");
assert_eq!(result.text_out(), "0", "printf '%#x' 0 should produce 0 not 0x0");
}
#[tokio::test]
async fn printf_precision_decimal() {
let k = kernel();
let result = k.execute("printf '%.3d' 5").await.expect("execute");
assert_eq!(result.text_out(), "005", "printf '%.3d' 5 should produce 005");
}
#[tokio::test]
async fn printf_precision_decimal_zero() {
let k = kernel();
let result = k.execute("printf '%.5d' 0").await.expect("execute");
assert_eq!(result.text_out(), "00000", "printf '%.5d' 0 should produce 00000");
}
#[tokio::test]
async fn printf_precision_hex() {
let k = kernel();
let result = k.execute("printf '%.4x' 255").await.expect("execute");
assert_eq!(result.text_out(), "00ff", "printf '%.4x' 255 should produce 00ff");
}
#[tokio::test]
async fn printf_precision_overrides_zero_flag() {
let k = kernel();
let result = k.execute("printf '%05.3d' 5").await.expect("execute");
assert_eq!(result.text_out(), " 005", "printf '%05.3d' 5: precision overrides zero flag");
}
#[tokio::test]
async fn printf_precision_string() {
let k = kernel();
let result = k.execute("printf '%.3s' abcdef").await.expect("execute");
assert_eq!(result.text_out(), "abc", "printf '%.3s' abcdef should produce abc");
}
#[tokio::test]
async fn printf_precision_string_shorter_than_precision() {
let k = kernel();
let result = k.execute("printf '%.10s' hello").await.expect("execute");
assert_eq!(result.text_out(), "hello", "printf '%.10s' hello should produce hello");
}
#[tokio::test]
async fn printf_precision_float() {
let k = kernel();
let result = k.execute("printf '%.2f' 3.14159").await.expect("execute");
assert_eq!(result.text_out(), "3.14", "printf '%.2f' 3.14159 should produce 3.14");
}
#[tokio::test]
async fn printf_u_conversion_positive() {
let k = kernel();
let result = k.execute("printf '%u' 5").await.expect("execute");
assert_eq!(result.text_out(), "5", "printf '%u' 5 should produce 5");
}
#[tokio::test]
async fn printf_big_e_conversion() {
let k = kernel();
let result = k.execute("printf '%E' 1000").await.expect("execute");
assert_eq!(result.text_out(), "1.000000E+03", "printf '%E' 1000 should produce 1.000000E+03");
}
#[tokio::test]
async fn printf_big_e_negative() {
let k = kernel();
let result = k.execute("printf '%E' -- -1000").await.expect("execute");
assert_eq!(result.text_out(), "-1.000000E+03", "printf '%E' -1000 should produce -1.000000E+03");
}
#[tokio::test]
async fn printf_big_e_with_precision() {
let k = kernel();
let result = k.execute("printf '%.2E' 1000").await.expect("execute");
assert_eq!(result.text_out(), "1.00E+03", "printf '%.2E' 1000 should produce 1.00E+03");
}
#[tokio::test]
async fn printf_big_g_large_number() {
let k = kernel();
let result = k.execute("printf '%G' 1234567").await.expect("execute");
assert_eq!(result.text_out(), "1.23457E+06", "printf '%G' 1234567 should produce 1.23457E+06");
}
#[tokio::test]
async fn printf_big_g_small_number() {
let k = kernel();
let result = k.execute("printf '%G' 0.000123").await.expect("execute");
assert_eq!(result.text_out(), "0.000123", "printf '%G' 0.000123 should produce 0.000123");
}
#[tokio::test]
async fn printf_big_g_one() {
let k = kernel();
let result = k.execute("printf '%G' 1.0").await.expect("execute");
assert_eq!(result.text_out(), "1", "printf '%G' 1.0 should produce 1");
}
#[tokio::test]
async fn printf_b_tab_escape() {
let k = kernel();
let result = k.execute(r#"printf '%b' '\t'"#).await.expect("execute");
assert_eq!(result.text_out(), "\t", "printf '%b' '\\t' should produce a tab");
}
#[tokio::test]
async fn printf_b_newline_escape() {
let k = kernel();
let result = k.execute(r#"printf '%b' 'hello\nworld'"#).await.expect("execute");
assert_eq!(result.text_out(), "hello\nworld", "printf '%b' 'hello\\nworld' should have a real newline");
}
#[tokio::test]
async fn printf_b_octal_escape() {
let k = kernel();
let result = k.execute(r#"printf '%b' '\101'"#).await.expect("execute");
assert_eq!(result.text_out(), "A", "printf '%b' '\\101' should produce A");
}
#[tokio::test]
async fn printf_b_plain_string_passthrough() {
let k = kernel();
let result = k.execute(r#"printf '%b' 'hello'"#).await.expect("execute");
assert_eq!(result.text_out(), "hello", "printf '%b' 'hello' should produce hello");
}
#[tokio::test]
async fn printf_format_octal_escape_zero_prefix() {
let k = kernel();
let result = k.execute(r#"printf '\0101'"#).await.expect("execute");
assert_eq!(
result.text_out(),
"\u{8}1",
"printf '\\0101' → BS + '1' (octal 010, then literal 1), like GNU printf"
);
assert_eq!(
kernel().execute(r#"printf '\012'"#).await.expect("execute").text_out(),
"\n",
);
assert_eq!(
kernel().execute(r#"printf '\0377'"#).await.expect("execute").text_out(),
"\u{1f}7",
);
assert_eq!(
kernel().execute(r#"printf '\101'"#).await.expect("execute").text_out(),
"A",
);
}
#[tokio::test]
async fn printf_format_octal_escape_no_zero_prefix() {
let k = kernel();
let result = k.execute(r#"printf '\101'"#).await.expect("execute");
assert_eq!(result.text_out(), "A", "printf '\\101' should produce A");
}
#[tokio::test]
async fn printf_width_plus_sign() {
let k = kernel();
let result = k.execute("printf '%+6d' 5").await.expect("execute");
assert_eq!(result.text_out(), " +5", "printf '%+6d' 5 should produce ' +5'");
}
#[tokio::test]
async fn printf_zero_pad_plus_sign() {
let k = kernel();
let result = k.execute("printf '%+06d' 5").await.expect("execute");
assert_eq!(result.text_out(), "+00005", "printf '%+06d' 5 should produce +00005");
}
#[tokio::test]
async fn printf_left_align_beats_zero_pad() {
let k = kernel();
let result = k.execute("printf '%-05d' 42").await.expect("execute");
assert_eq!(result.text_out(), "42 ", "printf '%-05d' 42: left-align should win over zero-pad");
}
#[tokio::test]
async fn printf_little_e_conversion() {
let k = kernel();
let result = k.execute("printf '%e' 1000").await.expect("execute");
assert_eq!(result.text_out(), "1.000000e+03", "printf '%e' 1000 should produce 1.000000e+03");
}
#[tokio::test]
async fn printf_little_g_large_number() {
let k = kernel();
let result = k.execute("printf '%g' 1234567").await.expect("execute");
assert_eq!(result.text_out(), "1.23457e+06", "printf '%g' 1234567 should produce 1.23457e+06");
}
#[tokio::test]
async fn printf_dash_only_literal() {
let k = kernel();
let result = k.execute("printf ---").await.expect("execute");
assert_eq!(result.text_out(), "---", "printf --- should produce ---");
}
#[tokio::test]
async fn printf_dash_only_after_double_dash() {
let k = kernel();
let result = k.execute("printf -- ---").await.expect("execute");
assert_eq!(result.text_out(), "---", "printf -- --- should produce ---");
}
#[tokio::test]
async fn printf_width_ascii_regression() {
let k = kernel();
let result = k.execute("printf '%10s' hi").await.expect("execute");
assert_eq!(result.text_out(), " hi", "printf '%10s' hi should right-pad to width 10");
}
#[tokio::test]
async fn printf_width_cjk_right_align() {
let k = kernel();
let result = k.execute("printf '%10s' '日本語'").await.expect("execute");
assert_eq!(
result.text_out(),
" 日本語",
"printf '%10s' 日本語 should pad to display width 10 (4 spaces + 6-wide string)"
);
}
#[tokio::test]
async fn printf_width_cjk_left_align() {
let k = kernel();
let result = k.execute("printf '%-10s' '日本語'").await.expect("execute");
assert_eq!(
result.text_out(),
"日本語 ",
"printf '%-10s' 日本語 should left-align and pad to display width 10"
);
}
#[tokio::test]
async fn printf_width_emoji() {
let k = kernel();
let result = k.execute("printf '%5s' '🎉'").await.expect("execute");
assert_eq!(
result.text_out(),
" 🎉",
"printf '%5s' 🎉 should pad to display width 5 (3 spaces + 2-wide emoji)"
);
}
#[tokio::test]
async fn printf_width_c_conversion_cjk() {
let k = kernel();
let result = k.execute("printf '%5c' '日'").await.expect("execute");
assert_eq!(
result.text_out(),
" 日",
"printf '%5c' 日 should pad to display width 5 (3 spaces + 2-wide char)"
);
}
#[tokio::test]
async fn printf_precision_multibyte_does_not_panic_or_split_char() {
let k = kernel();
let result = k.execute("printf '%.2s' '日本語'").await.expect("execute");
assert_eq!(
result.text_out(),
"日本",
"printf '%.2s' 日本語 should truncate to the first 2 characters without splitting a char"
);
}
#[tokio::test]
async fn printf_zero_pad_width_cjk_counts_display_width() {
let k = kernel();
let result = k.execute("printf '%010s' '日本語'").await.expect("execute");
assert_eq!(
result.text_out(),
"0000日本語",
"printf '%010s' 日本語 should zero-pad to display width 10 (4 zeros + 6-wide string)"
);
}
#[tokio::test]
async fn printf_width_and_precision_multibyte_pads_by_truncated_width() {
let k = kernel();
let result = k.execute("printf '%10.2s' '日本語'").await.expect("execute");
assert_eq!(
result.text_out(),
" 日本",
"printf '%10.2s' 日本語 should pad based on the truncated value's display width (6 spaces + 4-wide '日本')"
);
}
#[tokio::test]
async fn printf_width_zero_width_codepoint_no_underflow() {
let k = kernel();
let arg = "\u{0301}"; let cmd = format!("printf '%5s' '{arg}'");
let result = k.execute(&cmd).await.expect("execute");
assert_eq!(
result.text_out(),
format!(" {arg}"),
"printf '%5s' with a zero-width combining mark should pad with all 5 spaces, no underflow/panic"
);
}