From 1763f36c9d47550838793e129f2297ecfc8bebd1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 26 Dec 2013 18:28:24 -0800 Subject: [PATCH] Bring native process bindings up to date Move the tests into libstd, use the `iotest!` macro to test both native and uv bindings, and use the cloexec trick to figure out when the child process fails in exec. --- src/libnative/io/file.rs | 9 +- src/libnative/io/mod.rs | 19 ++- src/libnative/io/process.rs | 178 ++++++++++++++++++---------- src/libstd/io/comm_adapters.rs | 1 - src/libstd/io/net/tcp.rs | 3 +- src/libstd/io/process.rs | 151 ++++++++++++++++++++++- src/libstd/io/test.rs | 1 + src/libstd/run.rs | 2 +- src/test/run-pass/rtio-processes.rs | 178 ---------------------------- 9 files changed, 288 insertions(+), 254 deletions(-) delete mode 100644 src/test/run-pass/rtio-processes.rs diff --git a/src/libnative/io/file.rs b/src/libnative/io/file.rs index c1a378c7e3cc..543132cce156 100644 --- a/src/libnative/io/file.rs +++ b/src/libnative/io/file.rs @@ -37,7 +37,7 @@ fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 { let mut ret; loop { ret = f(data, amt); - if cfg!(not(windows)) { break } // windows has no eintr + if cfg!(windows) { break } // windows has no eintr // if we get an eintr, then try again if ret != -1 || os::errno() as int != eintr { break } } @@ -73,7 +73,10 @@ impl FileDesc { FileDesc { fd: fd, close_on_drop: close_on_drop } } - fn inner_read(&mut self, buf: &mut [u8]) -> Result { + // FIXME(#10465) these functions should not be public, but anything in + // native::io wanting to use them is forced to have all the + // rtio traits in scope + pub fn inner_read(&mut self, buf: &mut [u8]) -> Result { #[cfg(windows)] type rlen = libc::c_uint; #[cfg(not(windows))] type rlen = libc::size_t; let ret = keep_going(buf, |buf, len| { @@ -899,7 +902,7 @@ pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { #[cfg(test)] mod tests { - use super::{CFile, FileDesc}; + use super::{CFile, FileDesc, CloseFd}; use std::io; use std::libc; use std::os; diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 32056215e7c9..560961423492 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -55,7 +55,7 @@ fn unimpl() -> IoError { } } -fn last_error() -> IoError { +fn translate_error(errno: i32, detail: bool) -> IoError { #[cfg(windows)] fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { match errno { @@ -79,14 +79,16 @@ fn last_error() -> IoError { } } - let (kind, desc) = get_err(os::errno() as i32); + let (kind, desc) = get_err(errno); IoError { kind: kind, desc: desc, - detail: Some(os::last_os_error()) + detail: if detail {Some(os::last_os_error())} else {None}, } } +fn last_error() -> IoError { translate_error(os::errno() as i32, true) } + // unix has nonzero values as errors fn mkerr_libc(ret: libc::c_int) -> IoResult<()> { if ret != 0 { @@ -106,6 +108,17 @@ fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { } } +#[cfg(unix)] +fn retry(f: || -> libc::c_int) -> IoResult { + loop { + match f() { + -1 if os::errno() as int == libc::EINTR as int => {} + -1 => return Err(last_error()), + n => return Ok(n), + } + } +} + /// Implementation of rt::rtio's IoFactory trait to generate handles to the /// native I/O functionality. pub struct IoFactory; diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index 64ce9d7e3482..33abb27f16b3 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -18,6 +18,7 @@ use p = std::io::process; #[cfg(windows)] use std::cast; +use super::IoResult; use super::file; /** @@ -37,7 +38,7 @@ pub struct Process { priv handle: *(), /// None until finish() is called. - priv exit_code: Option, + priv exit_code: Option, } impl Process { @@ -105,7 +106,13 @@ impl Process { for pipe in err_pipe.iter() { libc::close(pipe.out); } } - Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, ret_io)) + match res { + Ok(res) => { + Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, + ret_io)) + } + Err(e) => Err(e) + } } } @@ -113,15 +120,14 @@ impl rtio::RtioProcess for Process { fn id(&self) -> pid_t { self.pid } fn wait(&mut self) -> p::ProcessExit { - let code = match self.exit_code { + match self.exit_code { Some(code) => code, None => { let code = waitpid(self.pid); self.exit_code = Some(code); code } - }; - return p::ExitStatus(code); // XXX: this is wrong + } } fn kill(&mut self, signum: int) -> Result<(), io::IoError> { @@ -177,7 +183,8 @@ struct SpawnProcessResult { fn spawn_process_os(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>, dir: Option<&Path>, - in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { + in_fd: c_int, out_fd: c_int, + err_fd: c_int) -> IoResult { use std::libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; use std::libc::consts::os::extra::{ TRUE, FALSE, @@ -241,7 +248,7 @@ fn spawn_process_os(prog: &str, args: &[~str], ptr::mut_null(), ptr::mut_null(), TRUE, 0, envp, dirp, &mut si, &mut pi); if created == FALSE { - create_err = Some(os::last_os_error()); + create_err = Some(super::last_error()); } }) }) @@ -251,21 +258,22 @@ fn spawn_process_os(prog: &str, args: &[~str], CloseHandle(si.hStdOutput); CloseHandle(si.hStdError); - for msg in create_err.iter() { - fail!("failure in CreateProcess: {}", *msg); + match create_err { + Some(err) => return Err(err), + None => {} } - // We close the thread handle because std::we don't care about keeping the + // We close the thread handle because we don't care about keeping the // thread id valid, and we aren't keeping the thread handle around to be // able to close it later. We don't close the process handle however // because std::we want the process id to stay valid at least until the // calling code closes the process handle. CloseHandle(pi.hThread); - SpawnProcessResult { + Ok(SpawnProcessResult { pid: pi.dwProcessId as pid_t, handle: pi.hProcess as *() - } + }) } } @@ -303,9 +311,8 @@ fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMA } } -// FIXME: this is only pub so it can be tested (see issue #4536) #[cfg(windows)] -pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { +fn make_command_line(prog: &str, args: &[~str]) -> ~str { let mut cmd = ~""; append_arg(&mut cmd, prog); for arg in args.iter() { @@ -360,9 +367,12 @@ pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { fn spawn_process_os(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>, dir: Option<&Path>, - in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { + in_fd: c_int, out_fd: c_int, + err_fd: c_int) -> IoResult { use std::libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; use std::libc::funcs::bsd44::getdtablesize; + use std::libc::c_ulong; + use std::unstable::intrinsics; mod rustrt { extern { @@ -370,45 +380,89 @@ fn spawn_process_os(prog: &str, args: &[~str], } } - #[cfg(windows)] - unsafe fn set_environ(_envp: *c_void) {} #[cfg(target_os = "macos")] unsafe fn set_environ(envp: *c_void) { extern { fn _NSGetEnviron() -> *mut *c_void; } *_NSGetEnviron() = envp; } - #[cfg(not(target_os = "macos"), not(windows))] + #[cfg(not(target_os = "macos"))] unsafe fn set_environ(envp: *c_void) { - extern { - static mut environ: *c_void; - } + extern { static mut environ: *c_void; } environ = envp; } - unsafe { + unsafe fn set_cloexec(fd: c_int) { + extern { fn ioctl(fd: c_int, req: c_ulong) -> c_int; } + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + static FIOCLEX: c_ulong = 0x20006601; + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + static FIOCLEX: c_ulong = 0x5451; + + let ret = ioctl(fd, FIOCLEX); + assert_eq!(ret, 0); + } + + let pipe = os::pipe(); + let mut input = file::FileDesc::new(pipe.input, file::CloseFd); + let mut output = file::FileDesc::new(pipe.out, file::CloseFd); + + unsafe { set_cloexec(output.fd()) }; + + unsafe { let pid = fork(); if pid < 0 { fail!("failure in fork: {}", os::last_os_error()); } else if pid > 0 { - return SpawnProcessResult {pid: pid, handle: ptr::null()}; + drop(output); + let mut bytes = [0, ..4]; + return match input.inner_read(bytes) { + Ok(4) => { + let errno = (bytes[0] << 24) as i32 | + (bytes[1] << 16) as i32 | + (bytes[2] << 8) as i32 | + (bytes[3] << 0) as i32; + Err(super::translate_error(errno, false)) + } + Err(e) => { + assert!(e.kind == io::BrokenPipe || + e.kind == io::EndOfFile, + "unexpected error: {:?}", e); + Ok(SpawnProcessResult { + pid: pid, + handle: ptr::null() + }) + } + Ok(..) => fail!("short read on the cloexec pipe"), + }; } + drop(input); rustrt::rust_unset_sigprocmask(); - if dup2(in_fd, 0) == -1 { + if in_fd == -1 { + libc::close(libc::STDIN_FILENO); + } else if dup2(in_fd, 0) == -1 { fail!("failure in dup2(in_fd, 0): {}", os::last_os_error()); } - if dup2(out_fd, 1) == -1 { + if out_fd == -1 { + libc::close(libc::STDOUT_FILENO); + } else if dup2(out_fd, 1) == -1 { fail!("failure in dup2(out_fd, 1): {}", os::last_os_error()); } - if dup2(err_fd, 2) == -1 { + if err_fd == -1 { + libc::close(libc::STDERR_FILENO); + } else if dup2(err_fd, 2) == -1 { fail!("failure in dup3(err_fd, 2): {}", os::last_os_error()); } // close all other fds for fd in range(3, getdtablesize()).invert() { - close(fd as c_int); + if fd != output.fd() { + close(fd as c_int); + } } with_dirp(dir, |dirp| { @@ -421,11 +475,18 @@ fn spawn_process_os(prog: &str, args: &[~str], if !envp.is_null() { set_environ(envp); } - with_argv(prog, args, |argv| { - execvp(*argv, argv); - // execvp only returns if an error occurred - fail!("failure in execvp: {}", os::last_os_error()); - }) + }); + with_argv(prog, args, |argv| { + execvp(*argv, argv); + let errno = os::errno(); + let bytes = [ + (errno << 24) as u8, + (errno << 16) as u8, + (errno << 8) as u8, + (errno << 0) as u8, + ]; + output.inner_write(bytes); + intrinsics::abort(); }) } } @@ -534,11 +595,11 @@ fn free_handle(_handle: *()) { * operate on a none-existent process or, even worse, on a newer process * with the same id. */ -fn waitpid(pid: pid_t) -> int { +fn waitpid(pid: pid_t) -> p::ProcessExit { return waitpid_os(pid); #[cfg(windows)] - fn waitpid_os(pid: pid_t) -> int { + fn waitpid_os(pid: pid_t) -> p::ProcessExit { use std::libc::types::os::arch::extra::DWORD; use std::libc::consts::os::extra::{ SYNCHRONIZE, @@ -572,7 +633,7 @@ fn waitpid(pid: pid_t) -> int { } if status != STILL_ACTIVE { CloseHandle(process); - return status as int; + return p::ExitStatus(status as int); } if WaitForSingleObject(process, INFINITE) == WAIT_FAILED { CloseHandle(process); @@ -583,43 +644,36 @@ fn waitpid(pid: pid_t) -> int { } #[cfg(unix)] - fn waitpid_os(pid: pid_t) -> int { + fn waitpid_os(pid: pid_t) -> p::ProcessExit { use std::libc::funcs::posix01::wait; #[cfg(target_os = "linux")] #[cfg(target_os = "android")] - fn WIFEXITED(status: i32) -> bool { - (status & 0xffi32) == 0i32 + mod imp { + pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 } + pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff } + pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f } } #[cfg(target_os = "macos")] #[cfg(target_os = "freebsd")] - fn WIFEXITED(status: i32) -> bool { - (status & 0x7fi32) == 0i32 - } - - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - fn WEXITSTATUS(status: i32) -> i32 { - (status >> 8i32) & 0xffi32 - } - - #[cfg(target_os = "macos")] - #[cfg(target_os = "freebsd")] - fn WEXITSTATUS(status: i32) -> i32 { - status >> 8i32 + mod imp { + pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 } + pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 } + pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 } } let mut status = 0 as c_int; - if unsafe { wait::waitpid(pid, &mut status, 0) } == -1 { - fail!("failure in waitpid: {}", os::last_os_error()); + match super::retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) { + Err(e) => fail!("unknown waitpid error: {:?}", e), + Ok(_ret) => { + if imp::WIFEXITED(status) { + p::ExitStatus(imp::WEXITSTATUS(status) as int) + } else { + p::ExitSignal(imp::WTERMSIG(status) as int) + } + } } - - return if WIFEXITED(status) { - WEXITSTATUS(status) as int - } else { - 1 - }; } } @@ -646,8 +700,4 @@ mod tests { ~"echo \"a b c\"" ); } - - // Currently most of the tests of this functionality live inside std::run, - // but they may move here eventually as a non-blocking backend is added to - // std::run } diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs index 7a78800c33d3..9b8a8b57e678 100644 --- a/src/libstd/io/comm_adapters.rs +++ b/src/libstd/io/comm_adapters.rs @@ -117,7 +117,6 @@ mod test { use prelude::*; use super::*; use io; - use comm; use task; #[test] diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index e7787692dd2f..c963057acdea 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -134,9 +134,8 @@ impl Acceptor for TcpAcceptor { #[cfg(test)] mod test { use super::*; - use io::net::ip::{Ipv4Addr, SocketAddr}; + use io::net::ip::SocketAddr; use io::*; - use io::test::{next_test_ip4, next_test_ip6}; use prelude::*; #[test] #[ignore] diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index bbb2a7ef3984..a8b7e8e00ead 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -169,5 +169,152 @@ impl Drop for Process { } } -// Tests for this module can be found in the rtio-processes run-pass test, along -// with the justification for why it's not located here. +#[cfg(test)] +mod tests { + use io::process::{ProcessConfig, Process}; + use prelude::*; + use str; + + // FIXME(#10380) + #[cfg(unix, not(target_os="android"))] + iotest!(fn smoke() { + let io = ~[]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"true"], + env: None, + cwd: None, + io: io, + }; + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + assert!(p.wait().success()); + }) + + // FIXME(#10380) + #[cfg(unix, not(target_os="android"))] + iotest!(fn smoke_failure() { + let io = ~[]; + let args = ProcessConfig { + program: "if-this-is-a-binary-then-the-world-has-ended", + args: [], + env: None, + cwd: None, + io: io, + }; + match io::result(|| Process::new(args)) { + Ok(..) => fail!(), + Err(..) => {} + } + }) + + // FIXME(#10380) + #[cfg(unix, not(target_os="android"))] + iotest!(fn exit_reported_right() { + let io = ~[]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"exit 1"], + env: None, + cwd: None, + io: io, + }; + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + assert!(p.wait().matches_exit_status(1)); + }) + + #[cfg(unix, not(target_os="android"))] + iotest!(fn signal_reported_right() { + let io = ~[]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"kill -1 $$"], + env: None, + cwd: None, + io: io, + }; + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + match p.wait() { + process::ExitSignal(1) => {}, + result => fail!("not terminated by signal 1 (instead, {})", result), + } + }) + + pub fn read_all(input: &mut Reader) -> ~str { + let mut ret = ~""; + let mut buf = [0, ..1024]; + loop { + match input.read(buf) { + None => { break } + Some(n) => { ret.push_str(str::from_utf8(buf.slice_to(n))); } + } + } + return ret; + } + + pub fn run_output(args: ProcessConfig) -> ~str { + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + assert!(p.io[0].is_none()); + assert!(p.io[1].is_some()); + let ret = read_all(p.io[1].get_mut_ref() as &mut Reader); + assert!(p.wait().success()); + return ret; + } + + // FIXME(#10380) + #[cfg(unix, not(target_os="android"))] + iotest!(fn stdout_works() { + let io = ~[Ignored, CreatePipe(false, true)]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"echo foobar"], + env: None, + cwd: None, + io: io, + }; + assert_eq!(run_output(args), ~"foobar\n"); + }) + + // FIXME(#10380) + #[cfg(unix, not(target_os="android"))] + iotest!(fn set_cwd_works() { + let io = ~[Ignored, CreatePipe(false, true)]; + let cwd = Some("/"); + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"pwd"], + env: None, + cwd: cwd, + io: io, + }; + assert_eq!(run_output(args), ~"/\n"); + }) + + // FIXME(#10380) + #[cfg(unix, not(target_os="android"))] + iotest!(fn stdin_works() { + let io = ~[CreatePipe(true, false), + CreatePipe(false, true)]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"read line; echo $line"], + env: None, + cwd: None, + io: io, + }; + let mut p = Process::new(args).expect("didn't create a proces?!"); + p.io[0].get_mut_ref().write("foobar".as_bytes()); + p.io[0] = None; // close stdin; + let out = read_all(p.io[1].get_mut_ref() as &mut Reader); + assert!(p.wait().success()); + assert_eq!(out, ~"foobar\n"); + }) + +} diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index 4be112279658..c189bd47b06a 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -33,6 +33,7 @@ macro_rules! iotest ( use io::net::udp::*; #[cfg(unix)] use io::net::unix::*; + use io::process::*; use str; use util; diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 69704c855ee8..33be746e604d 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -339,7 +339,7 @@ mod tests { use task::spawn; use unstable::running_on_valgrind; use io::pipe::PipeStream; - use io::{Writer, Reader, io_error, FileNotFound, OtherIoError}; + use io::{Writer, Reader, io_error, FileNotFound}; #[test] #[cfg(not(target_os="android"))] // FIXME(#10380) diff --git a/src/test/run-pass/rtio-processes.rs b/src/test/run-pass/rtio-processes.rs deleted file mode 100644 index 6463a1d53217..000000000000 --- a/src/test/run-pass/rtio-processes.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: --test -// xfail-fast - -// In the current state of affairs, libuv registers a SIGCHLD handler when a -// process is spawned through it. This is not done with a SA_RESTART flag, -// meaning that all of our syscalls run the risk of returning EINTR. This error -// is not correctly handled in the majority of std::io, so these can't run with -// the main body of tests there. -// -// That being said, libuv correctly handles EINTR completely, so these tests -// themselves are safe against that. Currently the test runner may run into this -// problem, but it's less likely than a whole suite of tests... -// -// See #9341 - -use std::io; -use std::io::process; -use std::io::process::{Process, ProcessConfig, CreatePipe, Ignored}; -use std::str; - -#[test] -// FIXME(#10380) -#[cfg(unix, not(target_os="android"))] -fn smoke() { - let io = ~[]; - let args = ProcessConfig { - program: "/bin/sh", - args: [~"-c", ~"true"], - env: None, - cwd: None, - io: io, - }; - let p = Process::new(args); - assert!(p.is_some()); - let mut p = p.unwrap(); - assert!(p.wait().success()); -} - -#[test] -// FIXME(#10380) -#[cfg(unix, not(target_os="android"))] -fn smoke_failure() { - let io = ~[]; - let args = ProcessConfig { - program: "if-this-is-a-binary-then-the-world-has-ended", - args: [], - env: None, - cwd: None, - io: io, - }; - match io::result(|| Process::new(args)) { - Ok(..) => fail!(), - Err(..) => {} - } -} - -#[test] -// FIXME(#10380) -#[cfg(unix, not(target_os="android"))] -fn exit_reported_right() { - let io = ~[]; - let args = ProcessConfig { - program: "/bin/sh", - args: [~"-c", ~"exit 1"], - env: None, - cwd: None, - io: io, - }; - let p = Process::new(args); - assert!(p.is_some()); - let mut p = p.unwrap(); - assert!(p.wait().matches_exit_status(1)); -} - -#[test] -#[cfg(unix, not(target_os="android"))] -fn signal_reported_right() { - let io = ~[]; - let args = ProcessConfig { - program: "/bin/sh", - args: [~"-c", ~"kill -1 $$"], - env: None, - cwd: None, - io: io, - }; - let p = Process::new(args); - assert!(p.is_some()); - let mut p = p.unwrap(); - match p.wait() { - process::ExitSignal(1) => {}, - result => fail!("not terminated by signal 1 (instead, {})", result), - } -} - -fn read_all(input: &mut Reader) -> ~str { - let mut ret = ~""; - let mut buf = [0, ..1024]; - loop { - match input.read(buf) { - None => { break } - Some(n) => { ret.push_str(str::from_utf8(buf.slice_to(n))); } - } - } - return ret; -} - -fn run_output(args: ProcessConfig) -> ~str { - let p = Process::new(args); - assert!(p.is_some()); - let mut p = p.unwrap(); - assert!(p.io[0].is_none()); - assert!(p.io[1].is_some()); - let ret = read_all(p.io[1].get_mut_ref() as &mut Reader); - assert!(p.wait().success()); - return ret; -} - -#[test] -// FIXME(#10380) -#[cfg(unix, not(target_os="android"))] -fn stdout_works() { - let io = ~[Ignored, CreatePipe(false, true)]; - let args = ProcessConfig { - program: "/bin/sh", - args: [~"-c", ~"echo foobar"], - env: None, - cwd: None, - io: io, - }; - assert_eq!(run_output(args), ~"foobar\n"); -} - -#[test] -// FIXME(#10380) -#[cfg(unix, not(target_os="android"))] -fn set_cwd_works() { - let io = ~[Ignored, CreatePipe(false, true)]; - let cwd = Some("/"); - let args = ProcessConfig { - program: "/bin/sh", - args: [~"-c", ~"pwd"], - env: None, - cwd: cwd, - io: io, - }; - assert_eq!(run_output(args), ~"/\n"); -} - -#[test] -// FIXME(#10380) -#[cfg(unix, not(target_os="android"))] -fn stdin_works() { - let io = ~[CreatePipe(true, false), - CreatePipe(false, true)]; - let args = ProcessConfig { - program: "/bin/sh", - args: [~"-c", ~"read line; echo $line"], - env: None, - cwd: None, - io: io, - }; - let mut p = Process::new(args).expect("didn't create a proces?!"); - p.io[0].get_mut_ref().write("foobar".as_bytes()); - p.io[0] = None; // close stdin; - let out = read_all(p.io[1].get_mut_ref() as &mut Reader); - assert!(p.wait().success()); - assert_eq!(out, ~"foobar\n"); -}