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 parsedStatus int
var stdout = createAnsiColorWriter(os.Stdout)
var stderr = createAnsiColorWriter(statusOutputFilter(os.Stderr))
var stdout = io.Writer(os.Stdout)
var stderr = statusOutputFilter(os.Stderr)
func buildAndRun() (int, error) {
var status int
@ -75,19 +75,29 @@ func buildAndRun() (int, error) {
}
func main() {
var vers, defs, sof bool
var vers, defs, sof, noclr bool
var tags, format string
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:])
if err != nil {
fmt.Fprintln(stderr, err)
os.Exit(1)
}
if noclr {
stdout = noColorsWriter(stdout)
stderr = noColorsWriter(stderr)
} else {
stdout = createAnsiColorWriter(stdout)
stderr = createAnsiColorWriter(stderr)
}
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?
}

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 (
"flag"
"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
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.StringVar(format, "format", "pretty", "")
set.StringVar(format, "f", "pretty", "")
set.StringVar(tags, "tags", "", "")
set.StringVar(tags, "t", "", "")
set.IntVar(cl, "concurrency", 1, "")
set.IntVar(cl, "c", 1, "")
set.BoolVar(defs, "definitions", false, "")
set.BoolVar(defs, "d", false, "")
set.BoolVar(sof, "stop-on-failure", false, "")
set.BoolVar(vers, "version", false, "")
set.Usage = usage
set.StringVar(format, "format", "pretty", descFormatOption)
set.StringVar(format, "f", "pretty", descFormatOption)
set.StringVar(tags, "tags", "", descTagsOption)
set.StringVar(tags, "t", "", descTagsOption)
set.IntVar(cr, "concurrency", 1, descConcurrencyOption)
set.IntVar(cr, "c", 1, descConcurrencyOption)
set.BoolVar(defs, "definitions", false, "Print all available step definitions.")
set.BoolVar(defs, "d", false, "Print all available step definitions.")
set.BoolVar(sof, "stop-on-failure", false, "Stop processing on first failed scenario.")
set.BoolVar(noclr, "no-colors", false, "Disable ansi colors.")
set.Usage = usage(set)
return set
}
func usage() {
// prints an option or argument with a description, or only description
opt := func(name, desc string) string {
if len(name) > 0 {
name += ":"
}
return s(2) + cl(name, green) + s(22-len(name)) + desc
}
// --- 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))
// --> paths
fmt.Println(opt("features", "Optional feature(s) to run. Can be:"))
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 ---
fmt.Println(cl("Options:", yellow))
// --> step definitions
fmt.Println(opt("-d, --definitions", "Print all available step definitions."))
// --> 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("")
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
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
// the step definitions and event handlers.
func Run(contextInitializer func(suite *Suite)) int {
var vers, defs, sof bool
var defs, sof, noclr bool
var tags, format string
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:])
fatal(err)
switch {
case vers:
fmt.Println(cl("Godog", green) + " version is " + cl(Version, yellow))
return 0
case defs:
if defs {
s := &Suite{}
contextInitializer(s)
s.printStepDefinitions()