summaryrefslogtreecommitdiffstats
path: root/tests-integration
diff options
context:
space:
mode:
authorCarl Lerche <me@carllerche.com>2019-10-25 12:50:15 -0700
committerGitHub <noreply@github.com>2019-10-25 12:50:15 -0700
commit227533d456fe32e48ffcd3796f1e6c8f9318b230 (patch)
tree498029aaf42dd64eeb8ef0e7d7f29802b45d4e95 /tests-integration
parent03a9378297c73c2e56a6d6b55db22b92427b850a (diff)
net: move into tokio crate (#1683)
A step towards collapsing Tokio sub crates into a single `tokio` crate (#1318). The `net` implementation is now provided by the main `tokio` crate. Functionality can be opted out of by using the various net related feature flags.
Diffstat (limited to 'tests-integration')
-rw-r--r--tests-integration/Cargo.toml14
-rw-r--r--tests-integration/README.md1
-rw-r--r--tests-integration/src/bin/test-cat.rs20
-rw-r--r--tests-integration/tests/process_stdio.rs127
4 files changed, 162 insertions, 0 deletions
diff --git a/tests-integration/Cargo.toml b/tests-integration/Cargo.toml
new file mode 100644
index 00000000..0dcc1e81
--- /dev/null
+++ b/tests-integration/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "tests-integration"
+version = "0.1.0"
+authors = ["Tokio Contributors <team@tokio.rs>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+
+[dev-dependencies]
+tokio = { path = "../tokio" }
+tokio-test = { path = "../tokio-test" }
+
+futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] }
diff --git a/tests-integration/README.md b/tests-integration/README.md
new file mode 100644
index 00000000..955472d0
--- /dev/null
+++ b/tests-integration/README.md
@@ -0,0 +1 @@
+Tests that require additional components than just the `tokio` crate.
diff --git a/tests-integration/src/bin/test-cat.rs b/tests-integration/src/bin/test-cat.rs
new file mode 100644
index 00000000..3b30bf18
--- /dev/null
+++ b/tests-integration/src/bin/test-cat.rs
@@ -0,0 +1,20 @@
+//! A cat-like utility that can be used as a subprocess to test I/O
+//! stream communication.
+
+use std::io;
+use std::io::Write;
+
+fn main() {
+ let stdin = io::stdin();
+ let mut stdout = io::stdout();
+ let mut line = String::new();
+ loop {
+ line.clear();
+ stdin.read_line(&mut line).unwrap();
+ if line.is_empty() {
+ break;
+ }
+ stdout.write_all(line.as_bytes()).unwrap();
+ }
+ stdout.flush().unwrap();
+}
diff --git a/tests-integration/tests/process_stdio.rs b/tests-integration/tests/process_stdio.rs
new file mode 100644
index 00000000..7e4f3d3e
--- /dev/null
+++ b/tests-integration/tests/process_stdio.rs
@@ -0,0 +1,127 @@
+#![warn(rust_2018_idioms)]
+
+use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
+use tokio::process::{Child, Command};
+use tokio_test::assert_ok;
+
+use futures::future::{self, FutureExt};
+use futures::stream::StreamExt;
+use std::env;
+use std::io;
+use std::process::{ExitStatus, Stdio};
+
+fn cat() -> Command {
+ let mut me = env::current_exe().unwrap();
+ me.pop();
+
+ if me.ends_with("deps") {
+ me.pop();
+ }
+
+ me.push("test-cat");
+
+ let mut cmd = Command::new(me);
+ cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
+ cmd
+}
+
+async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
+ let mut stdin = cat.stdin().take().unwrap();
+ let stdout = cat.stdout().take().unwrap();
+
+ // Produce n lines on the child's stdout.
+ let write = async {
+ for i in 0..n {
+ let bytes = format!("line {}\n", i).into_bytes();
+ stdin.write_all(&bytes).await.unwrap();
+ }
+
+ drop(stdin);
+ };
+
+ let read = async {
+ let mut reader = BufReader::new(stdout).lines();
+ let mut num_lines = 0;
+
+ // Try to read `n + 1` lines, ensuring the last one is empty
+ // (i.e. EOF is reached after `n` lines.
+ loop {
+ let data = reader
+ .next()
+ .await
+ .unwrap_or_else(|| Ok(String::new()))
+ .expect("failed to read line");
+
+ let num_read = data.len();
+ let done = num_lines >= n;
+
+ match (done, num_read) {
+ (false, 0) => panic!("broken pipe"),
+ (true, n) if n != 0 => panic!("extraneous data"),
+ _ => {
+ let expected = format!("line {}", num_lines);
+ assert_eq!(expected, data);
+ }
+ };
+
+ num_lines += 1;
+ if num_lines >= n {
+ break;
+ }
+ }
+ };
+
+ // Compose reading and writing concurrently.
+ future::join3(write, read, cat)
+ .map(|(_, _, status)| status)
+ .await
+}
+
+/// Check for the following properties when feeding stdin and
+/// consuming stdout of a cat-like process:
+///
+/// - A number of lines that amounts to a number of bytes exceeding a
+/// typical OS buffer size can be fed to the child without
+/// deadlock. This tests that we also consume the stdout
+/// concurrently; otherwise this would deadlock.
+///
+/// - We read the same lines from the child that we fed it.
+///
+/// - The child does produce EOF on stdout after the last line.
+#[tokio::test]
+async fn feed_a_lot() {
+ let child = cat().spawn().unwrap();
+ let status = feed_cat(child, 10000).await.unwrap();
+ assert_eq!(status.code(), Some(0));
+}
+
+#[tokio::test]
+async fn wait_with_output_captures() {
+ let mut child = cat().spawn().unwrap();
+ let mut stdin = child.stdin().take().unwrap();
+
+ let write_bytes = b"1234";
+
+ let future = async {
+ stdin.write_all(write_bytes).await?;
+ drop(stdin);
+ let out = child.wait_with_output();
+ out.await
+ };
+
+ let output = future.await.unwrap();
+
+ assert!(output.status.success());
+ assert_eq!(output.stdout, write_bytes);
+ assert_eq!(output.stderr.len(), 0);
+}
+
+#[tokio::test]
+async fn status_closes_any_pipes() {
+ // Cat will open a pipe between the parent and child.
+ // If `status_async` doesn't ensure the handles are closed,
+ // we would end up blocking forever (and time out).
+ let child = cat().status();
+
+ assert_ok!(child.await);
+}