Этот коммит содержится в:
Levi Noecker 2021-08-10 04:27:17 -05:00 коммит произвёл GitHub
родитель ad7feb3298
коммит c6c2a0885b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 584 добавлений и 304 удалений

Просмотреть файл

@ -13,11 +13,12 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
### Added ### Added
- Support for step definitions without return ([364](https://github.com/cucumber/godog/pull/364) - [titouanfreville]) - Support for step definitions without return ([364](https://github.com/cucumber/godog/pull/364) - [titouanfreville])
- Contextualized hooks for scenarios and steps ([409](https://github.com/cucumber/godog/pull/409)) - [vearutop]) - Contextualized hooks for scenarios and steps ([409](https://github.com/cucumber/godog/pull/409) - [vearutop])
- Step result status in After hook ([409](https://github.com/cucumber/godog/pull/409)) - [vearutop]) - Step result status in After hook ([409](https://github.com/cucumber/godog/pull/409) - [vearutop])
- Support auto converting doc strings to plain strings ([380](https://github.com/cucumber/godog/pull/380)) - [chirino]) - Support auto converting doc strings to plain strings ([380](https://github.com/cucumber/godog/pull/380) - [chirino])
- Use multiple formatters in the same test run ([392](https://github.com/cucumber/godog/pull/392)) - [vearutop]) - Use multiple formatters in the same test run ([392](https://github.com/cucumber/godog/pull/392) - [vearutop])
- Added `RetrieveFeatures()` method to `godog.TestSuite` ([276](https://github.com/cucumber/godog/pull/276)) - [radtriste]) - Added `RetrieveFeatures()` method to `godog.TestSuite` ([276](https://github.com/cucumber/godog/pull/276) - [radtriste])
- Added support to create custom formatters ([372](https://github.com/cucumber/godog/pull/372) - [leviable])
### Changed ### Changed

19
_examples/custom-formatter/README.md Обычный файл
Просмотреть файл

@ -0,0 +1,19 @@
# Custom Formatter Example
This example custom formatter demonstrates some ways to build and use custom formatters with godog
## Emoji Progress
The first example is the Emoji formatter, built on top of the Progress formatter that is included with godog.
To run it:
```
$ godog -f emoji
```
Which would output step progress as emojis rather than text:
![](imgs/emoji-output-example.png)

Просмотреть файл

@ -1,32 +0,0 @@
package customformatter
import (
"io"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
)
func init() {
godog.Format("custom", "Custom formatter", customFormatterFunc)
}
func customFormatterFunc(suite string, out io.Writer) godog.Formatter {
return &customFmt{suiteName: suite, out: out}
}
type customFmt struct {
suiteName string
out io.Writer
}
func (f *customFmt) TestRunStarted() {}
func (f *customFmt) Feature(*messages.GherkinDocument, string, []byte) {}
func (f *customFmt) Pickle(*godog.Scenario) {}
func (f *customFmt) Defined(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Passed(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Skipped(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Undefined(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Failed(*godog.Scenario, *godog.Step, *godog.StepDefinition, error) {}
func (f *customFmt) Pending(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Summary() {}

122
_examples/custom-formatter/emoji.go Обычный файл
Просмотреть файл

@ -0,0 +1,122 @@
package main
import (
"fmt"
"io"
"math"
"github.com/cucumber/godog"
)
const (
passedEmoji = "✅"
skippedEmoji = ""
failedEmoji = "❌"
undefinedEmoji = "❓"
pendingEmoji = "🚧"
)
func init() {
godog.Format("emoji", "Progress formatter with emojis", emojiFormatterFunc)
}
func emojiFormatterFunc(suite string, out io.Writer) godog.Formatter {
return newEmojiFmt(suite, out)
}
func newEmojiFmt(suite string, out io.Writer) *emojiFmt {
return &emojiFmt{
ProgressFmt: godog.NewProgressFmt(suite, out),
out: out,
}
}
type emojiFmt struct {
*godog.ProgressFmt
out io.Writer
}
func (f *emojiFmt) TestRunStarted() {}
func (f *emojiFmt) Passed(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Passed(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Skipped(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Skipped(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Undefined(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Undefined(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Failed(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition, err error) {
f.ProgressFmt.Base.Failed(scenario, step, match, err)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Pending(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Pending(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Summary() {
f.printSummaryLegend()
f.ProgressFmt.Summary()
}
func (f *emojiFmt) printSummaryLegend() {
fmt.Fprint(f.out, "\n\nOutput Legend:\n")
fmt.Fprint(f.out, fmt.Sprintf("\t%s Passed\n", passedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Failed\n", failedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Skipped\n", skippedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Undefined\n", undefinedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Pending\n", pendingEmoji))
}
func (f *emojiFmt) step(pickleStepID string) {
pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStepID)
switch pickleStepResult.Status {
case godog.StepPassed:
fmt.Fprint(f.out, fmt.Sprintf(" %s", passedEmoji))
case godog.StepSkipped:
fmt.Fprint(f.out, fmt.Sprintf(" %s", skippedEmoji))
case godog.StepFailed:
fmt.Fprint(f.out, fmt.Sprintf(" %s", failedEmoji))
case godog.StepUndefined:
fmt.Fprint(f.out, fmt.Sprintf(" %s", undefinedEmoji))
case godog.StepPending:
fmt.Fprint(f.out, fmt.Sprintf(" %s", pendingEmoji))
}
*f.Steps++
if math.Mod(float64(*f.Steps), float64(f.StepsPerRow)) == 0 {
fmt.Fprintf(f.out, " %d\n", *f.Steps)
}
}

Просмотреть файл

@ -0,0 +1,26 @@
# file: $GOPATH/godogs/features/godogs.feature
Feature: Custom emoji formatter examples
In order to be happy
As a hungry gopher
I need to be able to eat godogs
Scenario: Passing test
Given there are 12 godogs
When I eat 5
Then there should be 7 remaining
Scenario: Failing and Skipped test
Given there are 12 godogs
When I eat 5
Then there should be 6 remaining
And there should be 4 remaining
Scenario: Undefined steps
Given there are 12 godogs
When I eat 5
Then this step is not defined
Scenario: Pending step
Given there are 12 godogs
When I eat 5
Then this step is pending

Просмотреть файл

@ -3,10 +3,11 @@ module custom-formatter
go 1.14 go 1.14
require ( require (
github.com/cucumber/gherkin-go/v11 v11.0.0 // indirect
github.com/cucumber/godog v0.10.1-0.20210705192606-df8c6e49b40b github.com/cucumber/godog v0.10.1-0.20210705192606-df8c6e49b40b
github.com/cucumber/messages-go/v10 v10.0.3 // indirect
github.com/cucumber/messages-go/v16 v16.0.1
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-memdb v1.3.2 // indirect github.com/hashicorp/go-memdb v1.3.2 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/spf13/pflag v1.0.5
) )
replace github.com/cucumber/godog => ../..

Просмотреть файл

@ -19,7 +19,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aslakhellesoy/gox v1.0.100/go.mod h1:AJl542QsKKG96COVsv0N74HHzVQgDIQPceVUh1aeU2M=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@ -33,19 +32,8 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cucumber/gherkin-go/v11 v11.0.0 h1:cwVwN1Qn2VRSfHZNLEh5x00tPBmZcjATBWDpxsR5Xug=
github.com/cucumber/gherkin-go/v11 v11.0.0/go.mod h1:CX33k2XU2qog4e+TFjOValoq6mIUq0DmVccZs238R9w=
github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE= github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE=
github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw= github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw=
github.com/cucumber/godog v0.10.1-0.20210705192606-df8c6e49b40b h1:TthfOM7c3wz2M5kUvktCU27WXufvUSV/YtcxQk2yaq8=
github.com/cucumber/godog v0.10.1-0.20210705192606-df8c6e49b40b/go.mod h1:ql2U1OH5nlLZ2UDD/3fDJ1+0vkib0XGgEn8NYXCwDZQ=
github.com/cucumber/godog v0.11.0 h1:xgaWyJuAD6A+aW4TfVGNDBhuMyKW0jjl0cvY3KNxEak=
github.com/cucumber/godog v0.11.0/go.mod h1:GyxCIrsg1sgEgpL2GD/rMr3fIoNHpgkjm9nANw/89XY=
github.com/cucumber/messages-go/v10 v10.0.1/go.mod h1:kA5T38CBlBbYLU12TIrJ4fk4wSkVVOgyh7Enyy8WnSg=
github.com/cucumber/messages-go/v10 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY=
github.com/cucumber/messages-go/v16 v10.0.1/go.mod h1:kA5T38CBlBbYLU12TIrJ4fk4wSkVVOgyh7Enyy8WnSg=
github.com/cucumber/messages-go/v16 v10.0.3 h1:m/9SD/K/A15WP7i1aemIv7cwvUw+viS51Ui5HBw1cdE=
github.com/cucumber/messages-go/v16 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY=
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g= github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY= github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY=
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g= github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
@ -62,14 +50,10 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -88,6 +72,7 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@ -98,11 +83,9 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE=
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.3.0 h1:xdXq34gBOMEloa9rlGStLxmfX/dyIK8htOv36dQUwHU=
github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g= github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
github.com/hashicorp/go-memdb v1.3.2 h1:RBKHOsnSszpU6vxq80LzC2BaQjuuvoyaQbkLTf7V7g8= github.com/hashicorp/go-memdb v1.3.2 h1:RBKHOsnSszpU6vxq80LzC2BaQjuuvoyaQbkLTf7V7g8=
github.com/hashicorp/go-memdb v1.3.2/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g= github.com/hashicorp/go-memdb v1.3.2/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
@ -115,7 +98,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -130,15 +112,14 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -161,7 +142,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -186,7 +166,9 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@ -202,9 +184,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
@ -281,7 +260,6 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@ -322,8 +300,6 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@ -331,7 +307,6 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

6
_examples/custom-formatter/godogs.go Обычный файл
Просмотреть файл

@ -0,0 +1,6 @@
package main
// Godogs available to eat
var Godogs int
func main() { /* usual main func */ }

75
_examples/custom-formatter/godogs_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,75 @@
package main
import (
"context"
"fmt"
"os"
"testing"
"github.com/cucumber/godog"
"github.com/cucumber/godog/colors"
flag "github.com/spf13/pflag"
)
var opts = godog.Options{
Output: colors.Colored(os.Stdout),
Format: "emoji",
}
func init() {
godog.BindCommandLineFlags("godog.", &opts)
}
func TestMain(m *testing.M) {
flag.Parse()
opts.Paths = flag.Args()
status := godog.TestSuite{
Name: "godogs",
TestSuiteInitializer: InitializeTestSuite,
ScenarioInitializer: InitializeScenario,
Options: &opts,
}.Run()
os.Exit(status)
}
func thereAreGodogs(available int) error {
Godogs = available
return nil
}
func iEat(num int) error {
if Godogs < num {
return fmt.Errorf("you cannot eat %d godogs, there are %d available", num, Godogs)
}
Godogs -= num
return nil
}
func thereShouldBeRemaining(remaining int) error {
if Godogs != remaining {
return fmt.Errorf("expected %d godogs to be remaining, but there is %d", remaining, Godogs)
}
return nil
}
func thisStepIsPending() error {
return godog.ErrPending
}
func InitializeTestSuite(ctx *godog.TestSuiteContext) {
ctx.BeforeSuite(func() { Godogs = 0 })
}
func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
Godogs = 0 // clean the state before every scenario
return ctx, nil
})
ctx.Step(`^there are (\d+) godogs$`, thereAreGodogs)
ctx.Step(`^I eat (\d+)$`, iEat)
ctx.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
ctx.Step(`^this step is pending$`, thisStepIsPending)
}

Двоичные данные
_examples/custom-formatter/imgs/emoji-output-example.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 87 КиБ

48
fmt.go
Просмотреть файл

@ -74,3 +74,51 @@ func printStepDefinitions(steps []*models.StepDefinition, w io.Writer) {
fmt.Fprintln(w, "there were no contexts registered, could not find any step definition..") fmt.Fprintln(w, "there were no contexts registered, could not find any step definition..")
} }
} }
// NewBaseFmt creates a new base formatter.
func NewBaseFmt(suite string, out io.Writer) *BaseFmt {
return internal_fmt.NewBase(suite, out)
}
// NewProgressFmt creates a new progress formatter.
func NewProgressFmt(suite string, out io.Writer) *ProgressFmt {
return internal_fmt.NewProgress(suite, out)
}
// NewPrettyFmt creates a new pretty formatter.
func NewPrettyFmt(suite string, out io.Writer) *PrettyFmt {
return &PrettyFmt{Base: NewBaseFmt(suite, out)}
}
// NewEventsFmt creates a new event streaming formatter.
func NewEventsFmt(suite string, out io.Writer) *EventsFmt {
return &EventsFmt{Base: NewBaseFmt(suite, out)}
}
// NewCukeFmt creates a new Cucumber JSON formatter.
func NewCukeFmt(suite string, out io.Writer) *CukeFmt {
return &CukeFmt{Base: NewBaseFmt(suite, out)}
}
// NewJUnitFmt creates a new JUnit formatter.
func NewJUnitFmt(suite string, out io.Writer) *JUnitFmt {
return &JUnitFmt{Base: NewBaseFmt(suite, out)}
}
// BaseFmt exports Base formatter.
type BaseFmt = internal_fmt.Base
// ProgressFmt exports Progress formatter.
type ProgressFmt = internal_fmt.Progress
// PrettyFmt exports Pretty formatter.
type PrettyFmt = internal_fmt.Pretty
// EventsFmt exports Events formatter.
type EventsFmt = internal_fmt.Events
// CukeFmt exports Cucumber JSON formatter.
type CukeFmt = internal_fmt.Cuke
// JUnitFmt exports JUnit formatter.
type JUnitFmt = internal_fmt.JUnit

Просмотреть файл

@ -20,82 +20,82 @@ import (
"github.com/cucumber/godog/internal/utils" "github.com/cucumber/godog/internal/utils"
) )
// BaseFormatterFunc implements the FormatterFunc for the base formatter // BaseFormatterFunc implements the FormatterFunc for the base formatter.
func BaseFormatterFunc(suite string, out io.Writer) formatters.Formatter { func BaseFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return NewBaseFmt(suite, out) return NewBase(suite, out)
} }
// NewBaseFmt creates a new base formatter // NewBase creates a new base formatter.
func NewBaseFmt(suite string, out io.Writer) *Basefmt { func NewBase(suite string, out io.Writer) *Base {
return &Basefmt{ return &Base{
suiteName: suite, suiteName: suite,
indent: 2, indent: 2,
out: out, out: out,
lock: new(sync.Mutex), Lock: new(sync.Mutex),
} }
} }
// Basefmt ... // Base is a base formatter.
type Basefmt struct { type Base struct {
suiteName string suiteName string
out io.Writer out io.Writer
indent int indent int
storage *storage.Storage Storage *storage.Storage
lock *sync.Mutex Lock *sync.Mutex
} }
// SetStorage ... // SetStorage assigns gherkin data storage.
func (f *Basefmt) SetStorage(st *storage.Storage) { func (f *Base) SetStorage(st *storage.Storage) {
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.storage = st f.Storage = st
} }
// TestRunStarted ... // TestRunStarted is triggered on test start.
func (f *Basefmt) TestRunStarted() {} func (f *Base) TestRunStarted() {}
// Feature ... // Feature receives gherkin document.
func (f *Basefmt) Feature(*messages.GherkinDocument, string, []byte) {} func (f *Base) Feature(*messages.GherkinDocument, string, []byte) {}
// Pickle ... // Pickle receives scenario.
func (f *Basefmt) Pickle(*messages.Pickle) {} func (f *Base) Pickle(*messages.Pickle) {}
// Defined ... // Defined receives step definition.
func (f *Basefmt) Defined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) { func (f *Base) Defined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
} }
// Passed ... // Passed captures passed step.
func (f *Basefmt) Passed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {} func (f *Base) Passed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {}
// Skipped ... // Skipped captures skipped step.
func (f *Basefmt) Skipped(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) { func (f *Base) Skipped(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
} }
// Undefined ... // Undefined captures undefined step.
func (f *Basefmt) Undefined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) { func (f *Base) Undefined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
} }
// Failed ... // Failed captures failed step.
func (f *Basefmt) Failed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition, error) { func (f *Base) Failed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition, error) {
} }
// Pending ... // Pending captures pending step.
func (f *Basefmt) Pending(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) { func (f *Base) Pending(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
} }
// Summary ... // Summary renders summary information.
func (f *Basefmt) Summary() { func (f *Base) Summary() {
var totalSc, passedSc, undefinedSc int var totalSc, passedSc, undefinedSc int
var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int
pickleResults := f.storage.MustGetPickleResults() pickleResults := f.Storage.MustGetPickleResults()
for _, pr := range pickleResults { for _, pr := range pickleResults {
var prStatus models.StepResultStatus var prStatus models.StepResultStatus
totalSc++ totalSc++
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pr.PickleID) pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pr.PickleID)
if len(pickleStepResults) == 0 { if len(pickleStepResults) == 0 {
prStatus = undefined prStatus = undefined
@ -156,7 +156,7 @@ func (f *Basefmt) Summary() {
} }
scenarios = append(scenarios, parts...) scenarios = append(scenarios, parts...)
testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt testRunStartedAt := f.Storage.MustGetTestRunStarted().StartedAt
elapsed := utils.TimeNowFunc().Sub(testRunStartedAt) elapsed := utils.TimeNowFunc().Sub(testRunStartedAt)
fmt.Fprintln(f.out, "") fmt.Fprintln(f.out, "")
@ -194,9 +194,9 @@ func (f *Basefmt) Summary() {
} }
} }
// Snippets ... // Snippets returns code suggestions for undefined steps.
func (f *Basefmt) Snippets() string { func (f *Base) Snippets() string {
undefinedStepResults := f.storage.MustGetPickleStepResultsByStatus(undefined) undefinedStepResults := f.Storage.MustGetPickleStepResultsByStatus(undefined)
if len(undefinedStepResults) == 0 { if len(undefinedStepResults) == 0 {
return "" return ""
} }
@ -205,7 +205,7 @@ func (f *Basefmt) Snippets() string {
var snips []undefinedSnippet var snips []undefinedSnippet
// build snippets // build snippets
for _, u := range undefinedStepResults { for _, u := range undefinedStepResults {
pickleStep := f.storage.MustGetPickleStep(u.PickleStepID) pickleStep := f.Storage.MustGetPickleStep(u.PickleStepID)
steps := []string{pickleStep.Text} steps := []string{pickleStep.Text}
arg := pickleStep.Argument arg := pickleStep.Argument

Просмотреть файл

@ -30,15 +30,17 @@ func init() {
// CucumberFormatterFunc implements the FormatterFunc for the cucumber formatter // CucumberFormatterFunc implements the FormatterFunc for the cucumber formatter
func CucumberFormatterFunc(suite string, out io.Writer) formatters.Formatter { func CucumberFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &cukefmt{Basefmt: NewBaseFmt(suite, out)} return &Cuke{Base: NewBase(suite, out)}
} }
type cukefmt struct { // Cuke ...
*Basefmt type Cuke struct {
*Base
} }
func (f *cukefmt) Summary() { // Summary renders test result as Cucumber JSON.
features := f.storage.MustGetFeatures() func (f *Cuke) Summary() {
features := f.Storage.MustGetFeatures()
res := f.buildCukeFeatures(features) res := f.buildCukeFeatures(features)
@ -50,7 +52,7 @@ func (f *cukefmt) Summary() {
fmt.Fprintf(f.out, "%s\n", string(dat)) fmt.Fprintf(f.out, "%s\n", string(dat))
} }
func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatureJSON) { func (f *Cuke) buildCukeFeatures(features []*models.Feature) (res []CukeFeatureJSON) {
sort.Sort(sortFeaturesByName(features)) sort.Sort(sortFeaturesByName(features))
res = make([]CukeFeatureJSON, len(features)) res = make([]CukeFeatureJSON, len(features))
@ -58,7 +60,7 @@ func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatu
for idx, feat := range features { for idx, feat := range features {
cukeFeature := buildCukeFeature(feat) cukeFeature := buildCukeFeature(feat)
pickles := f.storage.MustGetPickles(feat.Uri) pickles := f.Storage.MustGetPickles(feat.Uri)
sort.Sort(sortPicklesByID(pickles)) sort.Sort(sortPicklesByID(pickles))
cukeFeature.Elements = f.buildCukeElements(pickles) cukeFeature.Elements = f.buildCukeElements(pickles)
@ -75,12 +77,12 @@ func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatu
return res return res
} }
func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeElement) { func (f *Cuke) buildCukeElements(pickles []*messages.Pickle) (res []cukeElement) {
res = make([]cukeElement, len(pickles)) res = make([]cukeElement, len(pickles))
for idx, pickle := range pickles { for idx, pickle := range pickles {
pickleResult := f.storage.MustGetPickleResult(pickle.Id) pickleResult := f.Storage.MustGetPickleResult(pickle.Id)
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id) pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
cukeElement := f.buildCukeElement(pickle) cukeElement := f.buildCukeElement(pickle)
@ -201,8 +203,8 @@ func buildCukeFeature(feat *models.Feature) CukeFeatureJSON {
return cukeFeature return cukeFeature
} }
func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeElement) { func (f *Cuke) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeElement) {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
scenario := feature.FindScenario(pickle.AstNodeIds[0]) scenario := feature.FindScenario(pickle.AstNodeIds[0])
cukeElement.Name = pickle.Name cukeElement.Name = pickle.Name
@ -245,9 +247,9 @@ func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeEle
return cukeElement return cukeElement
} }
func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult models.PickleStepResult) (cukeStep cukeStep) { func (f *Cuke) buildCukeStep(pickle *messages.Pickle, stepResult models.PickleStepResult) (cukeStep cukeStep) {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID) pickleStep := f.Storage.MustGetPickleStep(stepResult.PickleStepID)
step := feature.FindStep(pickleStep.AstNodeIds[0]) step := feature.FindStep(pickleStep.AstNodeIds[0])
line := step.Location.Line line := step.Location.Line

Просмотреть файл

@ -20,14 +20,15 @@ func init() {
// EventsFormatterFunc implements the FormatterFunc for the events formatter // EventsFormatterFunc implements the FormatterFunc for the events formatter
func EventsFormatterFunc(suite string, out io.Writer) formatters.Formatter { func EventsFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &eventsFormatter{Basefmt: NewBaseFmt(suite, out)} return &Events{Base: NewBase(suite, out)}
} }
type eventsFormatter struct { // Events - Events formatter
*Basefmt type Events struct {
*Base
} }
func (f *eventsFormatter) event(ev interface{}) { func (f *Events) event(ev interface{}) {
data, err := json.Marshal(ev) data, err := json.Marshal(ev)
if err != nil { if err != nil {
panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err)) panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err))
@ -35,11 +36,12 @@ func (f *eventsFormatter) event(ev interface{}) {
fmt.Fprintln(f.out, string(data)) fmt.Fprintln(f.out, string(data))
} }
func (f *eventsFormatter) Pickle(pickle *messages.Pickle) { // Pickle receives scenario.
f.Basefmt.Pickle(pickle) func (f *Events) Pickle(pickle *messages.Pickle) {
f.Base.Pickle(pickle)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.event(&struct { f.event(&struct {
Event string `json:"event"` Event string `json:"event"`
@ -68,11 +70,12 @@ func (f *eventsFormatter) Pickle(pickle *messages.Pickle) {
} }
} }
func (f *eventsFormatter) TestRunStarted() { // TestRunStarted is triggered on test start.
f.Basefmt.TestRunStarted() func (f *Events) TestRunStarted() {
f.Base.TestRunStarted()
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.event(&struct { f.event(&struct {
Event string `json:"event"` Event string `json:"event"`
@ -87,11 +90,12 @@ func (f *eventsFormatter) TestRunStarted() {
}) })
} }
func (f *eventsFormatter) Feature(ft *messages.GherkinDocument, p string, c []byte) { // Feature receives gherkin document.
f.Basefmt.Feature(ft, p, c) func (f *Events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
f.Base.Feature(ft, p, c)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.event(&struct { f.event(&struct {
Event string `json:"event"` Event string `json:"event"`
@ -104,16 +108,17 @@ func (f *eventsFormatter) Feature(ft *messages.GherkinDocument, p string, c []by
}) })
} }
func (f *eventsFormatter) Summary() { // Summary pushes summary information to JSON stream.
func (f *Events) Summary() {
// @TODO: determine status // @TODO: determine status
status := passed status := passed
f.storage.MustGetPickleStepResultsByStatus(failed) f.Storage.MustGetPickleStepResultsByStatus(failed)
if len(f.storage.MustGetPickleStepResultsByStatus(failed)) > 0 { if len(f.Storage.MustGetPickleStepResultsByStatus(failed)) > 0 {
status = failed status = failed
} else if len(f.storage.MustGetPickleStepResultsByStatus(passed)) == 0 { } else if len(f.Storage.MustGetPickleStepResultsByStatus(passed)) == 0 {
if len(f.storage.MustGetPickleStepResultsByStatus(undefined)) > len(f.storage.MustGetPickleStepResultsByStatus(pending)) { if len(f.Storage.MustGetPickleStepResultsByStatus(undefined)) > len(f.Storage.MustGetPickleStepResultsByStatus(pending)) {
status = undefined status = undefined
} else { } else {
status = pending status = pending
@ -140,9 +145,9 @@ func (f *eventsFormatter) Summary() {
}) })
} }
func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.PickleStep) { func (f *Events) step(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id) pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStep.Id)
step := feature.FindStep(pickleStep.AstNodeIds[0]) step := feature.FindStep(pickleStep.AstNodeIds[0])
var errMsg string var errMsg string
@ -166,7 +171,7 @@ func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.Pic
if isLastStep(pickle, pickleStep) { if isLastStep(pickle, pickleStep) {
var status string var status string
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id) pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults { for _, stepResult := range pickleStepResults {
switch stepResult.Status { switch stepResult.Status {
case passed, failed, undefined, pending: case passed, failed, undefined, pending:
@ -188,17 +193,18 @@ func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.Pic
} }
} }
func (f *eventsFormatter) Defined(pickle *messages.Pickle, pickleStep *messages.PickleStep, def *formatters.StepDefinition) { // Defined receives step definition.
f.Basefmt.Defined(pickle, pickleStep, def) func (f *Events) Defined(pickle *messages.Pickle, pickleStep *messages.PickleStep, def *formatters.StepDefinition) {
f.Base.Defined(pickle, pickleStep, def)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
step := feature.FindStep(pickleStep.AstNodeIds[0]) step := feature.FindStep(pickleStep.AstNodeIds[0])
if def != nil { if def != nil {
matchedDef := f.storage.MustGetStepDefintionMatch(pickleStep.AstNodeIds[0]) matchedDef := f.Storage.MustGetStepDefintionMatch(pickleStep.AstNodeIds[0])
m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:] m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:]
var args [][2]int var args [][2]int
@ -238,53 +244,58 @@ func (f *eventsFormatter) Defined(pickle *messages.Pickle, pickleStep *messages.
}) })
} }
func (f *eventsFormatter) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Passed captures passed step.
f.Basefmt.Passed(pickle, step, match) func (f *Events) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Passed(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(pickle, step) f.step(pickle, step)
} }
func (f *eventsFormatter) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Skipped captures skipped step.
f.Basefmt.Skipped(pickle, step, match) func (f *Events) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Skipped(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(pickle, step) f.step(pickle, step)
} }
func (f *eventsFormatter) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Undefined captures undefined step.
f.Basefmt.Undefined(pickle, step, match) func (f *Events) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Undefined(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(pickle, step) f.step(pickle, step)
} }
func (f *eventsFormatter) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) { // Failed captures failed step.
f.Basefmt.Failed(pickle, step, match, err) func (f *Events) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Base.Failed(pickle, step, match, err)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(pickle, step) f.step(pickle, step)
} }
func (f *eventsFormatter) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Pending captures pending step.
f.Basefmt.Pending(pickle, step, match) func (f *Events) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Pending(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(pickle, step) f.step(pickle, step)
} }
func (f *eventsFormatter) scenarioLocation(pickle *messages.Pickle) string { func (f *Events) scenarioLocation(pickle *messages.Pickle) string {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
scenario := feature.FindScenario(pickle.AstNodeIds[0]) scenario := feature.FindScenario(pickle.AstNodeIds[0])
line := scenario.Location.Line line := scenario.Location.Line

Просмотреть файл

@ -19,14 +19,16 @@ func init() {
// JUnitFormatterFunc implements the FormatterFunc for the junit formatter // JUnitFormatterFunc implements the FormatterFunc for the junit formatter
func JUnitFormatterFunc(suite string, out io.Writer) formatters.Formatter { func JUnitFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &junitFormatter{Basefmt: NewBaseFmt(suite, out)} return &JUnit{Base: NewBase(suite, out)}
} }
type junitFormatter struct { // JUnit renders test results in JUnit format.
*Basefmt type JUnit struct {
*Base
} }
func (f *junitFormatter) Summary() { // Summary renders summary information.
func (f *JUnit) Summary() {
suite := f.buildJUNITPackageSuite() suite := f.buildJUNITPackageSuite()
_, err := io.WriteString(f.out, xml.Header) _, err := io.WriteString(f.out, xml.Header)
@ -45,11 +47,11 @@ func junitTimeDuration(from, to time.Time) string {
return strconv.FormatFloat(to.Sub(from).Seconds(), 'f', -1, 64) return strconv.FormatFloat(to.Sub(from).Seconds(), 'f', -1, 64)
} }
func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite { func (f *JUnit) buildJUNITPackageSuite() JunitPackageSuite {
features := f.storage.MustGetFeatures() features := f.Storage.MustGetFeatures()
sort.Sort(sortFeaturesByName(features)) sort.Sort(sortFeaturesByName(features))
testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt testRunStartedAt := f.Storage.MustGetTestRunStarted().StartedAt
suite := JunitPackageSuite{ suite := JunitPackageSuite{
Name: f.suiteName, Name: f.suiteName,
@ -58,7 +60,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
} }
for idx, feature := range features { for idx, feature := range features {
pickles := f.storage.MustGetPickles(feature.Uri) pickles := f.Storage.MustGetPickles(feature.Uri)
sort.Sort(sortPicklesByID(pickles)) sort.Sort(sortPicklesByID(pickles))
ts := junitTestSuite{ ts := junitTestSuite{
@ -78,7 +80,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
for idx, pickle := range pickles { for idx, pickle := range pickles {
tc := junitTestCase{} tc := junitTestCase{}
pickleResult := f.storage.MustGetPickleResult(pickle.Id) pickleResult := f.Storage.MustGetPickleResult(pickle.Id)
if idx == 0 { if idx == 0 {
firstPickleStartedAt = pickleResult.StartedAt firstPickleStartedAt = pickleResult.StartedAt
@ -88,7 +90,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
if len(pickle.Steps) > 0 { if len(pickle.Steps) > 0 {
lastStep := pickle.Steps[len(pickle.Steps)-1] lastStep := pickle.Steps[len(pickle.Steps)-1]
lastPickleStepResult := f.storage.MustGetPickleStepResult(lastStep.Id) lastPickleStepResult := f.Storage.MustGetPickleStepResult(lastStep.Id)
lastPickleFinishedAt = lastPickleStepResult.FinishedAt lastPickleFinishedAt = lastPickleStepResult.FinishedAt
} }
@ -103,9 +105,9 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
ts.Tests++ ts.Tests++
suite.Tests++ suite.Tests++
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id) pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults { for _, stepResult := range pickleStepResults {
pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID) pickleStep := f.Storage.MustGetPickleStep(stepResult.PickleStepID)
switch stepResult.Status { switch stepResult.Status {
case passed: case passed:

Просмотреть файл

@ -20,50 +20,52 @@ func init() {
// PrettyFormatterFunc implements the FormatterFunc for the pretty formatter // PrettyFormatterFunc implements the FormatterFunc for the pretty formatter
func PrettyFormatterFunc(suite string, out io.Writer) formatters.Formatter { func PrettyFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &pretty{Basefmt: NewBaseFmt(suite, out)} return &Pretty{Base: NewBase(suite, out)}
} }
var outlinePlaceholderRegexp = regexp.MustCompile("<[^>]+>") var outlinePlaceholderRegexp = regexp.MustCompile("<[^>]+>")
// a built in default pretty formatter // Pretty is a formatter for readable output.
type pretty struct { type Pretty struct {
*Basefmt *Base
firstFeature *bool firstFeature *bool
} }
func (f *pretty) TestRunStarted() { // TestRunStarted is triggered on test start.
f.Basefmt.TestRunStarted() func (f *Pretty) TestRunStarted() {
f.Base.TestRunStarted()
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
firstFeature := true firstFeature := true
f.firstFeature = &firstFeature f.firstFeature = &firstFeature
} }
func (f *pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) { // Feature receives gherkin document.
f.lock.Lock() func (f *Pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) {
f.Lock.Lock()
if !*f.firstFeature { if !*f.firstFeature {
fmt.Fprintln(f.out, "") fmt.Fprintln(f.out, "")
} }
*f.firstFeature = false *f.firstFeature = false
f.lock.Unlock() f.Lock.Unlock()
f.Basefmt.Feature(gd, p, c) f.Base.Feature(gd, p, c)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.printFeature(gd.Feature) f.printFeature(gd.Feature)
} }
// Pickle takes a gherkin node for formatting // Pickle takes a gherkin node for formatting.
func (f *pretty) Pickle(pickle *messages.Pickle) { func (f *Pretty) Pickle(pickle *messages.Pickle) {
f.Basefmt.Pickle(pickle) f.Base.Pickle(pickle)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
if len(pickle.Steps) == 0 { if len(pickle.Steps) == 0 {
f.printUndefinedPickle(pickle) f.printUndefinedPickle(pickle)
@ -71,52 +73,57 @@ func (f *pretty) Pickle(pickle *messages.Pickle) {
} }
} }
func (f *pretty) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Passed captures passed step.
f.Basefmt.Passed(pickle, step, match) func (f *Pretty) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Passed(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.printStep(pickle, step) f.printStep(pickle, step)
} }
func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Skipped captures skipped step.
f.Basefmt.Skipped(pickle, step, match) func (f *Pretty) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Skipped(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.printStep(pickle, step) f.printStep(pickle, step)
} }
func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Undefined captures undefined step.
f.Basefmt.Undefined(pickle, step, match) func (f *Pretty) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Undefined(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.printStep(pickle, step) f.printStep(pickle, step)
} }
func (f *pretty) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) { // Failed captures failed step.
f.Basefmt.Failed(pickle, step, match, err) func (f *Pretty) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Base.Failed(pickle, step, match, err)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.printStep(pickle, step) f.printStep(pickle, step)
} }
func (f *pretty) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Pending captures pending step.
f.Basefmt.Pending(pickle, step, match) func (f *Pretty) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Pending(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.printStep(pickle, step) f.printStep(pickle, step)
} }
func (f *pretty) printFeature(feature *messages.Feature) { func (f *Pretty) printFeature(feature *messages.Feature) {
fmt.Fprintln(f.out, keywordAndName(feature.Keyword, feature.Name)) fmt.Fprintln(f.out, keywordAndName(feature.Keyword, feature.Name))
if strings.TrimSpace(feature.Description) != "" { if strings.TrimSpace(feature.Description) != "" {
for _, line := range strings.Split(feature.Description, "\n") { for _, line := range strings.Split(feature.Description, "\n") {
@ -133,8 +140,8 @@ func keywordAndName(keyword, name string) string {
return title return title
} }
func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) { func (f *Pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0]) astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0]) astBackground := feature.FindBackground(pickle.AstNodeIds[0])
@ -148,15 +155,15 @@ func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength
return scenarioHeaderLength, maxLength return scenarioHeaderLength, maxLength
} }
func (f *pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.Scenario, spaceFilling int) { func (f *Pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.Scenario, spaceFilling int) {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name) text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name)
text += s(spaceFilling) + line(feature.Uri, astScenario.Location) text += s(spaceFilling) + line(feature.Uri, astScenario.Location)
fmt.Fprintln(f.out, "\n"+text) fmt.Fprintln(f.out, "\n"+text)
} }
func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) { func (f *Pretty) printUndefinedPickle(pickle *messages.Pickle) {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0]) astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0]) astBackground := feature.FindBackground(pickle.AstNodeIds[0])
@ -197,18 +204,18 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
} }
} }
// Summary sumarize the feature formatter output // Summary renders summary information.
func (f *pretty) Summary() { func (f *Pretty) Summary() {
failedStepResults := f.storage.MustGetPickleStepResultsByStatus(failed) failedStepResults := f.Storage.MustGetPickleStepResultsByStatus(failed)
if len(failedStepResults) > 0 { if len(failedStepResults) > 0 {
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n") fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
sort.Sort(sortPickleStepResultsByPickleStepID(failedStepResults)) sort.Sort(sortPickleStepResultsByPickleStepID(failedStepResults))
for _, fail := range failedStepResults { for _, fail := range failedStepResults {
pickle := f.storage.MustGetPickle(fail.PickleID) pickle := f.Storage.MustGetPickle(fail.PickleID)
pickleStep := f.storage.MustGetPickleStep(fail.PickleStepID) pickleStep := f.Storage.MustGetPickleStep(fail.PickleStepID)
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0]) astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, pickle.Name) scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, pickle.Name)
@ -222,14 +229,14 @@ func (f *pretty) Summary() {
} }
} }
f.Basefmt.Summary() f.Base.Summary()
} }
func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps int) { func (f *Pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps int) {
var errorMsg string var errorMsg string
var clr = green var clr = green
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0]) astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle) scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)
@ -237,7 +244,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id
firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id) pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1 firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1
if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep { if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep {
@ -270,7 +277,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
if firstExamplesTable && printExampleHeader { if firstExamplesTable && printExampleHeader {
// in first example, we need to print steps // in first example, we need to print steps
pickleStep := f.storage.MustGetPickleStep(result.PickleStepID) pickleStep := f.Storage.MustGetPickleStep(result.PickleStepID)
astStep := feature.FindStep(pickleStep.AstNodeIds[0]) astStep := feature.FindStep(pickleStep.AstNodeIds[0])
var text = "" var text = ""
@ -327,7 +334,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
} }
} }
func (f *pretty) printTableRow(row *messages.TableRow, max []int, clr colors.ColorFunc) { func (f *Pretty) printTableRow(row *messages.TableRow, max []int, clr colors.ColorFunc) {
cells := make([]string, len(row.Cells)) cells := make([]string, len(row.Cells))
for i, cell := range row.Cells { for i, cell := range row.Cells {
@ -339,12 +346,12 @@ func (f *pretty) printTableRow(row *messages.TableRow, max []int, clr colors.Col
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |") fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |")
} }
func (f *pretty) printTableHeader(row *messages.TableRow, max []int) { func (f *Pretty) printTableHeader(row *messages.TableRow, max []int) {
f.printTableRow(row, max, cyan) f.printTableRow(row, max, cyan)
} }
func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleStep) { func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
astBackground := feature.FindBackground(pickle.AstNodeIds[0]) astBackground := feature.FindBackground(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0]) astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astStep := feature.FindStep(pickleStep.AstNodeIds[0]) astStep := feature.FindStep(pickleStep.AstNodeIds[0])
@ -387,7 +394,7 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS
f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength) f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength)
} }
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id) pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStep.Id)
text := s(f.indent*2) + pickleStepResult.Status.Color()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.Color()(pickleStep.Text) text := s(f.indent*2) + pickleStepResult.Status.Color()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.Color()(pickleStep.Text)
if pickleStepResult.Def != nil { if pickleStepResult.Def != nil {
text += s(maxLength - stepLength + 1) text += s(maxLength - stepLength + 1)
@ -414,7 +421,7 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS
} }
} }
func (f *pretty) printDocString(docString *messages.DocString) { func (f *Pretty) printDocString(docString *messages.DocString) {
var ct string var ct string
if len(docString.MediaType) > 0 { if len(docString.MediaType) > 0 {
@ -432,7 +439,7 @@ func (f *pretty) printDocString(docString *messages.DocString) {
// print table with aligned table cells // print table with aligned table cells
// @TODO: need to make example header cells bold // @TODO: need to make example header cells bold
func (f *pretty) printTable(t *messages.PickleTable, c colors.ColorFunc) { func (f *Pretty) printTable(t *messages.PickleTable, c colors.ColorFunc) {
maxColLengths := maxColLengths(t, c) maxColLengths := maxColLengths(t, c)
var cols = make([]string, len(t.Rows[0].Cells)) var cols = make([]string, len(t.Rows[0].Cells))
@ -512,7 +519,7 @@ func longestExampleRow(t *messages.Examples, clrs ...colors.ColorFunc) []int {
return longest return longest
} }
func (f *pretty) longestStep(steps []*messages.Step, pickleLength int) int { func (f *Pretty) longestStep(steps []*messages.Step, pickleLength int) int {
max := pickleLength max := pickleLength
for _, step := range steps { for _, step := range steps {
@ -533,10 +540,10 @@ func line(path string, loc *messages.Location) string {
return " " + blackb(fmt.Sprintf("# %s:%d", path, loc.Line)) return " " + blackb(fmt.Sprintf("# %s:%d", path, loc.Line))
} }
func (f *pretty) lengthPickleStep(keyword, text string) int { func (f *Pretty) lengthPickleStep(keyword, text string) int {
return f.indent*2 + utf8.RuneCountInString(strings.TrimSpace(keyword)+" "+text) return f.indent*2 + utf8.RuneCountInString(strings.TrimSpace(keyword)+" "+text)
} }
func (f *pretty) lengthPickle(keyword, name string) int { func (f *Pretty) lengthPickle(keyword, name string) int {
return f.indent + utf8.RuneCountInString(strings.TrimSpace(keyword)+": "+name) return f.indent + utf8.RuneCountInString(strings.TrimSpace(keyword)+": "+name)
} }

Просмотреть файл

@ -16,42 +16,49 @@ func init() {
formatters.Format("progress", "Prints a character per step.", ProgressFormatterFunc) formatters.Format("progress", "Prints a character per step.", ProgressFormatterFunc)
} }
// ProgressFormatterFunc implements the FormatterFunc for the progress formatter // ProgressFormatterFunc implements the FormatterFunc for the progress formatter.
func ProgressFormatterFunc(suite string, out io.Writer) formatters.Formatter { func ProgressFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return NewProgress(suite, out)
}
// NewProgress creates a new progress formatter.
func NewProgress(suite string, out io.Writer) *Progress {
steps := 0 steps := 0
return &progress{ return &Progress{
Basefmt: NewBaseFmt(suite, out), Base: NewBase(suite, out),
stepsPerRow: 70, StepsPerRow: 70,
steps: &steps, Steps: &steps,
} }
} }
type progress struct { // Progress is a minimalistic formatter.
*Basefmt type Progress struct {
stepsPerRow int *Base
steps *int StepsPerRow int
Steps *int
} }
func (f *progress) Summary() { // Summary renders summary information.
left := math.Mod(float64(*f.steps), float64(f.stepsPerRow)) func (f *Progress) Summary() {
left := math.Mod(float64(*f.Steps), float64(f.StepsPerRow))
if left != 0 { if left != 0 {
if *f.steps > f.stepsPerRow { if *f.Steps > f.StepsPerRow {
fmt.Fprintf(f.out, s(f.stepsPerRow-int(left))+fmt.Sprintf(" %d\n", *f.steps)) fmt.Fprintf(f.out, s(f.StepsPerRow-int(left))+fmt.Sprintf(" %d\n", *f.Steps))
} else { } else {
fmt.Fprintf(f.out, " %d\n", *f.steps) fmt.Fprintf(f.out, " %d\n", *f.Steps)
} }
} }
var failedStepsOutput []string var failedStepsOutput []string
failedSteps := f.storage.MustGetPickleStepResultsByStatus(failed) failedSteps := f.Storage.MustGetPickleStepResultsByStatus(failed)
sort.Sort(sortPickleStepResultsByPickleStepID(failedSteps)) sort.Sort(sortPickleStepResultsByPickleStepID(failedSteps))
for _, sr := range failedSteps { for _, sr := range failedSteps {
if sr.Status == failed { if sr.Status == failed {
pickle := f.storage.MustGetPickle(sr.PickleID) pickle := f.Storage.MustGetPickle(sr.PickleID)
pickleStep := f.storage.MustGetPickleStep(sr.PickleStepID) pickleStep := f.Storage.MustGetPickleStep(sr.PickleStepID)
feature := f.storage.MustGetFeature(pickle.Uri) feature := f.Storage.MustGetFeature(pickle.Uri)
sc := feature.FindScenario(pickle.AstNodeIds[0]) sc := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name) scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name)
@ -77,11 +84,11 @@ func (f *progress) Summary() {
} }
fmt.Fprintln(f.out, "") fmt.Fprintln(f.out, "")
f.Basefmt.Summary() f.Base.Summary()
} }
func (f *progress) step(pickleStepID string) { func (f *Progress) step(pickleStepID string) {
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStepID) pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStepID)
switch pickleStepResult.Status { switch pickleStepResult.Status {
case passed: case passed:
@ -96,54 +103,59 @@ func (f *progress) step(pickleStepID string) {
fmt.Fprint(f.out, yellow("P")) fmt.Fprint(f.out, yellow("P"))
} }
*f.steps++ *f.Steps++
if math.Mod(float64(*f.steps), float64(f.stepsPerRow)) == 0 { if math.Mod(float64(*f.Steps), float64(f.StepsPerRow)) == 0 {
fmt.Fprintf(f.out, " %d\n", *f.steps) fmt.Fprintf(f.out, " %d\n", *f.Steps)
} }
} }
func (f *progress) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Passed captures passed step.
f.Basefmt.Passed(pickle, step, match) func (f *Progress) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Passed(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(step.Id) f.step(step.Id)
} }
func (f *progress) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Skipped captures skipped step.
f.Basefmt.Skipped(pickle, step, match) func (f *Progress) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Skipped(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(step.Id) f.step(step.Id)
} }
func (f *progress) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Undefined captures undefined step.
f.Basefmt.Undefined(pickle, step, match) func (f *Progress) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Undefined(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(step.Id) f.step(step.Id)
} }
func (f *progress) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) { // Failed captures failed step.
f.Basefmt.Failed(pickle, step, match, err) func (f *Progress) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Base.Failed(pickle, step, match, err)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(step.Id) f.step(step.Id)
} }
func (f *progress) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) { // Pending captures pending step.
f.Basefmt.Pending(pickle, step, match) func (f *Progress) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Pending(pickle, step, match)
f.lock.Lock() f.Lock.Lock()
defer f.lock.Unlock() defer f.Lock.Unlock()
f.step(step.Id) f.step(step.Id)
} }

Просмотреть файл

@ -14,6 +14,11 @@ Now `godog` is able to use multiple formatters simultaneously with comma-separat
`--format pretty,junit:report.xml,cucumber:report.json` will write `pretty` format to stdout, `junit` to report.xml `--format pretty,junit:report.xml,cucumber:report.json` will write `pretty` format to stdout, `junit` to report.xml
and `cucumber` to report.json. and `cucumber` to report.json.
### Extensible formatters
Standard formatters are now exported with type aliases so that a custom formatter can be built on top of it.
Please check [an example](../_examples/custom-formatter).
### Contextualized hooks ### Contextualized hooks
Scenario and Step hooks are now passing context to allow custom state communication. Returned context should generally Scenario and Step hooks are now passing context to allow custom state communication. Returned context should generally

Просмотреть файл

@ -324,9 +324,9 @@ func (tc *godogFeaturesScenario) cleanupSnippet(snip string) string {
} }
func (tc *godogFeaturesScenario) theUndefinedStepSnippetsShouldBe(body *DocString) error { func (tc *godogFeaturesScenario) theUndefinedStepSnippetsShouldBe(body *DocString) error {
f, ok := tc.testedSuite.fmt.(*formatters.Basefmt) f, ok := tc.testedSuite.fmt.(*formatters.Base)
if !ok { if !ok {
return fmt.Errorf("this step requires *formatters.Basefmt, but there is: %T", tc.testedSuite.fmt) return fmt.Errorf("this step requires *formatters.Base, but there is: %T", tc.testedSuite.fmt)
} }
actual := tc.cleanupSnippet(f.Snippets()) actual := tc.cleanupSnippet(f.Snippets())