]> git.proxmox.com Git - rustc.git/blob - src/libstd/process.rs
Imported Upstream version 1.0.0-alpha.2
[rustc.git] / src / libstd / process.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Working with processes.
12
13 #![unstable(feature = "process", reason = "recently added via RFC 579")]
14 #![allow(non_upper_case_globals)]
15
16 use prelude::v1::*;
17 use io::prelude::*;
18
19 use ffi::AsOsStr;
20 use fmt;
21 use io::{self, Error, ErrorKind};
22 use path::AsPath;
23 use libc;
24 use sync::mpsc::{channel, Receiver};
25 use sys::pipe2::{self, AnonPipe};
26 use sys::process2::Process as ProcessImp;
27 use sys::process2::Command as CommandImp;
28 use sys::process2::ExitStatus as ExitStatusImp;
29 use sys_common::{AsInner, AsInnerMut};
30 use thread;
31
32 /// Representation of a running or exited child process.
33 ///
34 /// This structure is used to represent and manage child processes. A child
35 /// process is created via the `Command` struct, which configures the spawning
36 /// process and can itself be constructed using a builder-style interface.
37 ///
38 /// # Example
39 ///
40 /// ```should_fail
41 /// # #![feature(process)]
42 ///
43 /// use std::process::Command;
44 ///
45 /// let output = Command::new("/bin/cat").arg("file.txt").output().unwrap_or_else(|e| {
46 /// panic!("failed to execute child: {}", e)
47 /// });
48 /// let contents = output.stdout;
49 /// assert!(output.status.success());
50 /// ```
51 pub struct Child {
52 handle: ProcessImp,
53
54 /// None until wait() or wait_with_output() is called.
55 status: Option<ExitStatusImp>,
56
57 /// The handle for writing to the child's stdin, if it has been captured
58 pub stdin: Option<ChildStdin>,
59
60 /// The handle for reading from the child's stdout, if it has been captured
61 pub stdout: Option<ChildStdout>,
62
63 /// The handle for reading from the child's stderr, if it has been captured
64 pub stderr: Option<ChildStderr>,
65 }
66
67 /// A handle to a child procesess's stdin
68 pub struct ChildStdin {
69 inner: AnonPipe
70 }
71
72 impl Write for ChildStdin {
73 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
74 self.inner.write(buf)
75 }
76
77 fn flush(&mut self) -> io::Result<()> {
78 Ok(())
79 }
80 }
81
82 /// A handle to a child procesess's stdout
83 pub struct ChildStdout {
84 inner: AnonPipe
85 }
86
87 impl Read for ChildStdout {
88 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
89 self.inner.read(buf)
90 }
91 }
92
93 /// A handle to a child procesess's stderr
94 pub struct ChildStderr {
95 inner: AnonPipe
96 }
97
98 impl Read for ChildStderr {
99 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
100 self.inner.read(buf)
101 }
102 }
103
104 /// The `Command` type acts as a process builder, providing fine-grained control
105 /// over how a new process should be spawned. A default configuration can be
106 /// generated using `Command::new(program)`, where `program` gives a path to the
107 /// program to be executed. Additional builder methods allow the configuration
108 /// to be changed (for example, by adding arguments) prior to spawning:
109 ///
110 /// ```
111 /// # #![feature(process)]
112 ///
113 /// use std::process::Command;
114 ///
115 /// let output = Command::new("sh").arg("-c").arg("echo hello").output().unwrap_or_else(|e| {
116 /// panic!("failed to execute process: {}", e)
117 /// });
118 /// let hello = output.stdout;
119 /// ```
120 pub struct Command {
121 inner: CommandImp,
122
123 // Details explained in the builder methods
124 stdin: Option<StdioImp>,
125 stdout: Option<StdioImp>,
126 stderr: Option<StdioImp>,
127 }
128
129 impl Command {
130 /// Constructs a new `Command` for launching the program at
131 /// path `program`, with the following default configuration:
132 ///
133 /// * No arguments to the program
134 /// * Inherit the current process's environment
135 /// * Inherit the current process's working directory
136 /// * Inherit stdin/stdout/stderr for `run` or `status`, but create pipes for `output`
137 ///
138 /// Builder methods are provided to change these defaults and
139 /// otherwise configure the process.
140 pub fn new<S: AsOsStr + ?Sized>(program: &S) -> Command {
141 Command {
142 inner: CommandImp::new(program.as_os_str()),
143 stdin: None,
144 stdout: None,
145 stderr: None,
146 }
147 }
148
149 /// Add an argument to pass to the program.
150 pub fn arg<S: AsOsStr + ?Sized>(&mut self, arg: &S) -> &mut Command {
151 self.inner.arg(arg.as_os_str());
152 self
153 }
154
155 /// Add multiple arguments to pass to the program.
156 pub fn args<S: AsOsStr>(&mut self, args: &[S]) -> &mut Command {
157 self.inner.args(args.iter().map(AsOsStr::as_os_str));
158 self
159 }
160
161 /// Inserts or updates an environment variable mapping.
162 ///
163 /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
164 /// and case-sensitive on all other platforms.
165 pub fn env<S: ?Sized, T: ?Sized>(&mut self, key: &S, val: &T) -> &mut Command where
166 S: AsOsStr, T: AsOsStr
167 {
168 self.inner.env(key.as_os_str(), val.as_os_str());
169 self
170 }
171
172 /// Removes an environment variable mapping.
173 pub fn env_remove<S: ?Sized + AsOsStr>(&mut self, key: &S) -> &mut Command {
174 self.inner.env_remove(key.as_os_str());
175 self
176 }
177
178 /// Clears the entire environment map for the child process.
179 pub fn env_clear(&mut self) -> &mut Command {
180 self.inner.env_clear();
181 self
182 }
183
184 /// Set the working directory for the child process.
185 pub fn current_dir<P: AsPath + ?Sized>(&mut self, dir: &P) -> &mut Command {
186 self.inner.cwd(dir.as_path().as_os_str());
187 self
188 }
189
190 /// Configuration for the child process's stdin handle (file descriptor 0).
191 /// Defaults to `CreatePipe(true, false)` so the input can be written to.
192 pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
193 self.stdin = Some(cfg.0);
194 self
195 }
196
197 /// Configuration for the child process's stdout handle (file descriptor 1).
198 /// Defaults to `CreatePipe(false, true)` so the output can be collected.
199 pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
200 self.stdout = Some(cfg.0);
201 self
202 }
203
204 /// Configuration for the child process's stderr handle (file descriptor 2).
205 /// Defaults to `CreatePipe(false, true)` so the output can be collected.
206 pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
207 self.stderr = Some(cfg.0);
208 self
209 }
210
211 fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
212 let (their_stdin, our_stdin) = try!(
213 setup_io(self.stdin.as_ref().unwrap_or(&default_io), 0, true)
214 );
215 let (their_stdout, our_stdout) = try!(
216 setup_io(self.stdout.as_ref().unwrap_or(&default_io), 1, false)
217 );
218 let (their_stderr, our_stderr) = try!(
219 setup_io(self.stderr.as_ref().unwrap_or(&default_io), 2, false)
220 );
221
222 match ProcessImp::spawn(&self.inner, their_stdin, their_stdout, their_stderr) {
223 Err(e) => Err(e),
224 Ok(handle) => Ok(Child {
225 handle: handle,
226 status: None,
227 stdin: our_stdin.map(|fd| ChildStdin { inner: fd }),
228 stdout: our_stdout.map(|fd| ChildStdout { inner: fd }),
229 stderr: our_stderr.map(|fd| ChildStderr { inner: fd }),
230 })
231 }
232 }
233
234 /// Executes the command as a child process, returning a handle to it.
235 ///
236 /// By default, stdin, stdout and stderr are inherited by the parent.
237 pub fn spawn(&mut self) -> io::Result<Child> {
238 self.spawn_inner(StdioImp::Inherit)
239 }
240
241 /// Executes the command as a child process, waiting for it to finish and
242 /// collecting all of its output.
243 ///
244 /// By default, stdin, stdout and stderr are captured (and used to
245 /// provide the resulting output).
246 ///
247 /// # Example
248 ///
249 /// ```
250 /// # #![feature(process)]
251 /// use std::process::Command;
252 ///
253 /// let output = Command::new("cat").arg("foot.txt").output().unwrap_or_else(|e| {
254 /// panic!("failed to execute process: {}", e)
255 /// });
256 ///
257 /// println!("status: {}", output.status);
258 /// println!("stdout: {}", String::from_utf8_lossy(output.stdout.as_slice()));
259 /// println!("stderr: {}", String::from_utf8_lossy(output.stderr.as_slice()));
260 /// ```
261 pub fn output(&mut self) -> io::Result<Output> {
262 self.spawn_inner(StdioImp::Capture).and_then(|p| p.wait_with_output())
263 }
264
265 /// Executes a command as a child process, waiting for it to finish and
266 /// collecting its exit status.
267 ///
268 /// By default, stdin, stdout and stderr are inherited by the parent.
269 ///
270 /// # Example
271 ///
272 /// ```
273 /// # #![feature(process)]
274 /// use std::process::Command;
275 ///
276 /// let status = Command::new("ls").status().unwrap_or_else(|e| {
277 /// panic!("failed to execute process: {}", e)
278 /// });
279 ///
280 /// println!("process exited with: {}", status);
281 /// ```
282 pub fn status(&mut self) -> io::Result<ExitStatus> {
283 self.spawn().and_then(|mut p| p.wait())
284 }
285 }
286
287 #[stable(feature = "rust1", since = "1.0.0")]
288 impl fmt::Debug for Command {
289 /// Format the program and arguments of a Command for display. Any
290 /// non-utf8 data is lossily converted using the utf8 replacement
291 /// character.
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 try!(write!(f, "{:?}", self.inner.program));
294 for arg in &self.inner.args {
295 try!(write!(f, " {:?}", arg));
296 }
297 Ok(())
298 }
299 }
300
301 impl AsInner<CommandImp> for Command {
302 fn as_inner(&self) -> &CommandImp { &self.inner }
303 }
304
305 impl AsInnerMut<CommandImp> for Command {
306 fn as_inner_mut(&mut self) -> &mut CommandImp { &mut self.inner }
307 }
308
309 fn setup_io(io: &StdioImp, fd: libc::c_int, readable: bool)
310 -> io::Result<(Option<AnonPipe>, Option<AnonPipe>)>
311 {
312 use self::StdioImp::*;
313 Ok(match *io {
314 Null => {
315 (None, None)
316 }
317 Inherit => {
318 (Some(AnonPipe::from_fd(fd)), None)
319 }
320 Capture => {
321 let (reader, writer) = try!(unsafe { pipe2::anon_pipe() });
322 if readable {
323 (Some(reader), Some(writer))
324 } else {
325 (Some(writer), Some(reader))
326 }
327 }
328 })
329 }
330
331 /// The output of a finished process.
332 #[derive(PartialEq, Eq, Clone)]
333 pub struct Output {
334 /// The status (exit code) of the process.
335 pub status: ExitStatus,
336 /// The data that the process wrote to stdout.
337 pub stdout: Vec<u8>,
338 /// The data that the process wrote to stderr.
339 pub stderr: Vec<u8>,
340 }
341
342 /// Describes what to do with a standard io stream for a child process.
343 pub struct Stdio(StdioImp);
344
345 // The internal enum for stdio setup; see below for descriptions.
346 #[derive(Clone)]
347 enum StdioImp {
348 Capture,
349 Inherit,
350 Null,
351 }
352
353 impl Stdio {
354 /// A new pipe should be arranged to connect the parent and child processes.
355 pub fn capture() -> Stdio { Stdio(StdioImp::Capture) }
356
357 /// The child inherits from the corresponding parent descriptor.
358 pub fn inherit() -> Stdio { Stdio(StdioImp::Capture) }
359
360 /// This stream will be ignored. This is the equivalent of attaching the
361 /// stream to `/dev/null`
362 pub fn null() -> Stdio { Stdio(StdioImp::Capture) }
363 }
364
365 /// Describes the result of a process after it has terminated.
366 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
367 pub struct ExitStatus(ExitStatusImp);
368
369 impl ExitStatus {
370 /// Was termination successful? Signal termination not considered a success,
371 /// and success is defined as a zero exit status.
372 pub fn success(&self) -> bool {
373 self.0.success()
374 }
375
376 /// Return the exit code of the process, if any.
377 ///
378 /// On Unix, this will return `None` if the process was terminated
379 /// by a signal; `std::os::unix` provides an extension trait for
380 /// extracting the signal and other details from the `ExitStatus`.
381 pub fn code(&self) -> Option<i32> {
382 self.0.code()
383 }
384 }
385
386 impl AsInner<ExitStatusImp> for ExitStatus {
387 fn as_inner(&self) -> &ExitStatusImp { &self.0 }
388 }
389
390 impl fmt::Display for ExitStatus {
391 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392 self.0.fmt(f)
393 }
394 }
395
396 impl Child {
397 /// Forces the child to exit. This is equivalent to sending a
398 /// SIGKILL on unix platforms.
399 pub fn kill(&mut self) -> io::Result<()> {
400 #[cfg(unix)] fn collect_status(p: &mut Child) {
401 // On Linux (and possibly other unices), a process that has exited will
402 // continue to accept signals because it is "defunct". The delivery of
403 // signals will only fail once the child has been reaped. For this
404 // reason, if the process hasn't exited yet, then we attempt to collect
405 // their status with WNOHANG.
406 if p.status.is_none() {
407 match p.handle.try_wait() {
408 Some(status) => { p.status = Some(status); }
409 None => {}
410 }
411 }
412 }
413 #[cfg(windows)] fn collect_status(_p: &mut Child) {}
414
415 collect_status(self);
416
417 // if the process has finished, and therefore had waitpid called,
418 // and we kill it, then on unix we might ending up killing a
419 // newer process that happens to have the same (re-used) id
420 if self.status.is_some() {
421 return Err(Error::new(
422 ErrorKind::InvalidInput,
423 "invalid argument: can't kill an exited process",
424 None
425 ))
426 }
427
428 unsafe { self.handle.kill() }
429 }
430
431 /// Wait for the child to exit completely, returning the status that it
432 /// exited with. This function will continue to have the same return value
433 /// after it has been called at least once.
434 ///
435 /// The stdin handle to the child process, if any, will be closed
436 /// before waiting. This helps avoid deadlock: it ensures that the
437 /// child does not block waiting for input from the parent, while
438 /// the parent waits for the child to exit.
439 pub fn wait(&mut self) -> io::Result<ExitStatus> {
440 drop(self.stdin.take());
441 match self.status {
442 Some(code) => Ok(ExitStatus(code)),
443 None => {
444 let status = try!(self.handle.wait());
445 self.status = Some(status);
446 Ok(ExitStatus(status))
447 }
448 }
449 }
450
451 /// Simultaneously wait for the child to exit and collect all remaining
452 /// output on the stdout/stderr handles, returning a `Output`
453 /// instance.
454 ///
455 /// The stdin handle to the child process, if any, will be closed
456 /// before waiting. This helps avoid deadlock: it ensures that the
457 /// child does not block waiting for input from the parent, while
458 /// the parent waits for the child to exit.
459 pub fn wait_with_output(mut self) -> io::Result<Output> {
460 drop(self.stdin.take());
461 fn read<T: Read + Send + 'static>(stream: Option<T>) -> Receiver<io::Result<Vec<u8>>> {
462 let (tx, rx) = channel();
463 match stream {
464 Some(stream) => {
465 thread::spawn(move || {
466 let mut stream = stream;
467 let mut ret = Vec::new();
468 let res = stream.read_to_end(&mut ret);
469 tx.send(res.map(|_| ret)).unwrap();
470 });
471 }
472 None => tx.send(Ok(Vec::new())).unwrap()
473 }
474 rx
475 }
476 let stdout = read(self.stdout.take());
477 let stderr = read(self.stderr.take());
478 let status = try!(self.wait());
479
480 Ok(Output {
481 status: status,
482 stdout: stdout.recv().unwrap().unwrap_or(Vec::new()),
483 stderr: stderr.recv().unwrap().unwrap_or(Vec::new()),
484 })
485 }
486 }
487
488 #[cfg(test)]
489 mod tests {
490 use io::ErrorKind;
491 use io::prelude::*;
492 use prelude::v1::{Ok, Err, range, drop, Some, None, Vec};
493 use prelude::v1::{String, Clone};
494 use prelude::v1::{SliceExt, Str, StrExt, AsSlice, ToString, GenericPath};
495 use path::Path;
496 use old_path;
497 use old_io::fs::PathExtensions;
498 use rt::running_on_valgrind;
499 use str;
500 use super::{Child, Command, Output, ExitStatus, Stdio};
501 use sync::mpsc::channel;
502 use thread;
503 use time::Duration;
504
505 // FIXME(#10380) these tests should not all be ignored on android.
506
507 #[cfg(not(target_os="android"))]
508 #[test]
509 fn smoke() {
510 let p = Command::new("true").spawn();
511 assert!(p.is_ok());
512 let mut p = p.unwrap();
513 assert!(p.wait().unwrap().success());
514 }
515
516 #[cfg(not(target_os="android"))]
517 #[test]
518 fn smoke_failure() {
519 match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
520 Ok(..) => panic!(),
521 Err(..) => {}
522 }
523 }
524
525 #[cfg(not(target_os="android"))]
526 #[test]
527 fn exit_reported_right() {
528 let p = Command::new("false").spawn();
529 assert!(p.is_ok());
530 let mut p = p.unwrap();
531 assert!(p.wait().unwrap().code() == Some(1));
532 drop(p.wait().clone());
533 }
534
535 #[cfg(all(unix, not(target_os="android")))]
536 #[test]
537 fn signal_reported_right() {
538 use os::unix::ExitStatusExt;
539
540 let p = Command::new("/bin/sh").arg("-c").arg("kill -9 $$").spawn();
541 assert!(p.is_ok());
542 let mut p = p.unwrap();
543 match p.wait().unwrap().signal() {
544 Some(9) => {},
545 result => panic!("not terminated by signal 9 (instead, {:?})", result),
546 }
547 }
548
549 pub fn run_output(mut cmd: Command) -> String {
550 let p = cmd.spawn();
551 assert!(p.is_ok());
552 let mut p = p.unwrap();
553 assert!(p.stdout.is_some());
554 let mut ret = String::new();
555 p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
556 assert!(p.wait().unwrap().success());
557 return ret;
558 }
559
560 #[cfg(not(target_os="android"))]
561 #[test]
562 fn stdout_works() {
563 let mut cmd = Command::new("echo");
564 cmd.arg("foobar").stdout(Stdio::capture());
565 assert_eq!(run_output(cmd), "foobar\n");
566 }
567
568 #[cfg(all(unix, not(target_os="android")))]
569 #[test]
570 fn set_current_dir_works() {
571 let mut cmd = Command::new("/bin/sh");
572 cmd.arg("-c").arg("pwd")
573 .current_dir("/")
574 .stdout(Stdio::capture());
575 assert_eq!(run_output(cmd), "/\n");
576 }
577
578 #[cfg(all(unix, not(target_os="android")))]
579 #[test]
580 fn stdin_works() {
581 let mut p = Command::new("/bin/sh")
582 .arg("-c").arg("read line; echo $line")
583 .stdin(Stdio::capture())
584 .stdout(Stdio::capture())
585 .spawn().unwrap();
586 p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
587 drop(p.stdin.take());
588 let mut out = String::new();
589 p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
590 assert!(p.wait().unwrap().success());
591 assert_eq!(out, "foobar\n");
592 }
593
594
595 #[cfg(all(unix, not(target_os="android")))]
596 #[test]
597 fn uid_works() {
598 use os::unix::*;
599 use libc;
600 let mut p = Command::new("/bin/sh")
601 .arg("-c").arg("true")
602 .uid(unsafe { libc::getuid() })
603 .gid(unsafe { libc::getgid() })
604 .spawn().unwrap();
605 assert!(p.wait().unwrap().success());
606 }
607
608 #[cfg(all(unix, not(target_os="android")))]
609 #[test]
610 fn uid_to_root_fails() {
611 use os::unix::*;
612 use libc;
613
614 // if we're already root, this isn't a valid test. Most of the bots run
615 // as non-root though (android is an exception).
616 if unsafe { libc::getuid() == 0 } { return }
617 assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err());
618 }
619
620 #[cfg(not(target_os="android"))]
621 #[test]
622 fn test_process_status() {
623 let mut status = Command::new("false").status().unwrap();
624 assert!(status.code() == Some(1));
625
626 status = Command::new("true").status().unwrap();
627 assert!(status.success());
628 }
629
630 #[test]
631 fn test_process_output_fail_to_start() {
632 match Command::new("/no-binary-by-this-name-should-exist").output() {
633 Err(e) => assert_eq!(e.kind(), ErrorKind::FileNotFound),
634 Ok(..) => panic!()
635 }
636 }
637
638 #[cfg(not(target_os="android"))]
639 #[test]
640 fn test_process_output_output() {
641 let Output {status, stdout, stderr}
642 = Command::new("echo").arg("hello").output().unwrap();
643 let output_str = str::from_utf8(stdout.as_slice()).unwrap();
644
645 assert!(status.success());
646 assert_eq!(output_str.trim().to_string(), "hello");
647 // FIXME #7224
648 if !running_on_valgrind() {
649 assert_eq!(stderr, Vec::new());
650 }
651 }
652
653 #[cfg(not(target_os="android"))]
654 #[test]
655 fn test_process_output_error() {
656 let Output {status, stdout, stderr}
657 = Command::new("mkdir").arg(".").output().unwrap();
658
659 assert!(status.code() == Some(1));
660 assert_eq!(stdout, Vec::new());
661 assert!(!stderr.is_empty());
662 }
663
664 #[cfg(not(target_os="android"))]
665 #[test]
666 fn test_finish_once() {
667 let mut prog = Command::new("false").spawn().unwrap();
668 assert!(prog.wait().unwrap().code() == Some(1));
669 }
670
671 #[cfg(not(target_os="android"))]
672 #[test]
673 fn test_finish_twice() {
674 let mut prog = Command::new("false").spawn().unwrap();
675 assert!(prog.wait().unwrap().code() == Some(1));
676 assert!(prog.wait().unwrap().code() == Some(1));
677 }
678
679 #[cfg(not(target_os="android"))]
680 #[test]
681 fn test_wait_with_output_once() {
682 let prog = Command::new("echo").arg("hello").stdout(Stdio::capture())
683 .spawn().unwrap();
684 let Output {status, stdout, stderr} = prog.wait_with_output().unwrap();
685 let output_str = str::from_utf8(stdout.as_slice()).unwrap();
686
687 assert!(status.success());
688 assert_eq!(output_str.trim().to_string(), "hello");
689 // FIXME #7224
690 if !running_on_valgrind() {
691 assert_eq!(stderr, Vec::new());
692 }
693 }
694
695 #[cfg(all(unix, not(target_os="android")))]
696 pub fn pwd_cmd() -> Command {
697 Command::new("pwd")
698 }
699 #[cfg(target_os="android")]
700 pub fn pwd_cmd() -> Command {
701 let mut cmd = Command::new("/system/bin/sh");
702 cmd.arg("-c").arg("pwd");
703 cmd
704 }
705
706 #[cfg(windows)]
707 pub fn pwd_cmd() -> Command {
708 let mut cmd = Command::new("cmd");
709 cmd.arg("/c").arg("cd");
710 cmd
711 }
712
713 #[test]
714 fn test_keep_current_working_dir() {
715 use os;
716 let prog = pwd_cmd().spawn().unwrap();
717
718 let output = String::from_utf8(prog.wait_with_output().unwrap().stdout).unwrap();
719 let parent_dir = os::getcwd().unwrap();
720 let child_dir = old_path::Path::new(output.trim());
721
722 let parent_stat = parent_dir.stat().unwrap();
723 let child_stat = child_dir.stat().unwrap();
724
725 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
726 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
727 }
728
729 #[test]
730 fn test_change_working_directory() {
731 use os;
732 // test changing to the parent of os::getcwd() because we know
733 // the path exists (and os::getcwd() is not expected to be root)
734 let parent_dir = os::getcwd().unwrap().dir_path();
735 let result = pwd_cmd().current_dir(&parent_dir).output().unwrap();
736
737 let output = String::from_utf8(result.stdout).unwrap();
738 let child_dir = old_path::Path::new(output.trim());
739
740 let parent_stat = parent_dir.stat().unwrap();
741 let child_stat = child_dir.stat().unwrap();
742
743 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
744 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
745 }
746
747 #[cfg(all(unix, not(target_os="android")))]
748 pub fn env_cmd() -> Command {
749 Command::new("env")
750 }
751 #[cfg(target_os="android")]
752 pub fn env_cmd() -> Command {
753 let mut cmd = Command::new("/system/bin/sh");
754 cmd.arg("-c").arg("set");
755 cmd
756 }
757
758 #[cfg(windows)]
759 pub fn env_cmd() -> Command {
760 let mut cmd = Command::new("cmd");
761 cmd.arg("/c").arg("set");
762 cmd
763 }
764
765 #[cfg(not(target_os="android"))]
766 #[test]
767 fn test_inherit_env() {
768 use os;
769 if running_on_valgrind() { return; }
770
771 let result = env_cmd().output().unwrap();
772 let output = String::from_utf8(result.stdout).unwrap();
773
774 let r = os::env();
775 for &(ref k, ref v) in &r {
776 // don't check windows magical empty-named variables
777 assert!(k.is_empty() ||
778 output.contains(format!("{}={}", *k, *v).as_slice()),
779 "output doesn't contain `{}={}`\n{}",
780 k, v, output);
781 }
782 }
783 #[cfg(target_os="android")]
784 #[test]
785 fn test_inherit_env() {
786 use os;
787 if running_on_valgrind() { return; }
788
789 let mut result = env_cmd().output().unwrap();
790 let output = String::from_utf8(result.stdout).unwrap();
791
792 let r = os::env();
793 for &(ref k, ref v) in &r {
794 // don't check android RANDOM variables
795 if *k != "RANDOM".to_string() {
796 assert!(output.contains(format!("{}={}",
797 *k,
798 *v).as_slice()) ||
799 output.contains(format!("{}=\'{}\'",
800 *k,
801 *v).as_slice()));
802 }
803 }
804 }
805
806 #[test]
807 fn test_override_env() {
808 use env;
809
810 // In some build environments (such as chrooted Nix builds), `env` can
811 // only be found in the explicitly-provided PATH env variable, not in
812 // default places such as /bin or /usr/bin. So we need to pass through
813 // PATH to our sub-process.
814 let mut cmd = env_cmd();
815 cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
816 if let Some(p) = env::var_os("PATH") {
817 cmd.env("PATH", &p);
818 }
819 let result = cmd.output().unwrap();
820 let output = String::from_utf8_lossy(result.stdout.as_slice()).to_string();
821
822 assert!(output.contains("RUN_TEST_NEW_ENV=123"),
823 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
824 }
825
826 #[test]
827 fn test_add_to_env() {
828 let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
829 let output = String::from_utf8_lossy(result.stdout.as_slice()).to_string();
830
831 assert!(output.contains("RUN_TEST_NEW_ENV=123"),
832 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
833 }
834 }