• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![allow(dead_code)]
2 
3 use csv::Reader;
4 
5 use std::env;
6 use std::io::{self, Read, Write};
7 use std::path::PathBuf;
8 use std::process::{self, Command};
9 
10 static STRANGE: &'static str = include_str!("../examples/data/strange.csv");
11 static USPOP: &'static str = include_str!("../examples/data/uspop.csv");
12 static USPOP_NULL: &'static str =
13     include_str!("../examples/data/uspop-null.csv");
14 static USPOP_LATIN1: &'static [u8] =
15     include_bytes!("../examples/data/uspop-latin1.csv");
16 static WORLDPOP: &'static str =
17     include_str!("../examples/data/bench/worldcitiespop.csv");
18 static SMALLPOP: &'static str = include_str!("../examples/data/smallpop.csv");
19 static SMALLPOP_COLON: &'static str =
20     include_str!("../examples/data/smallpop-colon.csv");
21 static SMALLPOP_NO_HEADERS: &'static str =
22     include_str!("../examples/data/smallpop-no-headers.csv");
23 
24 #[test]
cookbook_read_basic()25 fn cookbook_read_basic() {
26     let mut cmd = cmd_for_example("cookbook-read-basic");
27     let out = cmd_output_with(&mut cmd, SMALLPOP.as_bytes());
28     assert_eq!(out.stdout().lines().count(), 10);
29 }
30 
31 #[test]
cookbook_read_serde()32 fn cookbook_read_serde() {
33     let mut cmd = cmd_for_example("cookbook-read-serde");
34     let out = cmd_output_with(&mut cmd, SMALLPOP.as_bytes());
35     assert_eq!(out.stdout().lines().count(), 10);
36 }
37 
38 #[test]
cookbook_read_colon()39 fn cookbook_read_colon() {
40     let mut cmd = cmd_for_example("cookbook-read-colon");
41     let out = cmd_output_with(&mut cmd, SMALLPOP_COLON.as_bytes());
42     assert_eq!(out.stdout().lines().count(), 10);
43 }
44 
45 #[test]
cookbook_read_no_headers()46 fn cookbook_read_no_headers() {
47     let mut cmd = cmd_for_example("cookbook-read-no-headers");
48     let out = cmd_output_with(&mut cmd, SMALLPOP_NO_HEADERS.as_bytes());
49     assert_eq!(out.stdout().lines().count(), 10);
50 }
51 
52 #[test]
cookbook_write_basic()53 fn cookbook_write_basic() {
54     let mut cmd = cmd_for_example("cookbook-write-basic");
55     let out = cmd_output(&mut cmd);
56     assert_eq!(out.stdout().lines().count(), 3);
57 }
58 
59 #[test]
cookbook_write_serde()60 fn cookbook_write_serde() {
61     let mut cmd = cmd_for_example("cookbook-write-serde");
62     let out = cmd_output(&mut cmd);
63     assert_eq!(out.stdout().lines().count(), 3);
64 }
65 
66 #[test]
tutorial_setup_01()67 fn tutorial_setup_01() {
68     let mut cmd = cmd_for_example("tutorial-setup-01");
69     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
70     assert_eq!(out.stdout().lines().count(), 100);
71 }
72 
73 #[test]
tutorial_error_01()74 fn tutorial_error_01() {
75     let mut cmd = cmd_for_example("tutorial-error-01");
76     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
77     assert_eq!(out.stdout().lines().count(), 100);
78 }
79 
80 #[test]
tutorial_error_01_errored()81 fn tutorial_error_01_errored() {
82     let data = "\
83 header1,header2
84 foo,bar
85 quux,baz,foobar
86 ";
87     let mut cmd = cmd_for_example("tutorial-error-01");
88     let out = cmd_output_with(&mut cmd, data.as_bytes());
89     assert!(out.stderr().contains("thread 'main' panicked"));
90 }
91 
92 #[test]
tutorial_error_02()93 fn tutorial_error_02() {
94     let mut cmd = cmd_for_example("tutorial-error-02");
95     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
96     assert_eq!(out.stdout().lines().count(), 100);
97 }
98 
99 #[test]
tutorial_error_02_errored()100 fn tutorial_error_02_errored() {
101     let data = "\
102 header1,header2
103 foo,bar
104 quux,baz,foobar
105 ";
106     let mut cmd = cmd_for_example("tutorial-error-02");
107     let out = cmd_output_with(&mut cmd, data.as_bytes());
108     assert!(out.stdout_failed().contains("error reading CSV from <stdin>"));
109 }
110 
111 #[test]
tutorial_error_03()112 fn tutorial_error_03() {
113     let mut cmd = cmd_for_example("tutorial-error-03");
114     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
115     assert_eq!(out.stdout().lines().count(), 100);
116 }
117 
118 #[test]
tutorial_error_03_errored()119 fn tutorial_error_03_errored() {
120     let data = "\
121 header1,header2
122 foo,bar
123 quux,baz,foobar
124 ";
125     let mut cmd = cmd_for_example("tutorial-error-03");
126     let out = cmd_output_with(&mut cmd, data.as_bytes());
127     assert!(out.stdout_failed().contains("CSV error:"));
128 }
129 
130 #[test]
tutorial_error_04()131 fn tutorial_error_04() {
132     let mut cmd = cmd_for_example("tutorial-error-04");
133     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
134     assert_eq!(out.stdout().lines().count(), 100);
135 }
136 
137 #[test]
tutorial_error_04_errored()138 fn tutorial_error_04_errored() {
139     let data = "\
140 header1,header2
141 foo,bar
142 quux,baz,foobar
143 ";
144     let mut cmd = cmd_for_example("tutorial-error-04");
145     let out = cmd_output_with(&mut cmd, data.as_bytes());
146     assert!(out.stdout_failed().contains("CSV error:"));
147 }
148 
149 #[test]
tutorial_read_01()150 fn tutorial_read_01() {
151     let mut cmd = cmd_for_example("tutorial-read-01");
152     cmd.arg(data_dir().join("uspop.csv"));
153     let out = cmd_output(&mut cmd);
154     assert_eq!(out.stdout().lines().count(), 100);
155 }
156 
157 #[test]
tutorial_read_headers_01()158 fn tutorial_read_headers_01() {
159     let mut cmd = cmd_for_example("tutorial-read-headers-01");
160     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
161     assert_eq!(out.stdout().lines().count(), 101);
162 }
163 
164 #[test]
tutorial_read_headers_02()165 fn tutorial_read_headers_02() {
166     let mut cmd = cmd_for_example("tutorial-read-headers-02");
167     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
168     assert_eq!(out.stdout().lines().count(), 102);
169 }
170 
171 #[test]
tutorial_read_delimiter_01()172 fn tutorial_read_delimiter_01() {
173     let mut cmd = cmd_for_example("tutorial-read-delimiter-01");
174     let out = cmd_output_with(&mut cmd, STRANGE.as_bytes());
175     assert_eq!(out.stdout().lines().count(), 6);
176 }
177 
178 #[test]
tutorial_read_serde_01()179 fn tutorial_read_serde_01() {
180     let mut cmd = cmd_for_example("tutorial-read-serde-01");
181     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
182     assert_eq!(out.stdout().lines().count(), 100);
183     assert!(out.stdout().lines().all(|x| x.contains("pop:")));
184 }
185 
186 #[test]
tutorial_read_serde_02()187 fn tutorial_read_serde_02() {
188     let mut cmd = cmd_for_example("tutorial-read-serde-02");
189     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
190     assert_eq!(out.stdout().lines().count(), 100);
191     assert!(out.stdout().lines().all(|x| x.starts_with("(")));
192 }
193 
194 #[test]
tutorial_read_serde_03()195 fn tutorial_read_serde_03() {
196     let mut cmd = cmd_for_example("tutorial-read-serde-03");
197     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
198     assert_eq!(out.stdout().lines().count(), 100);
199     assert!(out.stdout().lines().all(|x| x.contains("\"City\":")));
200 }
201 
202 #[test]
tutorial_read_serde_04()203 fn tutorial_read_serde_04() {
204     let mut cmd = cmd_for_example("tutorial-read-serde-04");
205     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
206     assert_eq!(out.stdout().lines().count(), 100);
207     assert!(out.stdout().lines().all(|x| x.starts_with("Record { latitude:")));
208 }
209 
210 #[test]
tutorial_read_serde_05_invalid()211 fn tutorial_read_serde_05_invalid() {
212     let mut cmd = cmd_for_example("tutorial-read-serde-invalid-01");
213     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
214     assert_eq!(out.stdout().lines().count(), 100);
215     assert!(out.stdout().lines().all(|x| x.starts_with("Record { latitude:")));
216 }
217 
218 #[test]
tutorial_read_serde_05_invalid_errored()219 fn tutorial_read_serde_05_invalid_errored() {
220     let mut cmd = cmd_for_example("tutorial-read-serde-invalid-01");
221     let out = cmd_output_with(&mut cmd, USPOP_NULL.as_bytes());
222     assert!(out.stdout_failed().contains("CSV deserialize error:"));
223 }
224 
225 #[test]
tutorial_read_serde_invalid_06()226 fn tutorial_read_serde_invalid_06() {
227     let mut cmd = cmd_for_example("tutorial-read-serde-invalid-02");
228     let out = cmd_output_with(&mut cmd, USPOP_NULL.as_bytes());
229     assert_eq!(out.stdout().lines().count(), 100);
230     assert!(out.stdout().lines().all(|x| x.starts_with("Record { latitude:")));
231 }
232 
233 #[test]
tutorial_write_01()234 fn tutorial_write_01() {
235     let mut cmd = cmd_for_example("tutorial-write-01");
236     let out = cmd_output(&mut cmd);
237     assert_eq!(out.stdout().lines().count(), 4);
238 }
239 
240 #[test]
tutorial_write_delimiter_01()241 fn tutorial_write_delimiter_01() {
242     let mut cmd = cmd_for_example("tutorial-write-delimiter-01");
243     let out = cmd_output(&mut cmd);
244     assert_eq!(out.stdout().lines().count(), 4);
245     assert!(out.stdout().lines().all(|x| x.contains('\t')));
246 }
247 
248 #[test]
tutorial_write_serde_01()249 fn tutorial_write_serde_01() {
250     let mut cmd = cmd_for_example("tutorial-write-serde-01");
251     let out = cmd_output(&mut cmd);
252     assert_eq!(out.stdout().lines().count(), 4);
253 }
254 
255 #[test]
tutorial_write_serde_02()256 fn tutorial_write_serde_02() {
257     let mut cmd = cmd_for_example("tutorial-write-serde-02");
258     let out = cmd_output(&mut cmd);
259     assert_eq!(out.stdout().lines().count(), 4);
260 }
261 
262 #[test]
tutorial_pipeline_search_01()263 fn tutorial_pipeline_search_01() {
264     let mut cmd = cmd_for_example("tutorial-pipeline-search-01");
265     cmd.arg("MA");
266     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
267     assert_eq!(out.stdout().lines().count(), 2);
268 }
269 
270 #[test]
tutorial_pipeline_search_01_errored()271 fn tutorial_pipeline_search_01_errored() {
272     let mut cmd = cmd_for_example("tutorial-pipeline-search-01");
273     cmd.arg("MA");
274     let out = cmd_output_with(&mut cmd, USPOP_LATIN1);
275     assert!(out.stdout_failed().contains("invalid utf-8"));
276 }
277 
278 #[test]
tutorial_pipeline_search_02()279 fn tutorial_pipeline_search_02() {
280     let mut cmd = cmd_for_example("tutorial-pipeline-search-02");
281     cmd.arg("MA");
282     let out = cmd_output_with(&mut cmd, USPOP_LATIN1);
283     assert_eq!(out.stdout().lines().count(), 2);
284 }
285 
286 #[test]
tutorial_pipeline_pop_01()287 fn tutorial_pipeline_pop_01() {
288     let mut cmd = cmd_for_example("tutorial-pipeline-pop-01");
289     cmd.arg("100000");
290     let out = cmd_output_with(&mut cmd, USPOP.as_bytes());
291     assert_eq!(out.stdout().lines().count(), 4);
292 }
293 
294 #[test]
tutorial_perf_alloc_01()295 fn tutorial_perf_alloc_01() {
296     let mut cmd = cmd_for_example("tutorial-perf-alloc-01");
297     let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes());
298     assert_eq!(out.stdout(), "11\n");
299 }
300 
301 #[test]
tutorial_perf_alloc_02()302 fn tutorial_perf_alloc_02() {
303     let mut cmd = cmd_for_example("tutorial-perf-alloc-02");
304     let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes());
305     assert_eq!(out.stdout(), "11\n");
306 }
307 
308 #[test]
tutorial_perf_alloc_03()309 fn tutorial_perf_alloc_03() {
310     let mut cmd = cmd_for_example("tutorial-perf-alloc-03");
311     let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes());
312     assert_eq!(out.stdout(), "11\n");
313 }
314 
315 #[test]
tutorial_perf_serde_01()316 fn tutorial_perf_serde_01() {
317     let mut cmd = cmd_for_example("tutorial-perf-serde-01");
318     let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes());
319     assert_eq!(out.stdout(), "11\n");
320 }
321 
322 #[test]
tutorial_perf_serde_02()323 fn tutorial_perf_serde_02() {
324     let mut cmd = cmd_for_example("tutorial-perf-serde-02");
325     let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes());
326     assert_eq!(out.stdout(), "11\n");
327 }
328 
329 #[test]
tutorial_perf_serde_03()330 fn tutorial_perf_serde_03() {
331     let mut cmd = cmd_for_example("tutorial-perf-serde-03");
332     let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes());
333     assert_eq!(out.stdout(), "11\n");
334 }
335 
336 #[test]
tutorial_perf_core_01()337 fn tutorial_perf_core_01() {
338     let mut cmd = cmd_for_example("tutorial-perf-core-01");
339     let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes());
340     assert_eq!(out.stdout(), "11\n");
341 }
342 
343 #[test]
no_infinite_loop_on_io_errors()344 fn no_infinite_loop_on_io_errors() {
345     struct FailingRead;
346     impl Read for FailingRead {
347         fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
348             Err(io::Error::new(io::ErrorKind::Other, "Broken reader"))
349         }
350     }
351 
352     let mut record_results = Reader::from_reader(FailingRead).into_records();
353     let first_result = record_results.next();
354     assert!(
355         matches!(&first_result, Some(Err(e)) if matches!(e.kind(), csv::ErrorKind::Io(_)))
356     );
357     assert!(record_results.next().is_none());
358 }
359 
360 // Helper functions follow.
361 
362 /// Return the target/debug directory path.
debug_dir() -> PathBuf363 fn debug_dir() -> PathBuf {
364     env::current_exe()
365         .expect("test binary path")
366         .parent()
367         .expect("test binary directory")
368         .parent()
369         .expect("example binary directory")
370         .to_path_buf()
371 }
372 
373 /// Return the directory containing the example test binaries.
example_bin_dir() -> PathBuf374 fn example_bin_dir() -> PathBuf {
375     debug_dir().join("examples")
376 }
377 
378 /// Return the repo root directory path.
repo_dir() -> PathBuf379 fn repo_dir() -> PathBuf {
380     PathBuf::from(env!("CARGO_MANIFEST_DIR"))
381 }
382 
383 /// Return the directory containing the example data.
data_dir() -> PathBuf384 fn data_dir() -> PathBuf {
385     repo_dir().join("examples").join("data")
386 }
387 
388 /// Return a command ready to execute the given example test binary.
389 ///
390 /// The command's current directory is set to the repo root.
cmd_for_example(name: &str) -> Command391 fn cmd_for_example(name: &str) -> Command {
392     let mut cmd = Command::new(example_bin_dir().join(name));
393     cmd.current_dir(repo_dir());
394     cmd
395 }
396 
397 /// Return the (stdout, stderr) of running the command as a string.
398 ///
399 /// If the command has a non-zero exit code, then this function panics.
cmd_output(cmd: &mut Command) -> Output400 fn cmd_output(cmd: &mut Command) -> Output {
401     cmd.stdout(process::Stdio::piped());
402     cmd.stderr(process::Stdio::piped());
403     let child = cmd.spawn().expect("command spawns successfully");
404     Output::new(cmd, child)
405 }
406 
407 /// Like cmd_output, but sends the given data as stdin to the given child.
cmd_output_with(cmd: &mut Command, data: &[u8]) -> Output408 fn cmd_output_with(cmd: &mut Command, data: &[u8]) -> Output {
409     cmd.stdin(process::Stdio::piped());
410     cmd.stdout(process::Stdio::piped());
411     cmd.stderr(process::Stdio::piped());
412     let mut child = cmd.spawn().expect("command spawns successfully");
413     {
414         let stdin = child.stdin.as_mut().expect("failed to get stdin");
415         stdin.write_all(data).expect("failed to write to stdin");
416     }
417     Output::new(cmd, child)
418 }
419 
420 struct Output {
421     stdout: String,
422     stderr: String,
423     command: String,
424     status: process::ExitStatus,
425 }
426 
427 impl Output {
428     /// Return the (stdout, stderr) of running the given child as a string.
429     ///
430     /// If the command has a non-zero exit code, then this function panics.
new(cmd: &mut Command, child: process::Child) -> Output431     fn new(cmd: &mut Command, child: process::Child) -> Output {
432         let out = child.wait_with_output().expect("command runs successfully");
433         let stdout =
434             String::from_utf8(out.stdout).expect("valid utf-8 (stdout)");
435         let stderr =
436             String::from_utf8(out.stderr).expect("valid utf-8 (stderr)");
437         Output {
438             stdout: stdout,
439             stderr: stderr,
440             command: format!("{:?}", cmd),
441             status: out.status,
442         }
443     }
444 
stdout(&self) -> &str445     fn stdout(&self) -> &str {
446         if !self.status.success() {
447             panic!(
448                 "\n\n==== {:?} ====\n\
449                  command failed but expected success!\
450                  \n\ncwd: {}\
451                  \n\nstatus: {}\
452                  \n\nstdout: {}\
453                  \n\nstderr: {}\
454                  \n\n=====\n",
455                 self.command,
456                 repo_dir().display(),
457                 self.status,
458                 self.stdout,
459                 self.stderr
460             );
461         }
462         &self.stdout
463     }
464 
stdout_failed(&self) -> &str465     fn stdout_failed(&self) -> &str {
466         if self.status.success() {
467             panic!(
468                 "\n\n==== {:?} ====\n\
469                  command succeeded but expected failure!\
470                  \n\ncwd: {}\
471                  \n\nstatus: {}\
472                  \n\nstdout: {}\
473                  \n\nstderr: {}\
474                  \n\n=====\n",
475                 self.command,
476                 repo_dir().display(),
477                 self.status,
478                 self.stdout,
479                 self.stderr
480             );
481         }
482         &self.stdout
483     }
484 
stderr(&self) -> &str485     fn stderr(&self) -> &str {
486         if self.status.success() {
487             panic!(
488                 "\n\n==== {:?} ====\n\
489                  command succeeded but expected failure!\
490                  \n\ncwd: {}\
491                  \n\nstatus: {}\
492                  \n\nstdout: {}\
493                  \n\nstderr: {}\
494                  \n\n=====\n",
495                 self.command,
496                 repo_dir().display(),
497                 self.status,
498                 self.stdout,
499                 self.stderr
500             );
501         }
502         &self.stderr
503     }
504 }
505 
506 /// Consume the reader given into a string.
read_to_string<R: io::Read>(mut rdr: R) -> String507 fn read_to_string<R: io::Read>(mut rdr: R) -> String {
508     let mut s = String::new();
509     rdr.read_to_string(&mut s).unwrap();
510     s
511 }
512