github.com/rigado/snapd@v2.42.5-go-mod+incompatible/progress/ansimeter_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package progress_test 21 22 import ( 23 "bytes" 24 "fmt" 25 "os" 26 "strings" 27 "time" 28 29 "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/progress" 32 ) 33 34 type ansiSuite struct { 35 stdout *os.File 36 } 37 38 var _ = check.Suite(ansiSuite{}) 39 40 func (ansiSuite) TestNorm(c *check.C) { 41 msg := []rune(strings.Repeat("0123456789", 100)) 42 high := []rune("🤗🤗🤗🤗🤗") 43 c.Assert(msg, check.HasLen, 1000) 44 for i := 1; i < 1000; i += 1 { 45 long := progress.Norm(i, msg) 46 short := progress.Norm(i, nil) 47 // a long message is truncated to fit 48 c.Check(long, check.HasLen, i) 49 c.Check(long[len(long)-1], check.Equals, rune('…')) 50 // a short message is padded to width 51 c.Check(short, check.HasLen, i) 52 c.Check(string(short), check.Equals, strings.Repeat(" ", i)) 53 // high unicode? no problem 54 c.Check(progress.Norm(i, high), check.HasLen, i) 55 } 56 // check it doesn't panic for negative nor zero widths 57 c.Check(progress.Norm(0, []rune("hello")), check.HasLen, 0) 58 c.Check(progress.Norm(-10, []rune("hello")), check.HasLen, 0) 59 } 60 61 func (ansiSuite) TestPercent(c *check.C) { 62 p := &progress.ANSIMeter{} 63 for i := -1000.; i < 1000.; i += 5 { 64 p.SetTotal(i) 65 for j := -1000.; j < 1000.; j += 3 { 66 p.SetWritten(j) 67 percent := p.Percent() 68 c.Check(percent, check.HasLen, 4) 69 c.Check(percent[len(percent)-1:], check.Equals, "%") 70 } 71 } 72 } 73 74 func (ansiSuite) TestStart(c *check.C) { 75 var buf bytes.Buffer 76 defer progress.MockStdout(&buf)() 77 defer progress.MockTermWidth(func() int { return 80 })() 78 79 p := &progress.ANSIMeter{} 80 p.Start("0123456789", 100) 81 c.Check(p.GetTotal(), check.Equals, 100.) 82 c.Check(p.GetWritten(), check.Equals, 0.) 83 c.Check(buf.String(), check.Equals, progress.CursorInvisible) 84 } 85 86 func (ansiSuite) TestFinish(c *check.C) { 87 var buf bytes.Buffer 88 defer progress.MockStdout(&buf)() 89 defer progress.MockTermWidth(func() int { return 80 })() 90 p := &progress.ANSIMeter{} 91 p.Finished() 92 c.Check(buf.String(), check.Equals, fmt.Sprint( 93 "\r", // move cursor to start of line 94 progress.ExitAttributeMode, // turn off color, reverse, bold, anything 95 progress.CursorVisible, // turn the cursor back on 96 progress.ClrEOL, // and clear the rest of the line 97 )) 98 } 99 100 func (ansiSuite) TestSetLayout(c *check.C) { 101 var buf bytes.Buffer 102 var width int 103 defer progress.MockStdout(&buf)() 104 defer progress.MockEmptyEscapes()() 105 defer progress.MockTermWidth(func() int { return width })() 106 107 p := &progress.ANSIMeter{} 108 msg := "0123456789" 109 ticker := time.NewTicker(time.Millisecond) 110 defer ticker.Stop() 111 p.Start(msg, 1E300) 112 for i := 1; i <= 80; i++ { 113 desc := check.Commentf("width %d", i) 114 width = i 115 buf.Reset() 116 <-ticker.C 117 p.Set(float64(i)) 118 out := buf.String() 119 c.Check([]rune(out), check.HasLen, i+1, desc) 120 switch { 121 case i < len(msg): 122 c.Check(out, check.Equals, "\r"+msg[:i-1]+"…", desc) 123 case i <= 15: 124 c.Check(out, check.Equals, fmt.Sprintf("\r%*s", -i, msg), desc) 125 case i <= 20: 126 c.Check(out, check.Equals, fmt.Sprintf("\r%*s ages!", -(i-6), msg), desc) 127 case i <= 29: 128 c.Check(out, check.Equals, fmt.Sprintf("\r%*s 0%% ages!", -(i-11), msg), desc) 129 default: 130 c.Check(out, check.Matches, fmt.Sprintf("\r%*s 0%% [ 0-9]{4}B/s ages!", -(i-20), msg), desc) 131 } 132 } 133 } 134 135 func (ansiSuite) TestSetEscapes(c *check.C) { 136 var buf bytes.Buffer 137 defer progress.MockStdout(&buf)() 138 defer progress.MockSimpleEscapes()() 139 defer progress.MockTermWidth(func() int { return 10 })() 140 141 p := &progress.ANSIMeter{} 142 msg := "0123456789" 143 p.Start(msg, 10) 144 for i := 0.; i <= 10; i++ { 145 buf.Reset() 146 p.Set(i) 147 // here we're using the fact that the message has the same 148 // length as p's total to make the test simpler :-) 149 expected := "\r<MR>" + msg[:int(i)] + "<ME>" + msg[int(i):] 150 c.Check(buf.String(), check.Equals, expected, check.Commentf("%g", i)) 151 } 152 } 153 154 func (ansiSuite) TestSpin(c *check.C) { 155 termWidth := 9 156 var buf bytes.Buffer 157 defer progress.MockStdout(&buf)() 158 defer progress.MockSimpleEscapes()() 159 defer progress.MockTermWidth(func() int { return termWidth })() 160 161 p := &progress.ANSIMeter{} 162 msg := "0123456789" 163 c.Assert(len(msg), check.Equals, 10) 164 p.Start(msg, 10) 165 166 // term too narrow to fit msg 167 for i, s := range progress.Spinner { 168 buf.Reset() 169 p.Spin(msg) 170 expected := "\r" + msg[:8] + "…" 171 c.Check(buf.String(), check.Equals, expected, check.Commentf("%d (%s)", i, s)) 172 } 173 174 // term fits msg but not spinner 175 termWidth = 11 176 for i, s := range progress.Spinner { 177 buf.Reset() 178 p.Spin(msg) 179 expected := "\r" + msg + " " 180 c.Check(buf.String(), check.Equals, expected, check.Commentf("%d (%s)", i, s)) 181 } 182 183 // term fits msg and spinner 184 termWidth = 12 185 for i, s := range progress.Spinner { 186 buf.Reset() 187 p.Spin(msg) 188 expected := "\r" + msg + " " + s 189 c.Check(buf.String(), check.Equals, expected, check.Commentf("%d (%s)", i, s)) 190 } 191 } 192 193 func (ansiSuite) TestNotify(c *check.C) { 194 var buf bytes.Buffer 195 var width int 196 defer progress.MockStdout(&buf)() 197 defer progress.MockSimpleEscapes()() 198 defer progress.MockTermWidth(func() int { return width })() 199 200 p := &progress.ANSIMeter{} 201 p.Start("working", 1E300) 202 203 width = 10 204 p.Set(0) 205 p.Notify("hello there") 206 p.Set(1) 207 c.Check(buf.String(), check.Equals, "<VI>"+ // the VI from Start() 208 "\r<MR><ME>working "+ // the Set(0) 209 "\r<ME><CE>hello\n"+ // first line of the Notify (note it wrapped at word end) 210 "there\n"+ 211 "\r<MR><ME>working ") // the Set(1) 212 213 buf.Reset() 214 p.Set(0) 215 p.Notify("supercalifragilisticexpialidocious") 216 p.Set(1) 217 c.Check(buf.String(), check.Equals, ""+ // no Start() this time 218 "\r<MR><ME>working "+ // the Set(0) 219 "\r<ME><CE>supercalif\n"+ // the Notify, word is too long so it's just split 220 "ragilistic\n"+ 221 "expialidoc\n"+ 222 "ious\n"+ 223 "\r<MR><ME>working ") // the Set(1) 224 225 buf.Reset() 226 width = 16 227 p.Set(0) 228 p.Notify("hello there") 229 p.Set(1) 230 c.Check(buf.String(), check.Equals, ""+ // no Start() 231 "\r<MR><ME>working ages!"+ // the Set(0) 232 "\r<ME><CE>hello there\n"+ // first line of the Notify (no wrap!) 233 "\r<MR><ME>working ages!") // the Set(1) 234 235 } 236 237 func (ansiSuite) TestWrite(c *check.C) { 238 var buf bytes.Buffer 239 defer progress.MockStdout(&buf)() 240 defer progress.MockSimpleEscapes()() 241 defer progress.MockTermWidth(func() int { return 10 })() 242 243 p := &progress.ANSIMeter{} 244 p.Start("123456789x", 10) 245 for i := 0; i < 10; i++ { 246 n, err := fmt.Fprintf(p, "%d", i) 247 c.Assert(err, check.IsNil) 248 c.Check(n, check.Equals, 1) 249 } 250 251 c.Check(buf.String(), check.Equals, strings.Join([]string{ 252 "<VI>", // Start() 253 "\r<MR>1<ME>23456789x", 254 "\r<MR>12<ME>3456789x", 255 "\r<MR>123<ME>456789x", 256 "\r<MR>1234<ME>56789x", 257 "\r<MR>12345<ME>6789x", 258 "\r<MR>123456<ME>789x", 259 "\r<MR>1234567<ME>89x", 260 "\r<MR>12345678<ME>9x", 261 "\r<MR>123456789<ME>x", 262 "\r<MR>123456789x<ME>", 263 }, "")) 264 }