github.com/schollz/progressbar/v3@v3.14.2/progressbar_test.go (about)

     1  package progressbar
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/md5"
     6  	"encoding/hex"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"os"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func TestMain(m *testing.M) {
    20  	termWidth = func() (int, error) {
    21  		return 0, os.ErrPermission
    22  	}
    23  	os.Exit(m.Run())
    24  }
    25  
    26  func BenchmarkRender(b *testing.B) {
    27  	bar := NewOptions64(100000000,
    28  		OptionSetWriter(os.Stderr),
    29  		OptionShowIts(),
    30  	)
    31  	for i := 0; i < b.N; i++ {
    32  		bar.Add(1)
    33  	}
    34  }
    35  
    36  func ExampleProgressBar() {
    37  	bar := New(100)
    38  	bar.Add(10)
    39  	// Output:
    40  	// 10% |████                                    |  [0s:0s]
    41  }
    42  
    43  func ExampleProgressBar_Set() {
    44  	bar := New(100)
    45  	bar.Set(10)
    46  	// Output:
    47  	// 10% |████                                    |  [0s:0s]
    48  }
    49  
    50  func ExampleProgressBar_Set64() {
    51  	bar := New(100)
    52  	bar.Set64(10)
    53  	// Output:
    54  	// 10% |████                                    |  [0s:0s]
    55  }
    56  
    57  func ExampleProgressBar_basic() {
    58  	bar := NewOptions(100, OptionSetWidth(10))
    59  	bar.Reset()
    60  	time.Sleep(1 * time.Second)
    61  	bar.Add(10)
    62  	// Output:
    63  	// 10% |█         |  [1s:9s]
    64  }
    65  
    66  func ExampleProgressBar_invisible() {
    67  	bar := NewOptions(100, OptionSetWidth(10), OptionSetRenderBlankState(true), OptionSetVisibility(false))
    68  	bar.Reset()
    69  	fmt.Println("hello, world")
    70  	time.Sleep(1 * time.Second)
    71  	bar.Add(10)
    72  	// Output:
    73  	// hello, world
    74  }
    75  
    76  func ExampleOptionThrottle() {
    77  	bar := NewOptions(100, OptionSetWidth(10), OptionThrottle(100*time.Millisecond))
    78  	bar.Reset()
    79  	bar.Add(5)
    80  	time.Sleep(150 * time.Millisecond)
    81  	bar.Add(5)
    82  	bar.Add(10)
    83  	// Output:
    84  	// 10% |█         |  [0s:1s]
    85  }
    86  
    87  func ExampleOptionClearOnFinish() {
    88  	bar := NewOptions(100, OptionSetWidth(10), OptionClearOnFinish())
    89  	bar.Reset()
    90  	bar.Finish()
    91  	fmt.Println("Finished")
    92  	// Output:
    93  	// Finished
    94  }
    95  
    96  func ExampleProgressBar_Finish() {
    97  	bar := NewOptions(100, OptionSetWidth(10))
    98  	bar.Finish()
    99  	// Output:
   100  	// 100% |██████████|
   101  }
   102  
   103  func Example_xOutOfY() {
   104  	bar := NewOptions(100, OptionSetPredictTime(true))
   105  
   106  	for i := 0; i < 100; i++ {
   107  		bar.Add(1)
   108  		time.Sleep(1 * time.Millisecond)
   109  	}
   110  }
   111  
   112  func ExampleOptionShowIts_count() {
   113  	bar := NewOptions(100, OptionSetWidth(10), OptionShowIts(), OptionShowCount())
   114  	bar.Reset()
   115  	time.Sleep(1 * time.Second)
   116  	bar.Add(10)
   117  	// Output:
   118  	// 10% |█         | (10/100, 10 it/s) [1s:9s]
   119  }
   120  
   121  func ExampleOptionShowIts() {
   122  	bar := NewOptions(100, OptionSetWidth(10), OptionShowIts(), OptionSetPredictTime(false))
   123  	bar.Reset()
   124  	time.Sleep(1 * time.Second)
   125  	bar.Add(10)
   126  	// Output:
   127  	// 10% |█         | (10 it/s)
   128  }
   129  
   130  func ExampleOptionShowCount_minuscule() {
   131  	bar := NewOptions(10000, OptionSetWidth(10), OptionShowCount(), OptionSetPredictTime(false))
   132  	bar.Add(1)
   133  	// Output:
   134  	// 0% |          | (1/10000)
   135  }
   136  
   137  func ExampleOptionSetPredictTime() {
   138  	bar := NewOptions(100, OptionSetWidth(10), OptionSetPredictTime(false))
   139  	_ = bar.Add(10)
   140  	// Output:
   141  	// 10% |█         |
   142  }
   143  
   144  func ExampleOptionShowDescriptionAtLineEnd() {
   145  	bar := NewOptions(100, OptionSetWidth(10), OptionShowDescriptionAtLineEnd(), OptionSetDescription("hello"))
   146  	_ = bar.Add(10)
   147  	// Output:
   148  	// 10% |█         |  [0s:0s] hello
   149  }
   150  
   151  func ExampleOptionShowDescriptionAtLineEnd_spinner() {
   152  	bar := NewOptions(-1, OptionSetWidth(10), OptionShowDescriptionAtLineEnd(), OptionSetDescription("hello"))
   153  	_ = bar.Add(1)
   154  	// Output:
   155  	// |  [0s] hello
   156  }
   157  
   158  func ExampleDefault() {
   159  	bar := Default(100)
   160  	for i := 0; i < 50; i++ {
   161  		bar.Add(1)
   162  		time.Sleep(10 * time.Millisecond)
   163  	}
   164  	// Output:
   165  	//
   166  }
   167  
   168  func ExampleProgressBar_ChangeMax() {
   169  	bar := NewOptions(100, OptionSetWidth(10), OptionSetPredictTime(false))
   170  	bar.ChangeMax(50)
   171  	bar.Add(50)
   172  	// Output:
   173  	// 100% |██████████|
   174  }
   175  
   176  func ExampleOptionShowIts_spinner() {
   177  	/*
   178  		Spinner test with iteration count and iteration rate
   179  	*/
   180  	bar := NewOptions(-1,
   181  		OptionSetWidth(10),
   182  		OptionShowIts(),
   183  		OptionShowCount(),
   184  	)
   185  	bar.Reset()
   186  	time.Sleep(1 * time.Second)
   187  	bar.Add(5)
   188  
   189  	// Output:
   190  	// -  (5/-, 5 it/s) [1s]
   191  }
   192  
   193  func TestSpinnerType(t *testing.T) {
   194  	bar := NewOptions(-1,
   195  		OptionSetWidth(10),
   196  		OptionSetDescription("indeterminate spinner"),
   197  		OptionShowIts(),
   198  		OptionShowCount(),
   199  		OptionSpinnerType(9),
   200  	)
   201  	bar.Reset()
   202  	for i := 0; i < 10; i++ {
   203  		time.Sleep(120 * time.Millisecond)
   204  		err := bar.Add(1)
   205  		if err != nil {
   206  			t.Errorf("Successfully tested one spinner option can be used.")
   207  		}
   208  	}
   209  	if false {
   210  		t.Errorf("error")
   211  	}
   212  }
   213  
   214  func TestSpinnerCustom(t *testing.T) {
   215  	bar := NewOptions(-1,
   216  		OptionSetWidth(10),
   217  		OptionSetDescription("indeterminate spinner"),
   218  		OptionShowIts(),
   219  		OptionShowCount(),
   220  		OptionSpinnerCustom([]string{"🐰", "🐰", "🥕", "🥕"}),
   221  	)
   222  	bar.Reset()
   223  	for i := 0; i < 10; i++ {
   224  		time.Sleep(120 * time.Millisecond)
   225  		err := bar.Add(1)
   226  		if err != nil {
   227  			t.Errorf("Successfully tested one spinner option can be used.")
   228  		}
   229  	}
   230  	if false {
   231  		t.Errorf("error")
   232  	}
   233  }
   234  
   235  func TestSpinnerTypeAndCustom(t *testing.T) {
   236  	bar := NewOptions(-1,
   237  		OptionSetWidth(10),
   238  		OptionSetDescription("indeterminate spinner"),
   239  		OptionShowIts(),
   240  		OptionShowCount(),
   241  		OptionSpinnerCustom([]string{"🐰", "🐰", "🥕", "🥕"}),
   242  		OptionSpinnerType(9),
   243  	)
   244  	bar.Reset()
   245  	for i := 0; i < 10; i++ {
   246  		time.Sleep(120 * time.Millisecond)
   247  		err := bar.Add(1)
   248  		if err == nil {
   249  			t.Errorf("Successfully tested both spinner options cannot be used together.")
   250  		}
   251  	}
   252  }
   253  
   254  func Test_IsFinished(t *testing.T) {
   255  	isCalled := false
   256  	bar := NewOptions(72, OptionOnCompletion(func() {
   257  		isCalled = true
   258  	}))
   259  
   260  	// Test1: If bar is not fully completed.
   261  	bar.Add(5)
   262  	if bar.IsFinished() || isCalled {
   263  		t.Errorf("Successfully tested bar is not yet finished.")
   264  	}
   265  
   266  	// Test2: Bar fully completed.
   267  	bar.Add(67)
   268  	if !bar.IsFinished() || !isCalled {
   269  		t.Errorf("Successfully tested bar is finished.")
   270  	}
   271  
   272  	// Test3: If increases maximum bytes error should be thrown and
   273  	// bar finished will remain false.
   274  	bar.Reset()
   275  	err := bar.Add(73)
   276  	if err == nil || bar.IsFinished() {
   277  		t.Errorf("Successfully got error when bytes increases max bytes, bar finished: %v", bar.IsFinished())
   278  	}
   279  }
   280  
   281  func ExampleOptionShowBytes_spinner() {
   282  	/*
   283  		Spinner test with iterations and count
   284  	*/
   285  	bar := NewOptions(-1,
   286  		OptionSetWidth(10),
   287  		OptionShowBytes(true),
   288  	)
   289  
   290  	bar.Reset()
   291  	time.Sleep(1 * time.Second)
   292  	// since 10 is the width and we don't know the max bytes
   293  	// it will do a infinite scrolling.
   294  	bar.Add(11)
   295  
   296  	// Output:
   297  	// -  (11 B/s) [1s]
   298  }
   299  
   300  func TestBarSlowAdd(t *testing.T) {
   301  	buf := strings.Builder{}
   302  	bar := NewOptions(100, OptionSetWidth(10), OptionShowIts(), OptionSetWriter(&buf))
   303  	bar.Reset()
   304  	time.Sleep(3 * time.Second)
   305  	bar.Add(1)
   306  	if !strings.Contains(buf.String(), "1%") {
   307  		t.Errorf("wrong string: %s", buf.String())
   308  	}
   309  	if !strings.Contains(buf.String(), "20 it/min") {
   310  		t.Errorf("wrong string: %s", buf.String())
   311  	}
   312  	if !strings.Contains(buf.String(), "[3s:") {
   313  		t.Errorf("wrong string: %s", buf.String())
   314  	}
   315  	// Output:
   316  	// 1% |          | (20 it/min) [3s:4m57s]
   317  }
   318  
   319  func TestBarSmallBytes(t *testing.T) {
   320  	buf := strings.Builder{}
   321  	bar := NewOptions64(100000000, OptionShowBytes(true), OptionShowCount(), OptionSetWidth(10), OptionSetWriter(&buf))
   322  	for i := 1; i < 10; i++ {
   323  		time.Sleep(100 * time.Millisecond)
   324  		bar.Add(1000)
   325  	}
   326  	if !strings.Contains(buf.String(), "9.0 kB/100 MB") {
   327  		t.Errorf("wrong string: %s", buf.String())
   328  	}
   329  	for i := 1; i < 10; i++ {
   330  		time.Sleep(10 * time.Millisecond)
   331  		bar.Add(1000000)
   332  	}
   333  	if !strings.Contains(buf.String(), "9.0/100 MB") {
   334  		t.Errorf("wrong string: %s", buf.String())
   335  	}
   336  }
   337  
   338  func TestBarFastBytes(t *testing.T) {
   339  	buf := strings.Builder{}
   340  	bar := NewOptions64(1e8, OptionShowBytes(true), OptionShowCount(), OptionSetWidth(10), OptionSetWriter(&buf))
   341  	time.Sleep(time.Millisecond)
   342  	bar.Add(1e7)
   343  	if !strings.Contains(buf.String(), " GB/s)") {
   344  		t.Errorf("wrong string: %s", buf.String())
   345  	}
   346  }
   347  
   348  func TestBar(t *testing.T) {
   349  	bar := New(0)
   350  	if err := bar.Add(1); err == nil {
   351  		t.Error("should have an error for 0 bar")
   352  	}
   353  	bar = New(10)
   354  	if err := bar.Add(11); err == nil {
   355  		t.Error("should have an error for adding > bar")
   356  	}
   357  }
   358  
   359  func TestState(t *testing.T) {
   360  	bar := NewOptions(100, OptionSetWidth(10))
   361  	bar.Reset()
   362  	time.Sleep(1 * time.Second)
   363  	bar.Add(10)
   364  	s := bar.State()
   365  	if s.CurrentPercent != 0.1 {
   366  		t.Error(s)
   367  	}
   368  }
   369  
   370  func ExampleOptionSetRenderBlankState() {
   371  	NewOptions(10, OptionSetWidth(10), OptionSetRenderBlankState(true))
   372  	// Output:
   373  	// 0% |          |  [0s:0s]
   374  }
   375  
   376  func TestBasicSets(t *testing.T) {
   377  	b := NewOptions(
   378  		999,
   379  		OptionSetWidth(888),
   380  		OptionSetRenderBlankState(true),
   381  		OptionSetWriter(io.Discard), // suppressing output for this test
   382  	)
   383  
   384  	tc := b.config
   385  
   386  	if tc.max != 999 {
   387  		t.Errorf("Expected %s to be %d, instead I got %d\n%+v", "max", 999, tc.max, b)
   388  	}
   389  
   390  	if tc.width != 888 {
   391  		t.Errorf("Expected %s to be %d, instead I got %d\n%+v", "width", 999, tc.max, b)
   392  	}
   393  
   394  	if !tc.renderWithBlankState {
   395  		t.Errorf("Expected %s to be %t, instead I got %t\n%+v", "renderWithBlankState", true, tc.renderWithBlankState, b)
   396  	}
   397  }
   398  
   399  func TestOptionSetTheme(t *testing.T) {
   400  	buf := strings.Builder{}
   401  	bar := NewOptions(
   402  		10,
   403  		OptionSetTheme(Theme{Saucer: "#", SaucerPadding: "-", BarStart: ">", BarEnd: "<"}),
   404  		OptionSetWidth(10),
   405  		OptionSetWriter(&buf),
   406  	)
   407  	bar.Add(5)
   408  	result := strings.TrimSpace(buf.String())
   409  	expect := "50% >#####-----<  [0s:0s]"
   410  	if result != expect {
   411  		t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
   412  	}
   413  }
   414  
   415  // TestOptionSetPredictTime ensures that when predict time is turned off, the progress
   416  // bar is showing the total steps completed of the given max, otherwise the predicted
   417  // time in seconds is specified.
   418  func TestOptionSetPredictTime(t *testing.T) {
   419  	buf := strings.Builder{}
   420  	bar := NewOptions(
   421  		10,
   422  		OptionSetPredictTime(false),
   423  		OptionSetWidth(10),
   424  		OptionSetWriter(&buf),
   425  	)
   426  
   427  	_ = bar.Add(2)
   428  	result := strings.TrimSpace(buf.String())
   429  	expect := "20% |██        |"
   430  
   431  	if result != expect {
   432  		t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
   433  	}
   434  
   435  	bar.Reset()
   436  	bar.config.predictTime = true
   437  	buf.Reset()
   438  
   439  	_ = bar.Add(7)
   440  	result = strings.TrimSpace(buf.String())
   441  	expect = "70% |███████   |  [0s:0s]"
   442  
   443  	if result != expect {
   444  		t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
   445  	}
   446  }
   447  
   448  func TestOptionSetElapsedTime_spinner(t *testing.T) {
   449  	buf := strings.Builder{}
   450  	bar := NewOptions(-1,
   451  		OptionSetWidth(10),
   452  		OptionSetWriter(&buf),
   453  		OptionShowIts(),
   454  		OptionShowCount(),
   455  		OptionSetElapsedTime(false),
   456  	)
   457  	bar.Reset()
   458  	time.Sleep(1 * time.Second)
   459  	bar.Add(5)
   460  	result := strings.TrimSpace(buf.String())
   461  	expect := "-  (5/-, 5 it/s)"
   462  	if result != expect {
   463  		t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
   464  	}
   465  }
   466  
   467  func TestShowElapsedTimeOnFinish(t *testing.T) {
   468  	buf := strings.Builder{}
   469  	bar := NewOptions(10,
   470  		OptionShowElapsedTimeOnFinish(),
   471  		OptionSetWidth(10),
   472  		OptionSetWriter(&buf),
   473  	)
   474  	bar.Reset()
   475  	time.Sleep(3 * time.Second)
   476  	bar.Add(10)
   477  	result := strings.TrimSpace(buf.String())
   478  	expect := "100% |██████████|  [3s]"
   479  	if result != expect {
   480  		t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
   481  	}
   482  }
   483  
   484  func TestSpinnerState(t *testing.T) {
   485  	bar := NewOptions(
   486  		-1,
   487  		OptionSetWidth(100),
   488  	)
   489  	bar.Reset()
   490  	time.Sleep(1 * time.Second)
   491  	bar.Add(10)
   492  
   493  	state := bar.State()
   494  	if state.Max != -1 {
   495  		t.Errorf("Max mismatched gotMax %d wantMax %d", state.Max, -1)
   496  	}
   497  	if state.CurrentNum != 10 {
   498  		t.Errorf("Number mismatched gotNum %d wantNum %d", state.CurrentNum, 10)
   499  	}
   500  	if state.CurrentBytes != 10.0 {
   501  		t.Errorf("Number of bytes mismatched gotBytes %f wantBytes %f", state.CurrentBytes, 10.0)
   502  	}
   503  	if state.CurrentPercent != 0.1 {
   504  		t.Errorf("Percent of bar mismatched got %f want %f", state.CurrentPercent, 0.1)
   505  	}
   506  
   507  	kbPerSec := fmt.Sprintf("%2.2f", state.KBsPerSecond)
   508  	if kbPerSec != "0.01" {
   509  		t.Errorf("Speed mismatched got %s want %s", kbPerSec, "0.01")
   510  	}
   511  }
   512  
   513  func TestReaderToBuffer(t *testing.T) {
   514  	if testing.Short() {
   515  		t.SkipNow()
   516  	}
   517  
   518  	urlToGet := "https://dl.google.com/go/go1.14.1.src.tar.gz"
   519  	req, err := http.NewRequest("GET", urlToGet, nil)
   520  	assert.Nil(t, err)
   521  	resp, err := http.DefaultClient.Do(req)
   522  	assert.Nil(t, err)
   523  	defer resp.Body.Close()
   524  
   525  	buf := new(bytes.Buffer)
   526  	bar := NewOptions(int(resp.ContentLength), OptionShowBytes(true), OptionShowCount())
   527  	out := io.MultiWriter(buf, bar)
   528  	_, err = io.Copy(out, resp.Body)
   529  	assert.Nil(t, err)
   530  
   531  	md5, err := md5sum(buf)
   532  	assert.Nil(t, err)
   533  	assert.Equal(t, "d441819a800f8c90825355dfbede7266", md5)
   534  }
   535  
   536  func TestReaderToFile(t *testing.T) {
   537  	if testing.Short() {
   538  		t.SkipNow()
   539  	}
   540  
   541  	urlToGet := "https://dl.google.com/go/go1.14.1.src.tar.gz"
   542  	req, err := http.NewRequest("GET", urlToGet, nil)
   543  	assert.Nil(t, err)
   544  	resp, err := http.DefaultClient.Do(req)
   545  	assert.Nil(t, err)
   546  	defer resp.Body.Close()
   547  
   548  	f, err := os.CreateTemp("", "progressbar_testfile")
   549  	if err != nil {
   550  		t.Fatal()
   551  	}
   552  	defer os.Remove(f.Name())
   553  	defer f.Close()
   554  
   555  	realStdout := os.Stdout
   556  	defer func() { os.Stdout = realStdout }()
   557  	r, fakeStdout, err := os.Pipe()
   558  	if err != nil {
   559  		t.Fatal(err)
   560  	}
   561  	os.Stdout = fakeStdout
   562  
   563  	bar := DefaultBytes(resp.ContentLength)
   564  	out := io.MultiWriter(f, bar)
   565  	_, err = io.Copy(out, resp.Body)
   566  	assert.Nil(t, err)
   567  	f.Sync()
   568  	f.Seek(0, 0)
   569  
   570  	if err := fakeStdout.Close(); err != nil {
   571  		t.Fatal(err)
   572  	}
   573  
   574  	b, err := io.ReadAll(r)
   575  	if err != nil {
   576  		t.Fatal(err)
   577  	}
   578  
   579  	if err := r.Close(); err != nil {
   580  		t.Fatal(err)
   581  	}
   582  
   583  	assert.Equal(t, "", string(b))
   584  
   585  	md5, err := md5sum(f)
   586  	assert.Nil(t, err)
   587  	assert.Equal(t, "d441819a800f8c90825355dfbede7266", md5)
   588  }
   589  
   590  func TestReaderToFileUnknownLength(t *testing.T) {
   591  	if testing.Short() {
   592  		t.SkipNow()
   593  	}
   594  
   595  	urlToGet := "https://dl.google.com/go/go1.14.1.src.tar.gz"
   596  	req, err := http.NewRequest("GET", urlToGet, nil)
   597  	assert.Nil(t, err)
   598  	resp, err := http.DefaultClient.Do(req)
   599  	assert.Nil(t, err)
   600  	defer resp.Body.Close()
   601  
   602  	f, err := os.CreateTemp("", "progressbar_testfile")
   603  	if err != nil {
   604  		t.Fatal()
   605  	}
   606  	defer os.Remove(f.Name())
   607  	defer f.Close()
   608  
   609  	realStdout := os.Stdout
   610  	defer func() { os.Stdout = realStdout }()
   611  	r, fakeStdout, err := os.Pipe()
   612  	if err != nil {
   613  		t.Fatal(err)
   614  	}
   615  	os.Stdout = fakeStdout
   616  
   617  	bar := DefaultBytes(-1, " downloading")
   618  	out := io.MultiWriter(f, bar)
   619  	_, err = io.Copy(out, resp.Body)
   620  	assert.Nil(t, err)
   621  	f.Sync()
   622  	f.Seek(0, 0)
   623  
   624  	if err := fakeStdout.Close(); err != nil {
   625  		t.Fatal(err)
   626  	}
   627  
   628  	b, err := io.ReadAll(r)
   629  	if err != nil {
   630  		t.Fatal(err)
   631  	}
   632  
   633  	if err := r.Close(); err != nil {
   634  		t.Fatal(err)
   635  	}
   636  
   637  	assert.Equal(t, "", string(b))
   638  
   639  	md5, err := md5sum(f)
   640  	assert.Nil(t, err)
   641  	assert.Equal(t, "d441819a800f8c90825355dfbede7266", md5)
   642  }
   643  
   644  func TestConcurrency(t *testing.T) {
   645  	buf := strings.Builder{}
   646  	bar := NewOptions(
   647  		1000,
   648  		OptionSetWriter(&buf),
   649  	)
   650  	var wg sync.WaitGroup
   651  	for i := 0; i < 900; i++ {
   652  		wg.Add(1)
   653  		go func(b *ProgressBar, wg *sync.WaitGroup) {
   654  			bar.Add(1)
   655  			wg.Done()
   656  		}(bar, &wg)
   657  	}
   658  	wg.Wait()
   659  	result := bar.state.currentNum
   660  	expect := int64(900)
   661  	assert.Equal(t, expect, result)
   662  }
   663  
   664  func TestIterationNames(t *testing.T) {
   665  	b := Default(20)
   666  	tc := b.config
   667  
   668  	// Checking for the default iterations per second or "it/s"
   669  	if tc.iterationString != "it" {
   670  		t.Errorf("Expected %s to be %s, instead I got %s", "iterationString", "it", tc.iterationString)
   671  	}
   672  
   673  	// Change the default "it/s" to provide context, downloads per second or "dl/s"
   674  	b = NewOptions(20, OptionSetItsString("dl"))
   675  	tc = b.config
   676  
   677  	if tc.iterationString != "dl" {
   678  		t.Errorf("Expected %s to be %s, instead I got %s", "iterationString", "dl", tc.iterationString)
   679  	}
   680  }
   681  
   682  func md5sum(r io.Reader) (string, error) {
   683  	hash := md5.New()
   684  	_, err := io.Copy(hash, r)
   685  	return hex.EncodeToString(hash.Sum(nil)), err
   686  }
   687  
   688  func TestProgressBar_Describe(t *testing.T) {
   689  	buf := strings.Builder{}
   690  	bar := NewOptions(100, OptionSetWidth(10), OptionSetWriter(&buf))
   691  	bar.Describe("performing axial adjustments")
   692  	bar.Add(10)
   693  	result := buf.String()
   694  	expect := "" +
   695  		"\rperforming axial adjustments   0% |          |  [0s:0s]" +
   696  		"\r                                                       \r" +
   697  		"\rperforming axial adjustments  10% |█         |  [0s:0s]"
   698  	if result != expect {
   699  		t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
   700  	}
   701  }
   702  
   703  func TestRenderBlankStateWithThrottle(t *testing.T) {
   704  	buf := strings.Builder{}
   705  	bar := NewOptions(100, OptionSetWidth(10), OptionSetRenderBlankState(true), OptionThrottle(time.Millisecond), OptionSetWriter(&buf))
   706  	result := strings.TrimSpace(buf.String())
   707  	expect := "0% |          |  [0s:0s]"
   708  	if result != expect {
   709  		t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
   710  	}
   711  }
   712  
   713  func TestOptionFullWidth(t *testing.T) {
   714  	var tests = []struct {
   715  		opts     []Option
   716  		expected string
   717  	}{
   718  		{ // 1
   719  			[]Option{},
   720  			"" +
   721  				"\r  10% |██████                                                        |  [1s:9s]" +
   722  				"\r                                                                               \r" +
   723  				"\r 100% |██████████████████████████████████████████████████████████████| ",
   724  		},
   725  		{ // 2
   726  			[]Option{OptionSetDescription("Progress:")},
   727  			"" +
   728  				"\rProgress:  10% |█████                                                |  [1s:9s]" +
   729  				"\r                                                                               \r" +
   730  				"\rProgress: 100% |█████████████████████████████████████████████████████| ",
   731  		},
   732  		{ // 3
   733  			[]Option{OptionSetDescription("<1/5>"), OptionShowDescriptionAtLineEnd()},
   734  			"" +
   735  				"\r  10% |█████                                                   |  [1s:9s] <1/5>" +
   736  				"\r                                                                               \r" +
   737  				"\r 100% |████████████████████████████████████████████████████████|  <1/5>",
   738  		},
   739  		{ // 4
   740  			[]Option{OptionSetPredictTime(false)},
   741  			"" +
   742  				"\r  10% |██████                                                          |  " +
   743  				"\r                                                                          \r" +
   744  				"\r 100% |████████████████████████████████████████████████████████████████|  ",
   745  		},
   746  		{ // 5
   747  			[]Option{OptionSetPredictTime(false), OptionShowElapsedTimeOnFinish()},
   748  			"" +
   749  				"\r  10% |██████                                                          |  " +
   750  				"\r                                                                          \r" +
   751  				"\r 100% |████████████████████████████████████████████████████████████████|  [2s] ",
   752  		},
   753  		{ // 6
   754  			[]Option{OptionSetPredictTime(false), OptionSetElapsedTime(false)},
   755  			"" +
   756  				"\r  10% |██████                                                               |  " +
   757  				"\r                                                                               \r" +
   758  				"\r 100% |█████████████████████████████████████████████████████████████████████|  ",
   759  		},
   760  		{ // 7
   761  			[]Option{OptionShowIts()},
   762  			"" +
   763  				"\r  10% |█████                                                | (10 it/s) [1s:9s]" +
   764  				"\r                                                                               \r" +
   765  				"\r 100% |█████████████████████████████████████████████████████| (50 it/s)",
   766  		},
   767  		{ // 8
   768  			[]Option{OptionShowCount()},
   769  			"" +
   770  				"\r  10% |█████                                                 | (10/100) [1s:9s]" +
   771  				"\r                                                                               \r" +
   772  				"\r 100% |█████████████████████████████████████████████████████| (100/100)",
   773  		},
   774  		{ // 9
   775  			[]Option{OptionShowIts(), OptionShowCount(), OptionShowElapsedTimeOnFinish()},
   776  			"" +
   777  				"\r  10% |████                                         | (10/100, 10 it/s) [1s:9s]" +
   778  				"\r                                                                               \r" +
   779  				"\r 100% |████████████████████████████████████████████| (100/100, 50 it/s) [2s]",
   780  		},
   781  		{ // 10
   782  			[]Option{OptionSetDescription("Progress:"), OptionShowIts(), OptionShowCount()},
   783  			"" +
   784  				"\rProgress:  10% |███                                 | (10/100, 10 it/s) [1s:9s]" +
   785  				"\r                                                                               \r" +
   786  				"\rProgress: 100% |███████████████████████████████████| (100/100, 50 it/s)",
   787  		},
   788  		{ // 11
   789  			[]Option{OptionSetDescription("<3/5>"), OptionShowIts(), OptionShowCount(), OptionShowElapsedTimeOnFinish(), OptionShowDescriptionAtLineEnd()},
   790  			"" +
   791  				"\r  10% |███                                    | (10/100, 10 it/s) [1s:9s] <3/5>" +
   792  				"\r                                                                               \r" +
   793  				"\r 100% |██████████████████████████████████████| (100/100, 50 it/s) [2s] <3/5>",
   794  		},
   795  		{ // 12
   796  			[]Option{OptionShowIts(), OptionShowCount(), OptionSetPredictTime(false)},
   797  			"" +
   798  				"\r  10% |████                                           | (10/100, 10 it/s) " +
   799  				"\r                                                                          \r" +
   800  				"\r 100% |██████████████████████████████████████████████| (100/100, 50 it/s) ",
   801  		},
   802  		{ // 13
   803  			[]Option{OptionShowIts(), OptionShowCount(), OptionSetPredictTime(false), OptionShowElapsedTimeOnFinish()},
   804  			"" +
   805  				"\r  10% |████                                           | (10/100, 10 it/s) " +
   806  				"\r                                                                          \r" +
   807  				"\r 100% |██████████████████████████████████████████████| (100/100, 50 it/s) [2s] ",
   808  		},
   809  		{ // 14
   810  			[]Option{OptionShowIts(), OptionShowCount(), OptionSetPredictTime(false), OptionSetElapsedTime(false)},
   811  			"" +
   812  				"\r  10% |█████                                               | (10/100, 10 it/s) " +
   813  				"\r                                                                               \r" +
   814  				"\r 100% |███████████████████████████████████████████████████| (100/100, 50 it/s) ",
   815  		},
   816  	}
   817  
   818  	for i, test := range tests {
   819  		test := test
   820  		t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
   821  			t.Parallel()
   822  			buf := strings.Builder{}
   823  			bar := NewOptions(100, append(test.opts, []Option{OptionFullWidth(), OptionSetWriter(&buf)}...)...)
   824  			time.Sleep(1 * time.Second)
   825  			bar.Add(10)
   826  			time.Sleep(1 * time.Second)
   827  			bar.Add(90)
   828  			assert.Equal(t, test.expected, buf.String())
   829  		})
   830  	}
   831  }
   832  
   833  func TestHumanizeBytesSI(t *testing.T) {
   834  	amount, suffix := humanizeBytes(float64(12.34)*1000*1000, false)
   835  	assert.Equal(t, "12 MB", fmt.Sprintf("%s%s", amount, suffix))
   836  
   837  	amount, suffix = humanizeBytes(float64(56.78)*1000*1000*1000, false)
   838  	assert.Equal(t, "57 GB", fmt.Sprintf("%s%s", amount, suffix))
   839  }
   840  
   841  func TestHumanizeBytesIEC(t *testing.T) {
   842  	amount, suffix := humanizeBytes(float64(12.34)*1024*1024, true)
   843  	assert.Equal(t, "12 MiB", fmt.Sprintf("%s%s", amount, suffix))
   844  
   845  	amount, suffix = humanizeBytes(float64(56.78)*1024*1024*1024, true)
   846  	assert.Equal(t, "57 GiB", fmt.Sprintf("%s%s", amount, suffix))
   847  }