Skip to content

Commit 2684cd1

Browse files
committed
Fix long output resulting in duplicate lines
1 parent 2fd7298 commit 2684cd1

5 files changed

Lines changed: 133 additions & 13 deletions

File tree

cmd/internal/runtest.go

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package internal
1515

1616
import (
17+
"bytes"
1718
"context"
1819
"encoding/json"
1920
"fmt"
@@ -36,6 +37,9 @@ type (
3637
printID bool
3738
s *Spinner
3839
w terminalWriter
40+
41+
mu sync.Mutex
42+
buf bytes.Buffer
3943
}
4044

4145
// RunOutputResult collects the RunOutput and its error, if any, from
@@ -80,17 +84,18 @@ func RunTest(ctx context.Context, target, location string, nodeIDs []int, limit
8084
}
8185
}()
8286

83-
f.StartSpinner()
87+
if outputJSON {
88+
f.StartSpinner()
89+
}
8490
var o *perfops.RunOutput
8591
for {
8692
select {
87-
case <-time.After(100 * time.Millisecond):
93+
case <-time.After(50 * time.Millisecond):
8894
}
8995
if o, err = res.Output(); err != nil {
9096
return err
9197
}
9298
if !outputJSON && o != nil {
93-
f.StopSpinner()
9499
PrintOutput(f, o)
95100
}
96101
if o != nil && o.IsFinished() {
@@ -109,6 +114,20 @@ func PrintOutput(f *Formatter, output *perfops.RunOutput) {
109114
if f.printID {
110115
f.Printf("Test ID: %v\n", output.ID)
111116
}
117+
spinner := f.s.Step()
118+
if !output.IsFinished() {
119+
f.Printf("%s", spinner)
120+
if len(output.Items) > 1 {
121+
finished := 0
122+
for _, item := range output.Items {
123+
if item.Result.IsFinished() {
124+
finished++
125+
}
126+
}
127+
f.Printf(" %d/%d", finished, len(output.Items))
128+
}
129+
f.Printf("\n")
130+
}
112131
for _, item := range output.Items {
113132
r := item.Result
114133
n := r.Node
@@ -121,12 +140,11 @@ func PrintOutput(f *Formatter, output *perfops.RunOutput) {
121140
} else if r.Message != "NO DATA" {
122141
f.Printf("Node%d, AS%d, %s, %s\n%s\n", n.ID, n.AsNumber, n.City, n.Country.Name, r.Message)
123142
}
143+
if !item.Result.IsFinished() {
144+
f.Printf("%s\n", spinner)
145+
}
124146
}
125-
spinner := f.s.Step()
126-
if !output.IsFinished() {
127-
f.Printf("%s\n", spinner)
128-
}
129-
f.w.Flush()
147+
f.Flush(!output.IsFinished())
130148
}
131149

132150
// PrintOutputJSON marshals the output into JSON and prints the JSON.
@@ -161,7 +179,53 @@ func (f *Formatter) StopSpinner() {
161179

162180
// Printf prints the arguments to the formatters writer.
163181
func (f *Formatter) Printf(format string, a ...interface{}) (int, error) {
164-
return fmt.Fprintf(f.w, format, a...)
182+
f.mu.Lock()
183+
defer f.mu.Unlock()
184+
return fmt.Fprintf(&f.buf, format, a...)
185+
}
186+
187+
// Flush writes to w and resets the buffer. It should be called after the
188+
// last call to Printf to ensure that any data buffered in the Writer is
189+
// written to output.
190+
// Any incomplete escape sequence at the end is considered complete for
191+
// formatting purposes.
192+
// An error is returned if the contents of the buffer cannot be written
193+
// to the underlying output stream
194+
func (f *Formatter) Flush(limit bool) error {
195+
cols, rows := termSize()
196+
197+
f.mu.Lock()
198+
defer f.mu.Unlock()
199+
200+
if len(f.buf.Bytes()) == 0 {
201+
return nil
202+
}
203+
204+
out := string(f.buf.Bytes())
205+
if limit {
206+
width := 0
207+
lines := 0
208+
for i, r := range out {
209+
if r == '\n' {
210+
width = 0
211+
lines++
212+
} else if width++; width > cols {
213+
width = 0
214+
lines++
215+
}
216+
if lines == rows {
217+
out = out[0:i]
218+
break
219+
}
220+
}
221+
}
222+
termStartOfRow(f.w)
223+
_, err := f.w.Write([]byte(out))
224+
f.buf.Reset()
225+
if err != nil {
226+
return err
227+
}
228+
return f.w.Flush()
165229
}
166230

167231
// SetOutput sets the output and the error.

cmd/internal/runtest_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,18 @@ func TestPrintOutput(t *testing.T) {
7979
"all": {
8080
func() *perfops.RunOutput {
8181
var o *perfops.RunOutput
82-
json.Unmarshal([]byte(`{"id":"706fc55e3377104da01f05569e35a30b","items":[{"id":"bba072473bd034d432df03730e116686","result":{"output":"121","node":{"id":27,"as_number":12345,"latitude":22.28512548314,"longitude":114.17507171631,"country":{"id":195,"name":"Hong Kong","continent":{"id":2,"name":"Asia","iso":"AS"},"iso":"HK","iso_numeric":"344"},"city":"Hong Kong","sub_region":"Eastern Asia"},"time":1508061924.088372}}],"requested":"sendergram.com","finished":"true","elapsedTime":0.66500000000000004}`), &o)
82+
json.Unmarshal([]byte(`{"id":"706fc55e3377104da01f05569e35a30b","items":[{"id":"bba072473bd034d432df03730e116686","result":{"output":"121","finished": true,"node":{"id":27,"as_number":12345,"latitude":22.28512548314,"longitude":114.17507171631,"country":{"id":195,"name":"Hong Kong","continent":{"id":2,"name":"Asia","iso":"AS"},"iso":"HK","iso_numeric":"344"},"city":"Hong Kong","sub_region":"Eastern Asia"},"time":1508061924.088372}}],"requested":"sendergram.com","finished":"true","elapsedTime":0.66500000000000004}`), &o)
8383
return o
8484
},
85-
"Node27, AS12345, Hong Kong, Hong Kong\n121\n",
85+
"\x1b[200DNode27, AS12345, Hong Kong, Hong Kong\n121\n",
8686
},
8787
"timeout": {
8888
func() *perfops.RunOutput {
8989
var o *perfops.RunOutput
90-
json.Unmarshal([]byte(`{"id":"706fc55e3377104da01f05569e35a30b","items":[{"id":"bba072473bd034d432df03730e116686","result":{"output":"-2","node":{"id":27,"as_number":23456,"latitude":22.28512548314,"longitude":114.17507171631,"country":{"id":195,"name":"Hong Kong","continent":{"id":2,"name":"Asia","iso":"AS"},"iso":"HK","iso_numeric":"344"},"city":"Hong Kong","sub_region":"Eastern Asia"},"time":1508061924.088372}}],"requested":"sendergram.com","finished":"true","elapsedTime":0.66500000000000004}`), &o)
90+
json.Unmarshal([]byte(`{"id":"706fc55e3377104da01f05569e35a30b","items":[{"id":"bba072473bd034d432df03730e116686","result":{"output":"-2","finished": true,"node":{"id":27,"as_number":23456,"latitude":22.28512548314,"longitude":114.17507171631,"country":{"id":195,"name":"Hong Kong","continent":{"id":2,"name":"Asia","iso":"AS"},"iso":"HK","iso_numeric":"344"},"city":"Hong Kong","sub_region":"Eastern Asia"},"time":1508061924.088372}}],"requested":"sendergram.com","finished":"true","elapsedTime":0.66500000000000004}`), &o)
9191
return o
9292
},
93-
"Node27, AS23456, Hong Kong, Hong Kong\nThe command timed-out. It either took too long to execute or we could not connect to your target at all.\n",
93+
"\x1b[200DNode27, AS23456, Hong Kong, Hong Kong\nThe command timed-out. It either took too long to execute or we could not connect to your target at all.\n",
9494
},
9595
}
9696

@@ -115,6 +115,7 @@ type testTerminalWriter struct {
115115
func (w *testTerminalWriter) Flush() error { return nil }
116116

117117
func newTestFormatter(w io.Writer, printID bool) *Formatter {
118+
termCols, termRows = 200, 50
118119
f := &Formatter{
119120
printID: printID,
120121
w: &testTerminalWriter{w},

cmd/internal/terminal.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// +build !windows
2+
3+
package internal
4+
5+
import (
6+
"fmt"
7+
"io"
8+
"os"
9+
"syscall"
10+
"unsafe"
11+
12+
"github.com/gosuri/uilive"
13+
)
14+
15+
func termSize() (int, int) {
16+
if termCols == 0 {
17+
if out, err := os.OpenFile("/dev/tty", syscall.O_WRONLY, 0); err == nil {
18+
defer out.Close()
19+
var size struct {
20+
rows uint16
21+
cols uint16
22+
xpixels uint16
23+
ypixels uint16
24+
}
25+
_, _, _ = syscall.Syscall(syscall.SYS_IOCTL, out.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&size)))
26+
termCols, termRows = int(size.cols), int(size.rows)
27+
}
28+
}
29+
return termCols, termRows
30+
}
31+
32+
func termStartOfRow(w io.Writer) {
33+
w.Write([]byte(fmt.Sprintf("%c[%dD", uilive.ESC, termCols)))
34+
}

cmd/internal/terminal_common.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package internal
2+
3+
var (
4+
// Cached columns and rows of the terminal window. Initialized by termSize().
5+
termCols int
6+
termRows int
7+
)

cmd/internal/terminal_windows.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package internal
2+
3+
import (
4+
"io"
5+
)
6+
7+
func termSize() (int, int) {
8+
// TODO: Implement me.
9+
return termCols, termRows
10+
}
11+
12+
func termStartOfRow(w io.Writer) {
13+
// TODO: Implement me.
14+
}

0 commit comments

Comments
 (0)