Merge pull request #4318 from MichaelEischer/status-output-truncation

Fix status output truncation
This commit is contained in:
Michael Eischer 2023-05-05 23:09:59 +02:00 committed by GitHub
commit db046c0acc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 10 deletions

View file

@ -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

View file

@ -334,6 +334,21 @@ func wideRune(s string) (wide bool, utfsize uint) {
return wide, uint(size) 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. // SetStatus updates the status lines.
// The lines should not contain newlines; this method adds them. // The lines should not contain newlines; this method adds them.
func (t *Terminal) SetStatus(lines []string) { func (t *Terminal) SetStatus(lines []string) {
@ -352,16 +367,7 @@ func (t *Terminal) SetStatus(lines []string) {
} }
} }
// Sanitize lines and truncate them if they're too long. sanitizeLines(lines, width)
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.
lines[i] = line + "\n"
}
}
select { select {
case t.status <- status{lines: lines}: case t.status <- status{lines: lines}:

View file

@ -1,12 +1,54 @@
package termstatus package termstatus
import ( import (
"bytes"
"context"
"fmt"
"io"
"strconv" "strconv"
"testing" "testing"
rtest "github.com/restic/restic/internal/test" 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) { func TestQuote(t *testing.T) {
for _, c := range []struct { for _, c := range []struct {
in string in string
@ -91,3 +133,23 @@ func BenchmarkTruncateUnicode(b *testing.B) {
benchmarkTruncate(b, s, w-1) 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(fmt.Sprintf("%s %d", test.input, test.width), func(t *testing.T) {
out := sanitizeLines(test.input, test.width)
rtest.Equals(t, test.output, out)
})
}
}