aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-12-30 15:43:21 -0500
committerJesse Luehrs <doy@tozt.net>2021-12-30 15:43:21 -0500
commitd1a9c4d1d878e7bcb4caac766fabcfb48fe418f2 (patch)
treef54c8030ea9fcdbd06da6c6c119575a0c142ce64 /tests
parent0eaa0416fe949d8b3f5273f2677725b113ce17d0 (diff)
downloadpty-process-d1a9c4d1d878e7bcb4caac766fabcfb48fe418f2.tar.gz
pty-process-d1a9c4d1d878e7bcb4caac766fabcfb48fe418f2.zip
improve tests
Diffstat (limited to 'tests')
-rw-r--r--tests/basic.rs107
-rw-r--r--tests/behavior.rs138
-rw-r--r--tests/fds.rs22
-rw-r--r--tests/fds_async.rs36
-rw-r--r--tests/helpers/mod.rs56
-rw-r--r--tests/multiple.rs58
-rw-r--r--tests/winch.rs32
7 files changed, 251 insertions, 198 deletions
diff --git a/tests/basic.rs b/tests/basic.rs
index f73b39e..cab2c16 100644
--- a/tests/basic.rs
+++ b/tests/basic.rs
@@ -1,27 +1,22 @@
+mod helpers;
+
#[test]
fn test_cat_blocking() {
- use std::io::{Read as _, Write as _};
+ use std::io::Write as _;
- let mut pty = pty_process::blocking::Pty::new().unwrap();
+ let pty = pty_process::blocking::Pty::new().unwrap();
pty.resize(pty_process::Size::new(24, 80)).unwrap();
let mut child = pty_process::blocking::Command::new("cat")
.spawn(&pty)
.unwrap();
- pty.write_all(b"foo\n").unwrap();
- // the pty will echo the written bytes back immediately, but the
- // subprocess needs to generate its own output, which takes time, so we
- // can't just read immediately (we may just get the echoed bytes). because
- // the output generation is happening in the subprocess, we also don't
- // have any way to know when (or if!) the subprocess will decide to send
- // its output, so sleeping is the best we can do.
- std::thread::sleep(std::time::Duration::from_secs(1));
+ (&pty).write_all(b"foo\n").unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).unwrap();
- assert_eq!(&buf[..bytes], b"foo\r\nfoo\r\n");
+ let mut output = helpers::output(&pty);
+ assert_eq!(output.next().unwrap(), "foo\r\n");
+ assert_eq!(output.next().unwrap(), "foo\r\n");
- pty.write_all(&[4u8]).unwrap();
+ (&pty).write_all(&[4u8]).unwrap();
let status = child.wait().unwrap();
assert_eq!(status.code().unwrap(), 0);
}
@@ -30,91 +25,21 @@ fn test_cat_blocking() {
#[test]
fn test_cat_async_std() {
use async_std::io::prelude::WriteExt as _;
- use async_std::io::ReadExt as _;
+ use futures::stream::StreamExt as _;
let status = async_std::task::block_on(async {
- let mut pty = pty_process::Pty::new().unwrap();
- pty.resize(pty_process::Size::new(24, 80)).unwrap();
- let mut child = pty_process::Command::new("cat").spawn(&pty).unwrap();
-
- pty.write_all(b"foo\n").await.unwrap();
- // the pty will echo the written bytes back immediately, but the
- // subprocess needs to generate its own output, which takes time, so
- // we can't just read immediately (we may just get the echoed bytes).
- // because the output generation is happening in the subprocess, we
- // also don't have any way to know when (or if!) the subprocess will
- // decide to send its output, so sleeping is the best we can do.
- async_std::task::sleep(std::time::Duration::from_secs(1)).await;
-
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).await.unwrap();
- assert_eq!(&buf[..bytes], b"foo\r\nfoo\r\n");
-
- pty.write_all(&[4u8]).await.unwrap();
- child.status().await.unwrap()
- });
- assert_eq!(status.code().unwrap(), 0);
-}
-
-#[cfg(feature = "async")]
-#[test]
-fn test_cat_smol() {
- use smol::io::{AsyncReadExt as _, AsyncWriteExt as _};
-
- let status = smol::block_on(async {
- let mut pty = pty_process::Pty::new().unwrap();
+ let pty = pty_process::Pty::new().unwrap();
pty.resize(pty_process::Size::new(24, 80)).unwrap();
let mut child = pty_process::Command::new("cat").spawn(&pty).unwrap();
- pty.write_all(b"foo\n").await.unwrap();
- // the pty will echo the written bytes back immediately, but the
- // subprocess needs to generate its own output, which takes time, so
- // we can't just read immediately (we may just get the echoed bytes).
- // because the output generation is happening in the subprocess, we
- // also don't have any way to know when (or if!) the subprocess will
- // decide to send its output, so sleeping is the best we can do.
- smol::Timer::after(std::time::Duration::from_secs(1)).await;
+ (&pty).write_all(b"foo\n").await.unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).await.unwrap();
- assert_eq!(&buf[..bytes], b"foo\r\nfoo\r\n");
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "foo\r\n");
+ assert_eq!(output.next().await.unwrap(), "foo\r\n");
- pty.write_all(&[4u8]).await.unwrap();
+ (&pty).write_all(&[4u8]).await.unwrap();
child.status().await.unwrap()
});
assert_eq!(status.code().unwrap(), 0);
}
-
-#[cfg(feature = "async")]
-#[test]
-fn test_cat_tokio() {
- use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
- use tokio_util::compat::FuturesAsyncReadCompatExt as _;
-
- async fn async_test_cat_tokio() {
- let pty = pty_process::Pty::new().unwrap();
- pty.resize(pty_process::Size::new(24, 80)).unwrap();
- let mut child = pty_process::Command::new("cat").spawn(&pty).unwrap();
-
- pty.compat().write_all(b"foo\n").await.unwrap();
- // the pty will echo the written bytes back immediately, but the
- // subprocess needs to generate its own output, which takes time, so
- // we can't just read immediately (we may just get the echoed bytes).
- // because the output generation is happening in the subprocess, we
- // also don't have any way to know when (or if!) the subprocess will
- // decide to send its output, so sleeping is the best we can do.
- tokio::time::sleep(std::time::Duration::from_secs(1)).await;
-
- let mut buf = [0u8; 1024];
- let bytes = pty.compat().read(&mut buf).await.unwrap();
- assert_eq!(&buf[..bytes], b"foo\r\nfoo\r\n");
-
- pty.compat().write_all(&[4u8]).await.unwrap();
-
- let status = child.status().await.unwrap();
- assert_eq!(status.code().unwrap(), 0);
- }
- tokio::runtime::Runtime::new().unwrap().block_on(async {
- async_test_cat_tokio().await;
- });
-}
diff --git a/tests/behavior.rs b/tests/behavior.rs
new file mode 100644
index 0000000..ef7d54f
--- /dev/null
+++ b/tests/behavior.rs
@@ -0,0 +1,138 @@
+mod helpers;
+
+#[test]
+fn test_multiple() {
+ let pty = pty_process::blocking::Pty::new().unwrap();
+ pty.resize(pty_process::Size::new(24, 80)).unwrap();
+
+ let mut child = pty_process::blocking::Command::new("echo")
+ .arg("foo")
+ .spawn(&pty)
+ .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(&pty)
+ .unwrap();
+
+ let mut output = helpers::output(&pty);
+ assert_eq!(output.next().unwrap(), "bar\r\n");
+
+ let status = child.wait().unwrap();
+ assert_eq!(status.code().unwrap(), 0);
+}
+
+#[cfg(feature = "async")]
+#[test]
+fn test_multiple_async() {
+ use futures::stream::StreamExt as _;
+
+ async_std::task::block_on(async {
+ let pty = pty_process::Pty::new().unwrap();
+ pty.resize(pty_process::Size::new(24, 80)).unwrap();
+
+ let mut child = pty_process::Command::new("echo")
+ .arg("foo")
+ .spawn(&pty)
+ .unwrap();
+
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "foo\r\n");
+
+ let status = child.status().await.unwrap();
+ assert_eq!(status.code().unwrap(), 0);
+
+ let mut child = pty_process::Command::new("echo")
+ .arg("bar")
+ .spawn(&pty)
+ .unwrap();
+
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "bar\r\n");
+
+ let status = child.status().await.unwrap();
+ assert_eq!(status.code().unwrap(), 0);
+ });
+}
+
+#[test]
+fn test_controlling_terminal() {
+ let pty = pty_process::blocking::Pty::new().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(&pty)
+ .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")]
+#[test]
+fn test_controlling_terminal_async() {
+ use futures::stream::StreamExt as _;
+
+ async_std::task::block_on(async {
+ let pty = pty_process::Pty::new().unwrap();
+ pty.resize(pty_process::Size::new(24, 80)).unwrap();
+ 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(&pty)
+ .unwrap();
+
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "true\r\n");
+
+ let status = child.status().await.unwrap();
+ assert_eq!(status.code().unwrap(), 0);
+ });
+}
+
+#[test]
+fn test_session_leader() {
+ let pty = pty_process::blocking::Pty::new().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(&pty)
+ .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")]
+#[test]
+fn test_session_leader_async() {
+ use futures::stream::StreamExt as _;
+
+ async_std::task::block_on(async {
+ let pty = pty_process::Pty::new().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(&pty)
+ .unwrap();
+
+ {
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "True\r\n");
+ }
+
+ let status = child.status().await.unwrap();
+ assert_eq!(status.code().unwrap(), 0);
+ });
+}
diff --git a/tests/fds.rs b/tests/fds.rs
index 7ef802d..8a6bb19 100644
--- a/tests/fds.rs
+++ b/tests/fds.rs
@@ -1,7 +1,7 @@
+mod helpers;
+
#[test]
fn test_fds() {
- use std::io::BufRead as _;
-
check_open_fds();
let pty = pty_process::blocking::Pty::new().unwrap();
@@ -10,11 +10,10 @@ fn test_fds() {
.arg("-Efor my $fd (0..255) { open my $fh, \"<&=$fd\"; print $fd if stat $fh }; say")
.spawn(&pty)
.unwrap();
- let mut buf = vec![];
- std::io::BufReader::new(&pty)
- .read_until(b'\n', &mut buf)
- .unwrap();
- assert_eq!(&buf, b"012\r\n");
+
+ let mut output = helpers::output(&pty);
+ assert_eq!(output.next().unwrap(), "012\r\n");
+
let status = child.wait().unwrap();
assert_eq!(status.code().unwrap(), 0);
drop(pty);
@@ -27,11 +26,10 @@ fn test_fds() {
.stderr(Some(std::process::Stdio::null()))
.spawn(&pty)
.unwrap();
- let mut buf = vec![];
- std::io::BufReader::new(&pty)
- .read_until(b'\n', &mut buf)
- .unwrap();
- assert_eq!(&buf, b"012\r\n");
+
+ let mut output = helpers::output(&pty);
+ assert_eq!(output.next().unwrap(), "012\r\n");
+
let status = child.wait().unwrap();
assert_eq!(status.code().unwrap(), 0);
drop(pty);
diff --git a/tests/fds_async.rs b/tests/fds_async.rs
index e2c369d..7710789 100644
--- a/tests/fds_async.rs
+++ b/tests/fds_async.rs
@@ -1,7 +1,9 @@
+mod helpers;
+
#[cfg(feature = "async")]
#[test]
fn test_fds_async() {
- use async_std::io::prelude::BufReadExt as _;
+ use futures::stream::StreamExt as _;
check_open_fds(&[0, 1, 2]);
@@ -14,12 +16,10 @@ fn test_fds_async() {
.arg("-Efor my $fd (0..255) { open my $fh, \"<&=$fd\"; print $fd if stat $fh }; say")
.spawn(&pty)
.unwrap();
- let mut buf = vec![];
- async_std::io::BufReader::new(&pty)
- .read_until(b'\n', &mut buf)
- .await
- .unwrap();
- assert_eq!(&buf, b"012\r\n");
+
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "012\r\n");
+
let status = child.status().await.unwrap();
assert_eq!(status.code().unwrap(), 0);
});
@@ -33,14 +33,13 @@ fn test_fds_async() {
.arg("-Efor my $fd (0..255) { open my $fh, \"<&=$fd\"; print $fd if stat $fh }; say")
.spawn(&pty)
.unwrap();
- let mut buf = vec![];
- async_std::io::BufReader::new(&pty)
- .read_until(b'\n', &mut buf)
- .await
- .unwrap();
- assert_eq!(&buf, b"012\r\n");
+
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "012\r\n");
+
let status = child.status().await.unwrap();
assert_eq!(status.code().unwrap(), 0);
+ drop(output);
drop(pty);
check_open_fds(&fds);
@@ -56,14 +55,13 @@ fn test_fds_async() {
.stderr(Some(std::process::Stdio::null()))
.spawn(&pty)
.unwrap();
- let mut buf = vec![];
- async_std::io::BufReader::new(&pty)
- .read_until(b'\n', &mut buf)
- .await
- .unwrap();
- assert_eq!(&buf, b"012\r\n");
+
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "012\r\n");
+
let status = child.status().await.unwrap();
assert_eq!(status.code().unwrap(), 0);
+ drop(output);
drop(pty);
check_open_fds(&fds);
diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs
new file mode 100644
index 0000000..4fee8df
--- /dev/null
+++ b/tests/helpers/mod.rs
@@ -0,0 +1,56 @@
+#![allow(dead_code)]
+
+use std::io::BufRead as _;
+
+pub struct Output<'a> {
+ pty: std::io::BufReader<&'a pty_process::blocking::Pty>,
+}
+
+impl<'a> Output<'a> {
+ fn new(pty: &'a pty_process::blocking::Pty) -> Self {
+ Self {
+ pty: std::io::BufReader::new(pty),
+ }
+ }
+}
+
+impl<'a> Iterator for Output<'a> {
+ type Item = String;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut buf = vec![];
+ nix::unistd::alarm::set(5);
+ self.pty.read_until(b'\n', &mut buf).unwrap();
+ nix::unistd::alarm::cancel();
+ Some(std::string::String::from_utf8(buf).unwrap())
+ }
+}
+
+pub fn output(pty: &pty_process::blocking::Pty) -> Output<'_> {
+ Output::new(pty)
+}
+
+#[cfg(feature = "async")]
+pub fn output_async(
+ pty: &pty_process::Pty,
+) -> std::pin::Pin<Box<dyn futures::stream::Stream<Item = String> + '_>> {
+ use async_std::io::prelude::BufReadExt as _;
+ use futures::FutureExt as _;
+
+ let pty = async_std::io::BufReader::new(pty);
+ Box::pin(futures::stream::unfold(pty, |mut pty| async move {
+ Some((
+ async_std::future::timeout(
+ std::time::Duration::from_secs(5),
+ async {
+ let mut buf = vec![];
+ pty.read_until(b'\n', &mut buf).await.unwrap();
+ std::string::String::from_utf8(buf).unwrap()
+ },
+ )
+ .map(|x| x.unwrap())
+ .await,
+ pty,
+ ))
+ }))
+}
diff --git a/tests/multiple.rs b/tests/multiple.rs
deleted file mode 100644
index 7e27512..0000000
--- a/tests/multiple.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-#[test]
-fn test_multiple() {
- use std::io::Read as _;
-
- let mut pty = pty_process::blocking::Pty::new().unwrap();
- pty.resize(pty_process::Size::new(24, 80)).unwrap();
-
- let mut child = pty_process::blocking::Command::new("echo")
- .arg("foo")
- .spawn(&pty)
- .unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).unwrap();
- assert_eq!(&buf[..bytes], b"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(&pty)
- .unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).unwrap();
- assert_eq!(&buf[..bytes], b"bar\r\n");
- let status = child.wait().unwrap();
- assert_eq!(status.code().unwrap(), 0);
-}
-
-#[cfg(feature = "async")]
-#[test]
-fn test_multiple_async() {
- use async_std::io::ReadExt as _;
-
- async_std::task::block_on(async {
- let mut pty = pty_process::Pty::new().unwrap();
- pty.resize(pty_process::Size::new(24, 80)).unwrap();
-
- let mut child = pty_process::Command::new("echo")
- .arg("foo")
- .spawn(&pty)
- .unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).await.unwrap();
- assert_eq!(&buf[..bytes], b"foo\r\n");
- let status = child.status().await.unwrap();
- assert_eq!(status.code().unwrap(), 0);
-
- let mut child = pty_process::Command::new("echo")
- .arg("bar")
- .spawn(&pty)
- .unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).await.unwrap();
- assert_eq!(&buf[..bytes], b"bar\r\n");
- let status = child.status().await.unwrap();
- assert_eq!(status.code().unwrap(), 0);
- });
-}
diff --git a/tests/winch.rs b/tests/winch.rs
index a189697..50447cb 100644
--- a/tests/winch.rs
+++ b/tests/winch.rs
@@ -1,8 +1,10 @@
+mod helpers;
+
#[test]
fn test_winch_std() {
- use std::io::{Read as _, Write as _};
+ use std::io::Write as _;
- let mut pty = pty_process::blocking::Pty::new().unwrap();
+ let pty = pty_process::blocking::Pty::new().unwrap();
pty.resize(pty_process::Size::new(24, 80)).unwrap();
let mut child = pty_process::blocking::Command::new("perl")
.args(&[
@@ -12,16 +14,13 @@ fn test_winch_std() {
.spawn(&pty)
.unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).unwrap();
- assert_eq!(&buf[..bytes], b"started\r\n");
+ let mut output = helpers::output(&pty);
+ assert_eq!(output.next().unwrap(), "started\r\n");
pty.resize(pty_process::Size::new(25, 80)).unwrap();
+ assert_eq!(output.next().unwrap(), "WINCH\r\n");
- let bytes = pty.read(&mut buf).unwrap();
- assert_eq!(&buf[..bytes], b"WINCH\r\n");
-
- pty.write_all(b"\n").unwrap();
+ (&pty).write_all(b"\n").unwrap();
let status = child.wait().unwrap();
assert_eq!(status.code().unwrap(), 0);
}
@@ -30,10 +29,10 @@ fn test_winch_std() {
#[test]
fn test_winch_async() {
use async_std::io::prelude::WriteExt as _;
- use async_std::io::ReadExt as _;
+ use futures::stream::StreamExt as _;
let status = async_std::task::block_on(async {
- let mut pty = pty_process::Pty::new().unwrap();
+ let pty = pty_process::Pty::new().unwrap();
pty.resize(pty_process::Size::new(24, 80)).unwrap();
let mut child = pty_process::Command::new("perl")
.args(&[
@@ -43,16 +42,13 @@ fn test_winch_async() {
.spawn(&pty)
.unwrap();
- let mut buf = [0u8; 1024];
- let bytes = pty.read(&mut buf).await.unwrap();
- assert_eq!(&buf[..bytes], b"started\r\n");
+ let mut output = helpers::output_async(&pty);
+ assert_eq!(output.next().await.unwrap(), "started\r\n");
pty.resize(pty_process::Size::new(25, 80)).unwrap();
+ assert_eq!(output.next().await.unwrap(), "WINCH\r\n");
- let bytes = pty.read(&mut buf).await.unwrap();
- assert_eq!(&buf[..bytes], b"WINCH\r\n");
-
- pty.write_all(b"\n").await.unwrap();
+ (&pty).write_all(b"\n").await.unwrap();
child.status().await.unwrap()
});
assert_eq!(status.code().unwrap(), 0);