package mpb_test
import (
"bytes"
"context"
"fmt"
"io"
"strings"
"testing"
"time"
"unicode/utf8"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
)
func TestBarCompleted(t *testing.T) {
p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
total := 80
bar := p.AddBar(int64(total))
if bar.Completed() {
t.Fail()
}
bar.IncrBy(total)
if !bar.Completed() {
t.Error("bar isn't completed after increment")
}
p.Wait()
}
func TestBarAborted(t *testing.T) {
p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
total := 80
bar := p.AddBar(int64(total))
if bar.Aborted() {
t.Fail()
}
bar.Abort(false)
if !bar.Aborted() {
t.Error("bar isn't aborted after abort call")
}
p.Wait()
}
func TestBarSetTotal(t *testing.T) {
p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
bar := p.AddBar(0)
bar.SetTotal(0, false)
if bar.Completed() {
t.Error("expected bar not to complete")
}
bar.SetTotal(0, true)
if !bar.Completed() {
t.Error("expected bar to complete")
}
p.Wait()
}
func TestBarEnableTriggerCompleteAndIncrementBefore(t *testing.T) {
p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
bar := p.AddBar(0) // never complete bar
for _, f := range []func(){
func() { bar.SetTotal(40, false) },
func() { bar.IncrBy(60) },
func() { bar.SetTotal(80, false) },
func() { bar.IncrBy(20) },
} {
f()
if bar.Completed() {
t.Fail()
}
}
bar.EnableTriggerComplete()
if !bar.Completed() {
t.Fail()
}
p.Wait()
}
func TestBarEnableTriggerCompleteAndIncrementAfter(t *testing.T) {
p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
bar := p.AddBar(0) // never complete bar
for _, f := range []func(){
func() { bar.SetTotal(40, false) },
func() { bar.IncrBy(60) },
func() { bar.SetTotal(80, false) },
func() { bar.EnableTriggerComplete() },
} {
f()
if bar.Completed() {
t.Fail()
}
}
bar.IncrBy(20)
if !bar.Completed() {
t.Fail()
}
p.Wait()
}
func TestBarID(t *testing.T) {
p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
total := 100
wantID := 11
bar := p.AddBar(int64(total), mpb.BarID(wantID))
gotID := bar.ID()
if gotID != wantID {
t.Errorf("Expected bar id: %d, got %d", wantID, gotID)
}
bar.IncrBy(total)
p.Wait()
}
func TestBarSetRefill(t *testing.T) {
var buf bytes.Buffer
p := mpb.New(
mpb.WithWidth(100),
mpb.WithOutput(&buf),
mpb.WithAutoRefresh(),
)
total := 100
till := 30
refiller := "+"
bar := p.New(int64(total), mpb.BarStyle().Refiller(refiller), mpb.BarFillerTrim())
bar.IncrBy(till)
bar.SetRefill(int64(till))
bar.IncrBy(total - till)
p.Wait()
wantBar := fmt.Sprintf("[%s%s]",
strings.Repeat(refiller, till-1),
strings.Repeat("=", total-till-1),
)
got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
if !strings.Contains(got, wantBar) {
t.Errorf("Want bar: %q, got bar: %q", wantBar, got)
}
}
func TestBarHas100PercentWithBarRemoveOnComplete(t *testing.T) {
var buf bytes.Buffer
p := mpb.New(
mpb.WithWidth(80),
mpb.WithOutput(&buf),
mpb.WithAutoRefresh(),
)
total := 50
bar := p.AddBar(int64(total),
mpb.BarRemoveOnComplete(),
mpb.AppendDecorators(decor.Percentage()),
)
bar.IncrBy(total)
p.Wait()
hundred := "100 %"
if !bytes.Contains(buf.Bytes(), []byte(hundred)) {
t.Errorf("Bar's buffer does not contain: %q", hundred)
}
}
func TestBarStyle(t *testing.T) {
var buf bytes.Buffer
customFormat := "╢▌▌░╟"
runes := []rune(customFormat)
total := 80
p := mpb.New(
mpb.WithWidth(80),
mpb.WithOutput(&buf),
mpb.WithAutoRefresh(),
)
bs := mpb.BarStyle()
bs = bs.Lbound(string(runes[0]))
bs = bs.Filler(string(runes[1]))
bs = bs.Tip(string(runes[2]))
bs = bs.Padding(string(runes[3]))
bs = bs.Rbound(string(runes[4]))
bar := p.New(int64(total), bs, mpb.BarFillerTrim())
bar.IncrBy(total)
p.Wait()
wantBar := fmt.Sprintf("%s%s%s%s",
string(runes[0]),
strings.Repeat(string(runes[1]), total-3),
string(runes[2]),
string(runes[4]),
)
got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
if !strings.Contains(got, wantBar) {
t.Errorf("Want bar: %q:%d, got bar: %q:%d", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
}
}
func TestDecorStatisticsAvailableWidth(t *testing.T) {
ch := make(chan int, 2)
td1 := func(s decor.Statistics) string {
ch <- s.AvailableWidth
return strings.Repeat("0", 20)
}
td2 := func(s decor.Statistics) string {
ch <- s.AvailableWidth
return ""
}
ctx, cancel := context.WithCancel(context.Background())
refresh := make(chan interface{})
p := mpb.NewWithContext(ctx,
mpb.WithWidth(100),
mpb.WithManualRefresh(refresh),
mpb.WithOutput(io.Discard),
)
_ = p.AddBar(0,
mpb.BarFillerTrim(),
mpb.PrependDecorators(
decor.Name(strings.Repeat("0", 20)),
decor.Meta(
decor.Any(td1),
func(s string) string {
return "\x1b[31;1m" + s + "\x1b[0m"
},
),
),
mpb.AppendDecorators(
decor.Name(strings.Repeat("0", 20)),
decor.Any(td2),
),
)
refresh <- time.Now()
go func() {
time.Sleep(10 * time.Millisecond)
cancel()
}()
p.Wait()
if availableWidth := <-ch; availableWidth != 80 {
t.Errorf("expected AvailableWidth %d got %d", 80, availableWidth)
}
if availableWidth := <-ch; availableWidth != 40 {
t.Errorf("expected AvailableWidth %d got %d", 40, availableWidth)
}
}
func TestBarQueueAfterBar(t *testing.T) {
shutdown := make(chan interface{})
ctx, cancel := context.WithCancel(context.Background())
p := mpb.NewWithContext(ctx,
mpb.WithOutput(io.Discard),
mpb.WithAutoRefresh(),
mpb.WithShutdownNotifier(shutdown),
)
a := p.AddBar(100)
b := p.AddBar(100, mpb.BarQueueAfter(a))
identity := map[*mpb.Bar]string{
a: "a",
b: "b",
}
a.IncrBy(100)
a.Wait()
cancel()
bars := (<-shutdown).([]*mpb.Bar)
if l := len(bars); l != 1 {
t.Errorf("Expected len of bars: %d, got: %d", 1, l)
}
p.Wait()
if bars[0] != b {
t.Errorf("Expected bars[0] == b, got: %s", identity[bars[0]])
}
}