diff --git a/progressbar.go b/progressbar.go index 4060005..79a8e6a 100644 --- a/progressbar.go +++ b/progressbar.go @@ -741,6 +741,10 @@ func (p *ProgressBar) Add64(num int64) error { return errors.New("max must be greater than 0") } + if !p.config.ignoreLength && p.state.currentNum+num > p.config.max { + return errors.New("current number exceeds max") + } + if p.state.currentNum < p.config.max { if p.config.ignoreLength { p.state.currentNum = (p.state.currentNum + num) % p.config.max @@ -774,9 +778,6 @@ func (p *ProgressBar) Add64(num int64) error { updateBar := p.state.currentPercent != p.state.lastPercent && p.state.currentPercent > 0 p.state.lastPercent = p.state.currentPercent - if p.state.currentNum > p.config.max { - return errors.New("current number exceeds max") - } // always update if show bytes/second or its/second if updateBar || p.config.showIterationsPerSecond || p.config.showIterationsCount { diff --git a/progressbar_test.go b/progressbar_test.go index acc7bbe..9ba3ac0 100644 --- a/progressbar_test.go +++ b/progressbar_test.go @@ -420,6 +420,34 @@ func TestBar(t *testing.T) { } } +func TestAddExceedsMaxDoesNotCorruptState(t *testing.T) { + bar := NewOptions(100, OptionSetWriter(io.Discard)) + + if err := bar.Add(95); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if err := bar.Add(10); err == nil { + t.Error("should have an error for adding past max") + } + + // A rejected Add must leave state untouched, not partially applied. + if bar.state.currentNum != 95 { + t.Errorf("expected currentNum to stay at 95 after a rejected Add, got %d", bar.state.currentNum) + } + if bar.state.currentPercent != 95 { + t.Errorf("expected currentPercent to stay at 95 after a rejected Add, got %d", bar.state.currentPercent) + } + + // The bar must still accept further, non-overshooting progress afterwards. + if err := bar.Add(5); err != nil { + t.Errorf("bar should still accept progress after a rejected Add, got error: %v", err) + } + if bar.state.currentNum != 100 { + t.Errorf("expected currentNum to reach 100, got %d", bar.state.currentNum) + } +} + func TestState(t *testing.T) { bar := NewOptions(100, OptionSetWidth(10)) bar.Reset()