no-colors flag to disable ansi colors, closes #36
Этот коммит содержится в:
родитель
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
Обычный файл
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
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
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()
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче