no-colors flag to disable ansi colors, closes #36

Этот коммит содержится в:
gedi 2016-06-08 21:38:47 +03:00
родитель 603066d939
коммит 9cc301f701
4 изменённых файлов: 195 добавлений и 75 удалений

Просмотреть файл

@ -17,8 +17,8 @@ import (
var statusMatch = regexp.MustCompile("^exit status (\\d+)") var statusMatch = regexp.MustCompile("^exit status (\\d+)")
var parsedStatus int var parsedStatus int
var stdout = createAnsiColorWriter(os.Stdout) var stdout = io.Writer(os.Stdout)
var stderr = createAnsiColorWriter(statusOutputFilter(os.Stderr)) var stderr = statusOutputFilter(os.Stderr)
func buildAndRun() (int, error) { func buildAndRun() (int, error) {
var status int var status int
@ -75,19 +75,29 @@ func buildAndRun() (int, error) {
} }
func main() { func main() {
var vers, defs, sof bool var vers, defs, sof, noclr bool
var tags, format string var tags, format string
var concurrency int var concurrency int
flagSet := godog.FlagSet(&format, &tags, &defs, &sof, &vers, &concurrency) flagSet := godog.FlagSet(&format, &tags, &defs, &sof, &noclr, &concurrency)
flagSet.BoolVar(&vers, "version", false, "Show current version.")
err := flagSet.Parse(os.Args[1:]) err := flagSet.Parse(os.Args[1:])
if err != nil { if err != nil {
fmt.Fprintln(stderr, err) fmt.Fprintln(stderr, err)
os.Exit(1) os.Exit(1)
} }
if noclr {
stdout = noColorsWriter(stdout)
stderr = noColorsWriter(stderr)
} else {
stdout = createAnsiColorWriter(stdout)
stderr = createAnsiColorWriter(stderr)
}
if vers { if vers {
fmt.Fprintln(stdout, "Godog version is", godog.Version) fmt.Fprintln(stdout, "Godog version is:", godog.Version)
os.Exit(0) // should it be 0? os.Exit(0) // should it be 0?
} }

57
cmd/godog/no_color.go Обычный файл
Просмотреть файл

@ -0,0 +1,57 @@
package main
import (
"bytes"
"fmt"
"io"
)
type noColors struct {
out io.Writer
lastbuf bytes.Buffer
}
func noColorsWriter(w io.Writer) io.Writer {
return &noColors{out: w}
}
func (w *noColors) Write(data []byte) (n int, err error) {
er := bytes.NewBuffer(data)
loop:
for {
c1, _, err := er.ReadRune()
if err != nil {
break loop
}
if c1 != 0x1b {
fmt.Fprint(w.out, string(c1))
continue
}
c2, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
break loop
}
if c2 != 0x5b {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
continue
}
var buf bytes.Buffer
for {
c, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
w.lastbuf.Write(buf.Bytes())
break loop
}
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
break
}
buf.Write([]byte(string(c)))
}
}
return len(data) - w.lastbuf.Len(), nil
}

145
flags.go
Просмотреть файл

@ -3,32 +3,112 @@ package godog
import ( import (
"flag" "flag"
"fmt" "fmt"
"strings"
) )
var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" +
s(4) + "- dir " + cl("(features/)", yellow) + "\n" +
s(4) + "- feature " + cl("(*.feature)", yellow) + "\n" +
s(4) + "- scenario at specific line " + cl("(*.feature:10)", yellow) + "\n" +
"If no feature paths are listed, suite tries " + cl("features", yellow) + " path by default.\n"
var descConcurrencyOption = "Run the test suite with concurrency level:\n" +
s(4) + "- " + cl(`= 1`, yellow) + ": supports all types of formats.\n" +
s(4) + "- " + cl(`>= 2`, yellow) + ": only supports " + cl("progress", yellow) + ". Note, that\n" +
s(4) + "your context needs to support parallel execution."
var descTagsOption = "Filter scenarios by tags. Expression can be:\n" +
s(4) + "- " + cl(`"@wip"`, yellow) + ": run all scenarios with wip tag\n" +
s(4) + "- " + cl(`"~@wip"`, yellow) + ": exclude all scenarios with wip tag\n" +
s(4) + "- " + cl(`"@wip && ~@new"`, yellow) + ": run wip scenarios, but exclude new\n" +
s(4) + "- " + cl(`"@wip,@undone"`, yellow) + ": run wip or undone scenarios"
// FlagSet allows to manage flags by external suite runner // FlagSet allows to manage flags by external suite runner
func FlagSet(format, tags *string, defs, sof, vers *bool, cl *int) *flag.FlagSet { func FlagSet(format, tags *string, defs, sof, noclr *bool, cr *int) *flag.FlagSet {
descFormatOption := "How to format tests output. Available formats:\n"
for _, f := range formatters {
descFormatOption += s(4) + "- " + cl(f.name, yellow) + ": " + f.description + "\n"
}
descFormatOption = strings.TrimSpace(descFormatOption)
set := flag.NewFlagSet("godog", flag.ExitOnError) set := flag.NewFlagSet("godog", flag.ExitOnError)
set.StringVar(format, "format", "pretty", "") set.StringVar(format, "format", "pretty", descFormatOption)
set.StringVar(format, "f", "pretty", "") set.StringVar(format, "f", "pretty", descFormatOption)
set.StringVar(tags, "tags", "", "") set.StringVar(tags, "tags", "", descTagsOption)
set.StringVar(tags, "t", "", "") set.StringVar(tags, "t", "", descTagsOption)
set.IntVar(cl, "concurrency", 1, "") set.IntVar(cr, "concurrency", 1, descConcurrencyOption)
set.IntVar(cl, "c", 1, "") set.IntVar(cr, "c", 1, descConcurrencyOption)
set.BoolVar(defs, "definitions", false, "") set.BoolVar(defs, "definitions", false, "Print all available step definitions.")
set.BoolVar(defs, "d", false, "") set.BoolVar(defs, "d", false, "Print all available step definitions.")
set.BoolVar(sof, "stop-on-failure", false, "") set.BoolVar(sof, "stop-on-failure", false, "Stop processing on first failed scenario.")
set.BoolVar(vers, "version", false, "") set.BoolVar(noclr, "no-colors", false, "Disable ansi colors.")
set.Usage = usage set.Usage = usage(set)
return set return set
} }
func usage() { type flagged struct {
short, long, descr, dflt string
}
func (f *flagged) name() string {
var name string
switch {
case len(f.short) > 0 && len(f.long) > 0:
name = fmt.Sprintf("-%s, --%s", f.short, f.long)
case len(f.long) > 0:
name = fmt.Sprintf("--%s", f.long)
case len(f.short) > 0:
name = fmt.Sprintf("-%s", f.short)
}
if f.dflt != "true" && f.dflt != "false" {
name += "=" + f.dflt
}
return name
}
func usage(set *flag.FlagSet) func() {
return func() {
var list []*flagged
var longest int
set.VisitAll(func(f *flag.Flag) {
var fl *flagged
for _, flg := range list {
if flg.descr == f.Usage {
fl = flg
break
}
}
if nil == fl {
fl = &flagged{
dflt: f.DefValue,
descr: f.Usage,
}
list = append(list, fl)
}
if len(f.Name) > 2 {
fl.long = f.Name
} else {
fl.short = f.Name
}
})
for _, f := range list {
if len(f.name()) > longest {
longest = len(f.name())
}
}
// prints an option or argument with a description, or only description // prints an option or argument with a description, or only description
opt := func(name, desc string) string { opt := func(name, desc string) string {
if len(name) > 0 { var ret []string
name += ":" lines := strings.Split(desc, "\n")
ret = append(ret, s(2)+cl(name, green)+s(longest+2-len(name))+lines[0])
if len(lines) > 1 {
for _, ln := range lines[1:] {
ret = append(ret, s(2)+s(longest+2)+ln)
} }
return s(2) + cl(name, green) + s(22-len(name)) + desc }
return strings.Join(ret, "\n")
} }
// --- GENERAL --- // --- GENERAL ---
@ -40,37 +120,14 @@ func usage() {
// --- ARGUMENTS --- // --- ARGUMENTS ---
fmt.Println(cl("Arguments:", yellow)) fmt.Println(cl("Arguments:", yellow))
// --> paths // --> features
fmt.Println(opt("features", "Optional feature(s) to run. Can be:")) fmt.Println(opt("features", descFeaturesArgument))
fmt.Println(opt("", s(4)+"- dir "+cl("(features/)", yellow)))
fmt.Println(opt("", s(4)+"- feature "+cl("(*.feature)", yellow)))
fmt.Println(opt("", s(4)+"- scenario at specific line "+cl("(*.feature:10)", yellow)))
fmt.Println(opt("", "If no feature paths are listed, suite tries "+cl("features", yellow)+" path by default."))
fmt.Println("")
// --- OPTIONS --- // --- OPTIONS ---
fmt.Println(cl("Options:", yellow)) fmt.Println(cl("Options:", yellow))
// --> step definitions for _, f := range list {
fmt.Println(opt("-d, --definitions", "Print all available step definitions.")) fmt.Println(opt(f.name(), f.descr))
// --> concurrency
fmt.Println(opt("-c, --concurrency=1", "Run the test suite with concurrency level:"))
fmt.Println(opt("", s(4)+"- "+cl(`= 1`, yellow)+": supports all types of formats."))
fmt.Println(opt("", s(4)+"- "+cl(`>= 2`, yellow)+": only supports "+cl("progress", yellow)+". Note, that"))
fmt.Println(opt("", s(4)+"your context needs to support parallel execution."))
// --> format
fmt.Println(opt("-f, --format=pretty", "How to format tests output. Available formats:"))
for _, f := range formatters {
fmt.Println(opt("", s(4)+"- "+cl(f.name, yellow)+": "+f.description))
} }
// --> tags
fmt.Println(opt("-t, --tags", "Filter scenarios by tags. Expression can be:"))
fmt.Println(opt("", s(4)+"- "+cl(`"@wip"`, yellow)+": run all scenarios with wip tag"))
fmt.Println(opt("", s(4)+"- "+cl(`"~@wip"`, yellow)+": exclude all scenarios with wip tag"))
fmt.Println(opt("", s(4)+"- "+cl(`"@wip && ~@new"`, yellow)+": run wip scenarios, but exclude new"))
fmt.Println(opt("", s(4)+"- "+cl(`"@wip,@undone"`, yellow)+": run wip or undone scenarios"))
// --> stop on failure
fmt.Println(opt("--stop-on-failure", "Stop processing on first failed scenario."))
// --> version
fmt.Println(opt("--version", "Show current "+cl("godog", yellow)+" version."))
fmt.Println("") fmt.Println("")
}
} }

10
run.go
Просмотреть файл

@ -53,18 +53,14 @@ func (r *runner) run() (failed bool) {
// contextInitializer must be able to register // contextInitializer must be able to register
// the step definitions and event handlers. // the step definitions and event handlers.
func Run(contextInitializer func(suite *Suite)) int { func Run(contextInitializer func(suite *Suite)) int {
var vers, defs, sof bool var defs, sof, noclr bool
var tags, format string var tags, format string
var concurrency int var concurrency int
flagSet := FlagSet(&format, &tags, &defs, &sof, &vers, &concurrency) flagSet := FlagSet(&format, &tags, &defs, &sof, &noclr, &concurrency)
err := flagSet.Parse(os.Args[1:]) err := flagSet.Parse(os.Args[1:])
fatal(err) fatal(err)
switch { if defs {
case vers:
fmt.Println(cl("Godog", green) + " version is " + cl(Version, yellow))
return 0
case defs:
s := &Suite{} s := &Suite{}
contextInitializer(s) contextInitializer(s)
s.printStepDefinitions() s.printStepDefinitions()