mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 20:20:16 +08:00
accounting: avoid negative ETA values for very slow speeds
Integer overflow would lead to ETA such as "-255y7w4h11m22s966ms", as reported in #6381. Now the value will be clipped at the maximum "292y24w3d23h47m16s", and it will be shown as infinity.
This commit is contained in:
parent
120cfcde70
commit
67132ecaec
|
@ -229,6 +229,11 @@ func (s *StatsInfo) totalDuration() time.Duration {
|
|||
return s.oldDuration + timeRanges.total()
|
||||
}
|
||||
|
||||
const (
|
||||
etaMaxSeconds = (1<<63 - 1) / int64(time.Second) // Largest possible ETA as number of seconds
|
||||
etaMax = time.Duration(etaMaxSeconds) * time.Second // Largest possible ETA, which is in second precision, representing "292y24w3d23h47m16s"
|
||||
)
|
||||
|
||||
// eta returns the ETA of the current operation,
|
||||
// rounded to full seconds.
|
||||
// If the ETA cannot be determined 'ok' returns false.
|
||||
|
@ -240,11 +245,17 @@ func eta(size, total int64, rate float64) (eta time.Duration, ok bool) {
|
|||
if remaining < 0 {
|
||||
return 0, false
|
||||
}
|
||||
seconds := float64(remaining) / rate
|
||||
seconds := int64(float64(remaining) / rate)
|
||||
if seconds < 0 {
|
||||
seconds = 0
|
||||
// Got Int64 overflow
|
||||
eta = etaMax
|
||||
} else if seconds >= etaMaxSeconds {
|
||||
// Would get Int64 overflow if converting from seconds to Duration (nanoseconds)
|
||||
eta = etaMax
|
||||
} else {
|
||||
eta = time.Duration(seconds) * time.Second
|
||||
}
|
||||
return time.Second * time.Duration(seconds), true
|
||||
return eta, true
|
||||
}
|
||||
|
||||
// etaString returns the ETA of the current operation,
|
||||
|
@ -255,6 +266,9 @@ func etaString(done, total int64, rate float64) string {
|
|||
if !ok {
|
||||
return "-"
|
||||
}
|
||||
if d == etaMax {
|
||||
return "-"
|
||||
}
|
||||
return fs.Duration(d).ReadableString()
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ func TestETA(t *testing.T) {
|
|||
{size: 0, total: 1.5 * 86400, rate: 1.0, wantETA: 1.5 * 86400 * time.Second, wantOK: true, wantString: "1d12h"},
|
||||
{size: 0, total: 95000, rate: 1.0, wantETA: 95000 * time.Second, wantOK: true, wantString: "1d2h23m20s"},
|
||||
// Standard Duration String Cases
|
||||
{size: 0, total: 1, rate: 2.0, wantETA: 0, wantOK: true, wantString: "0s"},
|
||||
{size: 0, total: 1, rate: 1.0, wantETA: time.Second, wantOK: true, wantString: "1s"},
|
||||
{size: 0, total: 1, rate: 0.5, wantETA: 2 * time.Second, wantOK: true, wantString: "2s"},
|
||||
{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"},
|
||||
|
@ -41,10 +44,15 @@ func TestETA(t *testing.T) {
|
|||
{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: "-"},
|
||||
// Extreme Cases
|
||||
{size: 0, total: (1 << 63) - 1, rate: 1.0, wantETA: (time.Duration((1<<63)-1) / time.Second) * time.Second, wantOK: true, wantString: "-"},
|
||||
{size: 0, total: ((1 << 63) - 1) / int64(time.Second), rate: 1.0, wantETA: (time.Duration((1<<63)-1) / time.Second) * time.Second, wantOK: true, wantString: "-"},
|
||||
{size: 0, total: ((1<<63)-1)/int64(time.Second) - 1, rate: 1.0, wantETA: (time.Duration((1<<63)-1)/time.Second - 1) * time.Second, wantOK: true, wantString: "292y24w3d23h47m15s"},
|
||||
{size: 0, total: ((1<<63)-1)/int64(time.Second) - 1, rate: 0.1, wantETA: (time.Duration((1<<63)-1) / time.Second) * time.Second, wantOK: true, 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, int64(test.wantETA), int64(gotETA))
|
||||
assert.Equal(t, test.wantOK, gotOK)
|
||||
gotString := etaString(test.size, test.total, test.rate)
|
||||
assert.Equal(t, test.wantString, gotString)
|
||||
|
|
Loading…
Reference in New Issue
Block a user