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()
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