...

Source file src/github.com/jedib0t/go-pretty/v6/progress/render_test.go

Documentation: github.com/jedib0t/go-pretty/v6/progress

     1  package progress
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"sort"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  type outputWriter struct {
    15  	Text strings.Builder
    16  }
    17  
    18  func (w *outputWriter) Write(p []byte) (n int, err error) {
    19  	return w.Text.Write(p)
    20  }
    21  
    22  func (w *outputWriter) String() string {
    23  	return w.Text.String()
    24  }
    25  
    26  func generateWriter() Writer {
    27  	pw := NewWriter()
    28  	pw.SetAutoStop(false)
    29  	pw.SetNumTrackersExpected(1)
    30  	pw.SetSortBy(SortByNone)
    31  	pw.SetStyle(StyleDefault)
    32  	pw.SetTrackerLength(25)
    33  	pw.SetTrackerPosition(PositionRight)
    34  	pw.SetUpdateFrequency(time.Millisecond * 50)
    35  	pw.Style().Colors = StyleColors{}
    36  	pw.Style().Options = StyleOptionsDefault
    37  	pw.Style().Visibility.Percentage = true
    38  	pw.Style().Visibility.Time = true
    39  	pw.Style().Visibility.Tracker = true
    40  	pw.Style().Visibility.TrackerOverall = false
    41  	pw.Style().Visibility.Value = true
    42  	return pw
    43  }
    44  
    45  func trackSomething(pw Writer, tracker *Tracker) {
    46  	incrementPerCycle := tracker.Total / 3
    47  
    48  	pw.AppendTracker(tracker)
    49  
    50  	c := time.Tick(time.Millisecond * 100)
    51  	for !tracker.IsDone() {
    52  		select {
    53  		case <-c:
    54  			if tracker.value+incrementPerCycle > tracker.Total {
    55  				tracker.Increment(tracker.Total - tracker.value)
    56  			} else {
    57  				tracker.Increment(incrementPerCycle)
    58  			}
    59  		}
    60  	}
    61  }
    62  
    63  func trackSomethingErrored(pw Writer, tracker *Tracker) {
    64  	incrementPerCycle := tracker.Total / 3
    65  	total := tracker.Total
    66  	tracker.Total = 0
    67  
    68  	pw.AppendTracker(tracker)
    69  
    70  	c := time.Tick(time.Millisecond * 100)
    71  	for !tracker.IsDone() {
    72  		select {
    73  		case <-c:
    74  			if tracker.value+incrementPerCycle > total {
    75  				tracker.MarkAsErrored()
    76  			} else {
    77  				tracker.IncrementWithError(incrementPerCycle)
    78  			}
    79  		}
    80  	}
    81  }
    82  
    83  func trackSomethingIndeterminate(pw Writer, tracker *Tracker) {
    84  	incrementPerCycle := tracker.Total / 3
    85  	total := tracker.Total
    86  	tracker.Total = 0
    87  
    88  	pw.AppendTracker(tracker)
    89  
    90  	c := time.Tick(time.Millisecond * 100)
    91  	for !tracker.IsDone() {
    92  		select {
    93  		case <-c:
    94  			if tracker.value+incrementPerCycle > total {
    95  				tracker.Increment(total - tracker.value)
    96  			} else {
    97  				tracker.Increment(incrementPerCycle)
    98  			}
    99  			if tracker.Value() >= total {
   100  				tracker.MarkAsDone()
   101  			}
   102  		}
   103  	}
   104  }
   105  
   106  func renderAndWait(pw Writer, autoStop bool) {
   107  	go pw.Render()
   108  	go pw.Render() // this call should be a no-op
   109  	time.Sleep(time.Millisecond * 100)
   110  	for pw.IsRenderInProgress() {
   111  		if pw.LengthActive() == 0 {
   112  			break
   113  		}
   114  		time.Sleep(time.Millisecond * 100)
   115  	}
   116  	if !autoStop {
   117  		pw.Stop()
   118  	}
   119  }
   120  
   121  func showOutputOnFailure(t *testing.T, out string) {
   122  	if t.Failed() {
   123  		lines := strings.Split(out, "\n")
   124  		sort.Strings(lines)
   125  		for _, line := range lines {
   126  			fmt.Printf("%#v,\n", line)
   127  		}
   128  	}
   129  }
   130  
   131  func TestProgress_generateTrackerStr(t *testing.T) {
   132  	pw := Progress{}
   133  	pw.Style().Chars = StyleChars{
   134  		BoxLeft:    "",
   135  		BoxRight:   "",
   136  		Finished:   "#",
   137  		Finished25: "1",
   138  		Finished50: "2",
   139  		Finished75: "3",
   140  		Unfinished: ".",
   141  	}
   142  
   143  	expectedTrackerStrMap := map[int64]string{
   144  		0:   "..........",
   145  		1:   "..........",
   146  		2:   "..........",
   147  		3:   "1.........",
   148  		4:   "1.........",
   149  		5:   "2.........",
   150  		6:   "2.........",
   151  		7:   "2.........",
   152  		8:   "3.........",
   153  		9:   "3.........",
   154  		10:  "#.........",
   155  		11:  "#.........",
   156  		12:  "#.........",
   157  		13:  "#1........",
   158  		14:  "#1........",
   159  		15:  "#2........",
   160  		16:  "#2........",
   161  		17:  "#2........",
   162  		18:  "#3........",
   163  		19:  "#3........",
   164  		20:  "##........",
   165  		21:  "##........",
   166  		22:  "##........",
   167  		23:  "##1.......",
   168  		24:  "##1.......",
   169  		25:  "##2.......",
   170  		26:  "##2.......",
   171  		27:  "##2.......",
   172  		28:  "##3.......",
   173  		29:  "##3.......",
   174  		30:  "###.......",
   175  		31:  "###.......",
   176  		32:  "###.......",
   177  		33:  "###1......",
   178  		34:  "###1......",
   179  		35:  "###2......",
   180  		36:  "###2......",
   181  		37:  "###2......",
   182  		38:  "###3......",
   183  		39:  "###3......",
   184  		40:  "####......",
   185  		41:  "####......",
   186  		42:  "####......",
   187  		43:  "####1.....",
   188  		44:  "####1.....",
   189  		45:  "####2.....",
   190  		46:  "####2.....",
   191  		47:  "####2.....",
   192  		48:  "####3.....",
   193  		49:  "####3.....",
   194  		50:  "#####.....",
   195  		51:  "#####.....",
   196  		52:  "#####.....",
   197  		53:  "#####1....",
   198  		54:  "#####1....",
   199  		55:  "#####2....",
   200  		56:  "#####2....",
   201  		57:  "#####2....",
   202  		58:  "#####3....",
   203  		59:  "#####3....",
   204  		60:  "######....",
   205  		61:  "######....",
   206  		62:  "######....",
   207  		63:  "######1...",
   208  		64:  "######1...",
   209  		65:  "######2...",
   210  		66:  "######2...",
   211  		67:  "######2...",
   212  		68:  "######3...",
   213  		69:  "######3...",
   214  		70:  "#######...",
   215  		71:  "#######...",
   216  		72:  "#######...",
   217  		73:  "#######1..",
   218  		74:  "#######1..",
   219  		75:  "#######2..",
   220  		76:  "#######2..",
   221  		77:  "#######2..",
   222  		78:  "#######3..",
   223  		79:  "#######3..",
   224  		80:  "########..",
   225  		81:  "########..",
   226  		82:  "########..",
   227  		83:  "########1.",
   228  		84:  "########1.",
   229  		85:  "########2.",
   230  		86:  "########2.",
   231  		87:  "########2.",
   232  		88:  "########3.",
   233  		89:  "########3.",
   234  		90:  "#########.",
   235  		91:  "#########.",
   236  		92:  "#########.",
   237  		93:  "#########1",
   238  		94:  "#########1",
   239  		95:  "#########2",
   240  		96:  "#########2",
   241  		97:  "#########2",
   242  		98:  "#########3",
   243  		99:  "#########3",
   244  		100: "##########",
   245  	}
   246  
   247  	finalOutput := strings.Builder{}
   248  	tr := Tracker{Total: 100}
   249  	for value := int64(0); value <= 100; value++ {
   250  		tr.value = value
   251  		actualStr := pw.generateTrackerStr(&tr, 10, renderHint{})
   252  		if expectedStr, ok := expectedTrackerStrMap[value]; ok {
   253  			assert.Equal(t, expectedStr, actualStr, "value=%d", value)
   254  		}
   255  		finalOutput.WriteString(fmt.Sprintf(" %d: \"%s\",\n", value, actualStr))
   256  	}
   257  	if t.Failed() {
   258  		fmt.Println(finalOutput.String())
   259  	}
   260  }
   261  
   262  func TestProgress_generateTrackerStr_Indeterminate(t *testing.T) {
   263  	pw := Progress{}
   264  	pw.Style().Chars = StyleChars{
   265  		BoxLeft:       "",
   266  		BoxRight:      "",
   267  		Finished:      "#",
   268  		Finished25:    "1",
   269  		Finished50:    "2",
   270  		Finished75:    "3",
   271  		Indeterminate: indeterminateIndicatorMovingBackAndForth("<=>"),
   272  		Unfinished:    ".",
   273  	}
   274  
   275  	expectedTrackerStrMap := map[int64]string{
   276  		0:   "<=>.......",
   277  		1:   ".<=>......",
   278  		2:   "..<=>.....",
   279  		3:   "...<=>....",
   280  		4:   "....<=>...",
   281  		5:   ".....<=>..",
   282  		6:   "......<=>.",
   283  		7:   ".......<=>",
   284  		8:   "......<=>.",
   285  		9:   ".....<=>..",
   286  		10:  "....<=>...",
   287  		11:  "...<=>....",
   288  		12:  "..<=>.....",
   289  		13:  ".<=>......",
   290  		14:  "<=>.......",
   291  		15:  ".<=>......",
   292  		16:  "..<=>.....",
   293  		17:  "...<=>....",
   294  		18:  "....<=>...",
   295  		19:  ".....<=>..",
   296  		20:  "......<=>.",
   297  		21:  ".......<=>",
   298  		22:  "......<=>.",
   299  		23:  ".....<=>..",
   300  		24:  "....<=>...",
   301  		25:  "...<=>....",
   302  		26:  "..<=>.....",
   303  		27:  ".<=>......",
   304  		28:  "<=>.......",
   305  		29:  ".<=>......",
   306  		30:  "..<=>.....",
   307  		31:  "...<=>....",
   308  		32:  "....<=>...",
   309  		33:  ".....<=>..",
   310  		34:  "......<=>.",
   311  		35:  ".......<=>",
   312  		36:  "......<=>.",
   313  		37:  ".....<=>..",
   314  		38:  "....<=>...",
   315  		39:  "...<=>....",
   316  		40:  "..<=>.....",
   317  		41:  ".<=>......",
   318  		42:  "<=>.......",
   319  		43:  ".<=>......",
   320  		44:  "..<=>.....",
   321  		45:  "...<=>....",
   322  		46:  "....<=>...",
   323  		47:  ".....<=>..",
   324  		48:  "......<=>.",
   325  		49:  ".......<=>",
   326  		50:  "......<=>.",
   327  		51:  ".....<=>..",
   328  		52:  "....<=>...",
   329  		53:  "...<=>....",
   330  		54:  "..<=>.....",
   331  		55:  ".<=>......",
   332  		56:  "<=>.......",
   333  		57:  ".<=>......",
   334  		58:  "..<=>.....",
   335  		59:  "...<=>....",
   336  		60:  "....<=>...",
   337  		61:  ".....<=>..",
   338  		62:  "......<=>.",
   339  		63:  ".......<=>",
   340  		64:  "......<=>.",
   341  		65:  ".....<=>..",
   342  		66:  "....<=>...",
   343  		67:  "...<=>....",
   344  		68:  "..<=>.....",
   345  		69:  ".<=>......",
   346  		70:  "<=>.......",
   347  		71:  ".<=>......",
   348  		72:  "..<=>.....",
   349  		73:  "...<=>....",
   350  		74:  "....<=>...",
   351  		75:  ".....<=>..",
   352  		76:  "......<=>.",
   353  		77:  ".......<=>",
   354  		78:  "......<=>.",
   355  		79:  ".....<=>..",
   356  		80:  "....<=>...",
   357  		81:  "...<=>....",
   358  		82:  "..<=>.....",
   359  		83:  ".<=>......",
   360  		84:  "<=>.......",
   361  		85:  ".<=>......",
   362  		86:  "..<=>.....",
   363  		87:  "...<=>....",
   364  		88:  "....<=>...",
   365  		89:  ".....<=>..",
   366  		90:  "......<=>.",
   367  		91:  ".......<=>",
   368  		92:  "......<=>.",
   369  		93:  ".....<=>..",
   370  		94:  "....<=>...",
   371  		95:  "...<=>....",
   372  		96:  "..<=>.....",
   373  		97:  ".<=>......",
   374  		98:  "<=>.......",
   375  		99:  ".<=>......",
   376  		100: "..<=>.....",
   377  	}
   378  
   379  	finalOutput := strings.Builder{}
   380  	tr := Tracker{Total: 0}
   381  	for value := int64(0); value <= 100; value++ {
   382  		tr.value = value
   383  		actualStr := pw.generateTrackerStr(&tr, 10, renderHint{})
   384  		if expectedStr, ok := expectedTrackerStrMap[value]; ok {
   385  			assert.Equal(t, expectedStr, actualStr, "value=%d", value)
   386  		}
   387  		finalOutput.WriteString(fmt.Sprintf(" %d: \"%s\",\n", value, actualStr))
   388  	}
   389  	if t.Failed() {
   390  		fmt.Println(finalOutput.String())
   391  	}
   392  }
   393  
   394  func TestProgress_RenderNothing(t *testing.T) {
   395  	renderOutput := outputWriter{}
   396  
   397  	pw := generateWriter()
   398  	pw.SetOutputWriter(&renderOutput)
   399  
   400  	go pw.Render()
   401  	time.Sleep(time.Second)
   402  	pw.Stop()
   403  	time.Sleep(time.Second)
   404  
   405  	assert.Empty(t, renderOutput.String())
   406  }
   407  
   408  func TestProgress_RenderSomeTrackers_OnLeftSide(t *testing.T) {
   409  	renderOutput := outputWriter{}
   410  
   411  	pw := generateWriter()
   412  	pw.SetOutputWriter(&renderOutput)
   413  	pw.SetTrackerPosition(PositionLeft)
   414  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   415  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   416  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   417  	renderAndWait(pw, false)
   418  
   419  	expectedOutPatterns := []*regexp.Regexp{
   420  		regexp.MustCompile(`\x1b\[K\d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms] \.\.\. Calculating Total   # 1`),
   421  		regexp.MustCompile(`\x1b\[K\d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms] \.\.\. Downloading File    # 2`),
   422  		regexp.MustCompile(`\x1b\[K\d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms] \.\.\. Transferring Amount # 3`),
   423  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   424  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   425  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   426  	}
   427  	out := renderOutput.String()
   428  	for _, expectedOutPattern := range expectedOutPatterns {
   429  		if !expectedOutPattern.MatchString(out) {
   430  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   431  		}
   432  	}
   433  	showOutputOnFailure(t, out)
   434  }
   435  
   436  func TestProgress_RenderSomeTrackers_OnRightSide(t *testing.T) {
   437  	renderOutput := outputWriter{}
   438  
   439  	pw := generateWriter()
   440  	pw.SetOutputWriter(&renderOutput)
   441  	pw.SetTrackerPosition(PositionRight)
   442  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   443  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   444  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   445  	renderAndWait(pw, false)
   446  
   447  	expectedOutPatterns := []*regexp.Regexp{
   448  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   449  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   450  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
   451  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   452  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   453  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   454  	}
   455  	out := renderOutput.String()
   456  	for _, expectedOutPattern := range expectedOutPatterns {
   457  		if !expectedOutPattern.MatchString(out) {
   458  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   459  		}
   460  	}
   461  	showOutputOnFailure(t, out)
   462  }
   463  
   464  func TestProgress_RenderSomeTrackers_WithAutoStop(t *testing.T) {
   465  	renderOutput := outputWriter{}
   466  
   467  	pw := generateWriter()
   468  	pw.SetAutoStop(true)
   469  	pw.SetOutputWriter(&renderOutput)
   470  	pw.SetTrackerPosition(PositionRight)
   471  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   472  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   473  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   474  	renderAndWait(pw, true)
   475  
   476  	expectedOutPatterns := []*regexp.Regexp{
   477  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   478  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   479  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
   480  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   481  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   482  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   483  	}
   484  	out := renderOutput.String()
   485  	for _, expectedOutPattern := range expectedOutPatterns {
   486  		if !expectedOutPattern.MatchString(out) {
   487  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   488  		}
   489  	}
   490  	showOutputOnFailure(t, out)
   491  }
   492  
   493  func TestProgress_RenderSomeTrackers_WithError(t *testing.T) {
   494  	renderOutput := outputWriter{}
   495  
   496  	pw := generateWriter()
   497  	pw.SetOutputWriter(&renderOutput)
   498  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   499  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   500  	go trackSomethingErrored(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   501  	renderAndWait(pw, false)
   502  
   503  	expectedOutPatterns := []*regexp.Regexp{
   504  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   505  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   506  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\.  \?\?\?  \[[<#>.]{23}] \[\$\d+ in [\d.]+ms]`),
   507  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   508  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   509  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. fail! \[\$\d+ in [\d.]+ms]`),
   510  	}
   511  	out := renderOutput.String()
   512  	for _, expectedOutPattern := range expectedOutPatterns {
   513  		if !expectedOutPattern.MatchString(out) {
   514  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   515  		}
   516  	}
   517  	showOutputOnFailure(t, out)
   518  }
   519  
   520  func TestProgress_RenderSomeTrackers_WithIndeterminateTracker(t *testing.T) {
   521  	renderOutput := outputWriter{}
   522  
   523  	pw := generateWriter()
   524  	pw.SetOutputWriter(&renderOutput)
   525  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   526  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   527  	go trackSomethingIndeterminate(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   528  	renderAndWait(pw, false)
   529  
   530  	expectedOutPatterns := []*regexp.Regexp{
   531  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   532  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   533  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\.  \?\?\?  \[[<#>.]{23}] \[\$\d+ in [\d.]+ms]`),
   534  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   535  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   536  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   537  	}
   538  	out := renderOutput.String()
   539  	for _, expectedOutPattern := range expectedOutPatterns {
   540  		if !expectedOutPattern.MatchString(out) {
   541  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   542  		}
   543  	}
   544  	showOutputOnFailure(t, out)
   545  }
   546  
   547  func TestProgress_RenderSomeTrackers_WithLineWidth1(t *testing.T) {
   548  	renderOutput := outputWriter{}
   549  
   550  	pw := generateWriter()
   551  	pw.SetMessageWidth(5)
   552  	pw.SetOutputWriter(&renderOutput)
   553  	pw.SetTrackerPosition(PositionRight)
   554  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   555  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   556  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   557  	renderAndWait(pw, false)
   558  
   559  	expectedOutPatterns := []*regexp.Regexp{
   560  		regexp.MustCompile(`\x1b\[KCalc~ \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   561  		regexp.MustCompile(`\x1b\[KDown~ \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   562  		regexp.MustCompile(`\x1b\[KTran~ \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
   563  		regexp.MustCompile(`\x1b\[KCalc~ \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   564  		regexp.MustCompile(`\x1b\[KDown~ \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   565  		regexp.MustCompile(`\x1b\[KTran~ \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   566  	}
   567  	out := renderOutput.String()
   568  	for _, expectedOutPattern := range expectedOutPatterns {
   569  		if !expectedOutPattern.MatchString(out) {
   570  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   571  		}
   572  	}
   573  	showOutputOnFailure(t, out)
   574  }
   575  
   576  func TestProgress_RenderSomeTrackers_WithLineWidth2(t *testing.T) {
   577  	renderOutput := outputWriter{}
   578  
   579  	pw := generateWriter()
   580  	pw.SetMessageWidth(50)
   581  	pw.SetOutputWriter(&renderOutput)
   582  	pw.SetTrackerPosition(PositionRight)
   583  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   584  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   585  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   586  	renderAndWait(pw, false)
   587  
   588  	expectedOutPatterns := []*regexp.Regexp{
   589  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1\s{28}\.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   590  		regexp.MustCompile(`\x1b\[KDownloading File    # 2\s{28}\.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   591  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3\s{28}\.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
   592  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1\s{28}\.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   593  		regexp.MustCompile(`\x1b\[KDownloading File    # 2\s{28}\.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   594  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3\s{28}\.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   595  	}
   596  	out := renderOutput.String()
   597  	for _, expectedOutPattern := range expectedOutPatterns {
   598  		if !expectedOutPattern.MatchString(out) {
   599  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   600  		}
   601  	}
   602  	showOutputOnFailure(t, out)
   603  }
   604  
   605  func TestProgress_RenderSomeTrackers_WithOverallTracker(t *testing.T) {
   606  	renderOutput := outputWriter{}
   607  
   608  	pw := generateWriter()
   609  	pw.SetOutputWriter(&renderOutput)
   610  	pw.SetTrackerPosition(PositionRight)
   611  	pw.Style().Options.TimeOverallPrecision = time.Millisecond
   612  	pw.Style().Visibility.TrackerOverall = true
   613  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   614  	go func() {
   615  		pw.Log("some information about something that happened at %s", time.Now().Format(time.RFC3339))
   616  	}()
   617  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   618  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   619  	renderAndWait(pw, false)
   620  
   621  	expectedOutPatterns := []*regexp.Regexp{
   622  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   623  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   624  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
   625  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   626  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   627  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   628  		regexp.MustCompile(`\x1b\[K\[[.#]+] \[[\d.ms]+; ~ETA: [\d.ms]+`),
   629  		regexp.MustCompile(`some information about something that happened at \d\d\d\d`),
   630  	}
   631  	out := renderOutput.String()
   632  	for _, expectedOutPattern := range expectedOutPatterns {
   633  		if !expectedOutPattern.MatchString(out) {
   634  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   635  		}
   636  	}
   637  	showOutputOnFailure(t, out)
   638  }
   639  
   640  func TestProgress_RenderSomeTrackers_WithOverallTracker_WithoutETAOverall(t *testing.T) {
   641  	renderOutput := outputWriter{}
   642  
   643  	pw := generateWriter()
   644  	pw.SetOutputWriter(&renderOutput)
   645  	pw.SetTrackerPosition(PositionRight)
   646  	pw.Style().Options.TimeOverallPrecision = time.Millisecond
   647  	pw.Style().Visibility.ETA = true
   648  	pw.Style().Visibility.ETAOverall = false
   649  	pw.Style().Visibility.TrackerOverall = true
   650  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   651  	go func() {
   652  		pw.Log("some information about something that happened at %s", time.Now().Format(time.RFC3339))
   653  	}()
   654  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   655  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   656  	renderAndWait(pw, false)
   657  
   658  	expectedOutPatterns := []*regexp.Regexp{
   659  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   660  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   661  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
   662  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   663  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   664  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   665  		regexp.MustCompile(`\x1b\[K\[[.#]+] \[[\d.ms]+]`),
   666  		regexp.MustCompile(`some information about something that happened at \d\d\d\d`),
   667  	}
   668  	out := renderOutput.String()
   669  	for _, expectedOutPattern := range expectedOutPatterns {
   670  		if !expectedOutPattern.MatchString(out) {
   671  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   672  		}
   673  	}
   674  	showOutputOnFailure(t, out)
   675  }
   676  
   677  func TestProgress_RenderSomeTrackers_WithoutOverallTracker_WithETA(t *testing.T) {
   678  	renderOutput := outputWriter{}
   679  
   680  	pw := generateWriter()
   681  	pw.SetOutputWriter(&renderOutput)
   682  	pw.SetTrackerPosition(PositionRight)
   683  	pw.Style().Visibility.ETA = true
   684  	pw.Style().Visibility.TrackerOverall = false
   685  	pw.Style().Options.ETAPrecision = time.Millisecond
   686  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   687  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   688  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   689  	renderAndWait(pw, false)
   690  
   691  	expectedOutPatterns := []*regexp.Regexp{
   692  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms; ~ETA: [\d]+ms]`),
   693  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms; ~ETA: [\d]+ms]`),
   694  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms; ~ETA: [\d]+ms]`),
   695  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   696  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   697  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   698  	}
   699  	out := renderOutput.String()
   700  	for _, expectedOutPattern := range expectedOutPatterns {
   701  		if !expectedOutPattern.MatchString(out) {
   702  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   703  		}
   704  	}
   705  	showOutputOnFailure(t, out)
   706  }
   707  
   708  func TestProgress_RenderSomeTrackers_WithOverallTracker_WithSpeedAndSpeedOverall(t *testing.T) {
   709  	renderOutput := outputWriter{}
   710  
   711  	pw := generateWriter()
   712  	pw.SetOutputWriter(&renderOutput)
   713  	pw.SetTrackerPosition(PositionRight)
   714  	pw.Style().Options.TimeOverallPrecision = time.Millisecond
   715  	pw.Style().Visibility.Speed = true
   716  	pw.Style().Visibility.SpeedOverall = true
   717  	pw.Style().Visibility.TrackerOverall = true
   718  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   719  	go func() {
   720  		pw.Log("some information about something that happened at %s", time.Now().Format(time.RFC3339))
   721  	}()
   722  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   723  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   724  	renderAndWait(pw, false)
   725  
   726  	expectedOutPatterns := []*regexp.Regexp{
   727  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms; \d+\.\d+\w+/s]`),
   728  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms; \d+\.\d+KB/s]`),
   729  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms; \$\d+\.\d+\w+/s]`),
   730  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms; \d+\.\d+K/s]`),
   731  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms; \d+\.\d+KB/s]`),
   732  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms; \$\d+\.\d+K/s]`),
   733  		regexp.MustCompile(`\x1b\[K\[[.#]+] \[[\d.ms]+; ~ETA: [\d.ms]+]`),
   734  		regexp.MustCompile(`some information about something that happened at \d\d\d\d`),
   735  	}
   736  	out := renderOutput.String()
   737  	for _, expectedOutPattern := range expectedOutPatterns {
   738  		if !expectedOutPattern.MatchString(out) {
   739  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   740  		}
   741  	}
   742  	showOutputOnFailure(t, out)
   743  }
   744  
   745  func TestProgress_RenderSomeTrackers_WithoutOverallTracker_WithSpeedOnLeft(t *testing.T) {
   746  	renderOutput := outputWriter{}
   747  
   748  	pw := generateWriter()
   749  	pw.SetOutputWriter(&renderOutput)
   750  	pw.SetTrackerPosition(PositionRight)
   751  	pw.Style().Options.SpeedPosition = PositionLeft
   752  	pw.Style().Options.TimeOverallPrecision = time.Millisecond
   753  	pw.Style().Visibility.Speed = true
   754  	pw.Style().Visibility.TrackerOverall = false
   755  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   756  	go func() {
   757  		pw.Log("some information about something that happened at %s", time.Now().Format(time.RFC3339))
   758  	}()
   759  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   760  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   761  	renderAndWait(pw, false)
   762  
   763  	expectedOutPatterns := []*regexp.Regexp{
   764  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+\.\d+\w+/s; \d+ in [\d.]+ms]`),
   765  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[[\d.]+(B|KB)/s; \d+(B|KB) in [\d.]+ms]`),
   766  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+\.\d+\w+/s; \$\d+ in [\d.]+ms]`),
   767  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+\w+/s; \d+\.\d+K in [\d.]+ms]`),
   768  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB/s; \d+\.\d+KB in [\d.]+ms]`),
   769  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+\w+/s; \$\d+\.\d+K in [\d.]+ms]`),
   770  	}
   771  	out := renderOutput.String()
   772  	for _, expectedOutPattern := range expectedOutPatterns {
   773  		if !expectedOutPattern.MatchString(out) {
   774  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   775  		}
   776  	}
   777  	showOutputOnFailure(t, out)
   778  }
   779  
   780  func TestProgress_RenderSomeTrackers_WithOverallTracker_WithSpeedOverall_WithoutFormatter(t *testing.T) {
   781  	renderOutput := outputWriter{}
   782  
   783  	pw := generateWriter()
   784  	pw.SetOutputWriter(&renderOutput)
   785  	pw.SetTrackerPosition(PositionRight)
   786  	pw.Style().Options.SpeedOverallFormatter = nil
   787  	pw.Style().Options.SpeedPosition = PositionLeft
   788  	pw.Style().Options.TimeOverallPrecision = time.Millisecond
   789  	pw.Style().Visibility.Speed = false
   790  	pw.Style().Visibility.SpeedOverall = true
   791  	pw.Style().Visibility.TrackerOverall = true
   792  	go trackSomething(pw, &Tracker{Message: "Calculating Total   # 1\r", Total: 1000, Units: UnitsDefault})
   793  	go func() {
   794  		pw.Log("some information about something that happened at %s", time.Now().Format(time.RFC3339))
   795  	}()
   796  	go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes})
   797  	go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar})
   798  	renderAndWait(pw, false)
   799  
   800  	expectedOutPatterns := []*regexp.Regexp{
   801  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`),
   802  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`),
   803  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`),
   804  		regexp.MustCompile(`\x1b\[KCalculating Total   # 1 \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`),
   805  		regexp.MustCompile(`\x1b\[KDownloading File    # 2 \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`),
   806  		regexp.MustCompile(`\x1b\[KTransferring Amount # 3 \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`),
   807  		regexp.MustCompile(`\x1b\[K\[[.#]+] \[\d+.\d+\w+/s; [\d.ms]+; ~ETA: [\d.ms]+]`),
   808  		regexp.MustCompile(`some information about something that happened at \d\d\d\d`),
   809  	}
   810  	out := renderOutput.String()
   811  	for _, expectedOutPattern := range expectedOutPatterns {
   812  		if !expectedOutPattern.MatchString(out) {
   813  			assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String())
   814  		}
   815  	}
   816  	showOutputOnFailure(t, out)
   817  }
   818  

View as plain text