From 5990573ccdb8e5c967c41514b7f0f6a064cca136 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 14 Jan 2019 16:12:39 +0000 Subject: [PATCH] accounting: fix layout of stats - fixes #2910 This fixes several things wrong with the layout of the stats. Transfers which haven't started are printed in the same format as those which have so the stats with `--progress` don't show horrible artifacts. Checkers and transfers now get a ": checkers" and ": transfers" label on the end of the stats line. Transfers will have the transfer stats when the transfer has started instead of this. There was a bug in the routine which shortened the file names (it always produces strings 1 too long). This is now fixed with a test. The formatting string was wrong with a fixed width of 45 - this is now replaces with the value of `--stats-file-name-length`. This also meant that there were unecessary leading spaces in the file names. So the default `--stats-file-name-length` was raised to 45 from 40. --- fs/accounting/accounting.go | 39 ++++++++++++++++----------- fs/accounting/accounting_test.go | 45 ++++++++++++++++++++++++++++++++ fs/accounting/stats.go | 4 +-- fs/accounting/stringset.go | 13 +++++++-- fs/config.go | 2 +- 5 files changed, 83 insertions(+), 20 deletions(-) diff --git a/fs/accounting/accounting.go b/fs/accounting/accounting.go index ef12c286e..adad7f280 100644 --- a/fs/accounting/accounting.go +++ b/fs/accounting/accounting.go @@ -6,6 +6,7 @@ import ( "io" "sync" "time" + "unicode/utf8" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/asyncreader" @@ -243,6 +244,24 @@ func (acc *Account) eta() (etaDuration time.Duration, ok bool) { return eta(acc.bytes, acc.size, acc.avg) } +// shortenName shortens in to size runes long +// If size <= 0 then in is left untouched +func shortenName(in string, size int) string { + if size <= 0 { + return in + } + if utf8.RuneCountInString(in) <= size { + return in + } + name := []rune(in) + size-- // don't count elipsis rune + suffixLength := size / 2 + prefixLength := size - suffixLength + suffixStart := len(name) - suffixLength + name = append(append(name[:prefixLength], '…'), name[suffixStart:]...) + return string(name) +} + // String produces stats for this file func (acc *Account) String() string { a, b := acc.progress() @@ -257,16 +276,6 @@ func (acc *Account) String() string { } } - name := []rune(acc.name) - if fs.Config.StatsFileNameLength > 0 { - if len(name) > fs.Config.StatsFileNameLength { - suffixLength := fs.Config.StatsFileNameLength / 2 - prefixLength := fs.Config.StatsFileNameLength - suffixLength - suffixStart := len(name) - suffixLength - name = append(append(name[:prefixLength], '…'), name[suffixStart:]...) - } - } - if fs.Config.DataRateUnit == "bits" { cur = cur * 8 } @@ -276,11 +285,11 @@ func (acc *Account) String() string { percentageDone = int(100 * float64(a) / float64(b)) } - done := fmt.Sprintf("%2d%% /%s", percentageDone, fs.SizeSuffix(b)) - - return fmt.Sprintf("%45s: %s, %s/s, %s", - string(name), - done, + return fmt.Sprintf("%*s:%3d%% /%s, %s/s, %s", + fs.Config.StatsFileNameLength, + shortenName(acc.name, fs.Config.StatsFileNameLength), + percentageDone, + fs.SizeSuffix(b), fs.SizeSuffix(cur), etas, ) diff --git a/fs/accounting/accounting_test.go b/fs/accounting/accounting_test.go index b197a4a7d..0767745f7 100644 --- a/fs/accounting/accounting_test.go +++ b/fs/accounting/accounting_test.go @@ -2,10 +2,12 @@ package accounting import ( "bytes" + "fmt" "io" "io/ioutil" "strings" "testing" + "unicode/utf8" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/asyncreader" @@ -208,3 +210,46 @@ func TestAccountMaxTransfer(t *testing.T) { assert.Equal(t, ErrorMaxTransferLimitReached, err) assert.True(t, fserrors.IsFatalError(err)) } + +func TestShortenName(t *testing.T) { + for _, test := range []struct { + in string + size int + want string + }{ + {"", 0, ""}, + {"abcde", 10, "abcde"}, + {"abcde", 0, "abcde"}, + {"abcde", -1, "abcde"}, + {"abcde", 5, "abcde"}, + {"abcde", 4, "ab…e"}, + {"abcde", 3, "a…e"}, + {"abcde", 2, "a…"}, + {"abcde", 1, "…"}, + {"abcdef", 6, "abcdef"}, + {"abcdef", 5, "ab…ef"}, + {"abcdef", 4, "ab…f"}, + {"abcdef", 3, "a…f"}, + {"abcdef", 2, "a…"}, + {"áßcdèf", 1, "…"}, + {"áßcdè", 5, "áßcdè"}, + {"áßcdè", 4, "áß…è"}, + {"áßcdè", 3, "á…è"}, + {"áßcdè", 2, "á…"}, + {"áßcdè", 1, "…"}, + {"áßcdèł", 6, "áßcdèł"}, + {"áßcdèł", 5, "áß…èł"}, + {"áßcdèł", 4, "áß…ł"}, + {"áßcdèł", 3, "á…ł"}, + {"áßcdèł", 2, "á…"}, + {"áßcdèł", 1, "…"}, + } { + t.Run(fmt.Sprintf("in=%q, size=%d", test.in, test.size), func(t *testing.T) { + got := shortenName(test.in, test.size) + assert.Equal(t, test.want, got) + if test.size > 0 { + assert.True(t, utf8.RuneCountInString(got) <= test.size, "too big") + } + }) + } +} diff --git a/fs/accounting/stats.go b/fs/accounting/stats.go index 54c8597d4..56010fbeb 100644 --- a/fs/accounting/stats.go +++ b/fs/accounting/stats.go @@ -92,8 +92,8 @@ type StatsInfo struct { // NewStats cretates an initialised StatsInfo func NewStats() *StatsInfo { return &StatsInfo{ - checking: newStringSet(fs.Config.Checkers), - transferring: newStringSet(fs.Config.Transfers), + checking: newStringSet(fs.Config.Checkers, "checking"), + transferring: newStringSet(fs.Config.Transfers, "transferring"), start: time.Now(), inProgress: newInProgress(), } diff --git a/fs/accounting/stringset.go b/fs/accounting/stringset.go index e65be6011..71744035d 100644 --- a/fs/accounting/stringset.go +++ b/fs/accounting/stringset.go @@ -1,21 +1,26 @@ package accounting import ( + "fmt" "sort" "strings" "sync" + + "github.com/ncw/rclone/fs" ) // stringSet holds a set of strings type stringSet struct { mu sync.RWMutex items map[string]struct{} + name string } // newStringSet creates a new empty string set of capacity size -func newStringSet(size int) *stringSet { +func newStringSet(size int, name string) *stringSet { return &stringSet{ items: make(map[string]struct{}, size), + name: name, } } @@ -57,7 +62,11 @@ func (ss *stringSet) Strings() []string { if acc := Stats.inProgress.get(name); acc != nil { out = acc.String() } else { - out = name + out = fmt.Sprintf("%*s: %s", + fs.Config.StatsFileNameLength, + shortenName(name, fs.Config.StatsFileNameLength), + ss.name, + ) } strings = append(strings, " * "+out) } diff --git a/fs/config.go b/fs/config.go index 3499a9fec..53e6835d1 100644 --- a/fs/config.go +++ b/fs/config.go @@ -110,7 +110,7 @@ func NewConfig() *ConfigInfo { c.BufferSize = SizeSuffix(16 << 20) c.UserAgent = "rclone/" + Version c.StreamingUploadCutoff = SizeSuffix(100 * 1024) - c.StatsFileNameLength = 40 + c.StatsFileNameLength = 45 c.AskPassword = true c.TPSLimitBurst = 1 c.MaxTransfer = -1