aboutsummaryrefslogblamecommitdiffstats
path: root/tests/behavior.rs
blob: 8712643b1dd1d65c9849b2e5a863dab695692be6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10




                                                         
                                 



                                                               
                    









                                                               
                    

                  






                                                  
              
                                

                                        


                                                        
 




                                                     
 

                                                        
 

                                             
 



                                                     
 
                                                        
 

                                             


       

                               
                                  

                                                         
                                 

                                                        





                                                                      

                                                                                          
                                                          

                              
                                                               






                                                                 
                                             


















                                                                             

                                             



















                                                                             
              
                                           
                                        
                                                                    
                                        
 




                                                        
                                                
                                                              
                                                                 
       
                                                    
                                                                
                                                                   



                                                    


                                      
     
                                                      

                              
                                                               




                                                                 
           






















































                                                                             


       

                                                         
                                 


                                                                                                      
                    









                                                   
              
                                            

                                        






                                                        
                                                                 


                    
 

                                                         
 

                                             




                                                         
                                 


                                                                 
                    









                                                   
              
                                      

                                        














                                                               
 








                                                              
mod helpers;

#[test]
fn test_multiple() {
    let pty = pty_process::blocking::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();

    let mut child = pty_process::blocking::Command::new("echo")
        .arg("foo")
        .spawn(&pts)
        .unwrap();

    let mut output = helpers::output(&pty);
    assert_eq!(output.next().unwrap(), "foo\r\n");

    let status = child.wait().unwrap();
    assert_eq!(status.code().unwrap(), 0);

    let mut child = pty_process::blocking::Command::new("echo")
        .arg("bar")
        .spawn(&pts)
        .unwrap();

    assert_eq!(output.next().unwrap(), "bar\r\n");

    let status = child.wait().unwrap();
    assert_eq!(status.code().unwrap(), 0);
}

#[cfg(feature = "async")]
#[tokio::test]
async fn test_multiple_async() {
    use futures::stream::StreamExt as _;

    let mut pty = pty_process::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();

    let mut child = pty_process::Command::new("echo")
        .arg("foo")
        .spawn(&pts)
        .unwrap();
    let (pty_r, _) = pty.split();

    let mut output = helpers::output_async(pty_r);
    assert_eq!(output.next().await.unwrap(), "foo\r\n");

    let status = child.wait().await.unwrap();
    assert_eq!(status.code().unwrap(), 0);

    let mut child = pty_process::Command::new("echo")
        .arg("bar")
        .spawn(&pts)
        .unwrap();

    assert_eq!(output.next().await.unwrap(), "bar\r\n");

    let status = child.wait().await.unwrap();
    assert_eq!(status.code().unwrap(), 0);
}

#[test]
fn test_multiple_configured() {
    use std::io::BufRead as _;
    use std::os::fd::AsRawFd as _;

    let pty = pty_process::blocking::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();

    let (stderr_pipe_r, stderr_pipe_w) = pipe();
    let mut stderr_pipe_r =
        std::io::BufReader::new(std::fs::File::from(stderr_pipe_r));
    let (pre_exec_pipe_r, pre_exec_pipe_w) = pipe();
    let mut pre_exec_pipe_r =
        std::io::BufReader::new(std::fs::File::from(pre_exec_pipe_r));
    let mut cmd = pty_process::blocking::Command::new("perl");
    cmd.arg("-Esay 'foo'; say STDERR 'foo-stderr'; open my $fh, '>&=3'; say $fh 'foo-3';")
        .stderr(std::process::Stdio::from(stderr_pipe_w));
    unsafe {
        cmd.pre_exec(move || {
            nix::unistd::dup2(pre_exec_pipe_w.as_raw_fd(), 3)?;
            nix::fcntl::fcntl(
                3,
                nix::fcntl::F_SETFD(nix::fcntl::FdFlag::empty()),
            )?;
            Ok(())
        });
    }
    let mut child = cmd.spawn(&pts).unwrap();

    let mut output = helpers::output(&pty);
    assert_eq!(output.next().unwrap(), "foo\r\n");

    let mut buf = vec![];
    nix::unistd::alarm::set(5);
    stderr_pipe_r.read_until(b'\n', &mut buf).unwrap();
    nix::unistd::alarm::cancel();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-stderr\n");

    let mut buf = vec![];
    nix::unistd::alarm::set(5);
    pre_exec_pipe_r.read_until(b'\n', &mut buf).unwrap();
    nix::unistd::alarm::cancel();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-3\n");

    let status = child.wait().unwrap();
    assert_eq!(status.code().unwrap(), 0);

    let mut child = cmd.spawn(&pts).unwrap();
    let mut output = helpers::output(&pty);

    assert_eq!(output.next().unwrap(), "foo\r\n");

    let mut buf = vec![];
    nix::unistd::alarm::set(5);
    stderr_pipe_r.read_until(b'\n', &mut buf).unwrap();
    nix::unistd::alarm::cancel();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-stderr\n");

    let mut buf = vec![];
    nix::unistd::alarm::set(5);
    pre_exec_pipe_r.read_until(b'\n', &mut buf).unwrap();
    nix::unistd::alarm::cancel();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-3\n");

    let status = child.wait().unwrap();
    assert_eq!(status.code().unwrap(), 0);
}

#[cfg(feature = "async")]
#[tokio::test]
async fn test_multiple_configured_async() {
    use futures::stream::StreamExt as _;
    use std::os::fd::{AsRawFd as _, FromRawFd as _, IntoRawFd as _};
    use tokio::io::AsyncBufReadExt as _;

    let mut pty = pty_process::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();
    let (pty_r, _) = pty.split();

    let (stderr_pipe_r, stderr_pipe_w) = pipe();
    let mut stderr_pipe_r = tokio::io::BufReader::new(unsafe {
        tokio::fs::File::from_raw_fd(stderr_pipe_r.into_raw_fd())
    });
    let (pre_exec_pipe_r, pre_exec_pipe_w) = pipe();
    let mut pre_exec_pipe_r = tokio::io::BufReader::new(unsafe {
        tokio::fs::File::from_raw_fd(pre_exec_pipe_r.into_raw_fd())
    });
    let mut cmd = pty_process::Command::new("perl");
    cmd.arg(
        "-Esay 'foo'; \
            say STDERR 'foo-stderr'; \
            open my $fh, '>&=3'; \
            say $fh 'foo-3';",
    )
    .stderr(std::process::Stdio::from(stderr_pipe_w));
    unsafe {
        cmd.pre_exec(move || {
            nix::unistd::dup2(pre_exec_pipe_w.as_raw_fd(), 3)?;
            nix::fcntl::fcntl(
                3,
                nix::fcntl::F_SETFD(nix::fcntl::FdFlag::empty()),
            )?;
            Ok(())
        });
    }
    let mut child = cmd.spawn(&pts).unwrap();

    let mut output = helpers::output_async(pty_r);
    assert_eq!(output.next().await.unwrap(), "foo\r\n");

    let mut buf = vec![];
    tokio::time::timeout(
        std::time::Duration::from_secs(5),
        stderr_pipe_r.read_until(b'\n', &mut buf),
    )
    .await
    .unwrap()
    .unwrap();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-stderr\n");

    let mut buf = vec![];
    tokio::time::timeout(
        std::time::Duration::from_secs(5),
        pre_exec_pipe_r.read_until(b'\n', &mut buf),
    )
    .await
    .unwrap()
    .unwrap();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-3\n");

    let status = child.wait().await.unwrap();
    assert_eq!(status.code().unwrap(), 0);

    let mut child = cmd.spawn(&pts).unwrap();

    assert_eq!(output.next().await.unwrap(), "foo\r\n");

    let mut buf = vec![];
    tokio::time::timeout(
        std::time::Duration::from_secs(5),
        stderr_pipe_r.read_until(b'\n', &mut buf),
    )
    .await
    .unwrap()
    .unwrap();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-stderr\n");

    let mut buf = vec![];
    tokio::time::timeout(
        std::time::Duration::from_secs(5),
        pre_exec_pipe_r.read_until(b'\n', &mut buf),
    )
    .await
    .unwrap()
    .unwrap();
    assert_eq!(std::string::String::from_utf8(buf).unwrap(), "foo-3\n");

    let status = child.wait().await.unwrap();
    assert_eq!(status.code().unwrap(), 0);
}

#[test]
fn test_controlling_terminal() {
    let pty = pty_process::blocking::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();
    let mut child = pty_process::blocking::Command::new("perl")
        .arg("-Eopen my $fh, '<', '/dev/tty' or die; if (-t $fh) { say 'true' } else { say 'false' }")
        .spawn(&pts)
        .unwrap();

    let mut output = helpers::output(&pty);
    assert_eq!(output.next().unwrap(), "true\r\n");

    let status = child.wait().unwrap();
    assert_eq!(status.code().unwrap(), 0);
}

#[cfg(feature = "async")]
#[tokio::test]
async fn test_controlling_terminal_async() {
    use futures::stream::StreamExt as _;

    let mut pty = pty_process::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();
    let (pty_r, _) = pty.split();
    let mut child = pty_process::Command::new("perl")
        .arg(
            "-Eopen my $fh, '<', '/dev/tty' or die; \
                if (-t $fh) { say 'true' } else { say 'false' }",
        )
        .spawn(&pts)
        .unwrap();

    let mut output = helpers::output_async(pty_r);
    assert_eq!(output.next().await.unwrap(), "true\r\n");

    let status = child.wait().await.unwrap();
    assert_eq!(status.code().unwrap(), 0);
}

#[test]
fn test_session_leader() {
    let pty = pty_process::blocking::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();
    let mut child = pty_process::blocking::Command::new("python")
        .arg("-cimport os; print(os.getpid() == os.getsid(0))")
        .spawn(&pts)
        .unwrap();

    let mut output = helpers::output(&pty);
    assert_eq!(output.next().unwrap(), "True\r\n");

    let status = child.wait().unwrap();
    assert_eq!(status.code().unwrap(), 0);
}

#[cfg(feature = "async")]
#[tokio::test]
async fn test_session_leader_async() {
    use futures::stream::StreamExt as _;

    let mut pty = pty_process::Pty::new().unwrap();
    let pts = pty.pts().unwrap();
    pty.resize(pty_process::Size::new(24, 80)).unwrap();
    let mut child = pty_process::Command::new("python")
        .arg("-cimport os; print(os.getpid() == os.getsid(0))")
        .spawn(&pts)
        .unwrap();

    let (pty_r, _) = pty.split();
    let mut output = helpers::output_async(pty_r);
    assert_eq!(output.next().await.unwrap(), "True\r\n");

    let status = child.wait().await.unwrap();
    eprintln!("{:?}", status);
    assert_eq!(status.code().unwrap(), 0);
}

fn pipe() -> (std::os::fd::OwnedFd, std::os::fd::OwnedFd) {
    use std::os::fd::FromRawFd as _;

    let (r, w) = nix::unistd::pipe().unwrap();
    (unsafe { std::os::fd::OwnedFd::from_raw_fd(r) }, unsafe {
        std::os::fd::OwnedFd::from_raw_fd(w)
    })
}