From 676e3718c312f42e2194d515aa580ddb38f07dae Mon Sep 17 00:00:00 2001 From: "Chirag B. Jadwani" Date: Mon, 29 Feb 2016 11:07:16 +0530 Subject: [PATCH 1/3] uniq: add tests --- Makefile | 1 + tests/fixtures/uniq/skip-1-char.expected | 9 ++ tests/fixtures/uniq/skip-2-fields.expected | 2 + .../uniq/skip-3-check-2-chars.expected | 6 ++ .../uniq/skip-3-check-5-chars.expected | 7 ++ tests/fixtures/uniq/skip-5-chars.expected | 5 + tests/fixtures/uniq/skip-chars.txt | 12 +++ tests/fixtures/uniq/skip-fields.txt | 8 ++ .../uniq/sorted-all-repeated-prepend.expected | 22 +++++ .../sorted-all-repeated-separate.expected | 21 ++++ .../uniq/sorted-all-repeated.expected | 16 ++++ tests/fixtures/uniq/sorted-counts.expected | 8 ++ .../fixtures/uniq/sorted-ignore-case.expected | 7 ++ .../uniq/sorted-repeated-only.expected | 6 ++ tests/fixtures/uniq/sorted-simple.expected | 8 ++ .../fixtures/uniq/sorted-unique-only.expected | 2 + tests/fixtures/uniq/sorted.txt | 18 ++++ tests/uniq.rs | 95 +++++++++++++++++++ 18 files changed, 253 insertions(+) create mode 100644 tests/fixtures/uniq/skip-1-char.expected create mode 100644 tests/fixtures/uniq/skip-2-fields.expected create mode 100644 tests/fixtures/uniq/skip-3-check-2-chars.expected create mode 100644 tests/fixtures/uniq/skip-3-check-5-chars.expected create mode 100644 tests/fixtures/uniq/skip-5-chars.expected create mode 100644 tests/fixtures/uniq/skip-chars.txt create mode 100644 tests/fixtures/uniq/skip-fields.txt create mode 100644 tests/fixtures/uniq/sorted-all-repeated-prepend.expected create mode 100644 tests/fixtures/uniq/sorted-all-repeated-separate.expected create mode 100644 tests/fixtures/uniq/sorted-all-repeated.expected create mode 100644 tests/fixtures/uniq/sorted-counts.expected create mode 100644 tests/fixtures/uniq/sorted-ignore-case.expected create mode 100644 tests/fixtures/uniq/sorted-repeated-only.expected create mode 100644 tests/fixtures/uniq/sorted-simple.expected create mode 100644 tests/fixtures/uniq/sorted-unique-only.expected create mode 100644 tests/fixtures/uniq/sorted.txt create mode 100644 tests/uniq.rs 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/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")); +} From fc5b798ff183ca6c565a608b3c361c2c25e1361b Mon Sep 17 00:00:00 2001 From: "Chirag B. Jadwani" Date: Mon, 29 Feb 2016 11:10:38 +0530 Subject: [PATCH 2/3] uniq: fix skip & check characters logic --- src/uniq/uniq.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/uniq/uniq.rs b/src/uniq/uniq.rs index f0e51e872..1ffd12a8a 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; @@ -84,16 +83,9 @@ impl Uniq { fn cmp_key(&self, line: &str) -> String { let len = line.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() + line.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, From d674a3bb638c63eecaf3216450b7c4a24d6d24ae Mon Sep 17 00:00:00 2001 From: "Chirag B. Jadwani" Date: Mon, 29 Feb 2016 11:16:58 +0530 Subject: [PATCH 3/3] uniq: minor refactoring in skip_fields --- src/uniq/uniq.rs | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/uniq/uniq.rs b/src/uniq/uniq.rs index 1ffd12a8a..0c1200a25 100644 --- a/src/uniq/uniq.rs +++ b/src/uniq/uniq.rs @@ -44,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); @@ -58,32 +58,33 @@ 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 { - line.chars() + fields_to_check.chars() .skip(self.slice_start.unwrap_or(0)) .take(self.slice_stop.unwrap_or(len)) .map(|c| match c { @@ -91,7 +92,7 @@ impl Uniq { _ => c, }).collect() } else { - line.to_owned() + fields_to_check.to_owned() } }