mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
287 lines
7.9 KiB
Go
287 lines
7.9 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
/*
|
|
* Copyright (C) 2017 Canonical Ltd
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 3 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
package progress_test
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/check.v1"
|
|
|
|
"github.com/snapcore/snapd/progress"
|
|
)
|
|
|
|
type ansiSuite struct{}
|
|
|
|
var _ = check.Suite(ansiSuite{})
|
|
|
|
func (ansiSuite) TestNorm(c *check.C) {
|
|
msg := []rune(strings.Repeat("0123456789", 100))
|
|
high := []rune("🤗🤗🤗🤗🤗")
|
|
c.Assert(msg, check.HasLen, 1000)
|
|
for i := 1; i < 1000; i += 1 {
|
|
long := progress.Norm(i, msg)
|
|
short := progress.Norm(i, nil)
|
|
// a long message is truncated to fit
|
|
c.Check(long, check.HasLen, i)
|
|
c.Check(long[len(long)-1], check.Equals, rune('…'))
|
|
// a short message is padded to width
|
|
c.Check(short, check.HasLen, i)
|
|
c.Check(string(short), check.Equals, strings.Repeat(" ", i))
|
|
// high unicode? no problem
|
|
c.Check(progress.Norm(i, high), check.HasLen, i)
|
|
}
|
|
// check it doesn't panic for negative nor zero widths
|
|
c.Check(progress.Norm(0, []rune("hello")), check.HasLen, 0)
|
|
c.Check(progress.Norm(-10, []rune("hello")), check.HasLen, 0)
|
|
}
|
|
|
|
func (ansiSuite) TestPercent(c *check.C) {
|
|
p := &progress.ANSIMeter{}
|
|
for i := -1000.; i < 1000.; i += 5 {
|
|
p.SetTotal(i)
|
|
for j := -1000.; j < 1000.; j += 3 {
|
|
p.SetWritten(j)
|
|
percent := p.Percent()
|
|
c.Check(percent, check.HasLen, 4)
|
|
c.Check(percent[len(percent)-1:], check.Equals, "%")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ansiSuite) TestStart(c *check.C) {
|
|
var buf bytes.Buffer
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockTermWidth(func() int { return 80 })()
|
|
|
|
p := &progress.ANSIMeter{}
|
|
p.Start("0123456789", 100)
|
|
c.Check(p.GetTotal(), check.Equals, 100.)
|
|
c.Check(p.GetWritten(), check.Equals, 0.)
|
|
c.Check(buf.String(), check.Equals, progress.CursorInvisible)
|
|
}
|
|
|
|
func (ansiSuite) TestFinish(c *check.C) {
|
|
var buf bytes.Buffer
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockTermWidth(func() int { return 80 })()
|
|
p := &progress.ANSIMeter{}
|
|
p.Finished()
|
|
c.Check(buf.String(), check.Equals, fmt.Sprint(
|
|
"\r", // move cursor to start of line
|
|
progress.ExitAttributeMode, // turn off color, reverse, bold, anything
|
|
progress.CursorVisible, // turn the cursor back on
|
|
progress.ClrEOL, // and clear the rest of the line
|
|
))
|
|
}
|
|
|
|
func (ansiSuite) TestSetLayout(c *check.C) {
|
|
var buf bytes.Buffer
|
|
var width int
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockEmptyEscapes()()
|
|
defer progress.MockTermWidth(func() int { return width })()
|
|
|
|
p := &progress.ANSIMeter{}
|
|
msg := "0123456789"
|
|
ticker := time.NewTicker(time.Millisecond)
|
|
defer ticker.Stop()
|
|
p.Start(msg, 1e300)
|
|
for i := 1; i <= 80; i++ {
|
|
desc := check.Commentf("width %d", i)
|
|
width = i
|
|
buf.Reset()
|
|
<-ticker.C
|
|
p.Set(float64(i))
|
|
out := buf.String()
|
|
c.Check([]rune(out), check.HasLen, i+1, desc)
|
|
switch {
|
|
case i < len(msg):
|
|
c.Check(out, check.Equals, "\r"+msg[:i-1]+"…", desc)
|
|
case i <= 15:
|
|
c.Check(out, check.Equals, fmt.Sprintf("\r%*s", -i, msg), desc)
|
|
case i <= 20:
|
|
c.Check(out, check.Equals, fmt.Sprintf("\r%*s ages!", -(i-6), msg), desc)
|
|
case i <= 29:
|
|
c.Check(out, check.Equals, fmt.Sprintf("\r%*s 0%% ages!", -(i-11), msg), desc)
|
|
default:
|
|
c.Check(out, check.Matches, fmt.Sprintf("\r%*s 0%% [ 0-9]{4}B/s ages!", -(i-20), msg), desc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ansiSuite) TestSetLayoutMultibyte(c *check.C) {
|
|
var buf bytes.Buffer
|
|
var duration string
|
|
var msg = "0123456789"
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockEmptyEscapes()()
|
|
defer progress.MockTermWidth(func() int { return 80 })()
|
|
defer progress.MockFormatDuration(func(_ float64) string {
|
|
return duration
|
|
})()
|
|
|
|
for _, dstr := range []string{"м", "語"} {
|
|
duration = dstr
|
|
buf.Reset()
|
|
|
|
p := &progress.ANSIMeter{}
|
|
p.Start(msg, 1e300)
|
|
p.Set(0.99 * 1e300)
|
|
out := buf.String()
|
|
c.Check([]rune(out), check.HasLen, 80+1, check.Commentf("unexpected length: %v", len(out)))
|
|
c.Check(out, check.Matches,
|
|
fmt.Sprintf("\r0123456789 \\s+ 99%% +[0-9]+(\\.[0-9]+)?[kMGTPEZY]?B/s %s", dstr))
|
|
}
|
|
}
|
|
|
|
func (ansiSuite) TestSetEscapes(c *check.C) {
|
|
var buf bytes.Buffer
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockSimpleEscapes()()
|
|
defer progress.MockTermWidth(func() int { return 10 })()
|
|
|
|
p := &progress.ANSIMeter{}
|
|
msg := "0123456789"
|
|
p.Start(msg, 10)
|
|
for i := 0.; i <= 10; i++ {
|
|
buf.Reset()
|
|
p.Set(i)
|
|
// here we're using the fact that the message has the same
|
|
// length as p's total to make the test simpler :-)
|
|
expected := "\r<MR>" + msg[:int(i)] + "<ME>" + msg[int(i):]
|
|
c.Check(buf.String(), check.Equals, expected, check.Commentf("%g", i))
|
|
}
|
|
}
|
|
|
|
func (ansiSuite) TestSpin(c *check.C) {
|
|
termWidth := 9
|
|
var buf bytes.Buffer
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockSimpleEscapes()()
|
|
defer progress.MockTermWidth(func() int { return termWidth })()
|
|
|
|
p := &progress.ANSIMeter{}
|
|
msg := "0123456789"
|
|
c.Assert(len(msg), check.Equals, 10)
|
|
p.Start(msg, 10)
|
|
|
|
// term too narrow to fit msg
|
|
for i, s := range progress.Spinner {
|
|
buf.Reset()
|
|
p.Spin(msg)
|
|
expected := "\r" + msg[:8] + "…"
|
|
c.Check(buf.String(), check.Equals, expected, check.Commentf("%d (%s)", i, s))
|
|
}
|
|
|
|
// term fits msg but not spinner
|
|
termWidth = 11
|
|
for i, s := range progress.Spinner {
|
|
buf.Reset()
|
|
p.Spin(msg)
|
|
expected := "\r" + msg + " "
|
|
c.Check(buf.String(), check.Equals, expected, check.Commentf("%d (%s)", i, s))
|
|
}
|
|
|
|
// term fits msg and spinner
|
|
termWidth = 12
|
|
for i, s := range progress.Spinner {
|
|
buf.Reset()
|
|
p.Spin(msg)
|
|
expected := "\r" + msg + " " + s
|
|
c.Check(buf.String(), check.Equals, expected, check.Commentf("%d (%s)", i, s))
|
|
}
|
|
}
|
|
|
|
func (ansiSuite) TestNotify(c *check.C) {
|
|
var buf bytes.Buffer
|
|
var width int
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockSimpleEscapes()()
|
|
defer progress.MockTermWidth(func() int { return width })()
|
|
|
|
p := &progress.ANSIMeter{}
|
|
p.Start("working", 1e300)
|
|
|
|
width = 10
|
|
p.Set(0)
|
|
p.Notify("hello there")
|
|
p.Set(1)
|
|
c.Check(buf.String(), check.Equals, "<VI>"+ // the VI from Start()
|
|
"\r<MR><ME>working "+ // the Set(0)
|
|
"\r<ME><CE>hello\n"+ // first line of the Notify (note it wrapped at word end)
|
|
"there\n"+
|
|
"\r<MR><ME>working ") // the Set(1)
|
|
|
|
buf.Reset()
|
|
p.Set(0)
|
|
p.Notify("supercalifragilisticexpialidocious")
|
|
p.Set(1)
|
|
c.Check(buf.String(), check.Equals, ""+ // no Start() this time
|
|
"\r<MR><ME>working "+ // the Set(0)
|
|
"\r<ME><CE>supercalif\n"+ // the Notify, word is too long so it's just split
|
|
"ragilistic\n"+
|
|
"expialidoc\n"+
|
|
"ious\n"+
|
|
"\r<MR><ME>working ") // the Set(1)
|
|
|
|
buf.Reset()
|
|
width = 16
|
|
p.Set(0)
|
|
p.Notify("hello there")
|
|
p.Set(1)
|
|
c.Check(buf.String(), check.Equals, ""+ // no Start()
|
|
"\r<MR><ME>working ages!"+ // the Set(0)
|
|
"\r<ME><CE>hello there\n"+ // first line of the Notify (no wrap!)
|
|
"\r<MR><ME>working ages!") // the Set(1)
|
|
|
|
}
|
|
|
|
func (ansiSuite) TestWrite(c *check.C) {
|
|
var buf bytes.Buffer
|
|
defer progress.MockStdout(&buf)()
|
|
defer progress.MockSimpleEscapes()()
|
|
defer progress.MockTermWidth(func() int { return 10 })()
|
|
|
|
p := &progress.ANSIMeter{}
|
|
p.Start("123456789x", 10)
|
|
for i := 0; i < 10; i++ {
|
|
n, err := fmt.Fprintf(p, "%d", i)
|
|
c.Assert(err, check.IsNil)
|
|
c.Check(n, check.Equals, 1)
|
|
}
|
|
|
|
c.Check(buf.String(), check.Equals, strings.Join([]string{
|
|
"<VI>", // Start()
|
|
"\r<MR>1<ME>23456789x",
|
|
"\r<MR>12<ME>3456789x",
|
|
"\r<MR>123<ME>456789x",
|
|
"\r<MR>1234<ME>56789x",
|
|
"\r<MR>12345<ME>6789x",
|
|
"\r<MR>123456<ME>789x",
|
|
"\r<MR>1234567<ME>89x",
|
|
"\r<MR>12345678<ME>9x",
|
|
"\r<MR>123456789<ME>x",
|
|
"\r<MR>123456789x<ME>",
|
|
}, ""))
|
|
}
|