pub const BIN_TYPESET: i32 = 0; pub const BIN_BG: i32 = 1; pub const BIN_FG: i32 = 2; pub const BIN_JOBS: i32 = 3; pub const BIN_WAIT: i32 = 4; pub const BIN_DISOWN: i32 = 5; pub const BIN_BREAK: i32 = 6; pub const BIN_CONTINUE: i32 = 7; pub const BIN_EXIT: i32 = 8; pub const BIN_RETURN: i32 = 9; pub const BIN_CD: i32 = 10; pub const BIN_POPD: i32 = 11; pub const BIN_PUSHD: i32 = 12; pub const BIN_PRINT: i32 = 13; pub const BIN_EVAL: i32 = 14; pub const BIN_SCHED: i32 = 15; pub const BIN_FC: i32 = 16; pub const BIN_R: i32 = 17; pub const BIN_PUSHLINE: i32 = 18; pub const BIN_LOGOUT: i32 = 19; pub const BIN_TEST: i32 = 20; pub const BIN_BRACKET: i32 = 21; pub const BIN_READONLY: i32 = 22; pub const BIN_ECHO: i32 = 23; pub const BIN_DISABLE: i32 = 24; pub const BIN_ENABLE: i32 = 25; pub const BIN_PRINTF: i32 = 26; pub const BIN_COMMAND: i32 = 27; pub const BIN_UNHASH: i32 = 28; pub const BIN_UNALIAS: i32 = 29; pub const BIN_UNFUNCTION: i32 = 30; pub const BIN_UNSET: i32 = 31; pub const BIN_EXPORT: i32 = 32;
pub const BIN_SETOPT: i32 = 0; pub const BIN_UNSETOPT: i32 = 1;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn overloaded_bin_ids_are_pairwise_distinct() {
let _g = crate::test_util::global_state_lock();
let ids = [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
];
let mut sorted = ids.to_vec();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), ids.len());
}
#[test]
fn bin_constants_match_c_source_position_for_position() {
let _g = crate::test_util::global_state_lock();
let table = [
("BIN_TYPESET", BIN_TYPESET, 0), ("BIN_BG", BIN_BG, 1), ("BIN_FG", BIN_FG, 2), ("BIN_JOBS", BIN_JOBS, 3), ("BIN_WAIT", BIN_WAIT, 4), ("BIN_DISOWN", BIN_DISOWN, 5), ("BIN_BREAK", BIN_BREAK, 6), ("BIN_CONTINUE", BIN_CONTINUE, 7), ("BIN_EXIT", BIN_EXIT, 8), ("BIN_RETURN", BIN_RETURN, 9), ("BIN_CD", BIN_CD, 10), ("BIN_POPD", BIN_POPD, 11), ("BIN_PUSHD", BIN_PUSHD, 12), ("BIN_PRINT", BIN_PRINT, 13), ("BIN_EVAL", BIN_EVAL, 14), ("BIN_SCHED", BIN_SCHED, 15), ("BIN_FC", BIN_FC, 16), ("BIN_R", BIN_R, 17), ("BIN_PUSHLINE", BIN_PUSHLINE, 18), ("BIN_LOGOUT", BIN_LOGOUT, 19), ("BIN_TEST", BIN_TEST, 20), ("BIN_BRACKET", BIN_BRACKET, 21), ("BIN_READONLY", BIN_READONLY, 22), ("BIN_ECHO", BIN_ECHO, 23), ("BIN_DISABLE", BIN_DISABLE, 24), ("BIN_ENABLE", BIN_ENABLE, 25), ("BIN_PRINTF", BIN_PRINTF, 26), ("BIN_COMMAND", BIN_COMMAND, 27), ("BIN_UNHASH", BIN_UNHASH, 28), ("BIN_UNALIAS", BIN_UNALIAS, 29), ("BIN_UNFUNCTION", BIN_UNFUNCTION, 30), ("BIN_UNSET", BIN_UNSET, 31), ("BIN_EXPORT", BIN_EXPORT, 32), ];
for (name, got, want) in table {
assert_eq!(
got, want,
"c:34-66 — {} must be {} (C source value)",
name, want
);
}
}
#[test]
fn setopt_unsetopt_pin_to_zero_and_one() {
let _g = crate::test_util::global_state_lock();
assert_eq!(
BIN_SETOPT, 0,
"c:69 — BIN_SETOPT must be 0 (load-bearing per c:68 comment)"
);
assert_eq!(
BIN_UNSETOPT, 1,
"c:70 — BIN_UNSETOPT must be 1 (load-bearing per c:68 comment)"
);
assert_eq!(
BIN_SETOPT, BIN_TYPESET,
"c:68 — intentional overlap: setopt/unsetopt vs typeset/bg use disjoint dispatch"
);
assert_eq!(BIN_UNSETOPT, BIN_BG);
}
#[test]
fn hashtable_h_corpus_break_continue_exit_return_sequence() {
assert_eq!(BIN_BREAK, 6);
assert_eq!(BIN_CONTINUE, 7);
assert_eq!(BIN_EXIT, 8);
assert_eq!(BIN_RETURN, 9);
}
#[test]
fn hashtable_h_corpus_directory_sequence() {
assert_eq!(BIN_CD, 10);
assert_eq!(BIN_POPD, 11);
assert_eq!(BIN_PUSHD, 12);
}
#[test]
fn hashtable_h_corpus_print_eval_sequence() {
assert_eq!(BIN_PRINT, 13);
assert_eq!(BIN_EVAL, 14);
}
#[test]
fn BIN_TYPESET_is_zero() {
assert_eq!(BIN_TYPESET, 0);
}
#[test]
fn job_control_builtin_codes_canonical() {
assert_eq!(BIN_BG, 1);
assert_eq!(BIN_FG, 2);
assert_eq!(BIN_JOBS, 3);
assert_eq!(BIN_WAIT, 4);
assert_eq!(BIN_DISOWN, 5);
}
#[test]
fn hist_builtin_codes_canonical() {
assert_eq!(BIN_SCHED, 15);
assert_eq!(BIN_FC, 16);
assert_eq!(BIN_R, 17);
assert_eq!(BIN_PUSHLINE, 18);
assert_eq!(BIN_LOGOUT, 19);
}
#[test]
fn test_readonly_echo_builtin_codes_canonical() {
assert_eq!(BIN_TEST, 20);
assert_eq!(BIN_BRACKET, 21);
assert_eq!(BIN_READONLY, 22);
assert_eq!(BIN_ECHO, 23);
assert_eq!(BIN_DISABLE, 24);
}
#[test]
fn all_bin_codes_pairwise_distinct() {
let codes = [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
];
let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
assert_eq!(unique.len(), codes.len(), "BIN_* must be pairwise distinct");
}
#[test]
fn all_bin_codes_non_negative() {
for c in [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
] {
assert!(c >= 0, "BIN_* code {} must be ≥ 0", c);
}
}
#[test]
fn bin_codes_form_contiguous_range() {
let mut codes: Vec<i32> = vec![
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
];
codes.sort();
for (i, c) in codes.iter().enumerate() {
assert_eq!(*c, i as i32, "BIN_* must form contiguous 0..N range");
}
}
#[test]
fn bin_codes_24_through_32_contiguous() {
let extras: Vec<i32> = vec![
BIN_DISABLE, BIN_ENABLE, BIN_PRINTF, BIN_COMMAND, BIN_UNHASH, BIN_UNALIAS, BIN_UNFUNCTION, BIN_UNSET, BIN_EXPORT, ];
for (i, &c) in extras.iter().enumerate() {
assert_eq!(c, 24 + i as i32, "BIN_* tail must be contiguous 24..32");
}
}
#[test]
fn bin_un_family_sequential() {
assert_eq!(BIN_UNHASH, 28, "c:62");
assert_eq!(BIN_UNALIAS, 29, "c:63");
assert_eq!(BIN_UNFUNCTION, 30, "c:64");
assert_eq!(BIN_UNSET, 31, "c:65");
}
#[test]
fn bin_output_exec_family_sequential() {
assert_eq!(BIN_PRINTF, 26, "c:60");
assert_eq!(BIN_COMMAND, 27, "c:61");
}
#[test]
fn bin_enable_disable_sequential() {
assert_eq!(BIN_DISABLE, 24, "c:58");
assert_eq!(BIN_ENABLE, 25, "c:59");
}
#[test]
fn bin_export_is_max_non_overlapping() {
let all = [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
];
let max = *all.iter().max().unwrap();
assert_eq!(
max, BIN_EXPORT,
"BIN_EXPORT must be max non-overlapping slot"
);
assert_eq!(BIN_EXPORT, 32, "c:66 = 32");
}
#[test]
fn bin_setopt_unsetopt_overlap_typeset_bg_is_intentional() {
assert_eq!(BIN_SETOPT, BIN_TYPESET, "c:69 SETOPT overlaps TYPESET");
assert_eq!(BIN_UNSETOPT, BIN_BG, "c:70 UNSETOPT overlaps BG");
}
#[test]
fn bin_overload_family_has_33_distinct_slots() {
let ids = [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
];
assert_eq!(ids.len(), 33, "33 explicitly numbered BIN_* overload slots");
let unique: std::collections::HashSet<_> = ids.iter().copied().collect();
assert_eq!(unique.len(), ids.len(), "all 33 must be pairwise distinct");
}
#[test]
fn all_bin_codes_fit_in_u8_range() {
let all = [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
BIN_SETOPT,
BIN_UNSETOPT,
];
for c in all {
assert!(
(0..256).contains(&c),
"BIN_* code {} must fit in 0..256 (u8 funcid headroom)",
c
);
}
}
#[test]
fn bin_break_continue_exit_return_sequential_six_through_nine() {
assert_eq!(BIN_BREAK, 6, "c:40");
assert_eq!(BIN_CONTINUE, 7, "c:41");
assert_eq!(BIN_EXIT, 8, "c:42");
assert_eq!(BIN_RETURN, 9, "c:43");
}
#[test]
fn bin_cd_popd_pushd_sequential_ten_through_twelve() {
assert_eq!(BIN_CD, 10, "c:44");
assert_eq!(BIN_POPD, 11, "c:45");
assert_eq!(BIN_PUSHD, 12, "c:46");
}
#[test]
fn bin_print_eval_canonical() {
assert_eq!(BIN_PRINT, 13, "c:47");
assert_eq!(BIN_EVAL, 14, "c:48");
}
#[test]
fn bin_sched_fc_r_pushline_canonical() {
assert_eq!(BIN_SCHED, 15, "c:49");
assert_eq!(BIN_FC, 16, "c:50");
assert_eq!(BIN_R, 17, "c:51");
assert_eq!(BIN_PUSHLINE, 18, "c:52");
}
#[test]
fn bin_logout_test_bracket_canonical() {
assert_eq!(BIN_LOGOUT, 19, "c:53");
assert_eq!(BIN_TEST, 20, "c:54");
assert_eq!(BIN_BRACKET, 21, "c:55");
}
#[test]
fn all_bin_codes_fit_in_i8_positive_range() {
for c in [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
] {
assert!(
(0..=i8::MAX as i32).contains(&c),
"BIN_* code {} must fit in i8 positive range",
c
);
}
}
#[test]
fn all_bin_codes_are_i32_type() {
let _: i32 = BIN_TYPESET;
let _: i32 = BIN_SETOPT;
let _: i32 = BIN_EXPORT;
}
#[test]
fn bin_typeset_is_canonical_first_slot() {
assert_eq!(BIN_TYPESET, 0, "TYPESET claims slot 0");
}
#[test]
fn bin_job_control_family_slots_zero_through_five() {
assert_eq!(BIN_TYPESET, 0);
assert_eq!(BIN_BG, 1);
assert_eq!(BIN_FG, 2);
assert_eq!(BIN_JOBS, 3);
assert_eq!(BIN_WAIT, 4);
assert_eq!(BIN_DISOWN, 5);
}
#[test]
fn bin_setopt_unsetopt_canonical_zero_one() {
assert_eq!(BIN_SETOPT, 0, "c:69");
assert_eq!(BIN_UNSETOPT, 1, "c:70");
}
#[test]
fn bin_readonly_through_printf_canonical() {
assert_eq!(BIN_READONLY, 22, "c:56");
assert_eq!(BIN_ECHO, 23, "c:57");
assert_eq!(BIN_DISABLE, 24, "c:58");
assert_eq!(BIN_ENABLE, 25, "c:59");
assert_eq!(BIN_PRINTF, 26, "c:60");
}
#[test]
fn bin_command_through_export_canonical() {
assert_eq!(BIN_COMMAND, 27, "c:61");
assert_eq!(BIN_UNHASH, 28, "c:62");
assert_eq!(BIN_UNALIAS, 29, "c:63");
assert_eq!(BIN_UNFUNCTION, 30, "c:64");
assert_eq!(BIN_UNSET, 31, "c:65");
assert_eq!(BIN_EXPORT, 32, "c:66");
}
#[test]
fn bin_overload_family_is_dense_0_through_32() {
let mut codes: Vec<i32> = vec![
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
];
codes.sort();
let expected: Vec<i32> = (0..=32).collect();
assert_eq!(
codes, expected,
"overload family must cover exactly 0..=32 with no gaps/dups"
);
}
#[test]
fn bin_setopt_family_has_distinct_pair() {
assert_ne!(
BIN_SETOPT, BIN_UNSETOPT,
"SETOPT and UNSETOPT must differ to drive bin_setopt's branch"
);
}
#[test]
fn bin_break_starts_control_flow_family() {
assert_eq!(BIN_BREAK, 6, "c:40 — BREAK leads control-flow family");
assert_eq!(BIN_CONTINUE, 7, "c:41");
assert_eq!(BIN_EXIT, 8, "c:42");
assert_eq!(BIN_RETURN, 9, "c:43");
}
#[test]
fn bin_dir_stack_family_canonical() {
assert_eq!(BIN_CD, 10, "c:44");
assert_eq!(BIN_POPD, 11, "c:45");
assert_eq!(BIN_PUSHD, 12, "c:46");
}
#[test]
fn bin_un_family_canonical() {
assert_eq!(BIN_UNHASH, 28, "c:62");
assert_eq!(BIN_UNALIAS, 29, "c:63");
assert_eq!(BIN_UNFUNCTION, 30, "c:64");
}
#[test]
fn bin_export_is_canonical_max_slot() {
for c in [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
] {
assert!(
c <= BIN_EXPORT,
"BIN_EXPORT (32) must be the max overload code; {} > 32",
c
);
}
}
#[test]
fn bin_overload_codes_all_non_negative() {
for c in [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
] {
assert!(c >= 0, "BIN_* must be non-negative; got {}", c);
}
}
#[test]
fn bin_setopt_family_codes_non_negative() {
assert!(BIN_SETOPT >= 0);
assert!(BIN_UNSETOPT >= 0);
}
#[test]
fn bin_main_slot_values_exact() {
assert_eq!(BIN_TYPESET, 0);
assert_eq!(BIN_BG, 1);
assert_eq!(BIN_FG, 2);
assert_eq!(BIN_JOBS, 3);
assert_eq!(BIN_WAIT, 4);
assert_eq!(BIN_DISOWN, 5);
assert_eq!(BIN_BREAK, 6);
assert_eq!(BIN_CONTINUE, 7);
assert_eq!(BIN_EXIT, 8);
assert_eq!(BIN_RETURN, 9);
assert_eq!(BIN_CD, 10);
assert_eq!(BIN_POPD, 11);
assert_eq!(BIN_PUSHD, 12);
assert_eq!(BIN_PRINT, 13);
assert_eq!(BIN_EVAL, 14);
assert_eq!(BIN_SCHED, 15);
assert_eq!(BIN_FC, 16);
assert_eq!(BIN_R, 17);
assert_eq!(BIN_PUSHLINE, 18);
assert_eq!(BIN_LOGOUT, 19);
}
#[test]
fn bin_main_slot_values_exact_part_two() {
assert_eq!(BIN_TEST, 20);
assert_eq!(BIN_BRACKET, 21);
assert_eq!(BIN_READONLY, 22);
assert_eq!(BIN_ECHO, 23);
assert_eq!(BIN_DISABLE, 24);
assert_eq!(BIN_ENABLE, 25);
assert_eq!(BIN_PRINTF, 26);
assert_eq!(BIN_COMMAND, 27);
assert_eq!(BIN_UNHASH, 28);
assert_eq!(BIN_UNALIAS, 29);
assert_eq!(BIN_UNFUNCTION, 30);
assert_eq!(BIN_UNSET, 31);
assert_eq!(BIN_EXPORT, 32);
}
#[test]
fn bin_main_slots_form_dense_0_to_32() {
let mut codes = [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
];
codes.sort();
let expected: Vec<i32> = (0..=32).collect();
assert_eq!(
codes.to_vec(),
expected,
"BIN_* main slots must be dense 0..=32 (no gaps)"
);
}
#[test]
fn bin_setopt_family_exact_values() {
assert_eq!(BIN_SETOPT, 0);
assert_eq!(BIN_UNSETOPT, 1);
}
#[test]
fn bin_setopt_family_pair_distinct() {
assert_ne!(
BIN_SETOPT, BIN_UNSETOPT,
"setopt vs unsetopt must be different overload codes"
);
}
#[test]
fn bin_main_codes_fit_in_i8_room_for_growth() {
for c in [BIN_TYPESET, BIN_EXPORT] {
assert!(
c < i8::MAX as i32,
"BIN_* main {} must fit in i8 (≥2x growth headroom)",
c
);
}
}
#[test]
fn bin_main_slots_count_is_33() {
let codes = [
BIN_TYPESET,
BIN_BG,
BIN_FG,
BIN_JOBS,
BIN_WAIT,
BIN_DISOWN,
BIN_BREAK,
BIN_CONTINUE,
BIN_EXIT,
BIN_RETURN,
BIN_CD,
BIN_POPD,
BIN_PUSHD,
BIN_PRINT,
BIN_EVAL,
BIN_SCHED,
BIN_FC,
BIN_R,
BIN_PUSHLINE,
BIN_LOGOUT,
BIN_TEST,
BIN_BRACKET,
BIN_READONLY,
BIN_ECHO,
BIN_DISABLE,
BIN_ENABLE,
BIN_PRINTF,
BIN_COMMAND,
BIN_UNHASH,
BIN_UNALIAS,
BIN_UNFUNCTION,
BIN_UNSET,
BIN_EXPORT,
];
assert_eq!(codes.len(), 33, "33 BIN_* main slots must be declared");
}
#[test]
fn bin_typeset_is_zero_sentinel() {
assert_eq!(BIN_TYPESET, 0, "BIN_TYPESET must be first slot (= 0)");
}
#[test]
fn bin_exit_return_are_sequential() {
assert_eq!(
BIN_RETURN - BIN_EXIT,
1,
"BIN_RETURN must immediately follow BIN_EXIT"
);
}
#[test]
fn bin_break_continue_are_sequential() {
assert_eq!(
BIN_CONTINUE - BIN_BREAK,
1,
"BIN_CONTINUE must immediately follow BIN_BREAK"
);
}
#[test]
fn bin_un_family_is_consecutive() {
let un = [BIN_UNHASH, BIN_UNALIAS, BIN_UNFUNCTION, BIN_UNSET];
for w in un.windows(2) {
assert_eq!(
w[1] - w[0],
1,
"un* family must be consecutive: {} → {}",
w[0],
w[1]
);
}
}
#[test]
fn bin_test_bracket_are_sequential() {
assert_eq!(
BIN_BRACKET - BIN_TEST,
1,
"BIN_BRACKET must immediately follow BIN_TEST"
);
}
}