diff --git a/cmd/demo-progress/demo.go b/cmd/demo-progress/demo.go index f141e89..813d443 100644 --- a/cmd/demo-progress/demo.go +++ b/cmd/demo-progress/demo.go @@ -24,6 +24,7 @@ var ( flagShowPinned = flag.Bool("show-pinned", false, "Show a pinned message?") flagRandomFail = flag.Bool("rnd-fail", false, "Introduce random failures in tracking") flagRandomDefer = flag.Bool("rnd-defer", false, "Introduce random deferred starts") + flagRandomRemove = flag.Bool("rnd-remove", false, "Introduce random remove of trackers on completion") flagRandomLogs = flag.Bool("rnd-logs", false, "Output random logs in the middle of tracking") messageColors = []text.Color{ @@ -72,7 +73,13 @@ func trackSomething(pw progress.Writer, idx int64, updateMessage bool) { units := getUnits(idx) message := getMessage(idx, units) - tracker := progress.Tracker{Message: message, Total: total, Units: *units, DeferStart: *flagRandomDefer && rand.Float64() < 0.5} + tracker := progress.Tracker{ + DeferStart: *flagRandomDefer && rand.Float64() < 0.5, + Message: message, + RemoveOnCompletion: *flagRandomRemove && rand.Float64() < 0.25, + Total: total, + Units: *units, + } if idx == int64(*flagNumTrackers) { tracker.Total = 0 } diff --git a/progress/render.go b/progress/render.go index 697d8aa..6e54d3c 100644 --- a/progress/render.go +++ b/progress/render.go @@ -78,7 +78,7 @@ func (p *Progress) extractDoneAndActiveTrackers() ([]*Tracker, []*Tracker) { if eta := tracker.ETA(); eta > maxETA { maxETA = eta } - } else { + } else if !tracker.RemoveOnCompletion { trackersDone = append(trackersDone, tracker) } } @@ -230,14 +230,16 @@ func (p *Progress) renderTracker(out *strings.Builder, t *Tracker, hint renderHi } func (p *Progress) renderTrackerDone(out *strings.Builder, t *Tracker, message string) { - out.WriteString(p.style.Colors.Message.Sprint(message)) - out.WriteString(p.style.Colors.Message.Sprint(p.style.Options.Separator)) - if !t.IsErrored() { - out.WriteString(p.style.Colors.Message.Sprint(p.style.Options.DoneString)) - } else { - out.WriteString(p.style.Colors.Error.Sprint(p.style.Options.ErrorString)) + if !t.RemoveOnCompletion { + out.WriteString(p.style.Colors.Message.Sprint(message)) + out.WriteString(p.style.Colors.Message.Sprint(p.style.Options.Separator)) + if !t.IsErrored() { + out.WriteString(p.style.Colors.Message.Sprint(p.style.Options.DoneString)) + } else { + out.WriteString(p.style.Colors.Error.Sprint(p.style.Options.ErrorString)) + } + p.renderTrackerStats(out, t, renderHint{hideTime: !p.style.Visibility.Time, hideValue: !p.style.Visibility.Value}) } - p.renderTrackerStats(out, t, renderHint{hideTime: !p.style.Visibility.Time, hideValue: !p.style.Visibility.Value}) } func (p *Progress) renderTrackerMessage(out *strings.Builder, t *Tracker, message string) { diff --git a/progress/render_test.go b/progress/render_test.go index c815e5f..49d2ca0 100644 --- a/progress/render_test.go +++ b/progress/render_test.go @@ -372,6 +372,43 @@ func TestProgress_RenderNothing(t *testing.T) { assert.Empty(t, renderOutput.String()) } +func TestProgress_RenderSomeTrackers_AndRemove(t *testing.T) { + renderOutput := outputWriter{} + + pw := generateWriter() + pw.SetOutputWriter(&renderOutput) + pw.SetTrackerPosition(PositionLeft) + go trackSomething(pw, &Tracker{Message: "Calculating Total # 1", Total: 1000, Units: UnitsDefault, RemoveOnCompletion: true}) + go trackSomething(pw, &Tracker{Message: "Downloading File # 2", Total: 1000, Units: UnitsBytes, RemoveOnCompletion: true}) + go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar, RemoveOnCompletion: true}) + renderAndWait(pw, false) + out := renderOutput.String() + + expectedOutPatterns := []*regexp.Regexp{ + regexp.MustCompile(`\d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms] \.\.\. Calculating Total # 1`), + regexp.MustCompile(`\d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms] \.\.\. Downloading File # 2`), + regexp.MustCompile(`\d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms] \.\.\. Transferring Amount # 3`), + } + for _, expectedOutPattern := range expectedOutPatterns { + if !expectedOutPattern.MatchString(out) { + assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String()) + } + } + + unexpectedOutPatterns := []*regexp.Regexp{ + regexp.MustCompile(`Calculating Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`), + regexp.MustCompile(`Downloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`), + regexp.MustCompile(`Transferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`), + } + for _, unexpectedOutPattern := range unexpectedOutPatterns { + if unexpectedOutPattern.MatchString(out) { + assert.Fail(t, "Found a pattern in the Output which was not expected.", unexpectedOutPattern.String()) + } + } + + showOutputOnFailure(t, out) +} + func TestProgress_RenderSomeTrackers_OnLeftSide(t *testing.T) { renderOutput := outputWriter{} diff --git a/progress/tracker.go b/progress/tracker.go index 30cb57c..dd70124 100644 --- a/progress/tracker.go +++ b/progress/tracker.go @@ -24,6 +24,9 @@ type Tracker struct { // ExpectedDuration tells how long this task is expected to take; and will // be used in calculation of the ETA value ExpectedDuration time.Duration + // RemoveOnCompletion tells the Progress Bar to remove this tracker when + // it is done, instead of rendering a "completed" line + RemoveOnCompletion bool // Total should be set to the (expected) Total/Final value to be reached Total int64 // Units defines the type of the "value" being tracked