2 use self::tempdir
::TempDir
;
4 use super::{sh, Expression}
;
6 use std
::collections
::HashMap
;
8 use std
::env
::consts
::EXE_EXTENSION
;
9 use std
::ffi
::{OsStr, OsString}
;
12 use std
::io
::prelude
::*;
13 use std
::path
::{Path, PathBuf}
;
14 use std
::process
::Command
;
16 use std
::sync
::{Arc, Once, ONCE_INIT}
;
18 fn path_to_exe(name
: &str) -> PathBuf
{
19 // This project defines some associated binaries for testing, and we shell out to them in
20 // these tests. `cargo test` doesn't automatically build associated binaries, so this
21 // function takes care of building them explicitly.
22 static CARGO_BUILD_ONCE
: Once
= ONCE_INIT
;
23 CARGO_BUILD_ONCE
.call_once(|| {
24 let build_status
= Command
::new("cargo")
29 assert
!(build_status
.success(),
30 "Cargo failed to build associated binaries.");
33 Path
::new("target").join("debug").join(name
).with_extension(EXE_EXTENSION
)
36 fn true_cmd() -> Expression
{
37 cmd
!(path_to_exe("status"), "0")
40 fn false_cmd() -> Expression
{
41 cmd
!(path_to_exe("status"), "1")
46 let output
= cmd
!(path_to_exe("echo"), "hi").read().unwrap();
47 assert_eq
!("hi", output
);
52 // Windows compatible.
53 let output
= sh("echo hi").read().unwrap();
54 assert_eq
!("hi", output
);
59 let handle1
= cmd
!(path_to_exe("echo"), "hi").stdout_capture().start().unwrap();
60 let handle2
= cmd
!(path_to_exe("echo"), "lo").stdout_capture().start().unwrap();
61 let output1
= handle1
.output().unwrap();
62 let output2
= handle2
.output().unwrap();
63 assert_eq
!("hi", str::from_utf8(&output1
.stdout
).unwrap().trim());
64 assert_eq
!("lo", str::from_utf8(&output2
.stdout
).unwrap().trim());
69 let result
= false_cmd().run();
70 if let Err(err
) = result
{
71 assert_eq
!(err
.kind(), io
::ErrorKind
::Other
);
73 panic
!("Expected a status error.");
79 let unchecked_false
= false_cmd().unchecked();
80 // Unchecked errors shouldn't prevent the right side of `then` from
81 // running, and they shouldn't cause `run` to return an error.
82 let output
= unchecked_false
.then(cmd
!(path_to_exe("echo"), "waa"))
83 .then(unchecked_false
)
87 // The value of the exit code is preserved.
88 assert_eq
!(1, output
.status
.code().unwrap());
89 assert_eq
!("waa", String
::from_utf8_lossy(&output
.stdout
).trim());
93 fn test_unchecked_in_pipe() {
94 let zero
= cmd
!(path_to_exe("status"), "0");
95 let one
= cmd
!(path_to_exe("status"), "1");
96 let two
= cmd
!(path_to_exe("status"), "2");
98 // Right takes precedence over left.
99 let output
= one
.pipe(two
.clone())
103 assert_eq
!(2, output
.status
.code().unwrap());
105 // Except that checked on the left takes precedence over unchecked on
107 let output
= one
.pipe(two
.unchecked())
111 assert_eq
!(1, output
.status
.code().unwrap());
113 // Right takes precedence over the left again if they're both unchecked.
114 let output
= one
.unchecked()
115 .pipe(two
.unchecked())
119 assert_eq
!(2, output
.status
.code().unwrap());
121 // Except that if the right is a success, the left takes precedence.
122 let output
= one
.unchecked()
123 .pipe(zero
.unchecked())
127 assert_eq
!(1, output
.status
.code().unwrap());
129 // Even if the right is checked.
130 let output
= one
.unchecked()
135 assert_eq
!(1, output
.status
.code().unwrap());
140 let output
= sh("echo xxx").pipe(cmd
!(path_to_exe("x_to_y"))).read().unwrap();
141 assert_eq
!("yyy", output
);
143 // Check that errors on either side are propagated.
144 let result
= true_cmd().pipe(false_cmd()).run();
145 assert
!(result
.is_err());
147 let result
= false_cmd().pipe(true_cmd()).run();
148 assert
!(result
.is_err());
152 fn test_pipe_with_kill() {
153 // Make sure both sides get killed.
154 let sleep_cmd
= cmd
!(path_to_exe("sleep"), "1000000");
155 let handle
= sleep_cmd
.pipe(sleep_cmd
.clone())
159 handle
.kill().unwrap();
160 handle
.wait().unwrap();
164 fn test_pipe_start() {
165 let nonexistent_cmd
= cmd
!(path_to_exe("nonexistent!!!"));
167 // Errors starting the left side of a pipe are returned immediately.
168 let res
= nonexistent_cmd
.pipe(true_cmd()).start();
169 assert
!(res
.is_err());
171 // Errors starting the right side are retained.
172 let handle
= true_cmd().pipe(nonexistent_cmd
).start().unwrap();
173 // But they show up during wait().
174 assert
!(handle
.wait().is_err());
179 let output
= true_cmd().then(sh("echo lo")).read().unwrap();
180 assert_eq
!("lo", output
);
182 // Check that errors on either side are propagated.
183 let result
= true_cmd().then(false_cmd()).run();
184 assert
!(result
.is_err());
186 let result
= false_cmd().then(true_cmd()).run();
187 assert
!(result
.is_err());
191 fn test_then_closes_handles() {
192 // Run a then expression that will short circuit because of a status error
193 // on the left, and which also captures output. Waiting on it will deadlock
194 // if we fail to close the write pipes before returning.
195 let expr
= false_cmd().then(true_cmd()).unchecked().stdout_capture();
196 let handle
= expr
.start().unwrap();
197 handle
.wait().unwrap();
199 // Do the same thing with try_wait. Right now this will always work if the
200 // first check above worked, because wait is both running on a background
201 // thread and called by try_wait for cleanup. But its nice to test it.
202 let handle
= expr
.start().unwrap();
204 match handle
.try_wait().unwrap() {
205 // We might get None a few times before the children exit, which is
206 // why we're doing this in a loop.
208 // Getting here without deadlocking is what we're testing for.
215 fn test_then_with_kill() {
216 // Kill should prevent the second command from starting. Test this with two
217 // long-running commands. The first command is unchecked, so the exit status
218 // alone won't short circuit the expression.
219 let sleep_cmd
= cmd
!(path_to_exe("sleep"), "1000000");
220 let handle
= sleep_cmd
.unchecked()
224 handle
.kill().unwrap();
225 handle
.wait().unwrap();
229 fn test_multiple_threads() {
230 // Wait on the sleep command in a background thread, while the main thread
232 let sleep_cmd
= cmd
!(path_to_exe("sleep"), "1000000");
233 let handle
= Arc
::new(sleep_cmd
.unchecked().start().unwrap());
234 let arc_clone
= handle
.clone();
235 let wait_thread
= std
::thread
::spawn(move || { arc_clone.wait().unwrap(); }
);
236 handle
.kill().unwrap();
237 wait_thread
.join().unwrap();
241 fn test_nonblocking_waits() {
242 let sleep_cmd
= cmd
!(path_to_exe("sleep"), "1000000");
243 // Build a big ol' thing with pipe and then.
244 let handle
= sleep_cmd
.then(&sleep_cmd
)
245 .pipe(sleep_cmd
.then(&sleep_cmd
))
249 // Make sure try_wait doesn't block on it.
250 assert
!(handle
.try_wait().unwrap().is_none());
251 handle
.kill().unwrap();
252 handle
.wait().unwrap();
256 fn test_then_makes_progress() {
257 // The right side of a then expression must start even if the caller isn't
258 // waiting. This is what we use the background thread for. Read from a pipe
259 // of our own to test that both sides are writing and exiting before the
261 let (mut read
, write
) = ::os_pipe
::pipe().unwrap();
262 let handle
= cmd
!(path_to_exe("echo"), "hi")
263 .then(cmd
!(path_to_exe("echo"), "lo"))
264 .stdout_handle(write
)
267 // Read *before* waiting.
268 let mut output
= String
::new();
269 read
.read_to_string(&mut output
).unwrap();
270 assert_eq
!(output
, "hi\nlo\n");
271 handle
.wait().unwrap();
276 let expr
= cmd
!(path_to_exe("x_to_y")).input("xxx");
277 let output
= expr
.read().unwrap();
278 assert_eq
!("yyy", output
);
283 let (mut reader
, writer
) = ::os_pipe
::pipe().unwrap();
284 sh("echo hi>&2").stderr_handle(writer
).run().unwrap();
285 let mut s
= String
::new();
286 reader
.read_to_string(&mut s
).unwrap();
287 assert_eq
!(s
.trim(), "hi");
292 let expr
= cmd
!(path_to_exe("cat")).stdin_null().stdout_null().stderr_null();
293 let output
= expr
.read().unwrap();
294 assert_eq
!("", output
);
299 let dir
= TempDir
::new("test_path").unwrap();
300 let input_file
= dir
.path().join("input_file");
301 let output_file
= dir
.path().join("output_file");
302 File
::create(&input_file
).unwrap().write_all(b
"xxx").unwrap();
303 let expr
= cmd
!(path_to_exe("x_to_y")).stdin(&input_file
).stdout(&output_file
);
304 let output
= expr
.read().unwrap();
305 assert_eq
!("", output
);
306 let mut file_output
= String
::new();
307 File
::open(&output_file
).unwrap().read_to_string(&mut file_output
).unwrap();
308 assert_eq
!("yyy", file_output
);
313 let output
= sh("echo hi")
318 let stderr
= str::from_utf8(&output
.stderr
).unwrap().trim();
319 assert_eq
!("hi", stderr
);
321 // Windows compatible. (Requires no space before the ">".)
322 let output
= sh("echo hi>&2").stderr_to_stdout().read().unwrap();
323 assert_eq
!("hi", output
);
328 let dir
= TempDir
::new("test_file").unwrap();
329 let file
= dir
.path().join("file");
330 File
::create(&file
).unwrap().write_all(b
"example").unwrap();
331 let expr
= cmd
!(path_to_exe("cat")).stdin_handle(File
::open(&file
).unwrap());
332 let output
= expr
.read().unwrap();
333 assert_eq
!(output
, "example");
337 fn test_ergonomics() {
338 let mystr
= "owned string".to_owned();
339 let mypathbuf
= Path
::new("a/b/c").to_owned();
340 let myvec
= vec
![1, 2, 3];
341 // These are nonsense expressions. We just want to make sure they compile.
342 let _
= sh("true").stdin(&*mystr
).input(&*myvec
).stdout(&*mypathbuf
);
343 let _
= sh("true").stdin(mystr
).input(myvec
).stdout(mypathbuf
);
345 // Unfortunately, this one doesn't work with our Into<Vec<u8>> bound on input().
346 // TODO: Is it worth having these impls for &Vec in other cases?
347 // let _ = sh("true").stdin(&mystr).input(&myvec).stdout(&mypathbuf);
351 fn test_capture_both() {
352 // Windows compatible, no space before ">", and we trim newlines at the end to avoid
353 // dealing with the different kinds.
354 let output
= sh("echo hi")
355 .then(sh("echo lo>&2"))
360 assert_eq
!("hi", str::from_utf8(&output
.stdout
).unwrap().trim());
361 assert_eq
!("lo", str::from_utf8(&output
.stderr
).unwrap().trim());
366 // This test checks the interaction of `dir` and relative exe paths.
367 // Make sure that's actually what we're testing.
368 let pwd_path
= path_to_exe("pwd");
369 assert
!(pwd_path
.is_relative());
371 let pwd
= cmd
!(pwd_path
);
373 // First assert that ordinary commands happen in the parent's dir.
374 let pwd_output
= pwd
.read().unwrap();
375 let pwd_path
= Path
::new(&pwd_output
);
376 assert_eq
!(pwd_path
, env
::current_dir().unwrap());
378 // Now create a temp dir and make sure we can set dir to it. This
379 // also tests the interaction of `dir` and relative exe paths.
380 let dir
= TempDir
::new("duct_test").unwrap();
381 let pwd_output
= pwd
.dir(dir
.path()).read().unwrap();
382 let pwd_path
= Path
::new(&pwd_output
);
383 // pwd_path isn't totally canonical on Windows, because it
384 // doesn't have a prefix. Thus we have to canonicalize both
385 // sides. (This also handles symlinks in TMP_DIR.)
386 assert_eq
!(pwd_path
.canonicalize().unwrap(),
387 dir
.path().canonicalize().unwrap());
392 let output
= cmd
!(path_to_exe("print_env"), "foo").env("foo", "bar").read().unwrap();
393 assert_eq
!("bar", output
);
398 let var_name
= "test_env_remove_var";
400 // Capture the parent env, and make sure it does *not* contain our variable.
401 let mut clean_env
: HashMap
<OsString
, OsString
> = env
::vars_os().collect();
402 clean_env
.remove(AsRef
::<OsStr
>::as_ref(var_name
));
404 // Run a child process with that map passed to full_env(). It should be guaranteed not to
405 // see our variable, regardless of any outer env() calls or changes in the parent.
406 let clean_child
= cmd
!(path_to_exe("print_env"), var_name
).full_env(clean_env
);
408 // Dirty the parent env. Should be suppressed.
409 env
::set_var(var_name
, "junk1");
410 // And make an outer env() call. Should also be suppressed.
411 let dirty_child
= clean_child
.env(var_name
, "junk2");
413 // Check that neither of those have any effect.
414 let output
= dirty_child
.read().unwrap();
415 assert_eq
!("", output
);
419 fn test_broken_pipe() {
420 // If the input writing thread fills up its pipe buffer, writing will block. If the process
421 // on the other end of the pipe exits while writer is waiting, the write will return an
422 // error. We need to swallow that error, rather than returning it.
423 let myvec
= vec
![0; 1_000_000];
424 true_cmd().input(myvec
).run().unwrap();
429 // A silly test, purely for coverage.
430 ::IoValue
::Null
.try_clone().unwrap();
434 fn test_path_sanitization() {
435 // We don't do any chdir'ing in this process, because the tests runner is multithreaded,
436 // and we don't want to screw up anyone else's relative paths. Instead, we shell out to a
437 // small test process that does that for us.
438 cmd
!(path_to_exe("exe_in_dir"), path_to_exe("status"), "0").run().unwrap();