1
1
Fork 0
mirror of https://github.com/schollz/croc.git synced 2025-10-11 13:21:00 +02:00

Updated dependencies

This commit is contained in:
Zack Scholl 2017-10-26 12:36:17 -06:00
parent caa7e7797e
commit e24d37db7d
46 changed files with 7 additions and 1751 deletions

26
Gopkg.lock generated
View file

@ -7,30 +7,12 @@
packages = ["."]
revision = "77ed807830b4df581417e7f89eb81d4872832b72"
[[projects]]
branch = "master"
name = "github.com/gosuri/uilive"
packages = ["."]
revision = "ac356e6e42cd31fcef8e6aec13ae9ed6fe87713e"
[[projects]]
branch = "master"
name = "github.com/gosuri/uiprogress"
packages = [".","util/strutil"]
revision = "d0567a9d84a1c40dd7568115ea66f4887bf57b33"
[[projects]]
branch = "master"
name = "github.com/mars9/crypt"
packages = ["."]
revision = "65899cf653ff022fe5c7fe504b439feed9e7e0fc"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
@ -43,6 +25,12 @@
packages = ["."]
revision = "15c9654387fad6d257aa28f9be57b9f124101955"
[[projects]]
name = "github.com/schollz/progressbar"
packages = ["."]
revision = "91090f7acee17a22f3cefcac2178c417b2ead337"
version = "v0.1.0"
[[projects]]
name = "github.com/sirupsen/logrus"
packages = ["."]
@ -76,6 +64,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "56157cf168219ec6f5596364497dc7fc93cb674ce0a159fd339d88d025f97e25"
inputs-digest = "bdbf624014ff879def63602194ddf9c775d9d8461e0b2e687314dc8b855ffc50"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,7 +0,0 @@
language: go
sudo: false
install:
- go get ./...
go:
- 1.4
- tip

View file

@ -1,10 +0,0 @@
MIT License
===========
Copyright (c) 2015, Greg Osuri
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,31 +0,0 @@
# uilive [![GoDoc](https://godoc.org/github.com/gosuri/uilive?status.svg)](https://godoc.org/github.com/gosuri/uilive) [![Build Status](https://travis-ci.org/gosuri/uilive.svg?branch=master)](https://travis-ci.org/gosuri/uilive)
uilive is a go library for updating terminal output in realtime. It provides a buffered [io.Writer](https://golang.org/pkg/io/#Writer) that is flushed at a timed interval. uilive powers [uiprogress](https://github.com/gosuri/uiprogress).
## Usage Example
Calling `uilive.New()` will create a new writer. To start rendering, simply call `writer.Start()` and update the ui by writing to the `writer`. Full source for the below example is in [example/main.go](example/main.go).
```go
writer := uilive.New()
// start listening for updates and render
writer.Start()
for i := 0; i <= 100; i++ {
fmt.Fprintf(writer, "Downloading.. (%d/%d) GB\n", i, 100)
time.Sleep(time.Millisecond * 5)
}
fmt.Fprintln(writer, "Finished: Downloaded 100GB")
writer.Stop() // flush and stop rendering
```
The above will render
![example](doc/example.gif)
## Installation
```sh
$ go get -v github.com/gosuri/uilive
```

View file

@ -1,2 +0,0 @@
// Package uilive provides a writer that live updates the terminal. It provides a buffered io.Writer that is flushed at a timed interval.
package uilive

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

View file

@ -1,26 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/gosuri/uilive"
)
func main() {
writer := uilive.New()
// start listening for updates and render
writer.Start()
for _, f := range []string{"Foo.zip", "Bar.iso"} {
for i := 0; i <= 50; i++ {
fmt.Fprintf(writer, "Downloading %s.. (%d/%d) GB\n", f, i, 50)
time.Sleep(time.Millisecond * 25)
}
fmt.Fprintf(writer.Bypass(), "Downloaded %s\n", f)
}
fmt.Fprintln(writer, "Finished: Downloaded 100GB")
writer.Stop() // flush and stop rendering
}

View file

@ -1,23 +0,0 @@
package uilive_test
import (
"fmt"
"time"
"github.com/gosuri/uilive"
)
func Example() {
writer := uilive.New()
// start listening to updates and render
writer.Start()
for i := 0; i <= 100; i++ {
fmt.Fprintf(writer, "Downloading.. (%d/%d) GB\n", i, 100)
time.Sleep(time.Millisecond * 5)
}
fmt.Fprintln(writer, "Finished: Downloaded 100GB")
writer.Stop() // flush and stop rendering
}

View file

@ -1,138 +0,0 @@
package uilive
import (
"bytes"
"errors"
"io"
"os"
"sync"
"time"
)
// ESC is the ASCII code for escape character
const ESC = 27
// RefreshInterval is the default refresh interval to update the ui
var RefreshInterval = time.Millisecond
// Out is the default output writer for the Writer
var Out = os.Stdout
// ErrClosedPipe is the error returned when trying to writer is not listening
var ErrClosedPipe = errors.New("uilive: read/write on closed pipe")
// FdWriter is a writer with a file descriptor.
type FdWriter interface {
io.Writer
Fd() uintptr
}
// Writer is a buffered the writer that updates the terminal. The contents of writer will be flushed on a timed interval or when Flush is called.
type Writer struct {
// Out is the writer to write to
Out io.Writer
// RefreshInterval is the time the UI sould refresh
RefreshInterval time.Duration
ticker *time.Ticker
tdone chan bool
buf bytes.Buffer
mtx *sync.Mutex
lineCount int
}
type bypass struct {
writer *Writer
}
// New returns a new Writer with defaults
func New() *Writer {
return &Writer{
Out: Out,
RefreshInterval: RefreshInterval,
mtx: &sync.Mutex{},
}
}
// Flush writes to the out and resets the buffer. It should be called after the last call to Write to ensure that any data buffered in the Writer is written to output.
// Any incomplete escape sequence at the end is considered complete for formatting purposes.
// An error is returned if the contents of the buffer cannot be written to the underlying output stream
func (w *Writer) Flush() error {
w.mtx.Lock()
defer w.mtx.Unlock()
// do nothing is buffer is empty
if len(w.buf.Bytes()) == 0 {
return nil
}
w.clearLines()
lines := 0
for _, b := range w.buf.Bytes() {
if b == '\n' {
lines++
}
}
w.lineCount = lines
_, err := w.Out.Write(w.buf.Bytes())
w.buf.Reset()
return err
}
// Start starts the listener in a non-blocking manner
func (w *Writer) Start() {
if w.ticker == nil {
w.ticker = time.NewTicker(w.RefreshInterval)
w.tdone = make(chan bool, 1)
}
go w.Listen()
}
// Stop stops the listener that updates the terminal
func (w *Writer) Stop() {
w.Flush()
close(w.tdone)
}
// Listen listens for updates to the writer's buffer and flushes to the out provided. It blocks the runtime.
func (w *Writer) Listen() {
for {
select {
case <-w.ticker.C:
if w.ticker != nil {
w.Flush()
}
case <-w.tdone:
w.mtx.Lock()
w.ticker.Stop()
w.ticker = nil
w.mtx.Unlock()
return
}
}
}
// Write save the contents of b to its buffers. The only errors returned are ones encountered while writing to the underlying buffer.
func (w *Writer) Write(b []byte) (n int, err error) {
w.mtx.Lock()
defer w.mtx.Unlock()
return w.buf.Write(b)
}
// Bypass creates an io.Writer which allows non-buffered output to be written to the underlying output
func (w *Writer) Bypass() io.Writer {
return &bypass{writer: w}
}
func (b *bypass) Write(p []byte) (n int, err error) {
b.writer.mtx.Lock()
defer b.writer.mtx.Unlock()
b.writer.clearLines()
b.writer.lineCount = 0
return b.writer.Out.Write(p)
}

View file

@ -1,14 +0,0 @@
// +build !windows
package uilive
import (
"fmt"
)
func (w *Writer) clearLines() {
for i := 0; i < w.lineCount; i++ {
fmt.Fprintf(w.Out, "%c[2K", ESC) // clear the line
fmt.Fprintf(w.Out, "%c[%dA", ESC, 1) // move the cursor up
}
}

View file

@ -1,24 +0,0 @@
package uilive
import (
"bytes"
"fmt"
"testing"
)
func TestWriter(t *testing.T) {
w := New()
b := &bytes.Buffer{}
w.Out = b
w.Start()
for i := 0; i < 2; i++ {
fmt.Fprintln(w, "foo")
}
w.Stop()
fmt.Fprintln(b, "bar")
want := "foo\nfoo\nbar\n"
if b.String() != want {
t.Fatalf("want %q, got %q", want, b.String())
}
}

View file

@ -1,74 +0,0 @@
// +build windows
package uilive
import (
"fmt"
"github.com/mattn/go-isatty"
"syscall"
"unsafe"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
)
type short int16
type dword uint32
type word uint16
type coord struct {
x short
y short
}
type smallRect struct {
left short
top short
right short
bottom short
}
type consoleScreenBufferInfo struct {
size coord
cursorPosition coord
attributes word
window smallRect
maximumWindowSize coord
}
func (w *Writer) clearLines() {
f, ok := w.Out.(FdWriter)
if ok && !isatty.IsTerminal(f.Fd()) {
ok = false
}
if !ok {
for i := 0; i < w.lineCount; i++ {
fmt.Fprintf(w.Out, "%c[%dA", ESC, 0) // move the cursor up
fmt.Fprintf(w.Out, "%c[2K\r", ESC) // clear the line
}
return
}
fd := f.Fd()
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&csbi)))
for i := 0; i < w.lineCount; i++ {
// move the cursor up
csbi.cursorPosition.y--
procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&csbi.cursorPosition))))
// clear the line
cursor := coord{
x: csbi.window.left,
y: csbi.window.top + csbi.cursorPosition.y,
}
var count, w dword
count = dword(csbi.size.x)
procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w)))
}
}

View file

@ -1,7 +0,0 @@
language: go
sudo: false
install:
- go get ./...
go:
- 1.4
- tip

View file

@ -1,10 +0,0 @@
MIT License
===========
Copyright (c) 2015, Greg Osuri
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,11 +0,0 @@
test:
@go test -race .
@go test -race ./util/strutil
examples:
go run -race example/full/full.go
go run -race example/incr/incr.go
go run -race example/multi/multi.go
go run -race example/simple/simple.go
.PHONY: test examples

View file

@ -1,146 +0,0 @@
# uiprogress [![GoDoc](https://godoc.org/github.com/gosuri/uiprogress?status.svg)](https://godoc.org/github.com/gosuri/uiprogress) [![Build Status](https://travis-ci.org/gosuri/uiprogress.svg?branch=master)](https://travis-ci.org/gosuri/uiprogress)
A Go library to render progress bars in terminal applications. It provides a set of flexible features with a customizable API.
![example](doc/example_full.gif)
Progress bars improve readability for terminal applications with long outputs by providing a concise feedback loop.
## Features
* __Multiple Bars__: uiprogress can render multiple progress bars that can be tracked concurrently
* __Dynamic Addition__: Add additional progress bars any time, even after the progress tracking has started
* __Prepend and Append Functions__: Append or prepend completion percent and time elapsed to the progress bars
* __Custom Decorator Functions__: Add custom functions around the bar along with helper functions
## Usage
To start listening for progress bars, call `uiprogress.Start()` and add a progress bar using `uiprogress.AddBar(total int)`. Update the progress using `bar.Incr()` or `bar.Set(n int)`. Full source code for the below example is available at [example/simple/simple.go](example/simple/simple.go)
```go
uiprogress.Start() // start rendering
bar := uiprogress.AddBar(100) // Add a new bar
// optionally, append and prepend completion and elapsed time
bar.AppendCompleted()
bar.PrependElapsed()
for bar.Incr() {
time.Sleep(time.Millisecond * 20)
}
```
This will render the below in the terminal
![example](doc/example_simple.gif)
### Using Custom Decorators
You can also add a custom decorator function in addition to default `bar.AppendCompleted()` and `bar.PrependElapsed()` decorators. The below example tracks the current step for an application deploy progress. Source code for the below example is available at [example/full/full.go](example/full/full.go)
```go
var steps = []string{"downloading source", "installing deps", "compiling", "packaging", "seeding database", "deploying", "staring servers"}
bar := uiprogress.AddBar(len(steps))
// prepend the current step to the bar
bar.PrependFunc(func(b *uiprogress.Bar) string {
return "app: " + steps[b.Current()-1]
})
for bar.Incr() {
time.Sleep(time.Millisecond * 10)
}
```
### Rendering Multiple bars
You can add multiple bars using `uiprogress.AddBar(n)`. The below example demonstrates updating multiple bars concurrently and adding a new bar later in the pipeline. Source for this example is available at [example/multi/multi.go](example/multi/multi.go)
```go
waitTime := time.Millisecond * 100
uiprogress.Start()
// start the progress bars in go routines
var wg sync.WaitGroup
bar1 := uiprogress.AddBar(20).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar1.Incr() {
time.Sleep(waitTime)
}
}()
bar2 := uiprogress.AddBar(40).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar2.Incr() {
time.Sleep(waitTime)
}
}()
time.Sleep(time.Second)
bar3 := uiprogress.AddBar(20).PrependElapsed().AppendCompleted()
wg.Add(1)
go func() {
defer wg.Done()
for i := 1; i <= bar3.Total; i++ {
bar3.Set(i)
time.Sleep(waitTime)
}
}()
// wait for all the go routines to finish
wg.Wait()
```
This will produce
![example](doc/example_multi.gif)
### `Incr` counter
[Bar.Incr()](https://godoc.org/github.com/gosuri/uiprogress#Bar.Incr) is an atomic counter and can be used as a general tracker, making it ideal for tracking progress of work fanned out to a lots of go routines. The source code for the below example is available at [example/incr/incr.go](example/incr/incr.go)
```go
runtime.GOMAXPROCS(runtime.NumCPU()) // use all available cpu cores
// create a new bar and prepend the task progress to the bar and fanout into 1k go routines
count := 1000
bar := uiprogress.AddBar(count).AppendCompleted().PrependElapsed()
bar.PrependFunc(func(b *uiprogress.Bar) string {
return fmt.Sprintf("Task (%d/%d)", b.Current(), count)
})
uiprogress.Start()
var wg sync.WaitGroup
// fanout into go routines
for i := 0; i < count; i++ {
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))
bar.Incr()
}()
}
time.Sleep(time.Second) // wait for a second for all the go routines to finish
wg.Wait()
uiprogress.Stop()
```
## Installation
```sh
$ go get -v github.com/gosuri/uiprogress
```
## Todos
- [ ] Resize bars and decorators by auto detecting window's dimensions
- [ ] Handle more progress bars than vertical screen allows
## License
uiprogress is released under the MIT License. See [LICENSE](https://github.com/gosuri/uiprogress/blob/master/LICENSE).

View file

@ -1,237 +0,0 @@
package uiprogress
import (
"bytes"
"errors"
"fmt"
"sync"
"time"
"github.com/gosuri/uiprogress/util/strutil"
)
var (
// Fill is the default character representing completed progress
Fill byte = '='
// Head is the default character that moves when progress is updated
Head byte = '>'
// Empty is the default character that represents the empty progress
Empty byte = '-'
// LeftEnd is the default character in the left most part of the progress indicator
LeftEnd byte = '['
// RightEnd is the default character in the right most part of the progress indicator
RightEnd byte = ']'
// Width is the default width of the progress bar
Width = 70
// ErrMaxCurrentReached is error when trying to set current value that exceeds the total value
ErrMaxCurrentReached = errors.New("errors: current value is greater total value")
)
// Bar represents a progress bar
type Bar struct {
// Total of the total for the progress bar
Total int
// LeftEnd is character in the left most part of the progress indicator. Defaults to '['
LeftEnd byte
// RightEnd is character in the right most part of the progress indicator. Defaults to ']'
RightEnd byte
// Fill is the character representing completed progress. Defaults to '='
Fill byte
// Head is the character that moves when progress is updated. Defaults to '>'
Head byte
// Empty is the character that represents the empty progress. Default is '-'
Empty byte
// TimeStated is time progress began
TimeStarted time.Time
// Width is the width of the progress bar
Width int
// timeElased is the time elapsed for the progress
timeElapsed time.Duration
current int
mtx *sync.RWMutex
appendFuncs []DecoratorFunc
prependFuncs []DecoratorFunc
}
// DecoratorFunc is a function that can be prepended and appended to the progress bar
type DecoratorFunc func(b *Bar) string
// NewBar returns a new progress bar
func NewBar(total int) *Bar {
return &Bar{
Total: total,
Width: Width,
LeftEnd: LeftEnd,
RightEnd: RightEnd,
Head: Head,
Fill: Fill,
Empty: Empty,
mtx: &sync.RWMutex{},
}
}
// Set the current count of the bar. It returns ErrMaxCurrentReached when trying n exceeds the total value. This is atomic operation and concurancy safe.
func (b *Bar) Set(n int) error {
b.mtx.Lock()
defer b.mtx.Unlock()
if n > b.Total {
return ErrMaxCurrentReached
}
b.current = n
return nil
}
// Incr increments the current value by 1, time elapsed to current time and returns true. It returns false if the cursor has reached or exceeds total value.
func (b *Bar) Incr() bool {
b.mtx.Lock()
defer b.mtx.Unlock()
n := b.current + 1
if n > b.Total {
return false
}
var t time.Time
if b.TimeStarted == t {
b.TimeStarted = time.Now()
}
b.timeElapsed = time.Since(b.TimeStarted)
b.current = n
return true
}
// Current returns the current progress of the bar
func (b *Bar) Current() int {
b.mtx.RLock()
defer b.mtx.RUnlock()
return b.current
}
// AppendFunc runs the decorator function and renders the output on the right of the progress bar
func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
b.mtx.Lock()
defer b.mtx.Unlock()
b.appendFuncs = append(b.appendFuncs, f)
return b
}
// AppendCompleted appends the completion percent to the progress bar
func (b *Bar) AppendCompleted() *Bar {
b.AppendFunc(func(b *Bar) string {
return b.CompletedPercentString()
})
return b
}
// AppendElapsed appends the time elapsed the be progress bar
func (b *Bar) AppendElapsed() *Bar {
b.AppendFunc(func(b *Bar) string {
return strutil.PadLeft(b.TimeElapsedString(), 5, ' ')
})
return b
}
// PrependFunc runs decorator function and render the output left the progress bar
func (b *Bar) PrependFunc(f DecoratorFunc) *Bar {
b.mtx.Lock()
defer b.mtx.Unlock()
b.prependFuncs = append(b.prependFuncs, f)
return b
}
// PrependCompleted prepends the precent completed to the progress bar
func (b *Bar) PrependCompleted() *Bar {
b.PrependFunc(func(b *Bar) string {
return b.CompletedPercentString()
})
return b
}
// PrependElapsed prepends the time elapsed to the begining of the bar
func (b *Bar) PrependElapsed() *Bar {
b.PrependFunc(func(b *Bar) string {
return strutil.PadLeft(b.TimeElapsedString(), 5, ' ')
})
return b
}
// Bytes returns the byte presentation of the progress bar
func (b *Bar) Bytes() []byte {
completedWidth := int(float64(b.Width) * (b.CompletedPercent() / 100.00))
// add fill and empty bits
var buf bytes.Buffer
for i := 0; i < completedWidth; i++ {
buf.WriteByte(b.Fill)
}
for i := 0; i < b.Width-completedWidth; i++ {
buf.WriteByte(b.Empty)
}
// set head bit
pb := buf.Bytes()
if completedWidth > 0 && completedWidth < b.Width {
pb[completedWidth-1] = b.Head
}
// set left and right ends bits
pb[0], pb[len(pb)-1] = b.LeftEnd, b.RightEnd
// render append functions to the right of the bar
for _, f := range b.appendFuncs {
pb = append(pb, ' ')
pb = append(pb, []byte(f(b))...)
}
// render prepend functions to the left of the bar
for _, f := range b.prependFuncs {
args := []byte(f(b))
args = append(args, ' ')
pb = append(args, pb...)
}
return pb
}
// String returns the string representation of the bar
func (b *Bar) String() string {
return string(b.Bytes())
}
// CompletedPercent return the percent completed
func (b *Bar) CompletedPercent() float64 {
return (float64(b.Current()) / float64(b.Total)) * 100.00
}
// CompletedPercentString returns the formatted string representation of the completed percent
func (b *Bar) CompletedPercentString() string {
return fmt.Sprintf("%3.f%%", b.CompletedPercent())
}
// TimeElapsed returns the time elapsed
func (b *Bar) TimeElapsed() time.Duration {
b.mtx.RLock()
defer b.mtx.RUnlock()
return b.timeElapsed
}
// TimeElapsedString returns the formatted string represenation of the time elapsed
func (b *Bar) TimeElapsedString() string {
return strutil.PrettyTime(b.TimeElapsed())
}

View file

@ -1,37 +0,0 @@
package uiprogress
import (
"math/rand"
"runtime"
"strings"
"sync"
"testing"
"time"
)
func TestBarPrepend(t *testing.T) {
b := NewBar(100)
b.PrependCompleted()
b.Set(50)
if !strings.Contains(b.String(), "50") {
t.Fatal("want", "50%", "in", b.String())
}
}
func TestBarIncr(t *testing.T) {
b := NewBar(10000)
runtime.GOMAXPROCS(runtime.NumCPU())
var wg sync.WaitGroup
for i := 0; i < 10000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
b.Incr()
time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)))
}()
}
wg.Wait()
if b.Current() != 10000 {
t.Fatal("need", 10000, "got", b.Current())
}
}

View file

@ -1,2 +0,0 @@
// Package uiprogress is a library to render progress bars in terminal applications
package uiprogress

Binary file not shown.

Before

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

View file

@ -1,50 +0,0 @@
package main
import (
"fmt"
"sync"
"time"
"github.com/gosuri/uiprogress"
)
func main() {
waitTime := time.Millisecond * 200
p := uiprogress.New()
p.Start()
var wg sync.WaitGroup
bar1 := p.AddBar(20).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar1.Incr() {
time.Sleep(waitTime)
}
fmt.Fprintln(p.Bypass(), "Bar1 finished")
}()
bar2 := p.AddBar(40).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar2.Incr() {
time.Sleep(waitTime)
}
fmt.Fprintln(p.Bypass(), "Bar2 finished")
}()
time.Sleep(time.Second)
bar3 := p.AddBar(20).PrependElapsed().AppendCompleted()
wg.Add(1)
go func() {
defer wg.Done()
for bar3.Incr() {
time.Sleep(waitTime)
}
fmt.Fprintln(p.Bypass(), "Bar3 finished")
}()
wg.Wait()
}

View file

@ -1,51 +0,0 @@
package main
import (
"fmt"
"math/rand"
"sync"
"time"
"github.com/gosuri/uiprogress"
"github.com/gosuri/uiprogress/util/strutil"
)
var steps = []string{
"downloading source",
"installing deps",
"compiling",
"packaging",
"seeding database",
"deploying",
"staring servers",
}
func main() {
fmt.Println("apps: deployment started: app1, app2")
uiprogress.Start()
var wg sync.WaitGroup
wg.Add(1)
go deploy("app1", &wg)
wg.Add(1)
go deploy("app2", &wg)
wg.Wait()
fmt.Println("apps: successfully deployed: app1, app2")
}
func deploy(app string, wg *sync.WaitGroup) {
defer wg.Done()
bar := uiprogress.AddBar(len(steps)).AppendCompleted().PrependElapsed()
bar.Width = 50
// prepend the deploy step to the bar
bar.PrependFunc(func(b *uiprogress.Bar) string {
return strutil.Resize(app+": "+steps[b.Current()-1], 22)
})
rand.Seed(500)
for bar.Incr() {
time.Sleep(time.Millisecond * time.Duration(rand.Intn(2000)))
}
}

View file

@ -1,38 +0,0 @@
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
"time"
"github.com/gosuri/uiprogress"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // use all available cpu cores
// create a new bar and prepend the task progress to the bar and fanout into 1k go routines
count := 1000
bar := uiprogress.AddBar(count).AppendCompleted().PrependElapsed()
bar.PrependFunc(func(b *uiprogress.Bar) string {
return fmt.Sprintf("Task (%d/%d)", b.Current(), count)
})
uiprogress.Start()
var wg sync.WaitGroup
// fanout into 1k go routines
for i := 0; i < count; i++ {
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))
bar.Incr()
}()
}
time.Sleep(time.Second) // wait for a second for all the go routines to finish
wg.Wait()
uiprogress.Stop()
}

View file

@ -1,45 +0,0 @@
package main
import (
"sync"
"time"
"github.com/gosuri/uiprogress"
)
func main() {
waitTime := time.Millisecond * 100
uiprogress.Start()
var wg sync.WaitGroup
bar1 := uiprogress.AddBar(20).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar1.Incr() {
time.Sleep(waitTime)
}
}()
bar2 := uiprogress.AddBar(40).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar2.Incr() {
time.Sleep(waitTime)
}
}()
time.Sleep(time.Second)
bar3 := uiprogress.AddBar(20).PrependElapsed().AppendCompleted()
wg.Add(1)
go func() {
defer wg.Done()
for bar3.Incr() {
time.Sleep(waitTime)
}
}()
wg.Wait()
}

View file

@ -1,20 +0,0 @@
package main
import (
"time"
"github.com/gosuri/uiprogress"
)
func main() {
uiprogress.Start() // start rendering
bar := uiprogress.AddBar(100) // Add a new bar
// optionally, append and prepend completion and elapsed time
bar.AppendCompleted()
bar.PrependElapsed()
for bar.Incr() {
time.Sleep(time.Millisecond * 20)
}
}

View file

@ -1,103 +0,0 @@
package uiprogress_test
import (
"fmt"
"math/rand"
"runtime"
"sync"
"time"
"github.com/gosuri/uiprogress"
)
func Example() {
uiprogress.Start() // start rendering
bar := uiprogress.AddBar(100) // Add a new bar
// optionally, append and prepend completion and elapsed time
bar.AppendCompleted()
bar.PrependElapsed()
for bar.Incr() {
time.Sleep(time.Millisecond * 20)
}
}
func ExampleProgress_AddBar() {
waitTime := time.Millisecond * 100
uiprogress.Start()
// start the progress bars in go routines
var wg sync.WaitGroup
bar1 := uiprogress.AddBar(20).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar1.Incr() {
time.Sleep(waitTime)
}
}()
bar2 := uiprogress.AddBar(40).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
defer wg.Done()
for bar2.Incr() {
time.Sleep(waitTime)
}
}()
time.Sleep(time.Second)
bar3 := uiprogress.AddBar(20).PrependElapsed().AppendCompleted()
wg.Add(1)
go func() {
defer wg.Done()
for i := 1; i <= bar3.Total; i++ {
bar3.Set(i)
time.Sleep(waitTime)
}
}()
// wait for all the go routines to finish
wg.Wait()
}
func ExampleDecoratorFunc() {
var steps = []string{"downloading source", "installing deps", "compiling", "packaging", "seeding database", "deploying", "staring servers"}
bar := uiprogress.AddBar(len(steps))
// prepend the current step to the bar
bar.PrependFunc(func(b *uiprogress.Bar) string {
return "app: " + steps[b.Current()-1]
})
for bar.Incr() {
time.Sleep(time.Millisecond)
}
}
func ExampleBar_Incr() {
runtime.GOMAXPROCS(runtime.NumCPU()) // use all available cpu cores
// create a new bar and prepend the task progress to the bar
count := 1000
bar := uiprogress.AddBar(count).AppendCompleted().PrependElapsed()
bar.PrependFunc(func(b *uiprogress.Bar) string {
return fmt.Sprintf("Task (%d/%d)", b.Current(), count)
})
uiprogress.Start()
var wg sync.WaitGroup
// fanout into 1k go routines
for i := 0; i < count; i++ {
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))
bar.Incr()
}()
}
time.Sleep(time.Second) // wait for a second for all the go routines to finish
wg.Wait()
uiprogress.Stop()
}

View file

@ -1,146 +0,0 @@
package uiprogress
import (
"fmt"
"io"
"os"
"sync"
"time"
"github.com/gosuri/uilive"
)
// Out is the default writer to render progress bars to
var Out = os.Stdout
// RefreshInterval in the default time duration to wait for refreshing the output
var RefreshInterval = time.Millisecond * 10
// defaultProgress is the default progress
var defaultProgress = New()
// Progress represents the container that renders progress bars
type Progress struct {
// Out is the writer to render progress bars to
Out io.Writer
// Width is the width of the progress bars
Width int
// Bars is the collection of progress bars
Bars []*Bar
// RefreshInterval in the time duration to wait for refreshing the output
RefreshInterval time.Duration
lw *uilive.Writer
ticker *time.Ticker
tdone chan bool
mtx *sync.RWMutex
}
// New returns a new progress bar with defaults
func New() *Progress {
lw := uilive.New()
lw.Out = Out
return &Progress{
Width: Width,
Out: Out,
Bars: make([]*Bar, 0),
RefreshInterval: RefreshInterval,
tdone: make(chan bool),
lw: uilive.New(),
mtx: &sync.RWMutex{},
}
}
// AddBar creates a new progress bar and adds it to the default progress container
func AddBar(total int) *Bar {
return defaultProgress.AddBar(total)
}
// Start starts the rendering the progress of progress bars using the DefaultProgress. It listens for updates using `bar.Set(n)` and new bars when added using `AddBar`
func Start() {
defaultProgress.Start()
}
// Stop stops listening
func Stop() {
defaultProgress.Stop()
}
// Listen listens for updates and renders the progress bars
func Listen() {
defaultProgress.Listen()
}
func (p *Progress) SetOut(o io.Writer) {
p.mtx.Lock()
defer p.mtx.Unlock()
p.Out = o
p.lw.Out = o
}
func (p *Progress) SetRefreshInterval(interval time.Duration) {
p.mtx.Lock()
defer p.mtx.Unlock()
p.RefreshInterval = interval
}
// AddBar creates a new progress bar and adds to the container
func (p *Progress) AddBar(total int) *Bar {
p.mtx.Lock()
defer p.mtx.Unlock()
bar := NewBar(total)
bar.Width = p.Width
p.Bars = append(p.Bars, bar)
return bar
}
// Listen listens for updates and renders the progress bars
func (p *Progress) Listen() {
for {
p.mtx.Lock()
interval := p.RefreshInterval
p.mtx.Unlock()
select {
case <-time.After(interval):
p.print()
case <-p.tdone:
p.print()
close(p.tdone)
return
}
}
}
func (p *Progress) print() {
p.mtx.Lock()
defer p.mtx.Unlock()
for _, bar := range p.Bars {
fmt.Fprintln(p.lw, bar.String())
}
p.lw.Flush()
}
// Start starts the rendering the progress of progress bars. It listens for updates using `bar.Set(n)` and new bars when added using `AddBar`
func (p *Progress) Start() {
go p.Listen()
}
// Stop stops listening
func (p *Progress) Stop() {
p.tdone <- true
<-p.tdone
}
// Bypass returns a writer which allows non-buffered data to be written to the underlying output
func (p *Progress) Bypass() io.Writer {
return p.lw.Bypass()
}

View file

@ -1,45 +0,0 @@
package uiprogress
import (
"bytes"
"fmt"
"strings"
"sync"
"testing"
"time"
)
func TestStoppingPrintout(t *testing.T) {
progress := New()
progress.SetRefreshInterval(time.Millisecond * 10)
var buffer = &bytes.Buffer{}
progress.SetOut(buffer)
bar := progress.AddBar(100)
progress.Start()
var wg sync.WaitGroup
wg.Add(1)
go func() {
for i := 0; i <= 80; i = i + 10 {
bar.Set(i)
time.Sleep(time.Millisecond * 5)
}
wg.Done()
}()
wg.Wait()
progress.Stop()
fmt.Fprintf(buffer, "foo")
var wantSuffix = "[======================================================>-------------]\nfoo"
if !strings.HasSuffix(buffer.String(), wantSuffix) {
t.Errorf("Content that should be printed after stop not appearing on buffer.")
}
}

View file

@ -1,61 +0,0 @@
// Package strutil provides various utilities for manipulating strings
package strutil
import (
"bytes"
"time"
)
// PadRight returns a new string of a specified length in which the end of the current string is padded with spaces or with a specified Unicode character.
func PadRight(str string, length int, pad byte) string {
if len(str) >= length {
return str
}
buf := bytes.NewBufferString(str)
for i := 0; i < length-len(str); i++ {
buf.WriteByte(pad)
}
return buf.String()
}
// PadLeft returns a new string of a specified length in which the beginning of the current string is padded with spaces or with a specified Unicode character.
func PadLeft(str string, length int, pad byte) string {
if len(str) >= length {
return str
}
var buf bytes.Buffer
for i := 0; i < length-len(str); i++ {
buf.WriteByte(pad)
}
buf.WriteString(str)
return buf.String()
}
// Resize resizes the string with the given length. It ellipses with '...' when the string's length exceeds
// the desired length or pads spaces to the right of the string when length is smaller than desired
func Resize(s string, length uint) string {
n := int(length)
if len(s) == n {
return s
}
// Pads only when length of the string smaller than len needed
s = PadRight(s, n, ' ')
if len(s) > n {
b := []byte(s)
var buf bytes.Buffer
for i := 0; i < n-3; i++ {
buf.WriteByte(b[i])
}
buf.WriteString("...")
s = buf.String()
}
return s
}
// PrettyTime returns the string representation of the duration. It rounds the time duration to a second and returns a "---" when duration is 0
func PrettyTime(t time.Duration) string {
if t == 0 {
return "---"
}
return (t - (t % time.Second)).String()
}

View file

@ -1,42 +0,0 @@
package strutil
import (
"testing"
"time"
)
func TestResize(t *testing.T) {
s := "foo"
got := Resize(s, 5)
if len(got) != 5 {
t.Fatal("want", 5, "got", len(got))
}
s = "foobar"
got = Resize(s, 5)
if got != "fo..." {
t.Fatal("want", "fo...", "got", got)
}
}
func TestPadRight(t *testing.T) {
got := PadRight("foo", 5, '-')
if got != "foo--" {
t.Fatal("want", "foo--", "got", got)
}
}
func TestPadLeft(t *testing.T) {
got := PadLeft("foo", 5, '-')
if got != "--foo" {
t.Fatal("want", "--foo", "got", got)
}
}
func TestPrettyTime(t *testing.T) {
d, _ := time.ParseDuration("")
got := PrettyTime(d)
if got != "---" {
t.Fatal("want", "---", "got", got)
}
}

View file

@ -1,9 +0,0 @@
language: go
go:
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5

View file

@ -1,9 +0,0 @@
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
MIT License (Expat)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,50 +0,0 @@
# go-isatty
[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty)
[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty)
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty)
isatty for golang
## Usage
```go
package main
import (
"fmt"
"github.com/mattn/go-isatty"
"os"
)
func main() {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println("Is Terminal")
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
fmt.Println("Is Cygwin/MSYS2 Terminal")
} else {
fmt.Println("Is Not Terminal")
}
}
```
## Installation
```
$ go get github.com/mattn/go-isatty
```
## License
MIT
## Author
Yasuhiro Matsumoto (a.k.a mattn)
## Thanks
* k-takata: base idea for IsCygwinTerminal
https://github.com/k-takata/go-iscygpty

View file

@ -1,2 +0,0 @@
// Package isatty implements interface to isatty
package isatty

View file

@ -1,18 +0,0 @@
package isatty_test
import (
"fmt"
"os"
"github.com/mattn/go-isatty"
)
func Example() {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println("Is Terminal")
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
fmt.Println("Is Cygwin/MSYS2 Terminal")
} else {
fmt.Println("Is Not Terminal")
}
}

View file

@ -1,15 +0,0 @@
// +build appengine
package isatty
// IsTerminal returns true if the file descriptor is terminal which
// is always false on on appengine classic which is a sandboxed PaaS.
func IsTerminal(fd uintptr) bool {
return false
}
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View file

@ -1,18 +0,0 @@
// +build darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TIOCGETA
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View file

@ -1,18 +0,0 @@
// +build linux
// +build !appengine,!ppc64,!ppc64le
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View file

@ -1,19 +0,0 @@
// +build linux
// +build ppc64 ppc64le
package isatty
import (
"unsafe"
syscall "golang.org/x/sys/unix"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View file

@ -1,10 +0,0 @@
// +build !windows
// +build !appengine
package isatty
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View file

@ -1,19 +0,0 @@
// +build !windows
package isatty
import (
"os"
"testing"
)
func TestTerminal(t *testing.T) {
// test for non-panic
IsTerminal(os.Stdout.Fd())
}
func TestCygwinPipeName(t *testing.T) {
if IsCygwinTerminal(os.Stdout.Fd()) {
t.Fatal("should be false always")
}
}

View file

@ -1,16 +0,0 @@
// +build solaris
// +build !appengine
package isatty
import (
"golang.org/x/sys/unix"
)
// IsTerminal returns true if the given file descriptor is a terminal.
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
func IsTerminal(fd uintptr) bool {
var termio unix.Termio
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
return err == nil
}

View file

@ -1,94 +0,0 @@
// +build windows
// +build !appengine
package isatty
import (
"strings"
"syscall"
"unicode/utf16"
"unsafe"
)
const (
fileNameInfo uintptr = 2
fileTypePipe = 3
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
procGetFileType = kernel32.NewProc("GetFileType")
)
func init() {
// Check if GetFileInformationByHandleEx is available.
if procGetFileInformationByHandleEx.Find() != nil {
procGetFileInformationByHandleEx = nil
}
}
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}
// Check pipe name is used for cygwin/msys2 pty.
// Cygwin/MSYS2 PTY has a name like:
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
func isCygwinPipeName(name string) bool {
token := strings.Split(name, "-")
if len(token) < 5 {
return false
}
if token[0] != `\msys` && token[0] != `\cygwin` {
return false
}
if token[1] == "" {
return false
}
if !strings.HasPrefix(token[2], "pty") {
return false
}
if token[3] != `from` && token[3] != `to` {
return false
}
if token[4] != "master" {
return false
}
return true
}
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal.
func IsCygwinTerminal(fd uintptr) bool {
if procGetFileInformationByHandleEx == nil {
return false
}
// Cygwin/msys's pty is a pipe.
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
if ft != fileTypePipe || e != 0 {
return false
}
var buf [2 + syscall.MAX_PATH]uint16
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
uintptr(len(buf)*2), 0, 0)
if r == 0 || e != 0 {
return false
}
l := *(*uint32)(unsafe.Pointer(&buf))
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
}

View file

@ -1,35 +0,0 @@
// +build windows
package isatty
import (
"testing"
)
func TestCygwinPipeName(t *testing.T) {
tests := []struct {
name string
result bool
}{
{``, false},
{`\msys-`, false},
{`\cygwin-----`, false},
{`\msys-x-PTY5-pty1-from-master`, false},
{`\cygwin-x-PTY5-from-master`, false},
{`\cygwin-x-pty2-from-toaster`, false},
{`\cygwin--pty2-from-master`, false},
{`\\cygwin-x-pty2-from-master`, false},
{`\cygwin-x-pty2-from-master-`, true}, // for the feature
{`\cygwin-e022582115c10879-pty4-from-master`, true},
{`\msys-e022582115c10879-pty4-to-master`, true},
{`\cygwin-e022582115c10879-pty4-to-master`, true},
}
for _, test := range tests {
want := test.result
got := isCygwinPipeName(test.name)
if want != got {
t.Fatalf("isatty(%q): got %v, want %v:", test.name, got, want)
}
}
}