mirror of
https://github.com/rclone/rclone
synced 2024-11-16 16:15:34 +01:00
fs/accounting: factor out eta and percent calculations and write tests
This commit is contained in:
parent
5e75a9ef5c
commit
f5617dadf3
@ -229,26 +229,13 @@ func (acc *Account) speed() (bps, current float64) {
|
|||||||
// eta returns the ETA of the current operation,
|
// eta returns the ETA of the current operation,
|
||||||
// rounded to full seconds.
|
// rounded to full seconds.
|
||||||
// If the ETA cannot be determined 'ok' returns false.
|
// If the ETA cannot be determined 'ok' returns false.
|
||||||
func (acc *Account) eta() (eta time.Duration, ok bool) {
|
func (acc *Account) eta() (etaDuration time.Duration, ok bool) {
|
||||||
if acc == nil || acc.size <= 0 {
|
if acc == nil {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
acc.statmu.Lock()
|
acc.statmu.Lock()
|
||||||
defer acc.statmu.Unlock()
|
defer acc.statmu.Unlock()
|
||||||
if acc.bytes == 0 {
|
return eta(acc.bytes, acc.size, acc.avg)
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
left := acc.size - acc.bytes
|
|
||||||
if left <= 0 {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
avg := acc.avg
|
|
||||||
if avg <= 0 {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
seconds := float64(left) / avg
|
|
||||||
|
|
||||||
return time.Second * time.Duration(seconds), true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String produces stats for this file
|
// String produces stats for this file
|
||||||
|
@ -140,6 +140,43 @@ func (s *StatsInfo) RemoteStats(in rc.Params) (out rc.Params, err error) {
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eta returns the ETA of the current operation,
|
||||||
|
// rounded to full seconds.
|
||||||
|
// If the ETA cannot be determined 'ok' returns false.
|
||||||
|
func eta(size, total int64, rate float64) (eta time.Duration, ok bool) {
|
||||||
|
if total <= 0 || size < 0 || rate <= 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
remaining := total - size
|
||||||
|
if remaining < 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
seconds := float64(remaining) / rate
|
||||||
|
return time.Second * time.Duration(seconds), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// etaString returns the ETA of the current operation,
|
||||||
|
// rounded to full seconds.
|
||||||
|
// If the ETA cannot be determined it returns "-"
|
||||||
|
func etaString(done, total int64, rate float64) string {
|
||||||
|
d, ok := eta(done, total, rate)
|
||||||
|
if !ok {
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
return d.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// percent returns a/b as a percentage rounded to the nearest integer
|
||||||
|
// as a string
|
||||||
|
//
|
||||||
|
// if the percentage is invalid it returns "-"
|
||||||
|
func percent(a int64, b int64) string {
|
||||||
|
if a < 0 || b <= 0 {
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d%%", int(float64(a)*100/float64(b)+0.5))
|
||||||
|
}
|
||||||
|
|
||||||
// String convert the StatsInfo to a string for printing
|
// String convert the StatsInfo to a string for printing
|
||||||
func (s *StatsInfo) String() string {
|
func (s *StatsInfo) String() string {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
|
51
fs/accounting/stats_test.go
Normal file
51
fs/accounting/stats_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package accounting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestETA(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
size, total int64
|
||||||
|
rate float64
|
||||||
|
wantETA time.Duration
|
||||||
|
wantOK bool
|
||||||
|
wantString string
|
||||||
|
}{
|
||||||
|
{size: 0, total: 100, rate: 1.0, wantETA: 100 * time.Second, wantOK: true, wantString: "1m40s"},
|
||||||
|
{size: 50, total: 100, rate: 1.0, wantETA: 50 * time.Second, wantOK: true, wantString: "50s"},
|
||||||
|
{size: 100, total: 100, rate: 1.0, wantETA: 0 * time.Second, wantOK: true, wantString: "0s"},
|
||||||
|
{size: -1, total: 100, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
|
{size: 200, total: 100, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
|
{size: 10, total: -1, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
|
{size: 10, total: 20, rate: 0.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
|
{size: 10, total: 20, rate: -1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
|
{size: 0, total: 0, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("size=%d/total=%d/rate=%f", test.size, test.total, test.rate), func(t *testing.T) {
|
||||||
|
gotETA, gotOK := eta(test.size, test.total, test.rate)
|
||||||
|
assert.Equal(t, test.wantETA, gotETA)
|
||||||
|
assert.Equal(t, test.wantOK, gotOK)
|
||||||
|
gotString := etaString(test.size, test.total, test.rate)
|
||||||
|
assert.Equal(t, test.wantString, gotString)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPercentage(t *testing.T) {
|
||||||
|
assert.Equal(t, percent(0, 1000), "0%")
|
||||||
|
assert.Equal(t, percent(1, 1000), "0%")
|
||||||
|
assert.Equal(t, percent(9, 1000), "1%")
|
||||||
|
assert.Equal(t, percent(500, 1000), "50%")
|
||||||
|
assert.Equal(t, percent(1000, 1000), "100%")
|
||||||
|
assert.Equal(t, percent(1E8, 1E9), "10%")
|
||||||
|
assert.Equal(t, percent(1E8, 1E9), "10%")
|
||||||
|
assert.Equal(t, percent(0, 0), "-")
|
||||||
|
assert.Equal(t, percent(100, -100), "-")
|
||||||
|
assert.Equal(t, percent(-100, 100), "-")
|
||||||
|
assert.Equal(t, percent(-100, -100), "-")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user