From 3f9f150736ec5145f58e861ec0f0799e7bbf74c5 Mon Sep 17 00:00:00 2001 From: sneak Date: Tue, 6 Oct 2026 05:37:37 +0000 Subject: [PATCH] Print the byte unit once in the summary line (closes #17) formatBytes already ends in a unit, so the summary line read "9 B bytes" or "1.0 KiB bytes". The format string in Stats.Print no longer adds the word "bytes". Print now takes the io.Writer to print to; its callers pass os.Stderr, and the new test passes a buffer to read the line for one small and one large byte count. Model: opus-5-5 --- TODO.md | 2 ++ attrsum.go | 12 ++++++------ attrsum_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index 134766e..fc59236 100644 --- a/TODO.md +++ b/TODO.md @@ -25,6 +25,8 @@ today), `.dockerignore`, `.gitea/workflows/check.yml` and # Completed Steps +* 2026-10-06: the summary line after `sum`, `check` and `clear` prints + the byte unit once (`1.5 KiB`, not `1.5 KiB bytes`) * 2026-10-06: a path that `--exclude` or `--exclude-dotfiles` excludes is skipped even when it cannot be read, so an excluded directory that cannot be listed no longer fails the run diff --git a/attrsum.go b/attrsum.go index 7ec7499..9c61b13 100644 --- a/attrsum.go +++ b/attrsum.go @@ -71,13 +71,13 @@ func (s *Stats) Duration() time.Duration { return time.Since(s.StartTime) } -func (s *Stats) Print(opts *options, operation string) { +func (s *Stats) Print(w io.Writer, opts *options, operation string) { if opts.quiet { return } - fmt.Fprintf(os.Stderr, - "\n%s complete: %d files processed, %d skipped, %d failed, %s bytes in %s\n", + _, _ = fmt.Fprintf(w, + "\n%s complete: %d files processed, %d skipped, %d failed, %s in %s\n", operation, s.FilesProcessed, s.FilesSkipped, @@ -227,7 +227,7 @@ func runOverPaths( } finishBar(bar) - stats.Print(opts, op) + stats.Print(os.Stderr, opts, op) return nil } @@ -481,7 +481,7 @@ func runCheck(opts *options, args []string, cont bool) error { if perr != nil { if !cont { finishBar(bar) - stats.Print(opts, "check") + stats.Print(os.Stderr, opts, "check") return perr } @@ -491,7 +491,7 @@ func runCheck(opts *options, args []string, cont bool) error { } finishBar(bar) - stats.Print(opts, "check") + stats.Print(os.Stderr, opts, "check") return finalErr } diff --git a/attrsum_test.go b/attrsum_test.go index c1e5db0..3020121 100644 --- a/attrsum_test.go +++ b/attrsum_test.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "errors" "os" "path/filepath" @@ -376,3 +377,27 @@ func TestCheckContinuePastUnreadable(t *testing.T) { t.Fatalf("expected verification error from runCheck, got %v", err) } } + +func TestSummaryPrintsByteUnitOnce(t *testing.T) { + t.Parallel() + + tests := []struct { + bytesProcessed int64 + want string + }{ + {9, ", 9 B in "}, + {1536, ", 1.5 KiB in "}, + } + + for _, tt := range tests { + var out bytes.Buffer + + stats := newTestStats() + stats.BytesProcessed = tt.bytesProcessed + stats.Print(&out, &options{}, "check") + + if !strings.Contains(out.String(), tt.want) { + t.Errorf("summary %q does not contain %q", out.String(), tt.want) + } + } +} -- 2.54.0