]> git.proxmox.com Git - rustc.git/blob - src/vendor/duct/src/test.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / vendor / duct / src / test.rs
1 extern crate tempdir;
2 use self::tempdir::TempDir;
3
4 use super::{sh, Expression};
5 use std;
6 use std::collections::HashMap;
7 use std::env;
8 use std::env::consts::EXE_EXTENSION;
9 use std::ffi::{OsStr, OsString};
10 use std::fs::File;
11 use std::io;
12 use std::io::prelude::*;
13 use std::path::{Path, PathBuf};
14 use std::process::Command;
15 use std::str;
16 use std::sync::{Arc, Once, ONCE_INIT};
17
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")
25 .arg("build")
26 .arg("--quiet")
27 .status()
28 .unwrap();
29 assert!(build_status.success(),
30 "Cargo failed to build associated binaries.");
31 });
32
33 Path::new("target").join("debug").join(name).with_extension(EXE_EXTENSION)
34 }
35
36 fn true_cmd() -> Expression {
37 cmd!(path_to_exe("status"), "0")
38 }
39
40 fn false_cmd() -> Expression {
41 cmd!(path_to_exe("status"), "1")
42 }
43
44 #[test]
45 fn test_cmd() {
46 let output = cmd!(path_to_exe("echo"), "hi").read().unwrap();
47 assert_eq!("hi", output);
48 }
49
50 #[test]
51 fn test_sh() {
52 // Windows compatible.
53 let output = sh("echo hi").read().unwrap();
54 assert_eq!("hi", output);
55 }
56
57 #[test]
58 fn test_start() {
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());
65 }
66
67 #[test]
68 fn test_error() {
69 let result = false_cmd().run();
70 if let Err(err) = result {
71 assert_eq!(err.kind(), io::ErrorKind::Other);
72 } else {
73 panic!("Expected a status error.");
74 }
75 }
76
77 #[test]
78 fn test_unchecked() {
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)
84 .stdout_capture()
85 .run()
86 .unwrap();
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());
90 }
91
92 #[test]
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");
97
98 // Right takes precedence over left.
99 let output = one.pipe(two.clone())
100 .unchecked()
101 .run()
102 .unwrap();
103 assert_eq!(2, output.status.code().unwrap());
104
105 // Except that checked on the left takes precedence over unchecked on
106 // the right.
107 let output = one.pipe(two.unchecked())
108 .unchecked()
109 .run()
110 .unwrap();
111 assert_eq!(1, output.status.code().unwrap());
112
113 // Right takes precedence over the left again if they're both unchecked.
114 let output = one.unchecked()
115 .pipe(two.unchecked())
116 .unchecked()
117 .run()
118 .unwrap();
119 assert_eq!(2, output.status.code().unwrap());
120
121 // Except that if the right is a success, the left takes precedence.
122 let output = one.unchecked()
123 .pipe(zero.unchecked())
124 .unchecked()
125 .run()
126 .unwrap();
127 assert_eq!(1, output.status.code().unwrap());
128
129 // Even if the right is checked.
130 let output = one.unchecked()
131 .pipe(zero)
132 .unchecked()
133 .run()
134 .unwrap();
135 assert_eq!(1, output.status.code().unwrap());
136 }
137
138 #[test]
139 fn test_pipe() {
140 let output = sh("echo xxx").pipe(cmd!(path_to_exe("x_to_y"))).read().unwrap();
141 assert_eq!("yyy", output);
142
143 // Check that errors on either side are propagated.
144 let result = true_cmd().pipe(false_cmd()).run();
145 assert!(result.is_err());
146
147 let result = false_cmd().pipe(true_cmd()).run();
148 assert!(result.is_err());
149 }
150
151 #[test]
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())
156 .unchecked()
157 .start()
158 .unwrap();
159 handle.kill().unwrap();
160 handle.wait().unwrap();
161 }
162
163 #[test]
164 fn test_pipe_start() {
165 let nonexistent_cmd = cmd!(path_to_exe("nonexistent!!!"));
166
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());
170
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());
175 }
176
177 #[test]
178 fn test_then() {
179 let output = true_cmd().then(sh("echo lo")).read().unwrap();
180 assert_eq!("lo", output);
181
182 // Check that errors on either side are propagated.
183 let result = true_cmd().then(false_cmd()).run();
184 assert!(result.is_err());
185
186 let result = false_cmd().then(true_cmd()).run();
187 assert!(result.is_err());
188 }
189
190 #[test]
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();
198
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();
203 loop {
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.
207 None => continue,
208 // Getting here without deadlocking is what we're testing for.
209 Some(_) => break,
210 }
211 }
212 }
213
214 #[test]
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()
221 .then(&sleep_cmd)
222 .start()
223 .unwrap();
224 handle.kill().unwrap();
225 handle.wait().unwrap();
226 }
227
228 #[test]
229 fn test_multiple_threads() {
230 // Wait on the sleep command in a background thread, while the main thread
231 // kills it.
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();
238 }
239
240 #[test]
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))
246 .unchecked()
247 .start()
248 .unwrap();
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();
253 }
254
255 #[test]
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
260 // wait.
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)
265 .start()
266 .unwrap();
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();
272 }
273
274 #[test]
275 fn test_input() {
276 let expr = cmd!(path_to_exe("x_to_y")).input("xxx");
277 let output = expr.read().unwrap();
278 assert_eq!("yyy", output);
279 }
280
281 #[test]
282 fn test_stderr() {
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");
288 }
289
290 #[test]
291 fn test_null() {
292 let expr = cmd!(path_to_exe("cat")).stdin_null().stdout_null().stderr_null();
293 let output = expr.read().unwrap();
294 assert_eq!("", output);
295 }
296
297 #[test]
298 fn test_path() {
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);
309 }
310
311 #[test]
312 fn test_swapping() {
313 let output = sh("echo hi")
314 .stdout_to_stderr()
315 .stderr_capture()
316 .run()
317 .unwrap();
318 let stderr = str::from_utf8(&output.stderr).unwrap().trim();
319 assert_eq!("hi", stderr);
320
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);
324 }
325
326 #[test]
327 fn test_file() {
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");
334 }
335
336 #[test]
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);
344
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);
348 }
349
350 #[test]
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"))
356 .stdout_capture()
357 .stderr_capture()
358 .run()
359 .unwrap();
360 assert_eq!("hi", str::from_utf8(&output.stdout).unwrap().trim());
361 assert_eq!("lo", str::from_utf8(&output.stderr).unwrap().trim());
362 }
363
364 #[test]
365 fn test_dir() {
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());
370
371 let pwd = cmd!(pwd_path);
372
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());
377
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());
388 }
389
390 #[test]
391 fn test_env() {
392 let output = cmd!(path_to_exe("print_env"), "foo").env("foo", "bar").read().unwrap();
393 assert_eq!("bar", output);
394 }
395
396 #[test]
397 fn test_full_env() {
398 let var_name = "test_env_remove_var";
399
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));
403
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);
407
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");
412
413 // Check that neither of those have any effect.
414 let output = dirty_child.read().unwrap();
415 assert_eq!("", output);
416 }
417
418 #[test]
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();
425 }
426
427 #[test]
428 fn test_silly() {
429 // A silly test, purely for coverage.
430 ::IoValue::Null.try_clone().unwrap();
431 }
432
433 #[test]
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();
439 }