-
Notifications
You must be signed in to change notification settings - Fork 121
/
dotformat.go
161 lines (142 loc) · 3.56 KB
/
dotformat.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package testjson
import (
"fmt"
"io"
"os"
"sort"
"strings"
"time"
"golang.org/x/term"
"gotest.tools/gotestsum/internal/dotwriter"
"gotest.tools/gotestsum/internal/log"
)
func dotsFormatV1(event TestEvent, exec *Execution) string {
pkg := exec.Package(event.Package)
switch {
case event.PackageEvent():
return ""
case event.Action == ActionRun && pkg.Total == 1:
return "[" + RelativePackagePath(event.Package) + "]"
}
return fmtDot(event)
}
func fmtDot(event TestEvent) string {
withColor := colorEvent(event)
switch event.Action {
case ActionPass:
return withColor("·")
case ActionFail:
return withColor("✖")
case ActionSkip:
return withColor("↷")
}
return ""
}
type dotFormatter struct {
pkgs map[string]*dotLine
order []string
writer *dotwriter.Writer
opts FormatOptions
termWidth int
}
type dotLine struct {
runes int
builder *strings.Builder
lastUpdate time.Time
}
func (l *dotLine) update(dot string) {
if dot == "" {
return
}
l.builder.WriteString(dot)
l.runes++
}
// checkWidth marks the line as full when the width of the line hits the
// terminal width.
func (l *dotLine) checkWidth(prefix, terminal int) {
if prefix+l.runes >= terminal {
l.builder.WriteString("\n" + strings.Repeat(" ", prefix))
l.runes = 0
}
}
func newDotFormatter(out io.Writer, opts FormatOptions) EventFormatter {
w, _, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil || w == 0 {
log.Warnf("Failed to detect terminal width for dots format, error: %v", err)
return &formatAdapter{format: dotsFormatV1, out: out}
}
return &dotFormatter{
pkgs: make(map[string]*dotLine),
writer: dotwriter.New(out),
termWidth: w,
opts: opts,
}
}
func (d *dotFormatter) Format(event TestEvent, exec *Execution) error {
if d.pkgs[event.Package] == nil {
d.pkgs[event.Package] = &dotLine{builder: new(strings.Builder)}
d.order = append(d.order, event.Package)
}
line := d.pkgs[event.Package]
line.lastUpdate = event.Time
if !event.PackageEvent() {
line.update(fmtDot(event))
}
switch event.Action {
case ActionOutput, ActionBench:
return nil
}
// Add an empty header to work around incorrect line counting
fmt.Fprint(d.writer, "\n\n")
sort.Slice(d.order, d.orderByLastUpdated)
for _, pkg := range d.order {
if d.opts.HideEmptyPackages && exec.Package(pkg).IsEmpty() {
continue
}
line := d.pkgs[pkg]
pkgname := RelativePackagePath(pkg) + " "
prefix := fmtDotElapsed(exec.Package(pkg))
line.checkWidth(len(prefix+pkgname), d.termWidth)
fmt.Fprintf(d.writer, prefix+pkgname+line.builder.String()+"\n")
}
PrintSummary(d.writer, exec, SummarizeNone)
return d.writer.Flush()
}
// orderByLastUpdated so that the most recently updated packages move to the
// bottom of the list, leaving completed package in the same order at the top.
func (d *dotFormatter) orderByLastUpdated(i, j int) bool {
return d.pkgs[d.order[i]].lastUpdate.Before(d.pkgs[d.order[j]].lastUpdate)
}
func fmtDotElapsed(p *Package) string {
f := func(v string) string {
return fmt.Sprintf(" %5s ", v)
}
elapsed := p.Elapsed()
switch {
case p.cached:
return f("🖴 ")
case elapsed <= 0:
return f("")
case elapsed >= time.Hour:
return f("⏳ ")
case elapsed < time.Second:
return f(elapsed.String())
}
const maxWidth = 7
var steps = []time.Duration{
time.Millisecond,
10 * time.Millisecond,
100 * time.Millisecond,
time.Second,
10 * time.Second,
time.Minute,
10 * time.Minute,
}
for _, trunc := range steps {
r := f(elapsed.Truncate(trunc).String())
if len(r) <= maxWidth {
return r
}
}
return f("")
}