1package pb 2 3import ( 4 "fmt" 5 "strings" 6 "testing" 7 "time" 8 9 "github.com/fatih/color" 10) 11 12func testState(total, value int64, maxWidth int, bools ...bool) (s *State) { 13 s = &State{ 14 total: total, 15 current: value, 16 adaptiveElWidth: maxWidth, 17 ProgressBar: new(ProgressBar), 18 } 19 if len(bools) > 0 { 20 s.Set(Bytes, bools[0]) 21 } 22 if len(bools) > 1 && bools[1] { 23 s.adaptive = true 24 } 25 return 26} 27 28func testElementBarString(t *testing.T, state *State, el Element, want string, args ...string) { 29 if state.ProgressBar == nil { 30 state.ProgressBar = new(ProgressBar) 31 } 32 res := el.ProgressElement(state, args...) 33 if res != want { 34 t.Errorf("Unexpected result: '%s'; want: '%s'", res, want) 35 } 36 if state.IsAdaptiveWidth() && state.AdaptiveElWidth() != CellCount(res) { 37 t.Errorf("Unepected width: %d; want: %d", CellCount(res), state.AdaptiveElWidth()) 38 } 39} 40 41func TestElementPercent(t *testing.T) { 42 testElementBarString(t, testState(100, 50, 0), ElementPercent, "50.00%") 43 testElementBarString(t, testState(100, 50, 0), ElementPercent, "50 percent", "%v percent") 44 testElementBarString(t, testState(0, 50, 0), ElementPercent, "?%") 45 testElementBarString(t, testState(0, 50, 0), ElementPercent, "unkn", "%v%%", "unkn") 46} 47 48func TestElementCounters(t *testing.T) { 49 testElementBarString(t, testState(100, 50, 0), ElementCounters, "50 / 100") 50 testElementBarString(t, testState(100, 50, 0), ElementCounters, "50 of 100", "%s of %s") 51 testElementBarString(t, testState(100, 50, 0, true), ElementCounters, "50 B of 100 B", "%s of %s") 52 testElementBarString(t, testState(100, 50, 0, true), ElementCounters, "50 B / 100 B") 53 testElementBarString(t, testState(0, 50, 0, true), ElementCounters, "50 B") 54 testElementBarString(t, testState(0, 50, 0, true), ElementCounters, "50 B / ?", "", "%[1]s / ?") 55} 56 57func TestElementBar(t *testing.T) { 58 // short 59 testElementBarString(t, testState(100, 50, 1, false, true), ElementBar, "[") 60 testElementBarString(t, testState(100, 50, 2, false, true), ElementBar, "[]") 61 testElementBarString(t, testState(100, 50, 3, false, true), ElementBar, "[>]") 62 testElementBarString(t, testState(100, 50, 4, false, true), ElementBar, "[>_]") 63 testElementBarString(t, testState(100, 50, 5, false, true), ElementBar, "[->_]") 64 // middle 65 testElementBarString(t, testState(100, 50, 10, false, true), ElementBar, "[--->____]") 66 testElementBarString(t, testState(100, 50, 10, false, true), ElementBar, "<--->____>", "<", "", "", "", ">") 67 // finished 68 st := testState(100, 100, 10, false, true) 69 st.finished = true 70 testElementBarString(t, st, ElementBar, "[--------]") 71 // empty color 72 st = testState(100, 50, 10, false, true) 73 st.Set(Terminal, true) 74 color.NoColor = false 75 testElementBarString(t, st, ElementBar, " --->____]", color.RedString("%s", "")) 76 // empty 77 testElementBarString(t, testState(0, 50, 10, false, true), ElementBar, "[________]") 78 // full 79 testElementBarString(t, testState(20, 20, 10, false, true), ElementBar, "[------->]") 80 // everflow 81 testElementBarString(t, testState(20, 50, 10, false, true), ElementBar, "[------->]") 82 // small width 83 testElementBarString(t, testState(20, 50, 2, false, true), ElementBar, "[]") 84 testElementBarString(t, testState(20, 50, 1, false, true), ElementBar, "[") 85 // negative counters 86 testElementBarString(t, testState(-50, -150, 10, false, true), ElementBar, "[------->]") 87 testElementBarString(t, testState(-150, -50, 10, false, true), ElementBar, "[-->_____]") 88 testElementBarString(t, testState(50, -150, 10, false, true), ElementBar, "[------->]") 89 testElementBarString(t, testState(-50, 150, 10, false, true), ElementBar, "[------->]") 90 // long entities / unicode 91 f1 := []string{"進捗|", "многобайт", "active", "пусто", "|end"} 92 testElementBarString(t, testState(100, 50, 1, false, true), ElementBar, " ", f1...) 93 testElementBarString(t, testState(100, 50, 3, false, true), ElementBar, "進 ", f1...) 94 testElementBarString(t, testState(100, 50, 4, false, true), ElementBar, "進捗", f1...) 95 testElementBarString(t, testState(100, 50, 29, false, true), ElementBar, "進捗|многactiveпустопусто|end", f1...) 96 testElementBarString(t, testState(100, 50, 11, false, true), ElementBar, "進捗|aп|end", f1...) 97 98 // unicode 99 f2 := []string{"⚑", ".", ">", "⟞", "⚐"} 100 testElementBarString(t, testState(100, 50, 8, false, true), ElementBar, "⚑..>⟞⟞⟞⚐", f2...) 101 102 // no adaptive 103 testElementBarString(t, testState(0, 50, 10), ElementBar, "[____________________________]") 104 105 var formats = [][]string{ 106 []string{}, 107 f1, f2, 108 } 109 110 // all widths / extreme values 111 // check for panic and correct width 112 for _, f := range formats { 113 for tt := int64(-2); tt < 12; tt++ { 114 for v := int64(-2); v < 12; v++ { 115 state := testState(tt, v, 0, false, true) 116 for w := -2; w < 20; w++ { 117 state.adaptiveElWidth = w 118 res := ElementBar(state, f...) 119 var we = w 120 if we <= 0 { 121 we = 30 122 } 123 if CellCount(res) != we { 124 t.Errorf("Unexpected len(%d): '%s'", we, res) 125 } 126 } 127 } 128 } 129 } 130} 131 132func TestElementSpeed(t *testing.T) { 133 var state = testState(1000, 0, 0, false) 134 state.time = time.Now() 135 for i := int64(0); i < 10; i++ { 136 state.id = uint64(i) + 1 137 state.current += 42 138 state.time = state.time.Add(time.Second) 139 state.finished = i == 9 140 if state.finished { 141 state.current += 100 142 } 143 r := ElementSpeed(state) 144 r2 := ElementSpeed(state) 145 if r != r2 { 146 t.Errorf("Must be the same: '%s' vs '%s'", r, r2) 147 } 148 if i < 1 { 149 // do not calc first result 150 if w := "? p/s"; r != w { 151 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 152 } 153 } else if state.finished { 154 if w := "58 p/s"; r != w { 155 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 156 } 157 state.time = state.time.Add(-time.Hour) 158 r = ElementSpeed(state) 159 if w := "? p/s"; r != w { 160 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 161 } 162 } else { 163 if w := "42 p/s"; r != w { 164 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 165 } 166 } 167 } 168} 169 170func TestElementRemainingTime(t *testing.T) { 171 var state = testState(100, 0, 0, false) 172 state.time = time.Now() 173 state.startTime = state.time 174 for i := int64(0); i < 10; i++ { 175 state.id = uint64(i) + 1 176 state.time = state.time.Add(time.Second) 177 state.finished = i == 9 178 r := ElementRemainingTime(state) 179 if i < 1 { 180 // do not calc first two results 181 if w := "?"; r != w { 182 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 183 } 184 } else if state.finished { 185 // final elapsed time 186 if w := "10s"; r != w { 187 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 188 } 189 } else { 190 w := fmt.Sprintf("%ds", 10-i) 191 if r != w { 192 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 193 } 194 } 195 state.current += 10 196 } 197} 198 199func TestElementElapsedTime(t *testing.T) { 200 t.Run("default behavior", func(t *testing.T) { 201 var state = testState(1000, 0, 0, false) 202 state.startTime = time.Now() 203 state.time = state.startTime 204 for i := int64(0); i <= 12; i++ { 205 r := ElementElapsedTime(state) 206 w := fmt.Sprintf("%d.0s", i) 207 if i == 0 || i >= 10 { 208 w = fmt.Sprintf("%ds", i) 209 } 210 if r != w { 211 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 212 } 213 state.time = state.time.Add(time.Second) 214 } 215 }) 216 t.Run("with round set", func(t *testing.T) { 217 var state = testState(1000, 0, 0, false) 218 state.Set(TimeRound, time.Second) 219 state.startTime = time.Now() 220 state.time = state.startTime 221 for i := int64(0); i <= 10; i++ { 222 r := ElementElapsedTime(state) 223 w := fmt.Sprintf("%ds", i) 224 if r != w { 225 t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) 226 } 227 state.time = state.time.Add(time.Second) 228 } 229 }) 230} 231 232func TestElementString(t *testing.T) { 233 var state = testState(0, 0, 0, false) 234 testElementBarString(t, state, ElementString, "", "myKey") 235 state.Set("myKey", "my value") 236 testElementBarString(t, state, ElementString, "my value", "myKey") 237 state.Set("myKey", "my value1") 238 testElementBarString(t, state, ElementString, "my value1", "myKey") 239 testElementBarString(t, state, ElementString, "") 240} 241 242func TestElementCycle(t *testing.T) { 243 var state = testState(0, 0, 0, false) 244 testElementBarString(t, state, ElementCycle, "") 245 testElementBarString(t, state, ElementCycle, "1", "1", "2", "3") 246 testElementBarString(t, state, ElementCycle, "2", "1", "2", "3") 247 testElementBarString(t, state, ElementCycle, "3", "1", "2", "3") 248 testElementBarString(t, state, ElementCycle, "1", "1", "2", "3") 249 testElementBarString(t, state, ElementCycle, "2", "1", "2") 250 testElementBarString(t, state, ElementCycle, "1", "1", "2") 251} 252 253func TestAdaptiveWrap(t *testing.T) { 254 var state = testState(0, 0, 0, false) 255 state.id = 1 256 state.Set("myKey", "my value") 257 el := adaptiveWrap(ElementString) 258 testElementBarString(t, state, el, adElPlaceholder, "myKey") 259 if v := state.recalc[0].ProgressElement(state); v != "my value" { 260 t.Errorf("Unexpected result: %s", v) 261 } 262 state.id = 2 263 testElementBarString(t, state, el, adElPlaceholder, "myKey1") 264 state.Set("myKey", "my value1") 265 if v := state.recalc[0].ProgressElement(state); v != "my value1" { 266 t.Errorf("Unexpected result: %s", v) 267 } 268} 269 270func TestRegisterElement(t *testing.T) { 271 var testEl ElementFunc = func(state *State, args ...string) string { 272 return strings.Repeat("*", state.AdaptiveElWidth()) 273 } 274 RegisterElement("testEl", testEl, true) 275 result := ProgressBarTemplate(`{{testEl . }}`).New(0).SetWidth(5).String() 276 if result != "*****" { 277 t.Errorf("Unexpected result: '%v'", result) 278 } 279} 280 281func BenchmarkBar(b *testing.B) { 282 var formats = map[string][]string{ 283 "simple": []string{".", ".", ".", ".", "."}, 284 "unicode": []string{"⚑", "⚒", "⚟", "⟞", "⚐"}, 285 "color": []string{color.RedString("%s", "."), color.RedString("%s", "."), color.RedString("%s", "."), color.RedString("%s", "."), color.RedString("%s", ".")}, 286 "long": []string{"..", "..", "..", "..", ".."}, 287 "longunicode": []string{"⚑⚑", "⚒⚒", "⚟⚟", "⟞⟞", "⚐⚐"}, 288 } 289 for name, args := range formats { 290 state := testState(100, 50, 100, false, true) 291 b.Run(name, func(b *testing.B) { 292 b.ReportAllocs() 293 for i := 0; i < b.N; i++ { 294 ElementBar(state, args...) 295 } 296 }) 297 } 298} 299