1use crate::ported::params::setiparam_no_convert;
9use crate::ported::utils::{
10 addmodulefd, errflag, fdtable_get, fdtable_set, movefd, redup, zerrnam, zwarnnam,
11};
12use crate::ported::zsh_h::{
17 features, module, options, FDT_EXTERNAL, FDT_UNUSED, OPT_ARG, OPT_ISSET,
18};
19use std::sync::{Mutex, OnceLock};
20pub fn bin_zsocket(
22 nam: &str,
23 args: &[String], ops: &options,
25 _func: i32,
26) -> i32 {
27 let mut soun: libc::sockaddr_un = unsafe { std::mem::zeroed() };
28 let mut sfd: i32;
29 let mut err: i32 = 1; let mut verbose = 0i32;
31 let mut test = 0i32;
32 let mut targetfd: i32 = 0;
33 let mut soun: libc::sockaddr_un = unsafe { std::mem::zeroed() };
34 let mut sfd: i32;
35
36 if OPT_ISSET(ops, b'v') {
37 verbose = 1;
38 } if OPT_ISSET(ops, b't') {
40 test = 1;
41 } if OPT_ISSET(ops, b'd') {
44 let darg = OPT_ARG(ops, b'd').unwrap_or("");
46 targetfd = darg.parse::<i32>().unwrap_or(0); if targetfd == 0 {
48 zwarnnam(nam, &format!("{} is an invalid argument to -d", darg)); return 1; }
52 if fdtable_get(targetfd) != FDT_UNUSED {
55 zwarnnam(
57 nam, &format!("file descriptor {} is in use by the shell", targetfd),
59 );
60 return 1; } else {
62 }
63 }
64
65 if OPT_ISSET(ops, b'l') {
66 if args.is_empty() {
68 zwarnnam(nam, "-l requires an argument");
70 return 1; }
72 let localfn = args[0].as_str(); sfd = unsafe { libc::socket(libc::PF_UNIX, libc::SOCK_STREAM, 0) }; if sfd == -1 {
75 zwarnnam(
77 nam,
78 &format!("socket error: {} ", std::io::Error::last_os_error()),
79 ); return 1; }
82 soun.sun_family = libc::AF_UNIX as _; let path_bytes = localfn.as_bytes();
84 let max_len = soun.sun_path.len() - 1;
85 let copy_len = path_bytes.len().min(max_len);
86 for (k, &b) in path_bytes[..copy_len].iter().enumerate() {
87 soun.sun_path[k] = b as libc::c_char;
89 }
90 let r = unsafe {
91 libc::bind(
93 sfd,
94 &soun as *const _ as *const libc::sockaddr,
95 size_of::<libc::sockaddr_un>() as libc::socklen_t,
96 )
97 };
98 if r != 0 {
99 zwarnnam(
101 nam,
102 &format!(
103 "could not bind to {}: {}",
104 localfn,
105 std::io::Error::last_os_error()
106 ),
107 ); unsafe {
109 libc::close(sfd);
110 } return 1; }
113 if unsafe { libc::listen(sfd, 1) } != 0 {
114 zwarnnam(
116 nam,
117 &format!(
118 "could not listen on socket: {}",
119 std::io::Error::last_os_error()
120 ),
121 ); unsafe {
123 libc::close(sfd);
124 } return 1; }
127 addmodulefd(sfd, FDT_EXTERNAL); if targetfd != 0 {
129 sfd = redup(sfd, targetfd); } else {
132 sfd = movefd(sfd); }
134 if sfd == -1 {
135 zerrnam(
137 nam,
138 &format!(
139 "cannot duplicate fd {}: {}",
140 sfd,
141 std::io::Error::last_os_error()
142 ),
143 ); return 1; }
146 fdtable_set(sfd, FDT_EXTERNAL); setiparam_no_convert("REPLY", sfd as i64); if verbose != 0 {
149 println!("{} listener is on fd {}", localfn, sfd); }
152 return 0; } else if OPT_ISSET(ops, b'a') {
154 if args.is_empty() {
156 zwarnnam(nam, "-a requires an argument");
158 return 1; }
160 let lfd = args[0].parse::<i32>().unwrap_or(0); if lfd == 0 {
162 zwarnnam(nam, "invalid numerical argument");
164 return 1; }
166 if test != 0 {
167 let mut pfd = libc::pollfd {
170 fd: lfd,
171 events: libc::POLLIN,
172 revents: 0,
173 };
174 let r = unsafe { libc::poll(&mut pfd, 1, 0) }; if r == 0 {
176 return 1;
177 }
178 else if r == -1 {
180 zwarnnam(
182 nam,
183 &format!("poll error: {}", std::io::Error::last_os_error()),
184 ); return 1; }
187 }
188 let mut len: libc::socklen_t = size_of::<libc::sockaddr_un>() as libc::socklen_t; let rfd: i32;
190 loop {
191 let r = unsafe {
193 libc::accept(
194 lfd, &mut soun as *mut _ as *mut libc::sockaddr,
196 &mut len,
197 )
198 };
199 if r >= 0 {
200 rfd = r;
201 break;
202 }
203 let osek = std::io::Error::last_os_error().raw_os_error();
204 if osek != Some(libc::EINTR) || errflag.load(std::sync::atomic::Ordering::Relaxed) != 0
205 {
206 rfd = r;
207 break;
208 } else {
209 }
210 }
211 if rfd == -1 {
212 zwarnnam(
214 nam,
215 &format!(
216 "could not accept connection: {}",
217 std::io::Error::last_os_error()
218 ),
219 ); return 1; }
222 addmodulefd(rfd, FDT_EXTERNAL); if targetfd != 0 {
224 sfd = redup(rfd, targetfd); if sfd < 0 {
227 zerrnam(
229 nam,
230 &format!(
231 "could not duplicate socket fd to {}: {}",
232 targetfd,
233 std::io::Error::last_os_error()
234 ),
235 ); let _ = crate::ported::utils::zclose(rfd);
243 return 1; }
245 fdtable_set(sfd, FDT_EXTERNAL); } else {
247 sfd = rfd; }
249 setiparam_no_convert("REPLY", sfd as i64); if verbose != 0 {
251 let path = soun
253 .sun_path
254 .iter()
255 .take_while(|&&c| c != 0)
256 .map(|&c| c as u8 as char)
257 .collect::<String>();
258 println!("new connection from {} is on fd {}", path, sfd); }
260 } else {
261 if args.is_empty() {
263 zwarnnam(nam, "zsocket requires an argument");
265 return 1; }
267 sfd = unsafe { libc::socket(libc::PF_UNIX, libc::SOCK_STREAM, 0) }; if sfd == -1 {
269 zwarnnam(
271 nam,
272 &format!(
273 "socket creation failed: {}",
274 std::io::Error::last_os_error()
275 ),
276 ); return 1; }
279 soun.sun_family = libc::AF_UNIX as _; let path_bytes = args[0].as_bytes();
281 let max_len = soun.sun_path.len() - 1;
282 let copy_len = path_bytes.len().min(max_len);
283 for (k, &b) in path_bytes[..copy_len].iter().enumerate() {
284 soun.sun_path[k] = b as libc::c_char;
286 }
287 err = unsafe {
288 libc::connect(
290 sfd,
291 &soun as *const _ as *const libc::sockaddr,
292 size_of::<libc::sockaddr_un>() as libc::socklen_t,
293 )
294 };
295 if err != 0 {
296 zwarnnam(
298 nam,
299 &format!("connection failed: {}", std::io::Error::last_os_error()),
300 ); unsafe {
302 libc::close(sfd);
303 } return 1; }
306 addmodulefd(sfd, FDT_EXTERNAL); if targetfd != 0 {
308 if redup(sfd, targetfd) < 0 {
310 zerrnam(
312 nam,
313 &format!(
314 "could not duplicate socket fd to {}: {}",
315 targetfd,
316 std::io::Error::last_os_error()
317 ),
318 ); let _ = crate::ported::utils::zclose(sfd);
325 return 1; }
327 sfd = targetfd; fdtable_set(sfd, FDT_EXTERNAL); }
330 setiparam_no_convert("REPLY", sfd as i64); if verbose != 0 {
332 let path = &args[0];
334 println!("{} is now on fd {}", path, sfd); }
336 }
337 let _ = (err, verbose, test, targetfd); 0 }
340
341#[allow(unused_variables)]
358pub fn setup_(m: *const module) -> i32 {
359 0 }
362
363pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
366 *features = featuresarray(m, module_features());
368 0 }
370
371pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
374 handlefeatures(m, module_features(), enables) }
377
378#[allow(unused_variables)]
380pub fn boot_(m: *const module) -> i32 {
381 0 }
384
385pub fn cleanup_(m: *const module) -> i32 {
388 setfeatureenables(m, module_features(), None) }
391
392#[allow(unused_variables)]
394pub fn finish_(m: *const module) -> i32 {
395 0 }
398
399static MODULE_FEATURES: OnceLock<Mutex<features>> = OnceLock::new();
400
401fn featuresarray(_m: *const module, _f: &Mutex<features>) -> Vec<String> {
411 vec!["b:zsocket".to_string()]
412}
413
414fn handlefeatures(_m: *const module, _f: &Mutex<features>, enables: &mut Option<Vec<i32>>) -> i32 {
419 if enables.is_none() {
420 *enables = Some(vec![1; 1]);
421 }
422 0
423}
424
425fn setfeatureenables(_m: *const module, _f: &Mutex<features>, _e: Option<&[i32]>) -> i32 {
430 0
431}
432
433fn module_features() -> &'static Mutex<features> {
460 MODULE_FEATURES.get_or_init(|| {
461 Mutex::new(features {
462 bn_list: None,
463 bn_size: 1,
464 cd_list: None,
465 cd_size: 0,
466 mf_list: None,
467 mf_size: 0,
468 pd_list: None,
469 pd_size: 0,
470 n_abstract: 0,
471 })
472 })
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478
479 #[test]
483 fn zsocket_l_without_arg_fails_before_socket_call() {
484 let _g = crate::test_util::global_state_lock();
485 let mut ops = empty_ops();
486 ops.ind[b'l' as usize] = 1;
487 assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
488 }
489
490 #[test]
494 fn zsocket_d_non_numeric_fails_before_dup2() {
495 let _g = crate::test_util::global_state_lock();
496 let mut ops = empty_ops();
497 ops.ind[b'd' as usize] = (1 << 2) | 1;
498 ops.args.push("not-a-number".to_string());
499 assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
500 }
501
502 fn empty_ops() -> options {
503 options {
504 ind: [0u8; crate::ported::zsh_h::MAX_OPS],
505 args: Vec::new(),
506 argscount: 0,
507 argsalloc: 0,
508 }
509 }
510
511 #[test]
516 fn zsocket_connect_mode_without_args_returns_one() {
517 let _g = crate::test_util::global_state_lock();
518 let ops = empty_ops();
519 assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
520 }
521
522 #[test]
526 fn zsocket_a_without_arg_fails_before_accept() {
527 let _g = crate::test_util::global_state_lock();
528 let mut ops = empty_ops();
529 ops.ind[b'a' as usize] = 1;
530 assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), 1);
531 }
532
533 #[test]
538 fn zsocket_a_zero_listen_fd_fails() {
539 let _g = crate::test_util::global_state_lock();
540 let mut ops = empty_ops();
541 ops.ind[b'a' as usize] = 1;
542 assert_eq!(bin_zsocket("zsocket", &["0".to_string()], &ops, 0), 1);
543 assert_eq!(
545 bin_zsocket("zsocket", &["not-numeric".to_string()], &ops, 0),
546 1
547 );
548 }
549
550 #[test]
554 fn module_lifecycle_shims_all_return_zero() {
555 let _g = crate::test_util::global_state_lock();
556 let m = std::ptr::null();
557 assert_eq!(setup_(m), 0);
558 assert_eq!(boot_(m), 0);
559 assert_eq!(cleanup_(m), 0);
560 assert_eq!(finish_(m), 0);
561 }
562
563 #[test]
567 fn features_returns_success() {
568 let _g = crate::test_util::global_state_lock();
569 let mut features = Vec::new();
570 assert_eq!(features_(std::ptr::null(), &mut features), 0);
571 }
572
573 fn ops_with_flag(flag: u8) -> options {
579 let mut ind = [0u8; crate::ported::zsh_h::MAX_OPS];
580 ind[flag as usize] = 1;
581 options {
582 ind,
583 args: Vec::new(),
584 argscount: 0,
585 argsalloc: 0,
586 }
587 }
588
589 #[test]
591 fn bin_zsocket_l_no_args_returns_one() {
592 let _g = crate::test_util::global_state_lock();
593 let ops = ops_with_flag(b'l');
594 let r = bin_zsocket("zsocket", &[], &ops, 0);
595 assert_eq!(r, 1, "-l with no args → usage error");
596 }
597
598 #[test]
601 fn bin_zsocket_no_args_no_panic() {
602 let _g = crate::test_util::global_state_lock();
603 let ops = options {
604 ind: [0; crate::ported::zsh_h::MAX_OPS],
605 args: Vec::new(),
606 argscount: 0,
607 argsalloc: 0,
608 };
609 let _ = bin_zsocket("zsocket", &[], &ops, 0);
610 }
612
613 #[test]
616 fn bin_zsocket_v_alone_no_panic() {
617 let _g = crate::test_util::global_state_lock();
618 let ops = ops_with_flag(b'v');
619 let _ = bin_zsocket("zsocket", &[], &ops, 0);
620 }
621
622 #[test]
625 fn bin_zsocket_d_flag_no_panic() {
626 let _g = crate::test_util::global_state_lock();
627 let ops = ops_with_flag(b'd');
628 let _ = bin_zsocket("zsocket", &[], &ops, 0);
629 }
630
631 #[test]
633 fn bin_zsocket_t_alone_no_panic() {
634 let _g = crate::test_util::global_state_lock();
635 let ops = ops_with_flag(b't');
636 let _ = bin_zsocket("zsocket", &[], &ops, 0);
637 }
638
639 #[test]
641 fn socket_setup_returns_zero_pin() {
642 let _g = crate::test_util::global_state_lock();
643 assert_eq!(setup_(std::ptr::null()), 0);
644 }
645
646 #[test]
648 fn socket_boot_returns_zero_pin() {
649 let _g = crate::test_util::global_state_lock();
650 assert_eq!(boot_(std::ptr::null()), 0);
651 }
652
653 #[test]
655 fn socket_cleanup_returns_zero_pin() {
656 let _g = crate::test_util::global_state_lock();
657 assert_eq!(cleanup_(std::ptr::null()), 0);
658 }
659
660 #[test]
662 fn socket_finish_returns_zero_pin() {
663 let _g = crate::test_util::global_state_lock();
664 assert_eq!(finish_(std::ptr::null()), 0);
665 }
666
667 #[test]
674 fn bin_zsocket_return_in_exit_code_range() {
675 let _g = crate::test_util::global_state_lock();
676 let ops = empty_ops();
677 for args in [
678 vec![],
679 vec!["/tmp/zshrs_test_sock".to_string()],
680 vec!["".to_string()],
681 ] {
682 let r = bin_zsocket("zsocket", &args, &ops, 0);
683 assert!(
684 (0..256).contains(&r),
685 "exit code {} must fit in u8 range for {:?}",
686 r,
687 args
688 );
689 }
690 }
691
692 #[test]
694 fn bin_zsocket_empty_path_returns_nonzero() {
695 let _g = crate::test_util::global_state_lock();
696 let ops = empty_ops();
697 let r = bin_zsocket("zsocket", &["".to_string()], &ops, 0);
698 assert_ne!(r, 0, "empty path → error");
699 }
700
701 #[test]
703 fn bin_zsocket_no_args_deterministic() {
704 let _g = crate::test_util::global_state_lock();
705 let ops = empty_ops();
706 let first = bin_zsocket("zsocket", &[], &ops, 0);
707 for _ in 0..5 {
708 assert_eq!(bin_zsocket("zsocket", &[], &ops, 0), first);
709 }
710 }
711
712 #[test]
722 fn bin_zsocket_a_l_flags_with_arbitrary_arg_no_panic() {
723 let _g = crate::test_util::global_state_lock();
724 let xyz_path = std::env::temp_dir().join("__zshrs_test_zsocket_xyz");
725 let abc_path = std::env::temp_dir().join("__zshrs_test_zsocket_abc");
726 let _ = std::fs::remove_file(&xyz_path);
727 let _ = std::fs::remove_file(&abc_path);
728 let mut ops = empty_ops();
729 ops.ind[b'a' as usize] = 1;
730 let _ = bin_zsocket("zsocket", &[xyz_path.to_string_lossy().into()], &ops, 0);
731 let mut ops2 = empty_ops();
732 ops2.ind[b'l' as usize] = 1;
733 let _ = bin_zsocket("zsocket", &[abc_path.to_string_lossy().into()], &ops2, 0);
734 let _ = std::fs::remove_file(&xyz_path);
735 let _ = std::fs::remove_file(&abc_path);
736 }
737
738 #[test]
740 fn socket_full_lifecycle_returns_zero_for_all() {
741 let _g = crate::test_util::global_state_lock();
742 let null = std::ptr::null();
743 assert_eq!(setup_(null), 0);
744 let mut feats = Vec::new();
745 let _ = features_(null, &mut feats);
746 let mut enables: Option<Vec<i32>> = None;
747 let _ = enables_(null, &mut enables);
748 assert_eq!(boot_(null), 0);
749 assert_eq!(cleanup_(null), 0);
750 assert_eq!(finish_(null), 0);
751 }
752
753 #[test]
755 fn socket_setup_idempotent() {
756 let _g = crate::test_util::global_state_lock();
757 for _ in 0..10 {
758 assert_eq!(setup_(std::ptr::null()), 0);
759 }
760 }
761
762 #[test]
764 fn socket_finish_idempotent() {
765 let _g = crate::test_util::global_state_lock();
766 for _ in 0..10 {
767 assert_eq!(finish_(std::ptr::null()), 0);
768 }
769 }
770
771 #[test]
773 fn socket_cleanup_idempotent() {
774 let _g = crate::test_util::global_state_lock();
775 for _ in 0..10 {
776 assert_eq!(cleanup_(std::ptr::null()), 0);
777 }
778 }
779
780 #[test]
782 fn socket_boot_idempotent() {
783 let _g = crate::test_util::global_state_lock();
784 for _ in 0..10 {
785 assert_eq!(boot_(std::ptr::null()), 0);
786 }
787 }
788
789 #[test]
791 fn bin_zsocket_multibyte_path_no_panic() {
792 let _g = crate::test_util::global_state_lock();
793 let ops = empty_ops();
794 let _ = bin_zsocket("zsocket", &["/tmp/日本".to_string()], &ops, 0);
795 }
796
797 #[test]
804 fn bin_zsocket_returns_i32_type() {
805 let _g = crate::test_util::global_state_lock();
806 let ops = empty_ops();
807 let _: i32 = bin_zsocket("zsocket", &[], &ops, 0);
808 }
809
810 #[test]
812 fn bin_zsocket_exit_codes_non_negative() {
813 let _g = crate::test_util::global_state_lock();
814 let ops = empty_ops();
815 for argv in [
816 vec![],
817 vec!["/tmp/sock".into()],
818 vec!["".into()],
819 vec!["/tmp/sock".into(), "extra".into()],
820 ] {
821 let r = bin_zsocket("zsocket", &argv, &ops, 0);
822 assert!(
823 r >= 0,
824 "exit code must be non-negative, got {} for {:?}",
825 r,
826 argv
827 );
828 }
829 }
830
831 #[test]
833 fn bin_zsocket_empty_path_deterministic() {
834 let _g = crate::test_util::global_state_lock();
835 let ops = empty_ops();
836 let first = bin_zsocket("zsocket", &["".to_string()], &ops, 0);
837 for _ in 0..5 {
838 assert_eq!(
839 bin_zsocket("zsocket", &["".to_string()], &ops, 0),
840 first,
841 "empty-path zsocket must be pure across calls"
842 );
843 }
844 }
845
846 #[test]
850 fn bin_zsocket_both_a_and_l_flags_no_panic() {
851 let _g = crate::test_util::global_state_lock();
852 let mut ops = empty_ops();
853 ops.ind[b'a' as usize] = 1;
854 ops.ind[b'l' as usize] = 1;
855 let _ = bin_zsocket("zsocket", &["/tmp/sock".to_string()], &ops, 0);
856 }
857
858 #[test]
860 fn bin_zsocket_d_flag_with_non_numeric_no_panic() {
861 let _g = crate::test_util::global_state_lock();
862 let mut ops = empty_ops();
863 ops.ind[b'd' as usize] = 1;
864 let _ = bin_zsocket("zsocket", &["not-a-number".to_string()], &ops, 0);
865 }
866
867 #[test]
869 fn bin_zsocket_long_path_no_panic() {
870 let _g = crate::test_util::global_state_lock();
871 let ops = empty_ops();
872 let path = "/tmp/".to_string() + &"x".repeat(2000);
873 let _ = bin_zsocket("zsocket", &[path], &ops, 0);
874 }
875
876 #[test]
878 fn socket_setup_returns_i32_type() {
879 let _g = crate::test_util::global_state_lock();
880 let _: i32 = setup_(std::ptr::null());
881 }
882
883 #[test]
885 fn socket_features_returns_i32_type() {
886 let _g = crate::test_util::global_state_lock();
887 let mut v: Vec<String> = Vec::new();
888 let _: i32 = features_(std::ptr::null(), &mut v);
889 }
890
891 #[test]
893 fn socket_enables_with_none_returns_i32() {
894 let _g = crate::test_util::global_state_lock();
895 let mut e: Option<Vec<i32>> = None;
896 let _: i32 = enables_(std::ptr::null(), &mut e);
897 }
898
899 #[test]
901 fn socket_enables_deterministic_for_null_in() {
902 let _g = crate::test_util::global_state_lock();
903 let mut a: Option<Vec<i32>> = None;
904 let first = enables_(std::ptr::null(), &mut a);
905 for _ in 0..3 {
906 let mut b: Option<Vec<i32>> = None;
907 assert_eq!(
908 enables_(std::ptr::null(), &mut b),
909 first,
910 "enables_ must be deterministic"
911 );
912 }
913 }
914
915 #[test]
917 fn socket_each_lifecycle_hook_returns_zero_individually() {
918 let _g = crate::test_util::global_state_lock();
919 let null = std::ptr::null();
920 let mut v: Vec<String> = Vec::new();
921 let mut e: Option<Vec<i32>> = None;
922 assert_eq!(setup_(null), 0, "c:351 setup_");
923 assert_eq!(features_(null, &mut v), 0, "c:358 features_");
924 assert_eq!(enables_(null, &mut e), 0, "c:366 enables_");
925 assert_eq!(boot_(null), 0, "c:373 boot_");
926 assert_eq!(cleanup_(null), 0, "c:380 cleanup_");
927 assert_eq!(finish_(null), 0, "c:387 finish_");
928 }
929
930 #[test]
937 fn socket_setup_idempotent_repeated_calls() {
938 let _g = crate::test_util::global_state_lock();
939 for _ in 0..10 {
940 assert_eq!(setup_(std::ptr::null()), 0);
941 }
942 }
943
944 #[test]
946 fn socket_cleanup_idempotent_repeated_calls() {
947 let _g = crate::test_util::global_state_lock();
948 for _ in 0..10 {
949 assert_eq!(cleanup_(std::ptr::null()), 0);
950 }
951 }
952
953 #[test]
955 fn socket_finish_idempotent_repeated_calls() {
956 let _g = crate::test_util::global_state_lock();
957 for _ in 0..10 {
958 assert_eq!(finish_(std::ptr::null()), 0);
959 }
960 }
961
962 #[test]
964 fn socket_cleanup_returns_i32_type() {
965 let _g = crate::test_util::global_state_lock();
966 let _: i32 = cleanup_(std::ptr::null());
967 }
968
969 #[test]
971 fn socket_finish_returns_i32_type() {
972 let _g = crate::test_util::global_state_lock();
973 let _: i32 = finish_(std::ptr::null());
974 }
975
976 #[test]
978 fn socket_boot_returns_i32_type() {
979 let _g = crate::test_util::global_state_lock();
980 let _: i32 = boot_(std::ptr::null());
981 }
982
983 #[test]
985 fn bin_zsocket_empty_args_non_negative() {
986 let _g = crate::test_util::global_state_lock();
987 let ops = crate::ported::zsh_h::options {
988 ind: [0u8; crate::ported::zsh_h::MAX_OPS],
989 args: Vec::new(),
990 argscount: 0,
991 argsalloc: 0,
992 };
993 let r = bin_zsocket("zsocket", &[], &ops, 0);
994 assert!(r >= 0, "bin_zsocket empty args must return ≥ 0, got {}", r);
995 }
996
997 #[test]
999 fn bin_zsocket_various_func_values_no_panic() {
1000 let _g = crate::test_util::global_state_lock();
1001 let ops = crate::ported::zsh_h::options {
1002 ind: [0u8; crate::ported::zsh_h::MAX_OPS],
1003 args: Vec::new(),
1004 argscount: 0,
1005 argsalloc: 0,
1006 };
1007 for func in [-1, 0, 1, 100, i32::MAX] {
1008 let _ = bin_zsocket("zsocket", &[], &ops, func);
1009 }
1010 }
1011
1012 #[test]
1014 fn bin_zsocket_deterministic_identical_args() {
1015 let _g = crate::test_util::global_state_lock();
1016 let ops = crate::ported::zsh_h::options {
1017 ind: [0u8; crate::ported::zsh_h::MAX_OPS],
1018 args: Vec::new(),
1019 argscount: 0,
1020 argsalloc: 0,
1021 };
1022 let args = vec!["test".to_string()];
1023 let r1 = bin_zsocket("zsocket", &args, &ops, 0);
1024 let r2 = bin_zsocket("zsocket", &args, &ops, 0);
1025 assert_eq!(r1, r2, "bin_zsocket must be deterministic");
1026 }
1027
1028 #[test]
1030 fn socket_setup_returns_i32_type_alt_pin() {
1031 let _g = crate::test_util::global_state_lock();
1032 let _: i32 = setup_(std::ptr::null());
1033 }
1034
1035 #[test]
1037 fn socket_enables_with_some_non_empty_no_panic() {
1038 let _g = crate::test_util::global_state_lock();
1039 let mut e: Option<Vec<i32>> = Some(vec![1, 2, 3]);
1040 let _ = enables_(std::ptr::null(), &mut e);
1041 }
1042
1043 #[test]
1045 fn socket_setup_then_boot_returns_zero_each() {
1046 let _g = crate::test_util::global_state_lock();
1047 let null = std::ptr::null();
1048 assert_eq!(setup_(null), 0);
1049 assert_eq!(boot_(null), 0);
1050 }
1051}