| #![allow(dead_code)] |
| |
| use csv::Reader; |
| |
| use std::env; |
| use std::io::{self, Read, Write}; |
| use std::path::PathBuf; |
| use std::process::{self, Command}; |
| |
| static STRANGE: &'static str = include_str!("../examples/data/strange.csv"); |
| static USPOP: &'static str = include_str!("../examples/data/uspop.csv"); |
| static USPOP_NULL: &'static str = |
| include_str!("../examples/data/uspop-null.csv"); |
| static USPOP_LATIN1: &'static [u8] = |
| include_bytes!("../examples/data/uspop-latin1.csv"); |
| static WORLDPOP: &'static str = |
| include_str!("../examples/data/bench/worldcitiespop.csv"); |
| static SMALLPOP: &'static str = include_str!("../examples/data/smallpop.csv"); |
| static SMALLPOP_COLON: &'static str = |
| include_str!("../examples/data/smallpop-colon.csv"); |
| static SMALLPOP_NO_HEADERS: &'static str = |
| include_str!("../examples/data/smallpop-no-headers.csv"); |
| |
| #[test] |
| fn cookbook_read_basic() { |
| let mut cmd = cmd_for_example("cookbook-read-basic"); |
| let out = cmd_output_with(&mut cmd, SMALLPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 10); |
| } |
| |
| #[test] |
| fn cookbook_read_serde() { |
| let mut cmd = cmd_for_example("cookbook-read-serde"); |
| let out = cmd_output_with(&mut cmd, SMALLPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 10); |
| } |
| |
| #[test] |
| fn cookbook_read_colon() { |
| let mut cmd = cmd_for_example("cookbook-read-colon"); |
| let out = cmd_output_with(&mut cmd, SMALLPOP_COLON.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 10); |
| } |
| |
| #[test] |
| fn cookbook_read_no_headers() { |
| let mut cmd = cmd_for_example("cookbook-read-no-headers"); |
| let out = cmd_output_with(&mut cmd, SMALLPOP_NO_HEADERS.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 10); |
| } |
| |
| #[test] |
| fn cookbook_write_basic() { |
| let mut cmd = cmd_for_example("cookbook-write-basic"); |
| let out = cmd_output(&mut cmd); |
| assert_eq!(out.stdout().lines().count(), 3); |
| } |
| |
| #[test] |
| fn cookbook_write_serde() { |
| let mut cmd = cmd_for_example("cookbook-write-serde"); |
| let out = cmd_output(&mut cmd); |
| assert_eq!(out.stdout().lines().count(), 3); |
| } |
| |
| #[test] |
| fn tutorial_setup_01() { |
| let mut cmd = cmd_for_example("tutorial-setup-01"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| } |
| |
| #[test] |
| fn tutorial_error_01() { |
| let mut cmd = cmd_for_example("tutorial-error-01"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| } |
| |
| #[test] |
| fn tutorial_error_01_errored() { |
| let data = "\ |
| header1,header2 |
| foo,bar |
| quux,baz,foobar |
| "; |
| let mut cmd = cmd_for_example("tutorial-error-01"); |
| let out = cmd_output_with(&mut cmd, data.as_bytes()); |
| assert!(out.stderr().contains("thread 'main' panicked")); |
| } |
| |
| #[test] |
| fn tutorial_error_02() { |
| let mut cmd = cmd_for_example("tutorial-error-02"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| } |
| |
| #[test] |
| fn tutorial_error_02_errored() { |
| let data = "\ |
| header1,header2 |
| foo,bar |
| quux,baz,foobar |
| "; |
| let mut cmd = cmd_for_example("tutorial-error-02"); |
| let out = cmd_output_with(&mut cmd, data.as_bytes()); |
| assert!(out.stdout_failed().contains("error reading CSV from <stdin>")); |
| } |
| |
| #[test] |
| fn tutorial_error_03() { |
| let mut cmd = cmd_for_example("tutorial-error-03"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| } |
| |
| #[test] |
| fn tutorial_error_03_errored() { |
| let data = "\ |
| header1,header2 |
| foo,bar |
| quux,baz,foobar |
| "; |
| let mut cmd = cmd_for_example("tutorial-error-03"); |
| let out = cmd_output_with(&mut cmd, data.as_bytes()); |
| assert!(out.stdout_failed().contains("CSV error:")); |
| } |
| |
| #[test] |
| fn tutorial_error_04() { |
| let mut cmd = cmd_for_example("tutorial-error-04"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| } |
| |
| #[test] |
| fn tutorial_error_04_errored() { |
| let data = "\ |
| header1,header2 |
| foo,bar |
| quux,baz,foobar |
| "; |
| let mut cmd = cmd_for_example("tutorial-error-04"); |
| let out = cmd_output_with(&mut cmd, data.as_bytes()); |
| assert!(out.stdout_failed().contains("CSV error:")); |
| } |
| |
| #[test] |
| fn tutorial_read_01() { |
| let mut cmd = cmd_for_example("tutorial-read-01"); |
| cmd.arg(data_dir().join("uspop.csv")); |
| let out = cmd_output(&mut cmd); |
| assert_eq!(out.stdout().lines().count(), 100); |
| } |
| |
| #[test] |
| fn tutorial_read_headers_01() { |
| let mut cmd = cmd_for_example("tutorial-read-headers-01"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 101); |
| } |
| |
| #[test] |
| fn tutorial_read_headers_02() { |
| let mut cmd = cmd_for_example("tutorial-read-headers-02"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 102); |
| } |
| |
| #[test] |
| fn tutorial_read_delimiter_01() { |
| let mut cmd = cmd_for_example("tutorial-read-delimiter-01"); |
| let out = cmd_output_with(&mut cmd, STRANGE.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 6); |
| } |
| |
| #[test] |
| fn tutorial_read_serde_01() { |
| let mut cmd = cmd_for_example("tutorial-read-serde-01"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| assert!(out.stdout().lines().all(|x| x.contains("pop:"))); |
| } |
| |
| #[test] |
| fn tutorial_read_serde_02() { |
| let mut cmd = cmd_for_example("tutorial-read-serde-02"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| assert!(out.stdout().lines().all(|x| x.starts_with("("))); |
| } |
| |
| #[test] |
| fn tutorial_read_serde_03() { |
| let mut cmd = cmd_for_example("tutorial-read-serde-03"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| assert!(out.stdout().lines().all(|x| x.contains("\"City\":"))); |
| } |
| |
| #[test] |
| fn tutorial_read_serde_04() { |
| let mut cmd = cmd_for_example("tutorial-read-serde-04"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| assert!(out.stdout().lines().all(|x| x.starts_with("Record { latitude:"))); |
| } |
| |
| #[test] |
| fn tutorial_read_serde_05_invalid() { |
| let mut cmd = cmd_for_example("tutorial-read-serde-invalid-01"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| assert!(out.stdout().lines().all(|x| x.starts_with("Record { latitude:"))); |
| } |
| |
| #[test] |
| fn tutorial_read_serde_05_invalid_errored() { |
| let mut cmd = cmd_for_example("tutorial-read-serde-invalid-01"); |
| let out = cmd_output_with(&mut cmd, USPOP_NULL.as_bytes()); |
| assert!(out.stdout_failed().contains("CSV deserialize error:")); |
| } |
| |
| #[test] |
| fn tutorial_read_serde_invalid_06() { |
| let mut cmd = cmd_for_example("tutorial-read-serde-invalid-02"); |
| let out = cmd_output_with(&mut cmd, USPOP_NULL.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 100); |
| assert!(out.stdout().lines().all(|x| x.starts_with("Record { latitude:"))); |
| } |
| |
| #[test] |
| fn tutorial_write_01() { |
| let mut cmd = cmd_for_example("tutorial-write-01"); |
| let out = cmd_output(&mut cmd); |
| assert_eq!(out.stdout().lines().count(), 4); |
| } |
| |
| #[test] |
| fn tutorial_write_delimiter_01() { |
| let mut cmd = cmd_for_example("tutorial-write-delimiter-01"); |
| let out = cmd_output(&mut cmd); |
| assert_eq!(out.stdout().lines().count(), 4); |
| assert!(out.stdout().lines().all(|x| x.contains('\t'))); |
| } |
| |
| #[test] |
| fn tutorial_write_serde_01() { |
| let mut cmd = cmd_for_example("tutorial-write-serde-01"); |
| let out = cmd_output(&mut cmd); |
| assert_eq!(out.stdout().lines().count(), 4); |
| } |
| |
| #[test] |
| fn tutorial_write_serde_02() { |
| let mut cmd = cmd_for_example("tutorial-write-serde-02"); |
| let out = cmd_output(&mut cmd); |
| assert_eq!(out.stdout().lines().count(), 4); |
| } |
| |
| #[test] |
| fn tutorial_pipeline_search_01() { |
| let mut cmd = cmd_for_example("tutorial-pipeline-search-01"); |
| cmd.arg("MA"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 2); |
| } |
| |
| #[test] |
| fn tutorial_pipeline_search_01_errored() { |
| let mut cmd = cmd_for_example("tutorial-pipeline-search-01"); |
| cmd.arg("MA"); |
| let out = cmd_output_with(&mut cmd, USPOP_LATIN1); |
| assert!(out.stdout_failed().contains("invalid utf-8")); |
| } |
| |
| #[test] |
| fn tutorial_pipeline_search_02() { |
| let mut cmd = cmd_for_example("tutorial-pipeline-search-02"); |
| cmd.arg("MA"); |
| let out = cmd_output_with(&mut cmd, USPOP_LATIN1); |
| assert_eq!(out.stdout().lines().count(), 2); |
| } |
| |
| #[test] |
| fn tutorial_pipeline_pop_01() { |
| let mut cmd = cmd_for_example("tutorial-pipeline-pop-01"); |
| cmd.arg("100000"); |
| let out = cmd_output_with(&mut cmd, USPOP.as_bytes()); |
| assert_eq!(out.stdout().lines().count(), 4); |
| } |
| |
| #[test] |
| fn tutorial_perf_alloc_01() { |
| let mut cmd = cmd_for_example("tutorial-perf-alloc-01"); |
| let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes()); |
| assert_eq!(out.stdout(), "11\n"); |
| } |
| |
| #[test] |
| fn tutorial_perf_alloc_02() { |
| let mut cmd = cmd_for_example("tutorial-perf-alloc-02"); |
| let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes()); |
| assert_eq!(out.stdout(), "11\n"); |
| } |
| |
| #[test] |
| fn tutorial_perf_alloc_03() { |
| let mut cmd = cmd_for_example("tutorial-perf-alloc-03"); |
| let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes()); |
| assert_eq!(out.stdout(), "11\n"); |
| } |
| |
| #[test] |
| fn tutorial_perf_serde_01() { |
| let mut cmd = cmd_for_example("tutorial-perf-serde-01"); |
| let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes()); |
| assert_eq!(out.stdout(), "11\n"); |
| } |
| |
| #[test] |
| fn tutorial_perf_serde_02() { |
| let mut cmd = cmd_for_example("tutorial-perf-serde-02"); |
| let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes()); |
| assert_eq!(out.stdout(), "11\n"); |
| } |
| |
| #[test] |
| fn tutorial_perf_serde_03() { |
| let mut cmd = cmd_for_example("tutorial-perf-serde-03"); |
| let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes()); |
| assert_eq!(out.stdout(), "11\n"); |
| } |
| |
| #[test] |
| fn tutorial_perf_core_01() { |
| let mut cmd = cmd_for_example("tutorial-perf-core-01"); |
| let out = cmd_output_with(&mut cmd, WORLDPOP.as_bytes()); |
| assert_eq!(out.stdout(), "11\n"); |
| } |
| |
| #[test] |
| fn no_infinite_loop_on_io_errors() { |
| struct FailingRead; |
| impl Read for FailingRead { |
| fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { |
| Err(io::Error::new(io::ErrorKind::Other, "Broken reader")) |
| } |
| } |
| |
| let mut record_results = Reader::from_reader(FailingRead).into_records(); |
| let first_result = record_results.next(); |
| assert!( |
| matches!(&first_result, Some(Err(e)) if matches!(e.kind(), csv::ErrorKind::Io(_))) |
| ); |
| assert!(record_results.next().is_none()); |
| } |
| |
| // Helper functions follow. |
| |
| /// Return the target/debug directory path. |
| fn debug_dir() -> PathBuf { |
| env::current_exe() |
| .expect("test binary path") |
| .parent() |
| .expect("test binary directory") |
| .parent() |
| .expect("example binary directory") |
| .to_path_buf() |
| } |
| |
| /// Return the directory containing the example test binaries. |
| fn example_bin_dir() -> PathBuf { |
| debug_dir().join("examples") |
| } |
| |
| /// Return the repo root directory path. |
| fn repo_dir() -> PathBuf { |
| PathBuf::from(env!("CARGO_MANIFEST_DIR")) |
| } |
| |
| /// Return the directory containing the example data. |
| fn data_dir() -> PathBuf { |
| repo_dir().join("examples").join("data") |
| } |
| |
| /// Return a command ready to execute the given example test binary. |
| /// |
| /// The command's current directory is set to the repo root. |
| fn cmd_for_example(name: &str) -> Command { |
| let mut cmd = Command::new(example_bin_dir().join(name)); |
| cmd.current_dir(repo_dir()); |
| cmd |
| } |
| |
| /// Return the (stdout, stderr) of running the command as a string. |
| /// |
| /// If the command has a non-zero exit code, then this function panics. |
| fn cmd_output(cmd: &mut Command) -> Output { |
| cmd.stdout(process::Stdio::piped()); |
| cmd.stderr(process::Stdio::piped()); |
| let child = cmd.spawn().expect("command spawns successfully"); |
| Output::new(cmd, child) |
| } |
| |
| /// Like cmd_output, but sends the given data as stdin to the given child. |
| fn cmd_output_with(cmd: &mut Command, data: &[u8]) -> Output { |
| cmd.stdin(process::Stdio::piped()); |
| cmd.stdout(process::Stdio::piped()); |
| cmd.stderr(process::Stdio::piped()); |
| let mut child = cmd.spawn().expect("command spawns successfully"); |
| { |
| let stdin = child.stdin.as_mut().expect("failed to get stdin"); |
| stdin.write_all(data).expect("failed to write to stdin"); |
| } |
| Output::new(cmd, child) |
| } |
| |
| struct Output { |
| stdout: String, |
| stderr: String, |
| command: String, |
| status: process::ExitStatus, |
| } |
| |
| impl Output { |
| /// Return the (stdout, stderr) of running the given child as a string. |
| /// |
| /// If the command has a non-zero exit code, then this function panics. |
| fn new(cmd: &mut Command, child: process::Child) -> Output { |
| let out = child.wait_with_output().expect("command runs successfully"); |
| let stdout = |
| String::from_utf8(out.stdout).expect("valid utf-8 (stdout)"); |
| let stderr = |
| String::from_utf8(out.stderr).expect("valid utf-8 (stderr)"); |
| Output { |
| stdout: stdout, |
| stderr: stderr, |
| command: format!("{:?}", cmd), |
| status: out.status, |
| } |
| } |
| |
| fn stdout(&self) -> &str { |
| if !self.status.success() { |
| panic!( |
| "\n\n==== {:?} ====\n\ |
| command failed but expected success!\ |
| \n\ncwd: {}\ |
| \n\nstatus: {}\ |
| \n\nstdout: {}\ |
| \n\nstderr: {}\ |
| \n\n=====\n", |
| self.command, |
| repo_dir().display(), |
| self.status, |
| self.stdout, |
| self.stderr |
| ); |
| } |
| &self.stdout |
| } |
| |
| fn stdout_failed(&self) -> &str { |
| if self.status.success() { |
| panic!( |
| "\n\n==== {:?} ====\n\ |
| command succeeded but expected failure!\ |
| \n\ncwd: {}\ |
| \n\nstatus: {}\ |
| \n\nstdout: {}\ |
| \n\nstderr: {}\ |
| \n\n=====\n", |
| self.command, |
| repo_dir().display(), |
| self.status, |
| self.stdout, |
| self.stderr |
| ); |
| } |
| &self.stdout |
| } |
| |
| fn stderr(&self) -> &str { |
| if self.status.success() { |
| panic!( |
| "\n\n==== {:?} ====\n\ |
| command succeeded but expected failure!\ |
| \n\ncwd: {}\ |
| \n\nstatus: {}\ |
| \n\nstdout: {}\ |
| \n\nstderr: {}\ |
| \n\n=====\n", |
| self.command, |
| repo_dir().display(), |
| self.status, |
| self.stdout, |
| self.stderr |
| ); |
| } |
| &self.stderr |
| } |
| } |
| |
| /// Consume the reader given into a string. |
| fn read_to_string<R: io::Read>(mut rdr: R) -> String { |
| let mut s = String::new(); |
| rdr.read_to_string(&mut s).unwrap(); |
| s |
| } |