use super::*;
use crate::io::prelude::*;
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
#[cfg(target_os = "android")]
use crate::os::android::net::{SocketAddrExt, UnixSocketExt};
#[cfg(target_os = "cygwin")]
use crate::os::cygwin::net::{SocketAddrExt, UnixSocketExt};
#[cfg(target_os = "linux")]
use crate::os::linux::net::{SocketAddrExt, UnixSocketExt};
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::os::unix::io::AsRawFd;
use crate::test_helpers::tmpdir;
use crate::thread;
use crate::time::Duration;

macro_rules! or_panic {
    ($e:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => panic!("{e}"),
        }
    };
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn basic() {
    let dir = tmpdir();
    let socket_path = dir.path().join("sock");
    let msg1 = b"hello";
    let msg2 = b"world!";

    let listener = or_panic!(UnixListener::bind(&socket_path));
    let thread = thread::spawn(move || {
        let mut stream = or_panic!(listener.accept()).0;
        let mut buf = [0; 5];
        or_panic!(stream.read(&mut buf));
        assert_eq!(&msg1[..], &buf[..]);
        or_panic!(stream.write_all(msg2));
    });

    let mut stream = or_panic!(UnixStream::connect(&socket_path));
    assert_eq!(Some(&*socket_path), stream.peer_addr().unwrap().as_pathname());
    or_panic!(stream.write_all(msg1));
    let mut buf = vec![];
    or_panic!(stream.read_to_end(&mut buf));
    assert_eq!(&msg2[..], &buf[..]);
    drop(stream);

    thread.join().unwrap();
}

#[test]
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn vectored() {
    let (mut s1, mut s2) = or_panic!(UnixStream::pair());

    let len = or_panic!(s1.write_vectored(&[
        IoSlice::new(b"hello"),
        IoSlice::new(b" "),
        IoSlice::new(b"world!")
    ],));
    assert_eq!(len, 12);

    let mut buf1 = [0; 6];
    let mut buf2 = [0; 7];
    let len =
        or_panic!(s2.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],));
    assert_eq!(len, 12);
    assert_eq!(&buf1, b"hello ");
    assert_eq!(&buf2, b"world!\0");
}

#[test]
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn pair() {
    let msg1 = b"hello";
    let msg2 = b"world!";

    let (mut s1, mut s2) = or_panic!(UnixStream::pair());
    let thread = thread::spawn(move || {
        // s1 must be moved in or the test will hang!
        let mut buf = [0; 5];
        or_panic!(s1.read(&mut buf));
        assert_eq!(&msg1[..], &buf[..]);
        or_panic!(s1.write_all(msg2));
    });

    or_panic!(s2.write_all(msg1));
    let mut buf = vec![];
    or_panic!(s2.read_to_end(&mut buf));
    assert_eq!(&msg2[..], &buf[..]);
    drop(s2);

    thread.join().unwrap();
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn try_clone() {
    let dir = tmpdir();
    let socket_path = dir.path().join("sock");
    let msg1 = b"hello";
    let msg2 = b"world";

    let listener = or_panic!(UnixListener::bind(&socket_path));
    let thread = thread::spawn(move || {
        let mut stream = or_panic!(listener.accept()).0;
        or_panic!(stream.write_all(msg1));
        or_panic!(stream.write_all(msg2));
    });

    let mut stream = or_panic!(UnixStream::connect(&socket_path));
    let mut stream2 = or_panic!(stream.try_clone());

    let mut buf = [0; 5];
    or_panic!(stream.read(&mut buf));
    assert_eq!(&msg1[..], &buf[..]);
    or_panic!(stream2.read(&mut buf));
    assert_eq!(&msg2[..], &buf[..]);

    thread.join().unwrap();
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn iter() {
    let dir = tmpdir();
    let socket_path = dir.path().join("sock");

    let listener = or_panic!(UnixListener::bind(&socket_path));
    let thread = thread::spawn(move || {
        for stream in listener.incoming().take(2) {
            let mut stream = or_panic!(stream);
            let mut buf = [0];
            or_panic!(stream.read(&mut buf));
        }
    });

    for _ in 0..2 {
        let mut stream = or_panic!(UnixStream::connect(&socket_path));
        or_panic!(stream.write_all(&[0]));
    }

    thread.join().unwrap();
}

#[test]
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn long_path() {
    let dir = tmpdir();
    let socket_path = dir.path().join(
        "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\
                                sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf",
    );
    match UnixStream::connect(&socket_path) {
        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
        Err(e) => panic!("unexpected error {e}"),
        Ok(_) => panic!("unexpected success"),
    }

    match UnixListener::bind(&socket_path) {
        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
        Err(e) => panic!("unexpected error {e}"),
        Ok(_) => panic!("unexpected success"),
    }

    match UnixDatagram::bind(&socket_path) {
        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
        Err(e) => panic!("unexpected error {e}"),
        Ok(_) => panic!("unexpected success"),
    }
}

#[test]
#[cfg(not(target_os = "nto"))]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn timeouts() {
    let dir = tmpdir();
    let socket_path = dir.path().join("sock");

    let _listener = or_panic!(UnixListener::bind(&socket_path));

    let stream = or_panic!(UnixStream::connect(&socket_path));
    let dur = Duration::new(15410, 0);

    assert_eq!(None, or_panic!(stream.read_timeout()));

    or_panic!(stream.set_read_timeout(Some(dur)));
    assert_eq!(Some(dur), or_panic!(stream.read_timeout()));

    assert_eq!(None, or_panic!(stream.write_timeout()));

    or_panic!(stream.set_write_timeout(Some(dur)));
    assert_eq!(Some(dur), or_panic!(stream.write_timeout()));

    or_panic!(stream.set_read_timeout(None));
    assert_eq!(None, or_panic!(stream.read_timeout()));

    or_panic!(stream.set_write_timeout(None));
    assert_eq!(None, or_panic!(stream.write_timeout()));
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_read_timeout() {
    let dir = tmpdir();
    let socket_path = dir.path().join("sock");

    let _listener = or_panic!(UnixListener::bind(&socket_path));

    let mut stream = or_panic!(UnixStream::connect(&socket_path));
    or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));

    let mut buf = [0; 10];
    let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
    assert!(
        kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
        "unexpected_error: {:?}",
        kind
    );
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_read_with_timeout() {
    let dir = tmpdir();
    let socket_path = dir.path().join("sock");

    let listener = or_panic!(UnixListener::bind(&socket_path));

    let mut stream = or_panic!(UnixStream::connect(&socket_path));
    or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));

    let mut other_end = or_panic!(listener.accept()).0;
    or_panic!(other_end.write_all(b"hello world"));

    let mut buf = [0; 11];
    or_panic!(stream.read(&mut buf));
    assert_eq!(b"hello world", &buf[..]);

    let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
    assert!(
        kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
        "unexpected_error: {:?}",
        kind
    );
}

// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
// when passed zero Durations
#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin connect needs handshake
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_stream_timeout_zero_duration() {
    let dir = tmpdir();
    let socket_path = dir.path().join("sock");

    let listener = or_panic!(UnixListener::bind(&socket_path));
    let stream = or_panic!(UnixStream::connect(&socket_path));

    let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::InvalidInput);

    let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::InvalidInput);

    drop(listener);
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_datagram() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock1");
    let path2 = dir.path().join("sock2");

    let sock1 = or_panic!(UnixDatagram::bind(&path1));
    let sock2 = or_panic!(UnixDatagram::bind(&path2));

    let msg = b"hello world";
    or_panic!(sock1.send_to(msg, &path2));
    let mut buf = [0; 11];
    or_panic!(sock2.recv_from(&mut buf));
    assert_eq!(msg, &buf[..]);
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin autobinds an address
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unnamed_unix_datagram() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock1");

    let sock1 = or_panic!(UnixDatagram::bind(&path1));
    let sock2 = or_panic!(UnixDatagram::unbound());

    let msg = b"hello world";
    or_panic!(sock2.send_to(msg, &path1));
    let mut buf = [0; 11];
    let (usize, addr) = or_panic!(sock1.recv_from(&mut buf));
    assert_eq!(usize, 11);
    assert!(addr.is_unnamed());
    assert_eq!(msg, &buf[..]);
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_datagram_connect_to_recv_addr() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock1");
    let path2 = dir.path().join("sock2");

    let sock1 = or_panic!(UnixDatagram::bind(&path1));
    let sock2 = or_panic!(UnixDatagram::bind(&path2));

    let msg = b"hello world";
    let sock1_addr = or_panic!(sock1.local_addr());
    or_panic!(sock2.send_to_addr(msg, &sock1_addr));
    let mut buf = [0; 11];
    let (_, addr) = or_panic!(sock1.recv_from(&mut buf));

    let new_msg = b"hello back";
    let mut new_buf = [0; 10];
    or_panic!(sock2.connect_addr(&addr));
    or_panic!(sock2.send(new_msg)); // set by connect_addr
    let usize = or_panic!(sock2.recv(&mut new_buf));
    assert_eq!(usize, 10);
    assert_eq!(new_msg, &new_buf[..]);
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin autobinds an address
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_connect_unix_datagram() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock1");
    let path2 = dir.path().join("sock2");

    let bsock1 = or_panic!(UnixDatagram::bind(&path1));
    let bsock2 = or_panic!(UnixDatagram::bind(&path2));
    let sock = or_panic!(UnixDatagram::unbound());
    or_panic!(sock.connect(&path1));

    // Check send()
    let msg = b"hello there";
    or_panic!(sock.send(msg));
    let mut buf = [0; 11];
    let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf));
    assert_eq!(usize, 11);
    assert!(addr.is_unnamed());
    assert_eq!(msg, &buf[..]);

    // Changing default socket works too
    or_panic!(sock.connect(&path2));
    or_panic!(sock.send(msg));
    or_panic!(bsock2.recv_from(&mut buf));
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_datagram_recv() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock1");

    let sock1 = or_panic!(UnixDatagram::bind(&path1));
    let sock2 = or_panic!(UnixDatagram::unbound());
    or_panic!(sock2.connect(&path1));

    let msg = b"hello world";
    or_panic!(sock2.send(msg));
    let mut buf = [0; 11];
    let size = or_panic!(sock1.recv(&mut buf));
    assert_eq!(size, 11);
    assert_eq!(msg, &buf[..]);
}

#[test]
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn datagram_pair() {
    let msg1 = b"hello";
    let msg2 = b"world!";

    let (s1, s2) = or_panic!(UnixDatagram::pair());
    let thread = thread::spawn(move || {
        // s1 must be moved in or the test will hang!
        let mut buf = [0; 5];
        or_panic!(s1.recv(&mut buf));
        assert_eq!(&msg1[..], &buf[..]);
        or_panic!(s1.send(msg2));
    });

    or_panic!(s2.send(msg1));
    let mut buf = [0; 6];
    or_panic!(s2.recv(&mut buf));
    assert_eq!(&msg2[..], &buf[..]);
    drop(s2);

    thread.join().unwrap();
}

// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
// when passed zero Durations
#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_datagram_timeout_zero_duration() {
    let dir = tmpdir();
    let path = dir.path().join("sock");

    let datagram = or_panic!(UnixDatagram::bind(&path));

    let result = datagram.set_write_timeout(Some(Duration::new(0, 0)));
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::InvalidInput);

    let result = datagram.set_read_timeout(Some(Duration::new(0, 0)));
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::InvalidInput);
}

#[cfg(any(target_os = "android", target_os = "linux"))]
#[test]
fn abstract_socket_addr_debug() {
    assert_eq!(
        r#""\0hello world\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff" (abstract)"#,
        format!("{:?}", SocketAddr::from_abstract_name(b"\0hello world\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff").unwrap()),
    );
}

#[test]
fn abstract_namespace_not_allowed_connect() {
    assert!(UnixStream::connect("\0asdf").is_err());
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))]
#[test]
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr
fn test_abstract_stream_connect() {
    let msg1 = b"hello";
    let msg2 = b"world";

    let socket_addr = or_panic!(SocketAddr::from_abstract_name(b"name"));
    let listener = or_panic!(UnixListener::bind_addr(&socket_addr));

    let thread = thread::spawn(move || {
        let mut stream = or_panic!(listener.accept()).0;
        let mut buf = [0; 5];
        or_panic!(stream.read(&mut buf));
        assert_eq!(&msg1[..], &buf[..]);
        or_panic!(stream.write_all(msg2));
    });

    let mut stream = or_panic!(UnixStream::connect_addr(&socket_addr));

    let peer = or_panic!(stream.peer_addr());
    assert_eq!(peer.as_abstract_name().unwrap(), b"name");

    or_panic!(stream.write_all(msg1));
    let mut buf = vec![];
    or_panic!(stream.read_to_end(&mut buf));
    assert_eq!(&msg2[..], &buf[..]);
    drop(stream);

    thread.join().unwrap();
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))]
#[test]
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr
fn test_abstract_stream_iter() {
    let addr = or_panic!(SocketAddr::from_abstract_name(b"hidden"));
    let listener = or_panic!(UnixListener::bind_addr(&addr));

    let thread = thread::spawn(move || {
        for stream in listener.incoming().take(2) {
            let mut stream = or_panic!(stream);
            let mut buf = [0];
            or_panic!(stream.read(&mut buf));
        }
    });

    for _ in 0..2 {
        let mut stream = or_panic!(UnixStream::connect_addr(&addr));
        or_panic!(stream.write_all(&[0]));
    }

    thread.join().unwrap();
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))]
#[test]
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr
fn test_abstract_datagram_bind_send_to_addr() {
    let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns1"));
    let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1));

    let local = or_panic!(sock1.local_addr());
    assert_eq!(local.as_abstract_name().unwrap(), b"ns1");

    let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns2"));
    let sock2 = or_panic!(UnixDatagram::bind_addr(&addr2));

    let msg = b"hello world";
    or_panic!(sock1.send_to_addr(msg, &addr2));
    let mut buf = [0; 11];
    let (len, addr) = or_panic!(sock2.recv_from(&mut buf));
    assert_eq!(msg, &buf[..]);
    assert_eq!(len, 11);
    assert_eq!(addr.as_abstract_name().unwrap(), b"ns1");
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))]
#[test]
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin cannot bind to abstract addr
fn test_abstract_datagram_connect_addr() {
    let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns3"));
    let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1));

    let sock = or_panic!(UnixDatagram::unbound());
    or_panic!(sock.connect_addr(&addr1));

    let msg = b"hello world";
    or_panic!(sock.send(msg));
    let mut buf = [0; 11];
    let (len, addr) = or_panic!(bsock1.recv_from(&mut buf));
    assert_eq!(len, 11);
    assert_eq!(addr.is_unnamed(), true);
    assert_eq!(msg, &buf[..]);

    let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns4"));
    let bsock2 = or_panic!(UnixDatagram::bind_addr(&addr2));

    or_panic!(sock.connect_addr(&addr2));
    or_panic!(sock.send(msg));
    or_panic!(bsock2.recv_from(&mut buf));
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))]
#[test]
fn test_abstract_name_too_long() {
    match SocketAddr::from_abstract_name(
        b"abcdefghijklmnopqrstuvwxyzabcdefghijklmn\
        opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi\
        jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",
    ) {
        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
        Err(e) => panic!("unexpected error {e}"),
        Ok(_) => panic!("unexpected success"),
    }
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))]
#[test]
fn test_abstract_no_pathname_and_not_unnamed() {
    let name = b"local";
    let addr = or_panic!(SocketAddr::from_abstract_name(name));
    assert_eq!(addr.as_pathname(), None);
    assert_eq!(addr.as_abstract_name(), Some(&name[..]));
    assert_eq!(addr.is_unnamed(), false);
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_stream_peek() {
    let (txdone, rxdone) = crate::sync::mpsc::channel();

    let dir = tmpdir();
    let path = dir.path().join("sock");

    let listener = or_panic!(UnixListener::bind(&path));
    let thread = thread::spawn(move || {
        let mut stream = or_panic!(listener.accept()).0;
        or_panic!(stream.write_all(&[1, 3, 3, 7]));
        or_panic!(rxdone.recv());
    });

    let mut stream = or_panic!(UnixStream::connect(&path));
    let mut buf = [0; 10];
    for _ in 0..2 {
        assert_eq!(or_panic!(stream.peek(&mut buf)), 4);
    }
    assert_eq!(or_panic!(stream.read(&mut buf)), 4);

    or_panic!(stream.set_nonblocking(true));
    match stream.peek(&mut buf) {
        Ok(_) => panic!("expected error"),
        Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
        Err(e) => panic!("unexpected error: {e}"),
    }

    or_panic!(txdone.send(()));
    thread.join().unwrap();
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_datagram_peek() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock");

    let sock1 = or_panic!(UnixDatagram::bind(&path1));
    let sock2 = or_panic!(UnixDatagram::unbound());
    or_panic!(sock2.connect(&path1));

    let msg = b"hello world";
    or_panic!(sock2.send(msg));
    for _ in 0..2 {
        let mut buf = [0; 11];
        let size = or_panic!(sock1.peek(&mut buf));
        assert_eq!(size, 11);
        assert_eq!(msg, &buf[..]);
    }

    let mut buf = [0; 11];
    let size = or_panic!(sock1.recv(&mut buf));
    assert_eq!(size, 11);
    assert_eq!(msg, &buf[..]);
}

#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]
fn test_unix_datagram_peek_from() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock");

    let sock1 = or_panic!(UnixDatagram::bind(&path1));
    let sock2 = or_panic!(UnixDatagram::unbound());
    or_panic!(sock2.connect(&path1));

    let msg = b"hello world";
    or_panic!(sock2.send(msg));
    for _ in 0..2 {
        let mut buf = [0; 11];
        let (size, _) = or_panic!(sock1.peek_from(&mut buf));
        assert_eq!(size, 11);
        assert_eq!(msg, &buf[..]);
    }

    let mut buf = [0; 11];
    let size = or_panic!(sock1.recv(&mut buf));
    assert_eq!(size, 11);
    assert_eq!(msg, &buf[..]);
}

#[cfg(any(target_os = "android", target_os = "linux"))]
#[test]
fn test_send_vectored_fds_unix_stream() {
    let (s1, s2) = or_panic!(UnixStream::pair());

    let buf1 = [1; 8];
    let bufs_send = &[IoSlice::new(&buf1[..])][..];

    let mut ancillary1_buffer = [0; 128];
    let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]);
    assert!(ancillary1.add_fds(&[s1.as_raw_fd()][..]));

    let usize = or_panic!(s1.send_vectored_with_ancillary(&bufs_send, &mut ancillary1));
    assert_eq!(usize, 8);

    let mut buf2 = [0; 8];
    let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..];

    let mut ancillary2_buffer = [0; 128];
    let mut ancillary2 = SocketAncillary::new(&mut ancillary2_buffer[..]);

    let usize = or_panic!(s2.recv_vectored_with_ancillary(&mut bufs_recv, &mut ancillary2));
    assert_eq!(usize, 8);
    assert_eq!(buf1, buf2);

    let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages());
    assert_eq!(ancillary_data_vec.len(), 1);
    if let AncillaryData::ScmRights(scm_rights) = ancillary_data_vec.pop().unwrap().unwrap() {
        let fd_vec = Vec::from_iter(scm_rights);
        assert_eq!(fd_vec.len(), 1);
        unsafe {
            libc::close(fd_vec[0]);
        }
    } else {
        unreachable!("must be ScmRights");
    }
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))]
#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
#[cfg_attr(target_os = "cygwin", ignore)] // Cygwin recvmsg doesn't support Unix sockets
fn test_send_vectored_with_ancillary_to_unix_datagram() {
    fn getpid() -> libc::pid_t {
        unsafe { libc::getpid() }
    }

    fn getuid() -> libc::uid_t {
        unsafe { libc::getuid() }
    }

    fn getgid() -> libc::gid_t {
        unsafe { libc::getgid() }
    }

    let dir = tmpdir();
    let path1 = dir.path().join("sock1");
    let path2 = dir.path().join("sock2");

    let bsock1 = or_panic!(UnixDatagram::bind(&path1));
    let bsock2 = or_panic!(UnixDatagram::bind(&path2));

    or_panic!(bsock2.set_passcred(true));

    let buf1 = [1; 8];
    let bufs_send = &[IoSlice::new(&buf1[..])][..];

    let mut ancillary1_buffer = [0; 128];
    let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]);
    let mut cred1 = SocketCred::new();
    cred1.set_pid(getpid());
    cred1.set_uid(getuid());
    cred1.set_gid(getgid());
    assert!(ancillary1.add_creds(&[cred1.clone()][..]));

    let usize =
        or_panic!(bsock1.send_vectored_with_ancillary_to(&bufs_send, &mut ancillary1, &path2));
    assert_eq!(usize, 8);

    let mut buf2 = [0; 8];
    let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..];

    let mut ancillary2_buffer = [0; 128];
    let mut ancillary2 = SocketAncillary::new(&mut ancillary2_buffer[..]);

    let (usize, truncated, _addr) =
        or_panic!(bsock2.recv_vectored_with_ancillary_from(&mut bufs_recv, &mut ancillary2));
    assert_eq!(ancillary2.truncated(), false);
    assert_eq!(usize, 8);
    assert_eq!(truncated, false);
    assert_eq!(buf1, buf2);

    let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages());
    assert_eq!(ancillary_data_vec.len(), 1);
    if let AncillaryData::ScmCredentials(scm_credentials) =
        ancillary_data_vec.pop().unwrap().unwrap()
    {
        let cred_vec = Vec::from_iter(scm_credentials);
        assert_eq!(cred_vec.len(), 1);
        assert_eq!(cred1.get_pid(), cred_vec[0].get_pid());
        assert_eq!(cred1.get_uid(), cred_vec[0].get_uid());
        assert_eq!(cred1.get_gid(), cred_vec[0].get_gid());
    } else {
        unreachable!("must be ScmCredentials");
    }
}

#[cfg(any(target_os = "android", target_os = "linux"))]
#[test]
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
fn test_send_vectored_with_ancillary_unix_datagram() {
    let dir = tmpdir();
    let path1 = dir.path().join("sock1");
    let path2 = dir.path().join("sock2");

    let bsock1 = or_panic!(UnixDatagram::bind(&path1));
    let bsock2 = or_panic!(UnixDatagram::bind(&path2));

    let buf1 = [1; 8];
    let bufs_send = &[IoSlice::new(&buf1[..])][..];

    let mut ancillary1_buffer = [0; 128];
    let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]);
    assert!(ancillary1.add_fds(&[bsock1.as_raw_fd()][..]));

    or_panic!(bsock1.connect(&path2));
    let usize = or_panic!(bsock1.send_vectored_with_ancillary(&bufs_send, &mut ancillary1));
    assert_eq!(usize, 8);

    let mut buf2 = [0; 8];
    let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..];

    let mut ancillary2_buffer = [0; 128];
    let mut ancillary2 = SocketAncillary::new(&mut ancillary2_buffer[..]);

    let (usize, truncated) =
        or_panic!(bsock2.recv_vectored_with_ancillary(&mut bufs_recv, &mut ancillary2));
    assert_eq!(usize, 8);
    assert_eq!(truncated, false);
    assert_eq!(buf1, buf2);

    let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages());
    assert_eq!(ancillary_data_vec.len(), 1);
    if let AncillaryData::ScmRights(scm_rights) = ancillary_data_vec.pop().unwrap().unwrap() {
        let fd_vec = Vec::from_iter(scm_rights);
        assert_eq!(fd_vec.len(), 1);
        unsafe {
            libc::close(fd_vec[0]);
        }
    } else {
        unreachable!("must be ScmRights");
    }
}
