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
}

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

@ -3,74 +3,131 @@ 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 {
// prints an option or argument with a description, or only description short, long, descr, dflt string
opt := func(name, desc string) string { }
if len(name) > 0 {
name += ":" func (f *flagged) name() string {
} var name string
return s(2) + cl(name, green) + s(22-len(name)) + desc switch {
} case len(f.short) > 0 && len(f.long) > 0:
name = fmt.Sprintf("-%s, --%s", f.short, f.long)
// --- GENERAL --- case len(f.long) > 0:
fmt.Println(cl("Usage:", yellow)) name = fmt.Sprintf("--%s", f.long)
fmt.Printf(s(2) + "godog [options] [<features>]\n\n") case len(f.short) > 0:
// description name = fmt.Sprintf("-%s", f.short)
fmt.Println("Builds a test package and runs given feature files.") }
fmt.Printf("Command should be run from the directory of tested package and contain buildable go source.\n\n") if f.dflt != "true" && f.dflt != "false" {
name += "=" + f.dflt
// --- ARGUMENTS --- }
fmt.Println(cl("Arguments:", yellow)) return name
// --> paths }
fmt.Println(opt("features", "Optional feature(s) to run. Can be:"))
fmt.Println(opt("", s(4)+"- dir "+cl("(features/)", yellow))) func usage(set *flag.FlagSet) func() {
fmt.Println(opt("", s(4)+"- feature "+cl("(*.feature)", yellow))) return func() {
fmt.Println(opt("", s(4)+"- scenario at specific line "+cl("(*.feature:10)", yellow))) var list []*flagged
fmt.Println(opt("", "If no feature paths are listed, suite tries "+cl("features", yellow)+" path by default.")) var longest int
fmt.Println("") set.VisitAll(func(f *flag.Flag) {
var fl *flagged
// --- OPTIONS --- for _, flg := range list {
fmt.Println(cl("Options:", yellow)) if flg.descr == f.Usage {
// --> step definitions fl = flg
fmt.Println(opt("-d, --definitions", "Print all available step definitions.")) break
// --> 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.")) if nil == fl {
fmt.Println(opt("", s(4)+"- "+cl(`>= 2`, yellow)+": only supports "+cl("progress", yellow)+". Note, that")) fl = &flagged{
fmt.Println(opt("", s(4)+"your context needs to support parallel execution.")) dflt: f.DefValue,
// --> format descr: f.Usage,
fmt.Println(opt("-f, --format=pretty", "How to format tests output. Available formats:")) }
for _, f := range formatters { list = append(list, fl)
fmt.Println(opt("", s(4)+"- "+cl(f.name, yellow)+": "+f.description)) }
} if len(f.Name) > 2 {
// --> tags fl.long = f.Name
fmt.Println(opt("-t, --tags", "Filter scenarios by tags. Expression can be:")) } else {
fmt.Println(opt("", s(4)+"- "+cl(`"@wip"`, yellow)+": run all scenarios with wip tag")) fl.short = f.Name
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 for _, f := range list {
fmt.Println(opt("--stop-on-failure", "Stop processing on first failed scenario.")) if len(f.name()) > longest {
// --> version longest = len(f.name())
fmt.Println(opt("--version", "Show current "+cl("godog", yellow)+" version.")) }
fmt.Println("") }
// prints an option or argument with a description, or only description
opt := func(name, desc string) string {
var ret []string
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 strings.Join(ret, "\n")
}
// --- GENERAL ---
fmt.Println(cl("Usage:", yellow))
fmt.Printf(s(2) + "godog [options] [<features>]\n\n")
// description
fmt.Println("Builds a test package and runs given feature files.")
fmt.Printf("Command should be run from the directory of tested package and contain buildable go source.\n\n")
// --- ARGUMENTS ---
fmt.Println(cl("Arguments:", yellow))
// --> features
fmt.Println(opt("features", descFeaturesArgument))
// --- OPTIONS ---
fmt.Println(cl("Options:", yellow))
for _, f := range list {
fmt.Println(opt(f.name(), f.descr))
}
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()