From bb40e49e75175e4c71df4390facc51261b1ebc60 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Mon, 1 May 2023 18:13:10 +0200 Subject: [PATCH 1/3] ui/termstatus: Fix truncation of status output The last line was not truncated as expected --- internal/ui/termstatus/status.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/ui/termstatus/status.go b/internal/ui/termstatus/status.go index 46bfb2868..6939f5e28 100644 --- a/internal/ui/termstatus/status.go +++ b/internal/ui/termstatus/status.go @@ -359,8 +359,9 @@ func (t *Terminal) SetStatus(lines []string) { line = Truncate(line, width-2) } if i < len(lines)-1 { // Last line gets no line break. - lines[i] = line + "\n" + line += "\n" } + lines[i] = line } select { From 6d10c655a05824cfc3837fda034758c23776d701 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Mon, 1 May 2023 18:21:08 +0200 Subject: [PATCH 2/3] termstatus: test status line sanitization --- changelog/unreleased/pull-4318 | 8 ++++++++ internal/ui/termstatus/status.go | 27 ++++++++++++++++----------- internal/ui/termstatus/status_test.go | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 changelog/unreleased/pull-4318 diff --git a/changelog/unreleased/pull-4318 b/changelog/unreleased/pull-4318 new file mode 100644 index 000000000..198b972d3 --- /dev/null +++ b/changelog/unreleased/pull-4318 @@ -0,0 +1,8 @@ +Bugfix: Correctly clean up status bar output of the `backup` command + +Due to a regression in restic 0.15.2, the status bar of the `backup` command +could leave some output behind. This happened if filenames were printed that +are wider than the current terminal width. This has been fixed. + +https://github.com/restic/restic/issues/4319 +https://github.com/restic/restic/pull/4318 diff --git a/internal/ui/termstatus/status.go b/internal/ui/termstatus/status.go index 6939f5e28..5b310ec80 100644 --- a/internal/ui/termstatus/status.go +++ b/internal/ui/termstatus/status.go @@ -334,6 +334,21 @@ func wideRune(s string) (wide bool, utfsize uint) { return wide, uint(size) } +func sanitizeLines(lines []string, width int) []string { + // Sanitize lines and truncate them if they're too long. + for i, line := range lines { + line = Quote(line) + if width > 0 { + line = Truncate(line, width-2) + } + if i < len(lines)-1 { // Last line gets no line break. + line += "\n" + } + lines[i] = line + } + return lines +} + // SetStatus updates the status lines. // The lines should not contain newlines; this method adds them. func (t *Terminal) SetStatus(lines []string) { @@ -352,17 +367,7 @@ func (t *Terminal) SetStatus(lines []string) { } } - // Sanitize lines and truncate them if they're too long. - for i, line := range lines { - line = Quote(line) - if width > 0 { - line = Truncate(line, width-2) - } - if i < len(lines)-1 { // Last line gets no line break. - line += "\n" - } - lines[i] = line - } + sanitizeLines(lines, width) select { case t.status <- status{lines: lines}: diff --git a/internal/ui/termstatus/status_test.go b/internal/ui/termstatus/status_test.go index 9f5e15cb1..94eb734a7 100644 --- a/internal/ui/termstatus/status_test.go +++ b/internal/ui/termstatus/status_test.go @@ -1,6 +1,7 @@ package termstatus import ( + "reflect" "strconv" "testing" @@ -91,3 +92,26 @@ func BenchmarkTruncateUnicode(b *testing.B) { benchmarkTruncate(b, s, w-1) } + +func TestSanitizeLines(t *testing.T) { + var tests = []struct { + input []string + width int + output []string + }{ + {[]string{""}, 80, []string{""}}, + {[]string{"too long test line"}, 10, []string{"too long"}}, + {[]string{"too long test line", "text"}, 10, []string{"too long\n", "text"}}, + {[]string{"too long test line", "second long test line"}, 10, []string{"too long\n", "second l"}}, + } + + for _, test := range tests { + t.Run("", func(t *testing.T) { + out := sanitizeLines(test.input, test.width) + if !reflect.DeepEqual(out, test.output) { + t.Fatalf("wrong output for input %v, width %d: want %q, got %q", + test.input, test.width, test.output, out) + } + }) + } +} From 65c5e511a16bbcfc1e744ea7e05fbf72bbff1018 Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Fri, 5 May 2023 11:10:02 +0200 Subject: [PATCH 3/3] ui/termstatus: Add test for Terminal.SetStatus --- internal/ui/termstatus/status_test.go | 50 +++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/internal/ui/termstatus/status_test.go b/internal/ui/termstatus/status_test.go index 94eb734a7..b59063076 100644 --- a/internal/ui/termstatus/status_test.go +++ b/internal/ui/termstatus/status_test.go @@ -1,13 +1,54 @@ package termstatus import ( - "reflect" + "bytes" + "context" + "fmt" + "io" "strconv" "testing" rtest "github.com/restic/restic/internal/test" ) +func TestSetStatus(t *testing.T) { + var buf bytes.Buffer + term := New(&buf, io.Discard, false) + + term.canUpdateStatus = true + term.fd = ^uintptr(0) + term.clearCurrentLine = posixClearCurrentLine + term.moveCursorUp = posixMoveCursorUp + + ctx, cancel := context.WithCancel(context.Background()) + go term.Run(ctx) + + const ( + clear = posixControlClearLine + home = posixControlMoveCursorHome + up = posixControlMoveCursorUp + ) + + term.SetStatus([]string{"first"}) + exp := home + clear + "first" + home + + term.SetStatus([]string{"foo", "bar", "baz"}) + exp += home + clear + "foo\n" + home + clear + "bar\n" + + home + clear + "baz" + home + up + up + + term.SetStatus([]string{"quux", "needs\nquote"}) + exp += home + clear + "quux\n" + + home + clear + "\"needs\\nquote\"\n" + + home + clear + home + up + up // Third line implicit. + + cancel() + exp += home + clear + "\n" + home + clear + "\n" + + home + up + up // Status cleared. + + <-term.closed + rtest.Equals(t, exp, buf.String()) +} + func TestQuote(t *testing.T) { for _, c := range []struct { in string @@ -106,12 +147,9 @@ func TestSanitizeLines(t *testing.T) { } for _, test := range tests { - t.Run("", func(t *testing.T) { + t.Run(fmt.Sprintf("%s %d", test.input, test.width), func(t *testing.T) { out := sanitizeLines(test.input, test.width) - if !reflect.DeepEqual(out, test.output) { - t.Fatalf("wrong output for input %v, width %d: want %q, got %q", - test.input, test.width, test.output, out) - } + rtest.Equals(t, test.output, out) }) } }