diff --git a/Makefile b/Makefile index f418606b6..19bc50449 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,7 @@ TEST_PROGS := \ truncate \ tsort \ unexpand \ + uniq \ unlink \ wc diff --git a/src/uniq/uniq.rs b/src/uniq/uniq.rs index f0e51e872..0c1200a25 100644 --- a/src/uniq/uniq.rs +++ b/src/uniq/uniq.rs @@ -16,7 +16,6 @@ extern crate getopts; extern crate uucore; use getopts::{Matches, Options}; -use std::cmp::min; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write}; use std::path::Path; @@ -45,7 +44,7 @@ impl Uniq { for io_line in reader.lines() { let line = crash_if_err!(1, io_line); - if !lines.is_empty() && self.cmp_key(&self.skip_fields(&lines[0])) != self.cmp_key(&self.skip_fields(&line)) { + if !lines.is_empty() && self.cmp_key(&lines[0]) != self.cmp_key(&line) { let print_delimiter = delimiters == "prepend" || (delimiters == "separate" && first_line_printed); first_line_printed |= self.print_lines(writer, &lines, print_delimiter); lines.truncate(0); @@ -59,47 +58,41 @@ impl Uniq { } fn skip_fields(&self, line: &str) -> String { - match self.skip_fields { - Some(skip_fields) => - if line.split_whitespace().count() > skip_fields { - let mut field = 0; - let mut i = 0; - while field < skip_fields && i < line.len() { - while i < line.len() && line.chars().nth(i).unwrap().is_whitespace() { - i = i + 1; - } - while i < line.len() && !line.chars().nth(i).unwrap().is_whitespace() { - i = i + 1; - } - field = field + 1; + if let Some(skip_fields) = self.skip_fields { + if line.split_whitespace().count() > skip_fields { + let mut field = 0; + let mut i = 0; + while field < skip_fields && i < line.len() { + while i < line.len() && line.chars().nth(i).unwrap().is_whitespace() { + i = i + 1; } - line[i..].to_owned() - } else { - "".to_owned() - }, - None => line[..].to_owned() + while i < line.len() && !line.chars().nth(i).unwrap().is_whitespace() { + i = i + 1; + } + field = field + 1; + } + line[i..].to_owned() + } else { + "".to_owned() + } + } else { + line[..].to_owned() } } fn cmp_key(&self, line: &str) -> String { - let len = line.len(); + let fields_to_check = &self.skip_fields(line); + let len = fields_to_check.len(); if len > 0 { - let slice_start = match self.slice_start { - Some(i) => min(i, len - 1), - None => 0 - }; - let slice_stop = match self.slice_stop { - Some(i) => min(slice_start + i, len), - None => len - }; - - line[slice_start..slice_stop].chars() + fields_to_check.chars() + .skip(self.slice_start.unwrap_or(0)) + .take(self.slice_stop.unwrap_or(len)) .map(|c| match c { 'a' ... 'z' if self.ignore_case => ((c as u8) - 32) as char, _ => c, }).collect() } else { - line.to_owned() + fields_to_check.to_owned() } } diff --git a/tests/fixtures/uniq/skip-1-char.expected b/tests/fixtures/uniq/skip-1-char.expected new file mode 100644 index 000000000..447c37cb3 --- /dev/null +++ b/tests/fixtures/uniq/skip-1-char.expected @@ -0,0 +1,9 @@ +aaaaa 1 +Xbbbb 2 +XXbbb 2 +ccccc 3 +Xdddd 3 +XXddd 3 +XXXdd 1 +eeee +fff diff --git a/tests/fixtures/uniq/skip-2-fields.expected b/tests/fixtures/uniq/skip-2-fields.expected new file mode 100644 index 000000000..8cffc2ebc --- /dev/null +++ b/tests/fixtures/uniq/skip-2-fields.expected @@ -0,0 +1,2 @@ + aaa aa a +aa a diff --git a/tests/fixtures/uniq/skip-3-check-2-chars.expected b/tests/fixtures/uniq/skip-3-check-2-chars.expected new file mode 100644 index 000000000..d2559b58d --- /dev/null +++ b/tests/fixtures/uniq/skip-3-check-2-chars.expected @@ -0,0 +1,6 @@ +aaaaa 1 +Xbbbb 2 +ccccc 3 +Xdddd 3 +eeee +fff diff --git a/tests/fixtures/uniq/skip-3-check-5-chars.expected b/tests/fixtures/uniq/skip-3-check-5-chars.expected new file mode 100644 index 000000000..29edbbd89 --- /dev/null +++ b/tests/fixtures/uniq/skip-3-check-5-chars.expected @@ -0,0 +1,7 @@ +aaaaa 1 +Xbbbb 2 +ccccc 3 +Xdddd 3 +XXXdd 1 +eeee +fff diff --git a/tests/fixtures/uniq/skip-5-chars.expected b/tests/fixtures/uniq/skip-5-chars.expected new file mode 100644 index 000000000..d0515b2c0 --- /dev/null +++ b/tests/fixtures/uniq/skip-5-chars.expected @@ -0,0 +1,5 @@ +aaaaa 1 +Xbbbb 2 +ccccc 3 +XXXdd 1 +eeee diff --git a/tests/fixtures/uniq/skip-chars.txt b/tests/fixtures/uniq/skip-chars.txt new file mode 100644 index 000000000..5f903ccf4 --- /dev/null +++ b/tests/fixtures/uniq/skip-chars.txt @@ -0,0 +1,12 @@ +aaaaa 1 +Xbbbb 2 +XXbbb 2 +ccccc 3 +Xcccc 3 +ccccc 3 +Xdddd 3 +XXddd 3 +XXXdd 1 +XXXdd 1 +eeee +fff diff --git a/tests/fixtures/uniq/skip-fields.txt b/tests/fixtures/uniq/skip-fields.txt new file mode 100644 index 000000000..f84e377a0 --- /dev/null +++ b/tests/fixtures/uniq/skip-fields.txt @@ -0,0 +1,8 @@ + aaa aa a + ZZZ aa a +ZZZ aa a + ZZZ bb a + ZZZ bb a +aa a +a + diff --git a/tests/fixtures/uniq/sorted-all-repeated-prepend.expected b/tests/fixtures/uniq/sorted-all-repeated-prepend.expected new file mode 100644 index 000000000..6aaa660c7 --- /dev/null +++ b/tests/fixtures/uniq/sorted-all-repeated-prepend.expected @@ -0,0 +1,22 @@ + + bbbbb ⅱ + bbbbb ⅱ + + ccccc ⅲ + ccccc ⅲ + ccccc ⅲ + + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + + fffff ⅲ + fffff ⅲ + + ggggg ⅲ + ggggg ⅲ + ggggg ⅲ + + GGGGG ⅲ + GGGGG ⅲ diff --git a/tests/fixtures/uniq/sorted-all-repeated-separate.expected b/tests/fixtures/uniq/sorted-all-repeated-separate.expected new file mode 100644 index 000000000..93d74ca04 --- /dev/null +++ b/tests/fixtures/uniq/sorted-all-repeated-separate.expected @@ -0,0 +1,21 @@ + bbbbb ⅱ + bbbbb ⅱ + + ccccc ⅲ + ccccc ⅲ + ccccc ⅲ + + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + + fffff ⅲ + fffff ⅲ + + ggggg ⅲ + ggggg ⅲ + ggggg ⅲ + + GGGGG ⅲ + GGGGG ⅲ diff --git a/tests/fixtures/uniq/sorted-all-repeated.expected b/tests/fixtures/uniq/sorted-all-repeated.expected new file mode 100644 index 000000000..2be1b8a31 --- /dev/null +++ b/tests/fixtures/uniq/sorted-all-repeated.expected @@ -0,0 +1,16 @@ + bbbbb ⅱ + bbbbb ⅱ + ccccc ⅲ + ccccc ⅲ + ccccc ⅲ + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + fffff ⅲ + fffff ⅲ + ggggg ⅲ + ggggg ⅲ + ggggg ⅲ + GGGGG ⅲ + GGGGG ⅲ diff --git a/tests/fixtures/uniq/sorted-counts.expected b/tests/fixtures/uniq/sorted-counts.expected new file mode 100644 index 000000000..486ab7c99 --- /dev/null +++ b/tests/fixtures/uniq/sorted-counts.expected @@ -0,0 +1,8 @@ + 1 aaaaa ⅰ + 2 bbbbb ⅱ + 3 ccccc ⅲ + 4 ddddd ⅲ + 1 eeeee ⅲ + 2 fffff ⅲ + 3 ggggg ⅲ + 2 GGGGG ⅲ diff --git a/tests/fixtures/uniq/sorted-ignore-case.expected b/tests/fixtures/uniq/sorted-ignore-case.expected new file mode 100644 index 000000000..6596aaf02 --- /dev/null +++ b/tests/fixtures/uniq/sorted-ignore-case.expected @@ -0,0 +1,7 @@ + aaaaa ⅰ + bbbbb ⅱ + ccccc ⅲ + ddddd ⅲ + eeeee ⅲ + fffff ⅲ + ggggg ⅲ diff --git a/tests/fixtures/uniq/sorted-repeated-only.expected b/tests/fixtures/uniq/sorted-repeated-only.expected new file mode 100644 index 000000000..101a99740 --- /dev/null +++ b/tests/fixtures/uniq/sorted-repeated-only.expected @@ -0,0 +1,6 @@ + bbbbb ⅱ + ccccc ⅲ + ddddd ⅲ + fffff ⅲ + ggggg ⅲ + GGGGG ⅲ diff --git a/tests/fixtures/uniq/sorted-simple.expected b/tests/fixtures/uniq/sorted-simple.expected new file mode 100644 index 000000000..b4e79bbb3 --- /dev/null +++ b/tests/fixtures/uniq/sorted-simple.expected @@ -0,0 +1,8 @@ + aaaaa ⅰ + bbbbb ⅱ + ccccc ⅲ + ddddd ⅲ + eeeee ⅲ + fffff ⅲ + ggggg ⅲ + GGGGG ⅲ diff --git a/tests/fixtures/uniq/sorted-unique-only.expected b/tests/fixtures/uniq/sorted-unique-only.expected new file mode 100644 index 000000000..d3ea8a83b --- /dev/null +++ b/tests/fixtures/uniq/sorted-unique-only.expected @@ -0,0 +1,2 @@ + aaaaa ⅰ + eeeee ⅲ diff --git a/tests/fixtures/uniq/sorted.txt b/tests/fixtures/uniq/sorted.txt new file mode 100644 index 000000000..85435eaa6 --- /dev/null +++ b/tests/fixtures/uniq/sorted.txt @@ -0,0 +1,18 @@ + aaaaa ⅰ + bbbbb ⅱ + bbbbb ⅱ + ccccc ⅲ + ccccc ⅲ + ccccc ⅲ + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + ddddd ⅲ + eeeee ⅲ + fffff ⅲ + fffff ⅲ + ggggg ⅲ + ggggg ⅲ + ggggg ⅲ + GGGGG ⅲ + GGGGG ⅲ diff --git a/tests/uniq.rs b/tests/uniq.rs new file mode 100644 index 000000000..af4f4c93e --- /dev/null +++ b/tests/uniq.rs @@ -0,0 +1,95 @@ +#[macro_use] +mod common; + +use common::util::*; + +static UTIL_NAME: &'static str = "uniq"; + +static INPUT: &'static str = "sorted.txt"; +static SKIP_CHARS: &'static str = "skip-chars.txt"; +static SKIP_FIELDS: &'static str = "skip-fields.txt"; + + +#[test] +fn test_stdin_default() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.run_piped_stdin(at.read(INPUT)); + assert_eq!(result.stdout, at.read("sorted-simple.expected")); +} + +#[test] +fn test_single_default() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.arg(INPUT).run(); + assert_eq!(result.stdout, at.read("sorted-simple.expected")); +} + +#[test] +fn test_stdin_counts() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["-c"]).run_piped_stdin(at.read(INPUT)); + assert_eq!(result.stdout, at.read("sorted-counts.expected")); +} + +#[test] +fn test_stdin_skip_1_char() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["-s1"]).run_piped_stdin(at.read(SKIP_CHARS)); + assert_eq!(result.stdout, at.read("skip-1-char.expected")); +} + +#[test] +fn test_stdin_skip_5_chars() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["-s5"]).run_piped_stdin(at.read(SKIP_CHARS)); + assert_eq!(result.stdout, at.read("skip-5-chars.expected")); +} + +#[test] +fn test_stdin_skip_and_check_2_chars() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["-s3", "-w2"]).run_piped_stdin(at.read(SKIP_CHARS)); + assert_eq!(result.stdout, at.read("skip-3-check-2-chars.expected")); +} + +#[test] +fn test_stdin_skip_1_field() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["-f2"]).run_piped_stdin(at.read(SKIP_FIELDS)); + assert_eq!(result.stdout, at.read("skip-2-fields.expected")); +} + +#[test] +fn test_stdin_all_repeated() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["--all-repeated"]).run_piped_stdin(at.read(INPUT)); + assert_eq!(result.stdout, at.read("sorted-all-repeated.expected")); +} + +#[test] +fn test_stdin_all_repeated_separate() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["--all-repeated", "separate"]).run_piped_stdin(at.read(INPUT)); + assert_eq!(result.stdout, at.read("sorted-all-repeated-separate.expected")); +} + +#[test] +fn test_stdin_all_repeated_prepend() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["--all-repeated", "prepend"]).run_piped_stdin(at.read(INPUT)); + assert_eq!(result.stdout, at.read("sorted-all-repeated-prepend.expected")); +} + +#[test] +fn test_stdin_unique_only() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["-u"]).run_piped_stdin(at.read(INPUT)); + assert_eq!(result.stdout, at.read("sorted-unique-only.expected")); +} + +#[test] +fn test_stdin_repeated_only() { + let (at, mut ucmd) = testing(UTIL_NAME); + let result = ucmd.args(&["-d"]).run_piped_stdin(at.read(INPUT)); + assert_eq!(result.stdout, at.read("sorted-repeated-only.expected")); +}