more error details, refactor arguments to interface type
Этот коммит содержится в:
		
							родитель
							
								
									774d3d1837
								
							
						
					
					
						коммит
						cb47b27090
					
				
					 8 изменённых файлов: 122 добавлений и 55 удалений
				
			
		
							
								
								
									
										90
									
								
								arguments.go
									
										
									
									
									
								
							
							
						
						
									
										90
									
								
								arguments.go
									
										
									
									
									
								
							|  | @ -7,84 +7,122 @@ import ( | ||||||
| 
 | 
 | ||||||
| // Arg is an argument for StepHandler parsed from | // Arg is an argument for StepHandler parsed from | ||||||
| // the regexp submatch to handle the step | // the regexp submatch to handle the step | ||||||
| type Arg string | type Arg struct { | ||||||
|  | 	value interface{} | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // Float64 converts an argument to float64 | // Float64 converts an argument to float64 | ||||||
| // or panics if unable to convert it | // or panics if unable to convert it | ||||||
| func (a Arg) Float64() float64 { | func (a *Arg) Float64() float64 { | ||||||
| 	v, err := strconv.ParseFloat(string(a), 64) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	v, err := strconv.ParseFloat(s, 64) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return v | 		return v | ||||||
| 	} | 	} | ||||||
| 	panic(fmt.Sprintf(`cannot convert "%s" to float64: %s`, a, err)) | 	panic(fmt.Sprintf(`cannot convert "%s" to float64: %s`, s, err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Float32 converts an argument to float32 | // Float32 converts an argument to float32 | ||||||
| // or panics if unable to convert it | // or panics if unable to convert it | ||||||
| func (a Arg) Float32() float32 { | func (a *Arg) Float32() float32 { | ||||||
| 	v, err := strconv.ParseFloat(string(a), 32) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	v, err := strconv.ParseFloat(s, 32) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return float32(v) | 		return float32(v) | ||||||
| 	} | 	} | ||||||
| 	panic(fmt.Sprintf(`cannot convert "%s" to float32: %s`, a, err)) | 	panic(fmt.Sprintf(`cannot convert "%s" to float32: %s`, s, err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Int converts an argument to int | // Int converts an argument to int | ||||||
| // or panics if unable to convert it | // or panics if unable to convert it | ||||||
| func (a Arg) Int() int { | func (a *Arg) Int() int { | ||||||
| 	v, err := strconv.ParseInt(string(a), 10, 0) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	v, err := strconv.ParseInt(s, 10, 0) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return int(v) | 		return int(v) | ||||||
| 	} | 	} | ||||||
| 	panic(fmt.Sprintf(`cannot convert "%s" to int: %s`, a, err)) | 	panic(fmt.Sprintf(`cannot convert "%s" to int: %s`, s, err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Int64 converts an argument to int64 | // Int64 converts an argument to int64 | ||||||
| // or panics if unable to convert it | // or panics if unable to convert it | ||||||
| func (a Arg) Int64() int64 { | func (a *Arg) Int64() int64 { | ||||||
| 	v, err := strconv.ParseInt(string(a), 10, 64) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	v, err := strconv.ParseInt(s, 10, 64) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return v | 		return v | ||||||
| 	} | 	} | ||||||
| 	panic(fmt.Sprintf(`cannot convert "%s" to int64: %s`, a, err)) | 	panic(fmt.Sprintf(`cannot convert "%s" to int64: %s`, s, err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Int32 converts an argument to int32 | // Int32 converts an argument to int32 | ||||||
| // or panics if unable to convert it | // or panics if unable to convert it | ||||||
| func (a Arg) Int32() int32 { | func (a *Arg) Int32() int32 { | ||||||
| 	v, err := strconv.ParseInt(string(a), 10, 32) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	v, err := strconv.ParseInt(s, 10, 32) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return int32(v) | 		return int32(v) | ||||||
| 	} | 	} | ||||||
| 	panic(fmt.Sprintf(`cannot convert "%s" to int32: %s`, a, err)) | 	panic(fmt.Sprintf(`cannot convert "%s" to int32: %s`, s, err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Int16 converts an argument to int16 | // Int16 converts an argument to int16 | ||||||
| // or panics if unable to convert it | // or panics if unable to convert it | ||||||
| func (a Arg) Int16() int16 { | func (a *Arg) Int16() int16 { | ||||||
| 	v, err := strconv.ParseInt(string(a), 10, 16) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	v, err := strconv.ParseInt(s, 10, 16) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return int16(v) | 		return int16(v) | ||||||
| 	} | 	} | ||||||
| 	panic(fmt.Sprintf(`cannot convert "%s" to int16: %s`, a, err)) | 	panic(fmt.Sprintf(`cannot convert "%s" to int16: %s`, s, err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Int8 converts an argument to int8 | // Int8 converts an argument to int8 | ||||||
| // or panics if unable to convert it | // or panics if unable to convert it | ||||||
| func (a Arg) Int8() int8 { | func (a *Arg) Int8() int8 { | ||||||
| 	v, err := strconv.ParseInt(string(a), 10, 8) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	v, err := strconv.ParseInt(s, 10, 8) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return int8(v) | 		return int8(v) | ||||||
| 	} | 	} | ||||||
| 	panic(fmt.Sprintf(`cannot convert "%s" to int8: %s`, a, err)) | 	panic(fmt.Sprintf(`cannot convert "%s" to int8: %s`, s, err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // String converts an argument to string | // String converts an argument to string | ||||||
| func (a Arg) String() string { | func (a *Arg) String() string { | ||||||
| 	return string(a) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	return s | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Bytes converts an argument string to bytes | // Bytes converts an argument string to bytes | ||||||
| func (a Arg) Bytes() []byte { | func (a *Arg) Bytes() []byte { | ||||||
| 	return []byte(a) | 	s, ok := a.value.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf(`cannot convert "%v" to string`, a.value)) | ||||||
|  | 	} | ||||||
|  | 	return []byte(s) | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								config.go
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								config.go
									
										
									
									
									
								
							|  | @ -108,6 +108,7 @@ func (c *config) validate() error { | ||||||
| 
 | 
 | ||||||
| func (c *config) features() (lst []*gherkin.Feature, err error) { | func (c *config) features() (lst []*gherkin.Feature, err error) { | ||||||
| 	for _, pat := range c.paths { | 	for _, pat := range c.paths { | ||||||
|  | 		// check if line number is specified | ||||||
| 		parts := strings.Split(pat, ":") | 		parts := strings.Split(pat, ":") | ||||||
| 		path := parts[0] | 		path := parts[0] | ||||||
| 		line := -1 | 		line := -1 | ||||||
|  | @ -117,6 +118,7 @@ func (c *config) features() (lst []*gherkin.Feature, err error) { | ||||||
| 				return lst, fmt.Errorf("line number should follow after colon path delimiter") | 				return lst, fmt.Errorf("line number should follow after colon path delimiter") | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		// parse features | ||||||
| 		err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error { | 		err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error { | ||||||
| 			if err == nil && !f.IsDir() && strings.HasSuffix(p, ".feature") { | 			if err == nil && !f.IsDir() && strings.HasSuffix(p, ".feature") { | ||||||
| 				ft, err := gherkin.Parse(p) | 				ft, err := gherkin.Parse(p) | ||||||
|  | @ -142,8 +144,14 @@ func (c *config) features() (lst []*gherkin.Feature, err error) { | ||||||
| 			} | 			} | ||||||
| 			return err | 			return err | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		// check error | ||||||
| 			return lst, fmt.Errorf(`feature path "%s" is not available or accessible`, path) | 		switch { | ||||||
|  | 		case os.IsNotExist(err): | ||||||
|  | 			return lst, fmt.Errorf(`feature path "%s" is not available`, path) | ||||||
|  | 		case os.IsPermission(err): | ||||||
|  | 			return lst, fmt.Errorf(`feature path "%s" is not accessible`, path) | ||||||
|  | 		case err != nil: | ||||||
|  | 			return lst, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
|  |  | ||||||
|  | @ -1 +1,9 @@ | ||||||
| Feature: suite hooks | Feature: suite hooks | ||||||
|  |   In order to run tasks before and after important events | ||||||
|  |   As a test suite | ||||||
|  |   I need to provide a way to hook into these events | ||||||
|  | 
 | ||||||
|  |   Background: | ||||||
|  |     Given I have a before scenario hook | ||||||
|  |     And a feature path "features/load_features.feature:6" | ||||||
|  |     # When I parse and run features | ||||||
|  |  | ||||||
|  | @ -99,6 +99,10 @@ func (f *pretty) canPrintStep(step *gherkin.Step) bool { | ||||||
| func (f *pretty) Node(node interface{}) { | func (f *pretty) Node(node interface{}) { | ||||||
| 	switch t := node.(type) { | 	switch t := node.(type) { | ||||||
| 	case *gherkin.Feature: | 	case *gherkin.Feature: | ||||||
|  | 		if f.feature != nil { | ||||||
|  | 			// not a first feature, add a newline | ||||||
|  | 			fmt.Println("") | ||||||
|  | 		} | ||||||
| 		f.feature = t | 		f.feature = t | ||||||
| 		f.doneBackground = false | 		f.doneBackground = false | ||||||
| 		f.background = nil | 		f.background = nil | ||||||
|  |  | ||||||
|  | @ -281,6 +281,9 @@ func (p *parser) parseFeature() (ft *Feature, err error) { | ||||||
| 
 | 
 | ||||||
| 		// there must be a scenario or scenario outline otherwise | 		// there must be a scenario or scenario outline otherwise | ||||||
| 		if !tok.OfType(SCENARIO, SCENARIO_OUTLINE) { | 		if !tok.OfType(SCENARIO, SCENARIO_OUTLINE) { | ||||||
|  | 			if tok.Type == EOF { | ||||||
|  | 				return ft, nil // there may not be a scenario defined after background | ||||||
|  | 			} | ||||||
| 			return ft, p.err("expected a scenario or scenario outline, but got '"+tok.Type.String()+"' instead", tok.Line) | 			return ft, p.err("expected a scenario or scenario outline, but got '"+tok.Type.String()+"' instead", tok.Line) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -51,8 +51,8 @@ func Test_feature_read(t *testing.T) { | ||||||
| 	if tok.Value != val { | 	if tok.Value != val { | ||||||
| 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) | 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) | ||||||
| 	} | 	} | ||||||
| 	if tok.Line != 0 { | 	if tok.Line != 1 { | ||||||
| 		t.Fatalf("Expected a token line to be '0', but got: '%d'", tok.Line) | 		t.Fatalf("Expected a token line to be '1', but got: '%d'", tok.Line) | ||||||
| 	} | 	} | ||||||
| 	if tok.Indent != 0 { | 	if tok.Indent != 0 { | ||||||
| 		t.Fatalf("Expected a token identation to be '0', but got: '%d'", tok.Indent) | 		t.Fatalf("Expected a token identation to be '0', but got: '%d'", tok.Indent) | ||||||
|  | @ -66,21 +66,6 @@ func Test_feature_read(t *testing.T) { | ||||||
| 	if tok.Value != val { | 	if tok.Value != val { | ||||||
| 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) | 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) | ||||||
| 	} | 	} | ||||||
| 	if tok.Line != 1 { |  | ||||||
| 		t.Fatalf("Expected a token line to be '1', but got: '%d'", tok.Line) |  | ||||||
| 	} |  | ||||||
| 	if tok.Indent != 2 { |  | ||||||
| 		t.Fatalf("Expected a token identation to be '2', but got: '%d'", tok.Indent) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tok = l.read() |  | ||||||
| 	if tok.Type != TEXT { |  | ||||||
| 		t.Fatalf("Expected a 'text' type, but got: '%s'", tok.Type) |  | ||||||
| 	} |  | ||||||
| 	val = "as gherkin lexer" |  | ||||||
| 	if tok.Value != val { |  | ||||||
| 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) |  | ||||||
| 	} |  | ||||||
| 	if tok.Line != 2 { | 	if tok.Line != 2 { | ||||||
| 		t.Fatalf("Expected a token line to be '2', but got: '%d'", tok.Line) | 		t.Fatalf("Expected a token line to be '2', but got: '%d'", tok.Line) | ||||||
| 	} | 	} | ||||||
|  | @ -92,7 +77,7 @@ func Test_feature_read(t *testing.T) { | ||||||
| 	if tok.Type != TEXT { | 	if tok.Type != TEXT { | ||||||
| 		t.Fatalf("Expected a 'text' type, but got: '%s'", tok.Type) | 		t.Fatalf("Expected a 'text' type, but got: '%s'", tok.Type) | ||||||
| 	} | 	} | ||||||
| 	val = "I need to be able to parse a feature" | 	val = "as gherkin lexer" | ||||||
| 	if tok.Value != val { | 	if tok.Value != val { | ||||||
| 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) | 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) | ||||||
| 	} | 	} | ||||||
|  | @ -103,6 +88,21 @@ func Test_feature_read(t *testing.T) { | ||||||
| 		t.Fatalf("Expected a token identation to be '2', but got: '%d'", tok.Indent) | 		t.Fatalf("Expected a token identation to be '2', but got: '%d'", tok.Indent) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	tok = l.read() | ||||||
|  | 	if tok.Type != TEXT { | ||||||
|  | 		t.Fatalf("Expected a 'text' type, but got: '%s'", tok.Type) | ||||||
|  | 	} | ||||||
|  | 	val = "I need to be able to parse a feature" | ||||||
|  | 	if tok.Value != val { | ||||||
|  | 		t.Fatalf("Expected a token value to be '%s', but got: '%s'", val, tok.Value) | ||||||
|  | 	} | ||||||
|  | 	if tok.Line != 4 { | ||||||
|  | 		t.Fatalf("Expected a token line to be '4', but got: '%d'", tok.Line) | ||||||
|  | 	} | ||||||
|  | 	if tok.Indent != 2 { | ||||||
|  | 		t.Fatalf("Expected a token identation to be '2', but got: '%d'", tok.Indent) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	tok = l.read() | 	tok = l.read() | ||||||
| 	if tok.Type != EOF { | 	if tok.Type != EOF { | ||||||
| 		t.Fatalf("Expected an 'eof' type, but got: '%s'", tok.Type) | 		t.Fatalf("Expected an 'eof' type, but got: '%s'", tok.Type) | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								suite.go
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								suite.go
									
										
									
									
									
								
							|  | @ -30,17 +30,17 @@ func (f BeforeScenarioHandlerFunc) BeforeScenario(scenario *gherkin.Scenario) { | ||||||
| // and that the feature runner can move on to the next | // and that the feature runner can move on to the next | ||||||
| // step. | // step. | ||||||
| type StepHandler interface { | type StepHandler interface { | ||||||
| 	HandleStep(args ...Arg) error | 	HandleStep(args ...*Arg) error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // StepHandlerFunc type is an adapter to allow the use of | // StepHandlerFunc type is an adapter to allow the use of | ||||||
| // ordinary functions as Step handlers.  If f is a function | // ordinary functions as Step handlers.  If f is a function | ||||||
| // with the appropriate signature, StepHandlerFunc(f) is a | // with the appropriate signature, StepHandlerFunc(f) is a | ||||||
| // StepHandler object that calls f. | // StepHandler object that calls f. | ||||||
| type StepHandlerFunc func(...Arg) error | type StepHandlerFunc func(...*Arg) error | ||||||
| 
 | 
 | ||||||
| // HandleStep calls f(step_arguments...). | // HandleStep calls f(step_arguments...). | ||||||
| func (f StepHandlerFunc) HandleStep(args ...Arg) error { | func (f StepHandlerFunc) HandleStep(args ...*Arg) error { | ||||||
| 	return f(args...) | 	return f(args...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -113,12 +113,18 @@ func (s *suite) Run() { | ||||||
| 
 | 
 | ||||||
| func (s *suite) runStep(step *gherkin.Step) (err error) { | func (s *suite) runStep(step *gherkin.Step) (err error) { | ||||||
| 	var match *stepMatchHandler | 	var match *stepMatchHandler | ||||||
| 	var args []Arg | 	var args []*Arg | ||||||
| 	for _, h := range s.stepHandlers { | 	for _, h := range s.stepHandlers { | ||||||
| 		if m := h.expr.FindStringSubmatch(step.Text); len(m) > 0 { | 		if m := h.expr.FindStringSubmatch(step.Text); len(m) > 0 { | ||||||
| 			match = h | 			match = h | ||||||
| 			for _, a := range m[1:] { | 			for _, a := range m[1:] { | ||||||
| 				args = append(args, Arg(a)) | 				args = append(args, &Arg{value: a}) | ||||||
|  | 			} | ||||||
|  | 			if step.Table != nil { | ||||||
|  | 				args = append(args, &Arg{value: step.Table}) | ||||||
|  | 			} | ||||||
|  | 			if step.PyString != nil { | ||||||
|  | 				args = append(args, &Arg{value: step.PyString}) | ||||||
| 			} | 			} | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -16,24 +16,24 @@ func (s *suiteFeature) BeforeScenario(scenario *gherkin.Scenario) { | ||||||
| 	cfg.paths = []string{} | 	cfg.paths = []string{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *suiteFeature) featurePath(args ...Arg) error { | func (s *suiteFeature) featurePath(args ...*Arg) error { | ||||||
| 	cfg.paths = append(cfg.paths, args[0].String()) | 	cfg.paths = append(cfg.paths, args[0].String()) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *suiteFeature) parseFeatures(args ...Arg) (err error) { | func (s *suiteFeature) parseFeatures(args ...*Arg) (err error) { | ||||||
| 	s.features, err = cfg.features() | 	s.features, err = cfg.features() | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *suiteFeature) numParsed(args ...Arg) (err error) { | func (s *suiteFeature) numParsed(args ...*Arg) (err error) { | ||||||
| 	if len(s.features) != args[0].Int() { | 	if len(s.features) != args[0].Int() { | ||||||
| 		err = fmt.Errorf("expected %d features to be parsed, but have %d", args[0].Int(), len(s.features)) | 		err = fmt.Errorf("expected %d features to be parsed, but have %d", args[0].Int(), len(s.features)) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *suiteFeature) numScenariosRegistered(args ...Arg) (err error) { | func (s *suiteFeature) numScenariosRegistered(args ...*Arg) (err error) { | ||||||
| 	var num int | 	var num int | ||||||
| 	for _, ft := range s.features { | 	for _, ft := range s.features { | ||||||
| 		num += len(ft.Scenarios) | 		num += len(ft.Scenarios) | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 gedi
						gedi