github.com/lonnblad/godog@v0.7.14-0.20200306004719-1b0cb3259847/stepdef.go (about) 1 package godog 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "reflect" 8 "regexp" 9 "runtime" 10 "strconv" 11 "strings" 12 13 "github.com/cucumber/messages-go/v9" 14 ) 15 16 var matchFuncDefRef = regexp.MustCompile(`\(([^\)]+)\)`) 17 18 // Steps allows to nest steps 19 // instead of returning an error in step func 20 // it is possible to return combined steps: 21 // 22 // func multistep(name string) godog.Steps { 23 // return godog.Steps{ 24 // fmt.Sprintf(`an user named "%s"`, name), 25 // fmt.Sprintf(`user "%s" is authenticated`, name), 26 // } 27 // } 28 // 29 // These steps will be matched and executed in 30 // sequential order. The first one which fails 31 // will result in main step failure. 32 type Steps []string 33 34 // StepDefinition is a registered step definition 35 // contains a StepHandler and regexp which 36 // is used to match a step. Args which 37 // were matched by last executed step 38 // 39 // This structure is passed to the formatter 40 // when step is matched and is either failed 41 // or successful 42 type StepDefinition struct { 43 args []interface{} 44 hv reflect.Value 45 Expr *regexp.Regexp 46 Handler interface{} 47 48 // multistep related 49 nested bool 50 undefined []string 51 } 52 53 func (sd *StepDefinition) definitionID() string { 54 ptr := sd.hv.Pointer() 55 f := runtime.FuncForPC(ptr) 56 file, line := f.FileLine(ptr) 57 dir := filepath.Dir(file) 58 59 fn := strings.Replace(f.Name(), dir, "", -1) 60 var parts []string 61 for _, gr := range matchFuncDefRef.FindAllStringSubmatch(fn, -1) { 62 parts = append(parts, strings.Trim(gr[1], "_.")) 63 } 64 if len(parts) > 0 { 65 // case when suite is a structure with methods 66 fn = strings.Join(parts, ".") 67 } else { 68 // case when steps are just plain funcs 69 fn = strings.Trim(fn, "_.") 70 } 71 72 if pkg := os.Getenv("GODOG_TESTED_PACKAGE"); len(pkg) > 0 { 73 fn = strings.Replace(fn, pkg, "", 1) 74 fn = strings.TrimLeft(fn, ".") 75 fn = strings.Replace(fn, "..", ".", -1) 76 } 77 78 return fmt.Sprintf("%s:%d -> %s", filepath.Base(file), line, fn) 79 } 80 81 // run a step with the matched arguments using 82 // reflect 83 func (sd *StepDefinition) run() interface{} { 84 typ := sd.hv.Type() 85 if len(sd.args) < typ.NumIn() { 86 return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.args)) 87 } 88 var values []reflect.Value 89 for i := 0; i < typ.NumIn(); i++ { 90 param := typ.In(i) 91 switch param.Kind() { 92 case reflect.Int: 93 s, err := sd.shouldBeString(i) 94 if err != nil { 95 return err 96 } 97 v, err := strconv.ParseInt(s, 10, 0) 98 if err != nil { 99 return fmt.Errorf(`cannot convert argument %d: "%s" to int: %s`, i, s, err) 100 } 101 values = append(values, reflect.ValueOf(int(v))) 102 case reflect.Int64: 103 s, err := sd.shouldBeString(i) 104 if err != nil { 105 return err 106 } 107 v, err := strconv.ParseInt(s, 10, 64) 108 if err != nil { 109 return fmt.Errorf(`cannot convert argument %d: "%s" to int64: %s`, i, s, err) 110 } 111 values = append(values, reflect.ValueOf(int64(v))) 112 case reflect.Int32: 113 s, err := sd.shouldBeString(i) 114 if err != nil { 115 return err 116 } 117 v, err := strconv.ParseInt(s, 10, 32) 118 if err != nil { 119 return fmt.Errorf(`cannot convert argument %d: "%s" to int32: %s`, i, s, err) 120 } 121 values = append(values, reflect.ValueOf(int32(v))) 122 case reflect.Int16: 123 s, err := sd.shouldBeString(i) 124 if err != nil { 125 return err 126 } 127 v, err := strconv.ParseInt(s, 10, 16) 128 if err != nil { 129 return fmt.Errorf(`cannot convert argument %d: "%s" to int16: %s`, i, s, err) 130 } 131 values = append(values, reflect.ValueOf(int16(v))) 132 case reflect.Int8: 133 s, err := sd.shouldBeString(i) 134 if err != nil { 135 return err 136 } 137 v, err := strconv.ParseInt(s, 10, 8) 138 if err != nil { 139 return fmt.Errorf(`cannot convert argument %d: "%s" to int8: %s`, i, s, err) 140 } 141 values = append(values, reflect.ValueOf(int8(v))) 142 case reflect.String: 143 s, err := sd.shouldBeString(i) 144 if err != nil { 145 return err 146 } 147 values = append(values, reflect.ValueOf(s)) 148 case reflect.Float64: 149 s, err := sd.shouldBeString(i) 150 if err != nil { 151 return err 152 } 153 v, err := strconv.ParseFloat(s, 64) 154 if err != nil { 155 return fmt.Errorf(`cannot convert argument %d: "%s" to float64: %s`, i, s, err) 156 } 157 values = append(values, reflect.ValueOf(v)) 158 case reflect.Float32: 159 s, err := sd.shouldBeString(i) 160 if err != nil { 161 return err 162 } 163 v, err := strconv.ParseFloat(s, 32) 164 if err != nil { 165 return fmt.Errorf(`cannot convert argument %d: "%s" to float32: %s`, i, s, err) 166 } 167 values = append(values, reflect.ValueOf(float32(v))) 168 case reflect.Ptr: 169 arg := sd.args[i] 170 switch param.Elem().String() { 171 case "messages.PickleStepArgument_PickleDocString": 172 if v, ok := arg.(*messages.PickleStepArgument); ok { 173 values = append(values, reflect.ValueOf(v.GetDocString())) 174 break 175 } 176 177 if v, ok := arg.(*messages.PickleStepArgument_PickleDocString); ok { 178 values = append(values, reflect.ValueOf(v)) 179 break 180 } 181 182 return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleDocString`, i, arg, arg) 183 case "messages.PickleStepArgument_PickleTable": 184 if v, ok := arg.(*messages.PickleStepArgument); ok { 185 values = append(values, reflect.ValueOf(v.GetDataTable())) 186 break 187 } 188 189 if v, ok := arg.(*messages.PickleStepArgument_PickleTable); ok { 190 values = append(values, reflect.ValueOf(v)) 191 break 192 } 193 194 return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleTable`, i, arg, arg) 195 default: 196 return fmt.Errorf("the argument %d type %T is not supported %s", i, arg, param.Elem().String()) 197 } 198 case reflect.Slice: 199 switch param { 200 case typeOfBytes: 201 s, err := sd.shouldBeString(i) 202 if err != nil { 203 return err 204 } 205 values = append(values, reflect.ValueOf([]byte(s))) 206 default: 207 return fmt.Errorf("the slice argument %d type %s is not supported", i, param.Kind()) 208 } 209 default: 210 return fmt.Errorf("the argument %d type %s is not supported", i, param.Kind()) 211 } 212 } 213 214 return sd.hv.Call(values)[0].Interface() 215 } 216 217 func (sd *StepDefinition) shouldBeString(idx int) (string, error) { 218 arg := sd.args[idx] 219 s, ok := arg.(string) 220 if !ok { 221 return "", fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to string`, idx, arg, arg) 222 } 223 return s, nil 224 }