progress/counter: Support updating the progress bar maximum

This commit is contained in:
Michael Eischer 2020-12-05 23:57:06 +01:00 committed by Alexander Neumann
parent eda8c67616
commit 505f8a2229
3 changed files with 47 additions and 13 deletions

View file

@ -33,11 +33,14 @@ func newProgressMax(show bool, max uint64, description string) *progress.Counter
}
interval := calculateProgressInterval()
return progress.New(interval, func(v uint64, d time.Duration, final bool) {
status := fmt.Sprintf("[%s] %s %d / %d %s",
formatDuration(d),
formatPercent(v, max),
v, max, description)
return progress.New(interval, max, func(v uint64, max uint64, d time.Duration, final bool) {
var status string
if max == 0 {
status = fmt.Sprintf("[%s] %d %s", formatDuration(d), v, description)
} else {
status = fmt.Sprintf("[%s] %s %d / %d %s",
formatDuration(d), formatPercent(v, max), v, max, description)
}
if w := stdoutTerminalWidth(); w > 0 {
status = shortenStatus(w, status)

View file

@ -12,7 +12,7 @@ import (
//
// The final argument is true if Counter.Done has been called,
// which means that the current call will be the last.
type Func func(value uint64, runtime time.Duration, final bool)
type Func func(value uint64, total uint64, runtime time.Duration, final bool)
// A Counter tracks a running count and controls a goroutine that passes its
// value periodically to a Func.
@ -27,16 +27,19 @@ type Counter struct {
valueMutex sync.Mutex
value uint64
max uint64
}
// New starts a new Counter.
func New(interval time.Duration, report Func) *Counter {
func New(interval time.Duration, total uint64, report Func) *Counter {
c := &Counter{
report: report,
start: time.Now(),
stopped: make(chan struct{}),
stop: make(chan struct{}),
max: total,
}
if interval > 0 {
c.tick = time.NewTicker(interval)
}
@ -56,6 +59,16 @@ func (c *Counter) Add(v uint64) {
c.valueMutex.Unlock()
}
// SetMax sets the maximum expected counter value. This method is concurrency-safe.
func (c *Counter) SetMax(max uint64) {
if c == nil {
return
}
c.valueMutex.Lock()
c.max = max
c.valueMutex.Unlock()
}
// Done tells a Counter to stop and waits for it to report its final value.
func (c *Counter) Done() {
if c == nil {
@ -77,11 +90,19 @@ func (c *Counter) get() uint64 {
return v
}
func (c *Counter) getMax() uint64 {
c.valueMutex.Lock()
max := c.max
c.valueMutex.Unlock()
return max
}
func (c *Counter) run() {
defer close(c.stopped)
defer func() {
// Must be a func so that time.Since isn't called at defer time.
c.report(c.get(), time.Since(c.start), true)
c.report(c.get(), c.getMax(), time.Since(c.start), true)
}()
var tick <-chan time.Time
@ -101,6 +122,6 @@ func (c *Counter) run() {
return
}
c.report(c.get(), now.Sub(c.start), false)
c.report(c.get(), c.getMax(), now.Sub(c.start), false)
}
}

View file

@ -10,23 +10,30 @@ import (
func TestCounter(t *testing.T) {
const N = 100
const startTotal = uint64(12345)
var (
finalSeen = false
increasing = true
last uint64
lastTotal = startTotal
ncalls int
nmaxChange int
)
report := func(value uint64, d time.Duration, final bool) {
report := func(value uint64, total uint64, d time.Duration, final bool) {
finalSeen = true
if value < last {
increasing = false
}
last = value
if total != lastTotal {
nmaxChange++
}
lastTotal = total
ncalls++
}
c := progress.New(10*time.Millisecond, report)
c := progress.New(10*time.Millisecond, startTotal, report)
done := make(chan struct{})
go func() {
@ -35,6 +42,7 @@ func TestCounter(t *testing.T) {
time.Sleep(time.Millisecond)
c.Add(1)
}
c.SetMax(42)
}()
<-done
@ -43,6 +51,8 @@ func TestCounter(t *testing.T) {
test.Assert(t, finalSeen, "final call did not happen")
test.Assert(t, increasing, "values not increasing")
test.Equals(t, uint64(N), last)
test.Equals(t, uint64(42), lastTotal)
test.Equals(t, int(1), nmaxChange)
t.Log("number of calls:", ncalls)
}
@ -58,14 +68,14 @@ func TestCounterNoTick(t *testing.T) {
finalSeen := false
otherSeen := false
report := func(value uint64, d time.Duration, final bool) {
report := func(value, total uint64, d time.Duration, final bool) {
if final {
finalSeen = true
} else {
otherSeen = true
}
}
c := progress.New(0, report)
c := progress.New(0, 1, report)
time.Sleep(time.Millisecond)
c.Done()