Skip to content

Commit

Permalink
Merge 3aed962 into 9fc035f
Browse files Browse the repository at this point in the history
  • Loading branch information
jedib0t committed Jan 20, 2021
2 parents 9fc035f + 3aed962 commit 9bd4a14
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 50 deletions.
2 changes: 1 addition & 1 deletion bench_test.go
Expand Up @@ -22,7 +22,7 @@ var (
{20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"},
{300, "Tyrion", "Lannister", 5000},
}
tracker1 = progress.Tracker{Message: "Calculation Total # 1", Total: 1000, Units: progress.UnitsDefault}
tracker1 = progress.Tracker{Message: "Calculating Total # 1", Total: 1000, Units: progress.UnitsDefault}
tracker2 = progress.Tracker{Message: "Downloading File # 2", Total: 1000, Units: progress.UnitsBytes}
tracker3 = progress.Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: progress.UnitsCurrencyDollar}
)
Expand Down
3 changes: 2 additions & 1 deletion cmd/demo-progress/demo.go
Expand Up @@ -42,7 +42,7 @@ func trackSomething(pw progress.Writer, idx int64) {

pw.AppendTracker(&tracker)

c := time.Tick(time.Millisecond * 100)
c := time.Tick(time.Millisecond * 500)
for !tracker.IsDone() {
select {
case <-c:
Expand All @@ -59,6 +59,7 @@ func main() {
pw := progress.NewWriter()
pw.SetAutoStop(*autoStop)
pw.SetTrackerLength(25)
pw.ShowETA(true)
pw.ShowOverallTracker(true)
pw.ShowTime(true)
pw.ShowTracker(true)
Expand Down
2 changes: 1 addition & 1 deletion cmd/profile-progress/profile.go
Expand Up @@ -12,7 +12,7 @@ import (
)

var (
tracker1 = progress.Tracker{Message: "Calculation Total # 1", Total: 1000, Units: progress.UnitsDefault}
tracker1 = progress.Tracker{Message: "Calculating Total # 1", Total: 1000, Units: progress.UnitsDefault}
tracker2 = progress.Tracker{Message: "Downloading File # 2", Total: 1000, Units: progress.UnitsBytes}
tracker3 = progress.Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: progress.UnitsCurrencyDollar}
profilers = []func(*profile.Profile){
Expand Down
8 changes: 7 additions & 1 deletion progress/progress.go
Expand Up @@ -35,6 +35,7 @@ type Progress struct {
overallTrackerMutex sync.RWMutex
renderInProgress bool
renderInProgressMutex sync.RWMutex
showETA bool
showOverallTracker bool
sortBy SortBy
style *Style
Expand Down Expand Up @@ -199,12 +200,17 @@ func (p *Progress) SetUpdateFrequency(frequency time.Duration) {
p.updateFrequency = frequency
}

// ShowETA toggles showing the ETA for all individual trackers.
func (p *Progress) ShowETA(show bool) {
p.showETA = show
}

// ShowPercentage toggles showing the Percent complete for each Tracker.
func (p *Progress) ShowPercentage(show bool) {
p.hidePercentage = !show
}

// ShowOverallTracker toggles showing the Overall progress tracker.
// ShowOverallTracker toggles showing the Overall progress tracker with an ETA.
func (p *Progress) ShowOverallTracker(show bool) {
p.showOverallTracker = show
}
Expand Down
27 changes: 16 additions & 11 deletions progress/render.go
Expand Up @@ -64,10 +64,12 @@ func (p *Progress) renderTrackers(lastRenderLength int) int {
p.trackersActiveMutex.Unlock()

// render the overall tracker
p.renderTracker(&out, p.overallTracker, renderHint{isOverallTracker: true})
if p.showOverallTracker {
p.renderTracker(&out, p.overallTracker, renderHint{isOverallTracker: true})
}

// write the text to the output writer
p.outputWriter.Write([]byte(out.String()))
_, _ = p.outputWriter.Write([]byte(out.String()))

// stop if auto stop is enabled and there are no more active trackers
if p.autoStop && p.LengthActive() == 0 {
Expand Down Expand Up @@ -160,9 +162,6 @@ func (p *Progress) moveCursorToTheTop(out *strings.Builder) {
}

func (p *Progress) renderTracker(out *strings.Builder, t *Tracker, hint renderHint) {
if hint.isOverallTracker && !p.showOverallTracker {
return
}
if strings.Contains(t.Message, "\t") {
t.Message = strings.Replace(t.Message, "\t", " ", -1)
}
Expand Down Expand Up @@ -266,16 +265,22 @@ func (p *Progress) renderTrackerStats(out *strings.Builder, t *Tracker, hint ren
tp = p.style.Options.TimeInProgressPrecision
}
outStats.WriteString(p.style.Colors.Time.Sprint(td.Round(tp)))
if hint.isOverallTracker {
tpO := p.style.Options.TimeOverallPrecision
if eta := t.ETA().Round(tpO) + tpO; true || eta > tpO {
outStats.WriteString("; ~ETA: ")
outStats.WriteString(p.style.Colors.Time.Sprint(eta))
}
if p.showETA || hint.isOverallTracker {
p.renderTrackerStatsETA(&outStats, t, hint)
}
}
outStats.WriteRune(']')

out.WriteString(p.style.Colors.Stats.Sprint(outStats.String()))
}
}

func (p *Progress) renderTrackerStatsETA(out *strings.Builder, t *Tracker, hint renderHint) {
tpETA := p.style.Options.ETAPrecision
if eta := t.ETA().Round(tpETA); hint.isOverallTracker || eta > tpETA {
out.WriteString("; ")
out.WriteString(p.style.Options.ETAString)
out.WriteString(": ")
out.WriteString(p.style.Colors.Time.Sprint(eta))
}
}
72 changes: 51 additions & 21 deletions progress/render_test.go
Expand Up @@ -13,12 +13,12 @@ type outputWriter struct {
Text strings.Builder
}

func (rc *outputWriter) Write(p []byte) (n int, err error) {
return rc.Text.Write(p)
func (w *outputWriter) Write(p []byte) (n int, err error) {
return w.Text.Write(p)
}

func (rc *outputWriter) String() string {
return rc.Text.String()
func (w *outputWriter) String() string {
return w.Text.String()
}

func generateWriter() Writer {
Expand Down Expand Up @@ -218,16 +218,16 @@ func TestProgress_RenderSomeTrackers_OnLeftSide(t *testing.T) {
pw := generateWriter()
pw.SetOutputWriter(&renderOutput)
pw.SetTrackerPosition(PositionLeft)
go trackSomething(pw, &Tracker{Message: "Calculation Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, false)

expectedOutPatterns := []*regexp.Regexp{
regexp.MustCompile(`\x1b\[K\d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms] \.\.\. Calculation Total # 1`),
regexp.MustCompile(`\x1b\[K\d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms] \.\.\. Calculating Total # 1`),
regexp.MustCompile(`\x1b\[K\d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms] \.\.\. Downloading File # 2`),
regexp.MustCompile(`\x1b\[K\d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms] \.\.\. Transferring Amount # 3`),
regexp.MustCompile(`\x1b\[KCalculation Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
}
Expand All @@ -245,16 +245,16 @@ func TestProgress_RenderSomeTrackers_OnRightSide(t *testing.T) {
pw := generateWriter()
pw.SetOutputWriter(&renderOutput)
pw.SetTrackerPosition(PositionRight)
go trackSomething(pw, &Tracker{Message: "Calculation Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, false)

expectedOutPatterns := []*regexp.Regexp{
regexp.MustCompile(`\x1b\[KCalculation Total # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculation Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
}
Expand All @@ -273,16 +273,16 @@ func TestProgress_RenderSomeTrackers_WithAutoStop(t *testing.T) {
pw.SetAutoStop(true)
pw.SetOutputWriter(&renderOutput)
pw.SetTrackerPosition(PositionRight)
go trackSomething(pw, &Tracker{Message: "Calculation Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, true)

expectedOutPatterns := []*regexp.Regexp{
regexp.MustCompile(`\x1b\[KCalculation Total # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculation Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
}
Expand All @@ -301,7 +301,7 @@ func TestProgress_RenderSomeTrackers_WithLineWidth1(t *testing.T) {
pw.SetMessageWidth(5)
pw.SetOutputWriter(&renderOutput)
pw.SetTrackerPosition(PositionRight)
go trackSomething(pw, &Tracker{Message: "Calculation Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, false)
Expand Down Expand Up @@ -329,16 +329,16 @@ func TestProgress_RenderSomeTrackers_WithLineWidth2(t *testing.T) {
pw.SetMessageWidth(50)
pw.SetOutputWriter(&renderOutput)
pw.SetTrackerPosition(PositionRight)
go trackSomething(pw, &Tracker{Message: "Calculation Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, false)

expectedOutPatterns := []*regexp.Regexp{
regexp.MustCompile(`\x1b\[KCalculation Total # 1\s{28}\.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1\s{28}\.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2\s{28}\.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3\s{28}\.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculation Total # 1\s{28}\.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1\s{28}\.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2\s{28}\.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3\s{28}\.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
}
Expand All @@ -358,19 +358,49 @@ func TestProgress_RenderSomeTrackers_WithOverallTracker(t *testing.T) {
pw.SetTrackerPosition(PositionRight)
pw.ShowOverallTracker(true)
pw.Style().Options.TimeOverallPrecision = time.Millisecond
go trackSomething(pw, &Tracker{Message: "Calculation Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, false)

expectedOutPatterns := []*regexp.Regexp{
regexp.MustCompile(`\x1b\[KCalculation Total # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculation Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[K\[[.#]+] \[[\d.ms]+; ~ETA: [\d.ms]+`),
}
out := renderOutput.String()
for _, expectedOutPattern := range expectedOutPatterns {
if !expectedOutPattern.MatchString(out) {
assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
}
}
}

func TestProgress_RenderSomeTrackers_WithoutOverallTracker_WithETA(t *testing.T) {
renderOutput := outputWriter{}

pw := generateWriter()
pw.SetOutputWriter(&renderOutput)
pw.SetTrackerPosition(PositionRight)
pw.ShowETA(true)
pw.ShowOverallTracker(false)
pw.Style().Options.ETAPrecision = time.Millisecond
go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault})
go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
renderAndWait(pw, false)

expectedOutPatterns := []*regexp.Regexp{
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms; ~ETA: [\d]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms; ~ETA: [\d]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms; ~ETA: [\d]+ms]`),
regexp.MustCompile(`\x1b\[KCalculating Total # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KDownloading File # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
regexp.MustCompile(`\[[\d.ms]+; ~ETA: [\d.ms]+`),
}
out := renderOutput.String()
for _, expectedOutPattern := range expectedOutPatterns {
Expand Down
16 changes: 10 additions & 6 deletions progress/style.go
Expand Up @@ -123,19 +123,21 @@ var (
// StyleColorsExample defines a few choice color options. Use this is just as
// an example to customize the Tracker/text colors.
StyleColorsExample = StyleColors{
Message: text.Colors{text.FgWhite, text.BgBlack},
Percent: text.Colors{text.FgHiRed, text.BgBlack},
Stats: text.Colors{text.FgHiBlack, text.BgBlack},
Time: text.Colors{text.FgGreen, text.BgBlack},
Tracker: text.Colors{text.FgYellow, text.BgBlack},
Value: text.Colors{text.FgCyan, text.BgBlack},
Message: text.Colors{text.FgWhite},
Percent: text.Colors{text.FgHiRed},
Stats: text.Colors{text.FgHiBlack},
Time: text.Colors{text.FgGreen},
Tracker: text.Colors{text.FgYellow},
Value: text.Colors{text.FgCyan},
}
)

// StyleOptions defines misc. options to control how the Tracker or its parts
// gets rendered.
type StyleOptions struct {
DoneString string // "done!" string
ETAPrecision time.Duration // precision for ETA
ETAString string // string for ETA
Separator string // text between message and tracker
SnipIndicator string // text denoting message snipping
PercentFormat string // formatting to use for percentage
Expand All @@ -149,6 +151,8 @@ var (
// example to customize the Tracker rendering.
StyleOptionsDefault = StyleOptions{
DoneString: "done!",
ETAPrecision: time.Second,
ETAString: "~ETA",
PercentFormat: "%5.2f%%",
Separator: " ... ",
SnipIndicator: "~",
Expand Down
19 changes: 11 additions & 8 deletions progress/tracker.go
Expand Up @@ -50,12 +50,7 @@ func (t *Tracker) ETA() time.Duration {
// Increment updates the current value of the task being tracked.
func (t *Tracker) Increment(value int64) {
t.mutex.Lock()
if !t.done {
t.value += value
if t.Total > 0 && t.value >= t.Total {
t.stop()
}
}
t.incrementWithoutLock(value)
t.mutex.Unlock()
}

Expand All @@ -74,7 +69,7 @@ func (t *Tracker) MarkAsDone() {
t.mutex.Lock()
t.Total = t.value
t.stop()
defer t.mutex.Unlock()
t.mutex.Unlock()
}

// PercentDone returns the currently completed percentage value.
Expand Down Expand Up @@ -105,9 +100,17 @@ func (t *Tracker) SetValue(value int64) {
t.done = false
t.timeStop = time.Time{}
t.value = 0
t.incrementWithoutLock(value)
t.mutex.Unlock()
}

t.Increment(value)
func (t *Tracker) incrementWithoutLock(value int64) {
if !t.done {
t.value += value
if t.Total > 0 && t.value >= t.Total {
t.stop()
}
}
}

func (t *Tracker) start() {
Expand Down
1 change: 1 addition & 0 deletions progress/writer.go
Expand Up @@ -23,6 +23,7 @@ type Writer interface {
SetStyle(style Style)
SetTrackerLength(length int)
SetTrackerPosition(position Position)
ShowETA(show bool)
ShowOverallTracker(show bool)
ShowPercentage(show bool)
ShowTime(show bool)
Expand Down

0 comments on commit 9bd4a14

Please sign in to comment.