github.com/maps90/godog@v0.7.5-0.20170923143419-0093943021d4/flags.go (about) 1 package godog 2 3 import ( 4 "flag" 5 "fmt" 6 "io" 7 "math/rand" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/DATA-DOG/godog/colors" 13 ) 14 15 var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" + 16 s(4) + "- dir " + colors.Yellow("(features/)") + "\n" + 17 s(4) + "- feature " + colors.Yellow("(*.feature)") + "\n" + 18 s(4) + "- scenario at specific line " + colors.Yellow("(*.feature:10)") + "\n" + 19 "If no feature paths are listed, suite tries " + colors.Yellow("features") + " path by default.\n" 20 21 var descConcurrencyOption = "Run the test suite with concurrency level:\n" + 22 s(4) + "- " + colors.Yellow(`= 1`) + ": supports all types of formats.\n" + 23 s(4) + "- " + colors.Yellow(`>= 2`) + ": only supports " + colors.Yellow("progress") + ". Note, that\n" + 24 s(4) + "your context needs to support parallel execution." 25 26 var descTagsOption = "Filter scenarios by tags. Expression can be:\n" + 27 s(4) + "- " + colors.Yellow(`"@wip"`) + ": run all scenarios with wip tag\n" + 28 s(4) + "- " + colors.Yellow(`"~@wip"`) + ": exclude all scenarios with wip tag\n" + 29 s(4) + "- " + colors.Yellow(`"@wip && ~@new"`) + ": run wip scenarios, but exclude new\n" + 30 s(4) + "- " + colors.Yellow(`"@wip,@undone"`) + ": run wip or undone scenarios" 31 32 var descRandomOption = "Randomly shuffle the scenario execution order.\n" + 33 "Specify SEED to reproduce the shuffling from a previous run.\n" + 34 s(4) + `e.g. ` + colors.Yellow(`--random`) + " or " + colors.Yellow(`--random=5738`) 35 36 // FlagSet allows to manage flags by external suite runner 37 func FlagSet(opt *Options) *flag.FlagSet { 38 descFormatOption := "How to format tests output. Built-in formats:\n" 39 // @TODO: sort by name 40 for name, desc := range AvailableFormatters() { 41 descFormatOption += s(4) + "- " + colors.Yellow(name) + ": " + desc + "\n" 42 } 43 descFormatOption = strings.TrimSpace(descFormatOption) 44 45 set := flag.NewFlagSet("godog", flag.ExitOnError) 46 set.StringVar(&opt.Format, "format", "pretty", descFormatOption) 47 set.StringVar(&opt.Format, "f", "pretty", descFormatOption) 48 set.StringVar(&opt.Tags, "tags", "", descTagsOption) 49 set.StringVar(&opt.Tags, "t", "", descTagsOption) 50 set.IntVar(&opt.Concurrency, "concurrency", 1, descConcurrencyOption) 51 set.IntVar(&opt.Concurrency, "c", 1, descConcurrencyOption) 52 set.BoolVar(&opt.ShowStepDefinitions, "definitions", false, "Print all available step definitions.") 53 set.BoolVar(&opt.ShowStepDefinitions, "d", false, "Print all available step definitions.") 54 set.BoolVar(&opt.StopOnFailure, "stop-on-failure", false, "Stop processing on first failed scenario.") 55 set.BoolVar(&opt.Strict, "strict", false, "Fail suite when there are pending or undefined steps.") 56 set.BoolVar(&opt.NoColors, "no-colors", false, "Disable ansi colors.") 57 set.Var(&randomSeed{&opt.Randomize}, "random", descRandomOption) 58 set.Usage = usage(set, opt.Output) 59 return set 60 } 61 62 type flagged struct { 63 short, long, descr, dflt string 64 } 65 66 func (f *flagged) name() string { 67 var name string 68 switch { 69 case len(f.short) > 0 && len(f.long) > 0: 70 name = fmt.Sprintf("-%s, --%s", f.short, f.long) 71 case len(f.long) > 0: 72 name = fmt.Sprintf("--%s", f.long) 73 case len(f.short) > 0: 74 name = fmt.Sprintf("-%s", f.short) 75 } 76 77 if f.long == "random" { 78 // `random` is special in that we will later assign it randomly 79 // if the user specifies `--random` without specifying one, 80 // so mask the "default" value here to avoid UI confusion about 81 // what the value will end up being. 82 name += "[=SEED]" 83 } else if f.dflt != "true" && f.dflt != "false" { 84 name += "=" + f.dflt 85 } 86 return name 87 } 88 89 func usage(set *flag.FlagSet, w io.Writer) func() { 90 return func() { 91 var list []*flagged 92 var longest int 93 set.VisitAll(func(f *flag.Flag) { 94 var fl *flagged 95 for _, flg := range list { 96 if flg.descr == f.Usage { 97 fl = flg 98 break 99 } 100 } 101 if nil == fl { 102 fl = &flagged{ 103 dflt: f.DefValue, 104 descr: f.Usage, 105 } 106 list = append(list, fl) 107 } 108 if len(f.Name) > 2 { 109 fl.long = f.Name 110 } else { 111 fl.short = f.Name 112 } 113 }) 114 115 for _, f := range list { 116 if len(f.name()) > longest { 117 longest = len(f.name()) 118 } 119 } 120 121 // prints an option or argument with a description, or only description 122 opt := func(name, desc string) string { 123 var ret []string 124 lines := strings.Split(desc, "\n") 125 ret = append(ret, s(2)+colors.Green(name)+s(longest+2-len(name))+lines[0]) 126 if len(lines) > 1 { 127 for _, ln := range lines[1:] { 128 ret = append(ret, s(2)+s(longest+2)+ln) 129 } 130 } 131 return strings.Join(ret, "\n") 132 } 133 134 // --- GENERAL --- 135 fmt.Fprintln(w, colors.Yellow("Usage:")) 136 fmt.Fprintf(w, s(2)+"godog [options] [<features>]\n\n") 137 // description 138 fmt.Fprintln(w, "Builds a test package and runs given feature files.") 139 fmt.Fprintf(w, "Command should be run from the directory of tested package and contain buildable go source.\n\n") 140 141 // --- ARGUMENTS --- 142 fmt.Fprintln(w, colors.Yellow("Arguments:")) 143 // --> features 144 fmt.Fprintln(w, opt("features", descFeaturesArgument)) 145 146 // --- OPTIONS --- 147 fmt.Fprintln(w, colors.Yellow("Options:")) 148 for _, f := range list { 149 fmt.Fprintln(w, opt(f.name(), f.descr)) 150 } 151 fmt.Fprintln(w, "") 152 } 153 } 154 155 // randomSeed implements `flag.Value`, see https://golang.org/pkg/flag/#Value 156 type randomSeed struct { 157 ref *int64 158 } 159 160 // Choose randomly assigns a convenient pseudo-random seed value. 161 // The resulting seed will be between `1-99999` for later ease of specification. 162 func (rs *randomSeed) choose() { 163 r := rand.New(rand.NewSource(time.Now().UTC().UnixNano())) 164 *rs.ref = r.Int63n(99998) + 1 165 } 166 167 func (rs *randomSeed) Set(s string) error { 168 if s == "true" { 169 rs.choose() 170 return nil 171 } 172 173 if s == "false" { 174 *rs.ref = 0 175 return nil 176 } 177 178 i, err := strconv.ParseInt(s, 10, 64) 179 *rs.ref = i 180 return err 181 } 182 183 func (rs randomSeed) String() string { 184 return strconv.FormatInt(*rs.ref, 10) 185 } 186 187 // If a Value has an IsBoolFlag() bool method returning true, the command-line 188 // parser makes -name equivalent to -name=true rather than using the next 189 // command-line argument. 190 func (rs *randomSeed) IsBoolFlag() bool { 191 return *rs.ref == 0 192 }