From 9c49b78593aecf52973b3128d550dffa44d933f2 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Sat, 15 Oct 2022 19:31:42 +0200 Subject: [PATCH] added unit test + docs for the various sort modes. --- TODO.md | 4 -- cmd/root.go | 2 + cmd/tablizer.go | 13 ++++++ lib/common.go | 8 ++-- lib/helpers.go | 13 ++++++ lib/parser_test.go | 2 +- lib/printer_test.go | 111 +++++++++++++++++++++++++++++++++++++++++++- lib/sort.go | 8 ++-- tablizer.1 | 15 +++++- tablizer.pod | 22 ++++++++- 10 files changed, 183 insertions(+), 15 deletions(-) diff --git a/TODO.md b/TODO.md index 82a78c4..d5bebae 100644 --- a/TODO.md +++ b/TODO.md @@ -2,10 +2,6 @@ ## Features to be implemented -- sorting by: numerical, time, duration, string(default) [DONE] - -- add unit tests for the above - - add output modes yaml and csv - add --no-headers option diff --git a/cmd/root.go b/cmd/root.go index c60c4d8..68fff44 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -65,6 +65,8 @@ var rootCmd = &cobra.Command{ return err } + lib.PrepareSortFlags() + return lib.ProcessFiles(args) }, } diff --git a/cmd/tablizer.go b/cmd/tablizer.go index 706594a..db0fbea 100644 --- a/cmd/tablizer.go +++ b/cmd/tablizer.go @@ -21,8 +21,11 @@ SYNOPSIS -M, --markdown Enable markdown table output -O, --orgtbl Enable org-mode table output -s, --separator string Custom field separator + -a, --sort-age sort according to age (duration) string -k, --sort-by int Sort by column (default: 1) -D, --sort-desc Sort in descending order (default: ascending) + -i, --sort-numeric sort according to string numerical value + -t, --sort-time sort according to time string -v, --version Print program version DESCRIPTION @@ -81,6 +84,16 @@ DESCRIPTION (as in GNU sort(1)). The default sort column is the first one. To disable sorting at all, supply 0 (Zero) to -k. The default sort order is ascending. You can change this to descending order using the option -D. + The default sort order is by string, but there are other sort modes: + + -a --sort-age + Sorts duration strings like "1d4h32m51s". + + -i --sort-numeric + Sorts numeric fields. + + -t --sort-time + Sorts timestamps. Finally the -d option enables debugging output which is mostly useful for the developer. diff --git a/lib/common.go b/lib/common.go index e765ca2..9af63ed 100644 --- a/lib/common.go +++ b/lib/common.go @@ -78,9 +78,11 @@ var ( // sorting SortByColumn int SortDescending bool - SortNumeric bool - SortTime bool - SortAge bool + + SortNumeric bool + SortTime bool + SortAge bool + SortMode string ) // contains a whole parsed table diff --git a/lib/helpers.go b/lib/helpers.go index 1283292..89150cf 100644 --- a/lib/helpers.go +++ b/lib/helpers.go @@ -174,6 +174,19 @@ func PrepareModeFlags() error { return nil } +func PrepareSortFlags() { + switch { + case SortNumeric: + SortMode = "numeric" + case SortAge: + SortMode = "duration" + case SortTime: + SortMode = "time" + default: + SortMode = "string" + } +} + func trimRow(row []string) []string { // FIXME: remove this when we only use Tablewriter and strip in ParseFile()! var fixedrow []string diff --git a/lib/parser_test.go b/lib/parser_test.go index e6d9885..995a725 100644 --- a/lib/parser_test.go +++ b/lib/parser_test.go @@ -92,7 +92,7 @@ asd igig cxxxncnc 19191 EDD 1 X` for _, tt := range tests { - testname := fmt.Sprintf("parse-with-inverted-pattern-%t", tt.invert) + testname := fmt.Sprintf("parse-with-pattern-%s-inverted-%t", tt.pattern, tt.invert) t.Run(testname, func(t *testing.T) { InvertMatch = tt.invert diff --git a/lib/printer_test.go b/lib/printer_test.go index 7506e96..bc3d16d 100644 --- a/lib/printer_test.go +++ b/lib/printer_test.go @@ -201,7 +201,8 @@ abc 345 b1`, origStdout, reader := stdout2pipe(t) for _, tt := range tests { - testname := fmt.Sprintf("print-sorted-table-by-%d-desc-%t", tt.sortby, tt.desc) + testname := fmt.Sprintf("print-sorted-table-by-column-%d-desc-%t", + tt.sortby, tt.desc) t.Run(testname, func(t *testing.T) { SortByColumn = tt.sortby SortDescending = tt.desc @@ -226,3 +227,111 @@ abc 345 b1`, // Restore os.Stdout = origStdout } + +func TestSortByPrinter(t *testing.T) { + data := Tabdata{ + maxwidthHeader: 8, + maxwidthPerCol: []int{ + 5, + 9, + 3, + 26, + }, + columns: 4, + headers: []string{ + "NAME", + "DURATION", + "COUNT", + "WHEN", + }, + entries: [][]string{ + { + "beta", + "1d10h5m1s", + "33", + "3/1/2014", + }, + { + "alpha", + "4h35m", + "170", + "2013-Feb-03", + }, + { + "ceta", + "33d12h", + "9", + "06/Jan/2008 15:04:05 -0700", + }, + }, + } + + var tests = []struct { + sortby string + column int + desc bool + expect string + }{ + { + column: 3, + sortby: "numeric", + desc: false, + expect: `NAME(1) DURATION(2) COUNT(3) WHEN(4) +ceta 33d12h 9 06/Jan/2008 15:04:05 -0700 +beta 1d10h5m1s 33 3/1/2014 +alpha 4h35m 170 2013-Feb-03`, + }, + { + column: 2, + sortby: "duration", + desc: false, + expect: `NAME(1) DURATION(2) COUNT(3) WHEN(4) +alpha 4h35m 170 2013-Feb-03 +beta 1d10h5m1s 33 3/1/2014 +ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`, + }, + { + column: 4, + sortby: "time", + desc: false, + expect: `NAME(1) DURATION(2) COUNT(3) WHEN(4) +ceta 33d12h 9 06/Jan/2008 15:04:05 -0700 +alpha 4h35m 170 2013-Feb-03 +beta 1d10h5m1s 33 3/1/2014`, + }, + } + + NoColor = true + OutputMode = "ascii" + origStdout, reader := stdout2pipe(t) + + for _, tt := range tests { + testname := fmt.Sprintf("print-sorted-table-by-column-%d-desc-%t-sort-by-%s", + tt.column, tt.desc, tt.sortby) + + t.Run(testname, func(t *testing.T) { + SortByColumn = tt.column + SortDescending = tt.desc + SortMode = tt.sortby + + testdata := data + printData(&testdata) + + buf := make([]byte, 1024) + n, err := reader.Read(buf) + if err != nil { + t.Fatal(err) + } + buf = buf[:n] + output := strings.TrimSpace(string(buf)) + + if output != tt.expect { + t.Errorf("sort column: %d, sortby: %s, got:\n%s\nwant:\n%s", + tt.column, tt.sortby, output, tt.expect) + } + }) + } + + // Restore + os.Stdout = origStdout +} diff --git a/lib/sort.go b/lib/sort.go index f5a1877..3decfcf 100644 --- a/lib/sort.go +++ b/lib/sort.go @@ -51,8 +51,8 @@ func sortTable(data *Tabdata, col int) { func compare(a string, b string) bool { var comp bool - switch { - case SortNumeric: + switch SortMode { + case "numeric": left, err := strconv.Atoi(a) if err != nil { left = 0 @@ -62,7 +62,7 @@ func compare(a string, b string) bool { right = 0 } comp = left < right - case SortAge: + case "duration": left, err := str2duration.ParseDuration(a) if err != nil { left = 0 @@ -72,7 +72,7 @@ func compare(a string, b string) bool { right = 0 } comp = left.Seconds() < right.Seconds() - case SortTime: + case "time": left, _ := dateparse.ParseAny(a) right, _ := dateparse.ParseAny(b) comp = left.Unix() < right.Unix() diff --git a/tablizer.1 b/tablizer.1 index 7a81ac8..9b491b2 100644 --- a/tablizer.1 +++ b/tablizer.1 @@ -159,8 +159,11 @@ tablizer \- Manipulate tabular output of other programs \& \-M, \-\-markdown Enable markdown table output \& \-O, \-\-orgtbl Enable org\-mode table output \& \-s, \-\-separator string Custom field separator +\& \-a, \-\-sort\-age sort according to age (duration) string \& \-k, \-\-sort\-by int Sort by column (default: 1) \& \-D, \-\-sort\-desc Sort in descending order (default: ascending) +\& \-i, \-\-sort\-numeric sort according to string numerical value +\& \-t, \-\-sort\-time sort according to time string \& \-v, \-\-version Print program version .Ve .SH "DESCRIPTION" @@ -228,7 +231,17 @@ Use the \fB\-k\fR option to specify by which column to sort the tabular data (as in \s-1GNU\s0 \fBsort\fR\|(1)). The default sort column is the first one. To disable sorting at all, supply 0 (Zero) to \-k. The default sort order is ascending. You can change this to descending order using the option -\&\fB\-D\fR. +\&\fB\-D\fR. The default sort order is by string, but there are other sort +modes: +.IP "\fB\-a \-\-sort\-age\fR" 4 +.IX Item "-a --sort-age" +Sorts duration strings like \*(L"1d4h32m51s\*(R". +.IP "\fB\-i \-\-sort\-numeric\fR" 4 +.IX Item "-i --sort-numeric" +Sorts numeric fields. +.IP "\fB\-t \-\-sort\-time\fR" 4 +.IX Item "-t --sort-time" +Sorts timestamps. .PP Finally the \fB\-d\fR option enables debugging output which is mostly useful for the developer. diff --git a/tablizer.pod b/tablizer.pod index ef63f9c..44aca7a 100644 --- a/tablizer.pod +++ b/tablizer.pod @@ -20,8 +20,11 @@ tablizer - Manipulate tabular output of other programs -M, --markdown Enable markdown table output -O, --orgtbl Enable org-mode table output -s, --separator string Custom field separator + -a, --sort-age sort according to age (duration) string -k, --sort-by int Sort by column (default: 1) -D, --sort-desc Sort in descending order (default: ascending) + -i, --sort-numeric sort according to string numerical value + -t, --sort-time sort according to time string -v, --version Print program version @@ -84,7 +87,24 @@ Use the B<-k> option to specify by which column to sort the tabular data (as in GNU sort(1)). The default sort column is the first one. To disable sorting at all, supply 0 (Zero) to -k. The default sort order is ascending. You can change this to descending order using the option -B<-D>. +B<-D>. The default sort order is by string, but there are other sort +modes: + +=over + +=item B<-a --sort-age> + +Sorts duration strings like "1d4h32m51s". + +=item B<-i --sort-numeric> + +Sorts numeric fields. + +=item B<-t --sort-time> + +Sorts timestamps. + +=back Finally the B<-d> option enables debugging output which is mostly useful for the developer.