vendor: bump ginkgo, gover

Signed-off-by: Casey Callendrello <cdc@redhat.com>
This commit is contained in:
Casey Callendrello
2020-12-08 14:51:48 +01:00
parent 509d645ee9
commit b47d178ae0
408 changed files with 194680 additions and 1961 deletions

View File

@ -1,4 +1,7 @@
.DS_Store
TODO
tmp/**/*
*.coverprofile
*.coverprofile
.vscode
.idea/
*.log

View File

@ -1,15 +1,25 @@
language: go
go:
- 1.3
- 1.4
- 1.5
- 1.13.x
- 1.14.x
- tip
cache:
directories:
- $GOPATH/pkg/mod
# allow internal package imports, necessary for forked repositories
go_import_path: github.com/onsi/ginkgo
install:
- go get -v -t ./...
- go get golang.org/x/tools/cmd/cover
- go get github.com/onsi/gomega
- go install github.com/onsi/ginkgo/ginkgo
- GO111MODULE="off" go get -v -t ./...
- GO111MODULE="off" go get golang.org/x/tools/cmd/cover
- GO111MODULE="off" go get github.com/onsi/gomega
- GO111MODULE="off" go install github.com/onsi/ginkgo/ginkgo
- export PATH=$PATH:$HOME/gopath/bin
script: $HOME/gopath/bin/ginkgo -r --randomizeAllSpecs --randomizeSuites --race --trace
script:
- GO111MODULE="on" go mod tidy
- diff -u <(echo -n) <(git diff go.mod)
- diff -u <(echo -n) <(git diff go.sum)
- $HOME/gopath/bin/ginkgo -r --randomizeAllSpecs --randomizeSuites --race --trace && go vet

View File

@ -1,9 +1,163 @@
## HEAD
## 1.12.1
### Fixes
- Make unfocus ("blur") much faster (#674) [8b18061]
- Fix typo (#673) [7fdcbe8]
- Test against 1.14 and remove 1.12 [d5c2ad6]
- Test if a coverprofile content is empty before checking its latest character (#670) [14d9fa2]
- replace tail package with maintained one. this fixes go get errors (#667) [4ba33d4]
- improve ginkgo performance - makes progress on #644 [a14f98e]
- fix convert integration tests [1f8ba69]
- fix typo succesful -> successful (#663) [1ea49cf]
- Fix invalid link (#658) [b886136]
- convert utility : Include comments from source (#657) [1077c6d]
- Explain what BDD means [d79e7fb]
- skip race detector test on unsupported platform (#642) [f8ab89d]
- Use Dup2 from golang.org/x/sys/unix instead of syscallDup (#638) [5d53c55]
- Fix missing newline in combined coverage file (#641) [6a07ea2]
- check if a spec is run before returning SpecSummary (#645) [8850000]
## 1.12.0
### Features
- Add module definition (#630) [78916ab]
## 1.11.0
### Features
- Add syscall for riscv64 architecture [f66e896]
- teamcity reporter: output location of test failure as well as test definition (#626) [9869142]
- teamcity reporter: output newline after every service message (#625) [3cfa02d]
- Add support for go module when running `generate` command (#578) [9c89e3f]
## 1.10.3
### Fixes
- Set go_import_path in travis.yml to allow internal packages in forks (#607) [3b721db]
- Add integration test [d90e0dc]
- Fix coverage files combining [e5dde8c]
- A new CLI option: -ginkgo.reportFile <file path> (#601) [034fd25]
## 1.10.2
### Fixes
- speed up table entry generateIt() (#609) [5049dc5]
- Fix. Write errors to stderr instead of stdout (#610) [7bb3091]
## 1.10.1
### Fixes
- stack backtrace: fix skipping (#600) [2a4c0bd]
## 1.10.0
### Fixes
- stack backtrace: fix alignment and skipping [66915d6]
- fix typo in documentation [8f97b93]
## 1.9.0
### Features
- Option to print output into report, when tests have passed [0545415]
### Fixes
- Fixed typos in comments [0ecbc58]
- gofmt code [a7f8bfb]
- Simplify code [7454d00]
- Simplify concatenation, incrementation and function assignment [4825557]
- Avoid unnecessary conversions [9d9403c]
- JUnit: include more detailed information about panic [19cca4b]
- Print help to stdout when the user asks for help [4cb7441]
## 1.8.0
### New Features
- allow config of the vet flag for `go test` (#562) [3cd45fa]
- Support projects using go modules [d56ee76]
### Fixes and Minor Improvements
- chore(godoc): fixes typos in Measurement funcs [dbaca8e]
- Optimize focus to avoid allocations [f493786]
- Ensure generated test file names are underscored [505cc35]
## 1.7.0
### New Features
- Add JustAfterEach (#484) [0d4f080]
### Fixes
- Correctly round suite time in junit reporter [2445fc1]
- Avoid using -i argument to go test for Golang 1.10+ [46bbc26]
## 1.6.0
### New Features
- add --debug flag to emit node output to files (#499) [39febac]
### Fixes
- fix: for `go vet` to pass [69338ec]
- docs: fix for contributing instructions [7004cb1]
- consolidate and streamline contribution docs (#494) [d848015]
- Make generated Junit file compatable with "Maven Surefire" (#488) [e51bee6]
- all: gofmt [000d317]
- Increase eventually timeout to 30s [c73579c]
- Clarify asynchronous test behaviour [294d8f4]
- Travis badge should only show master [26d2143]
## 1.5.0 5/10/2018
### New Features
- Supports go v1.10 (#443, #446, #451) [e873237, 468e89e, e37dbfe, a37f4c0, c0b857d, bca5260, 4177ca8]
- Add a When() synonym for Context() (#386) [747514b, 7484dad, 7354a07, dd826c8]
- Re-add noisySkippings flag [652e15c]
- Allow coverage to be displayed for focused specs (#367) [11459a8]
- Handle -outputdir flag (#364) [228e3a8]
- Handle -coverprofile flag (#355) [43392d5]
### Fixes
- When using custom reporters register the custom reporters *before* the default reporter. This allows users to see the output of any print statements in their customer reporters. (#365) [8382b23]
- When running a test and calculating the coverage using the `-coverprofile` and `-outputdir` flags, Ginkgo fails with an error if the directory does not exist. This is due to an [issue in go 1.10](https://github.com/golang/go/issues/24588) (#446) [b36a6e0]
- `unfocus` command ignores vendor folder (#459) [e5e551c, c556e43, a3b6351, 9a820dd]
- Ignore packages whose tests are all ignored by go (#456) [7430ca7, 6d8be98]
- Increase the threshold when checking time measuments (#455) [2f714bf, 68f622c]
- Fix race condition in coverage tests (#423) [a5a8ff7, ab9c08b]
- Add an extra new line after reporting spec run completion for test2json [874520d]
- added name name field to junit reported testsuite [ae61c63]
- Do not set the run time of a spec when the dryRun flag is used (#438) [457e2d9, ba8e856]
- Process FWhen and FSpecify when unfocusing (#434) [9008c7b, ee65bd, df87dfe]
- Synchronise the access to the state of specs to avoid race conditions (#430) [7d481bc, ae6829d]
- Added Duration on GinkgoTestDescription (#383) [5f49dad, 528417e, 0747408, 329d7ed]
- Fix Ginkgo stack trace on failure for Specify (#415) [b977ede, 65ca40e, 6c46eb8]
- Update README with Go 1.6+, Golang -> Go (#409) [17f6b97, bc14b66, 20d1598]
- Use fmt.Errorf instead of errors.New(fmt.Sprintf (#401) [a299f56, 44e2eaa]
- Imports in generated code should follow conventions (#398) [0bec0b0, e8536d8]
- Prevent data race error when Recording a benchmark value from multiple go routines (#390) [c0c4881, 7a241e9]
- Replace GOPATH in Environment [4b883f0]
## 1.4.0 7/16/2017
- `ginkgo` now provides a hint if you accidentally forget to run `ginkgo bootstrap` to generate a `*_suite_test.go` file that actually invokes the Ginkgo test runner. [#345](https://github.com/onsi/ginkgo/pull/345)
- thanks to improvements in `go test -c` `ginkgo` no longer needs to fix Go's compilation output to ensure compilation errors are expressed relative to the CWD. [#357]
- `ginkgo watch -watchRegExp=...` allows you to specify a custom regular expression to watch. Only files matching the regular expression are watched for changes (the default is `\.go$`) [#356]
- `ginkgo` now always emits compilation output. Previously, only failed compilation output was printed out. [#277]
- `ginkgo -requireSuite` now fails the test run if there are `*_test.go` files but `go test` fails to detect any tests. Typically this means you forgot to run `ginkgo bootstrap` to generate a suite file. [#344]
- `ginkgo -timeout=DURATION` allows you to adjust the timeout for the entire test suite (default is 24 hours) [#248]
## 1.3.0 3/28/2017
Improvements:
- Significantly improved parallel test distribution. Now instead of pre-sharding test cases across workers (which can result in idle workers and poor test performance) Ginkgo uses a shared queue to keep all workers busy until all tests are complete. This improves test-time performance and consistency.
- `Skip(message)` can be used to skip the current test.
- Added `extensions/table` - a Ginkgo DSL for [Table Driven Tests](http://onsi.github.io/ginkgo/#table-driven-tests)
- Add `GinkgoRandomSeed()` - shorthand for `config.GinkgoConfig.RandomSeed`
- Support for retrying flaky tests with `--flakeAttempts`
- `ginkgo ./...` now recurses as you'd expect
- Added `Specify` a synonym for `It`
- Support colorise on Windows
- Broader support for various go compilation flags in the `ginkgo` CLI
Bug Fixes:
@ -68,12 +222,12 @@ No changes, just dropping the beta.
New Features:
- `ginkgo watch` now monitors packages *and their dependencies* for changes. The depth of the dependency tree can be modified with the `-depth` flag.
- Test suites with a programmatic focus (`FIt`, `FDescribe`, etc...) exit with non-zero status code, evne when they pass. This allows CI systems to detect accidental commits of focused test suites.
- Test suites with a programmatic focus (`FIt`, `FDescribe`, etc...) exit with non-zero status code, even when they pass. This allows CI systems to detect accidental commits of focused test suites.
- `ginkgo -p` runs the testsuite in parallel with an auto-detected number of nodes.
- `ginkgo -tags=TAG_LIST` passes a list of tags down to the `go build` command.
- `ginkgo --failFast` aborts the test suite after the first failure.
- `ginkgo generate file_1 file_2` can take multiple file arguments.
- Ginkgo now summarizes any spec failures that occured at the end of the test run.
- Ginkgo now summarizes any spec failures that occurred at the end of the test run.
- `ginkgo --randomizeSuites` will run tests *suites* in random order using the generated/passed-in seed.
Improvements:

33
vendor/github.com/onsi/ginkgo/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,33 @@
# Contributing to Ginkgo
Your contributions to Ginkgo are essential for its long-term maintenance and improvement.
- Please **open an issue first** - describe what problem you are trying to solve and give the community a forum for input and feedback ahead of investing time in writing code!
- Ensure adequate test coverage:
- When adding to the Ginkgo library, add unit and/or integration tests (under the `integration` folder).
- When adding to the Ginkgo CLI, note that there are very few unit tests. Please add an integration test.
- Update the documentation. Ginko uses `godoc` comments and documentation on the `gh-pages` branch.
If relevant, please submit a docs PR to that branch alongside your code PR.
Thanks for supporting Ginkgo!
## Setup
Fork the repo, then:
```
go get github.com/onsi/ginkgo
go get github.com/onsi/gomega/...
cd $GOPATH/src/github.com/onsi/ginkgo
git remote add fork git@github.com:<NAME>/ginkgo.git
ginkgo -r -p # ensure tests are green
go vet ./... # ensure linter is happy
```
## Making the PR
- go to a new branch `git checkout -b my-feature`
- make your changes
- run tests and linter again (see above)
- `git push fork`
- open PR 🎉

View File

@ -1,31 +1,31 @@
![Ginkgo: A Golang BDD Testing Framework](http://onsi.github.io/ginkgo/images/ginkgo.png)
![Ginkgo: A Go BDD Testing Framework](https://onsi.github.io/ginkgo/images/ginkgo.png)
[![Build Status](https://travis-ci.org/onsi/ginkgo.png)](https://travis-ci.org/onsi/ginkgo)
[![Build Status](https://travis-ci.org/onsi/ginkgo.svg?branch=master)](https://travis-ci.org/onsi/ginkgo)
Jump to the [docs](http://onsi.github.io/ginkgo/) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)!
Jump to the [docs](https://onsi.github.io/ginkgo/) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)!
To discuss Ginkgo and get updates, join the [google group](https://groups.google.com/d/forum/ginkgo-and-gomega).
If you have a question, comment, bug report, feature request, etc. please open a GitHub issue.
## Feature List
- Ginkgo uses Go's `testing` package and can live alongside your existing `testing` tests. It's easy to [bootstrap](http://onsi.github.io/ginkgo/#bootstrapping-a-suite) and start writing your [first tests](http://onsi.github.io/ginkgo/#adding-specs-to-a-suite)
- Ginkgo uses Go's `testing` package and can live alongside your existing `testing` tests. It's easy to [bootstrap](https://onsi.github.io/ginkgo/#bootstrapping-a-suite) and start writing your [first tests](https://onsi.github.io/ginkgo/#adding-specs-to-a-suite)
- Structure your BDD-style tests expressively:
- Nestable [`Describe` and `Context` container blocks](http://onsi.github.io/ginkgo/#organizing-specs-with-containers-describe-and-context)
- [`BeforeEach` and `AfterEach` blocks](http://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach) for setup and teardown
- [`It` blocks](http://onsi.github.io/ginkgo/#individual-specs-) that hold your assertions
- [`JustBeforeEach` blocks](http://onsi.github.io/ginkgo/#separating-creation-and-configuration-justbeforeeach) that separate creation from configuration (also known as the subject action pattern).
- [`BeforeSuite` and `AfterSuite` blocks](http://onsi.github.io/ginkgo/#global-setup-and-teardown-beforesuite-and-aftersuite) to prep for and cleanup after a suite.
- Ginkgo allows you to write tests in Go using expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style:
- Nestable [`Describe`, `Context` and `When` container blocks](https://onsi.github.io/ginkgo/#organizing-specs-with-containers-describe-and-context)
- [`BeforeEach` and `AfterEach` blocks](https://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach) for setup and teardown
- [`It` and `Specify` blocks](https://onsi.github.io/ginkgo/#individual-specs-it) that hold your assertions
- [`JustBeforeEach` blocks](https://onsi.github.io/ginkgo/#separating-creation-and-configuration-justbeforeeach) that separate creation from configuration (also known as the subject action pattern).
- [`BeforeSuite` and `AfterSuite` blocks](https://onsi.github.io/ginkgo/#global-setup-and-teardown-beforesuite-and-aftersuite) to prep for and cleanup after a suite.
- A comprehensive test runner that lets you:
- Mark specs as [pending](http://onsi.github.io/ginkgo/#pending-specs)
- [Focus](http://onsi.github.io/ginkgo/#focused-specs) individual specs, and groups of specs, either programmatically or on the command line
- Run your tests in [random order](http://onsi.github.io/ginkgo/#spec-permutation), and then reuse random seeds to replicate the same order.
- Break up your test suite into parallel processes for straightforward [test parallelization](http://onsi.github.io/ginkgo/#parallel-specs)
- Mark specs as [pending](https://onsi.github.io/ginkgo/#pending-specs)
- [Focus](https://onsi.github.io/ginkgo/#focused-specs) individual specs, and groups of specs, either programmatically or on the command line
- Run your tests in [random order](https://onsi.github.io/ginkgo/#spec-permutation), and then reuse random seeds to replicate the same order.
- Break up your test suite into parallel processes for straightforward [test parallelization](https://onsi.github.io/ginkgo/#parallel-specs)
- `ginkgo`: a command line interface with plenty of handy command line arguments for [running your tests](http://onsi.github.io/ginkgo/#running-tests) and [generating](http://onsi.github.io/ginkgo/#generators) test files. Here are a few choice examples:
- `ginkgo`: a command line interface with plenty of handy command line arguments for [running your tests](https://onsi.github.io/ginkgo/#running-tests) and [generating](https://onsi.github.io/ginkgo/#generators) test files. Here are a few choice examples:
- `ginkgo -nodes=N` runs your tests in `N` parallel processes and print out coherent output in realtime
- `ginkgo -cover` runs your tests using Golang's code coverage tool
- `ginkgo -cover` runs your tests using Go's code coverage tool
- `ginkgo convert` converts an XUnit-style `testing` package to a Ginkgo-style package
- `ginkgo -focus="REGEXP"` and `ginkgo -skip="REGEXP"` allow you to specify a subset of tests to run via regular expression
- `ginkgo -r` runs all tests suites under the current directory
@ -37,34 +37,36 @@ To discuss Ginkgo and get updates, join the [google group](https://groups.google
- `ginkgo watch` [watches](https://onsi.github.io/ginkgo/#watching-for-changes) packages *and their dependencies* for changes, then reruns tests. Run tests immediately as you develop!
- Built-in support for testing [asynchronicity](http://onsi.github.io/ginkgo/#asynchronous-tests)
- Built-in support for testing [asynchronicity](https://onsi.github.io/ginkgo/#asynchronous-tests)
- Built-in support for [benchmarking](http://onsi.github.io/ginkgo/#benchmark-tests) your code. Control the number of benchmark samples as you gather runtimes and other, arbitrary, bits of numerical information about your code.
- Built-in support for [benchmarking](https://onsi.github.io/ginkgo/#benchmark-tests) your code. Control the number of benchmark samples as you gather runtimes and other, arbitrary, bits of numerical information about your code.
- [Completions for Sublime Text](https://github.com/onsi/ginkgo-sublime-completions): just use [Package Control](https://sublime.wbond.net/) to install `Ginkgo Completions`.
- Straightforward support for third-party testing libraries such as [Gomock](https://code.google.com/p/gomock/) and [Testify](https://github.com/stretchr/testify). Check out the [docs](http://onsi.github.io/ginkgo/#third-party-integrations) for details.
- [Completions for VSCode](https://github.com/onsi/vscode-ginkgo): just use VSCode's extension installer to install `vscode-ginkgo`.
- Straightforward support for third-party testing libraries such as [Gomock](https://code.google.com/p/gomock/) and [Testify](https://github.com/stretchr/testify). Check out the [docs](https://onsi.github.io/ginkgo/#third-party-integrations) for details.
- A modular architecture that lets you easily:
- Write [custom reporters](http://onsi.github.io/ginkgo/#writing-custom-reporters) (for example, Ginkgo comes with a [JUnit XML reporter](http://onsi.github.io/ginkgo/#generating-junit-xml-output) and a TeamCity reporter).
- [Adapt an existing matcher library (or write your own!)](http://onsi.github.io/ginkgo/#using-other-matcher-libraries) to work with Ginkgo
- Write [custom reporters](https://onsi.github.io/ginkgo/#writing-custom-reporters) (for example, Ginkgo comes with a [JUnit XML reporter](https://onsi.github.io/ginkgo/#generating-junit-xml-output) and a TeamCity reporter).
- [Adapt an existing matcher library (or write your own!)](https://onsi.github.io/ginkgo/#using-other-matcher-libraries) to work with Ginkgo
## [Gomega](http://github.com/onsi/gomega): Ginkgo's Preferred Matcher Library
## [Gomega](https://github.com/onsi/gomega): Ginkgo's Preferred Matcher Library
Ginkgo is best paired with Gomega. Learn more about Gomega [here](http://onsi.github.io/gomega/)
Ginkgo is best paired with Gomega. Learn more about Gomega [here](https://onsi.github.io/gomega/)
## [Agouti](http://github.com/sclevine/agouti): A Golang Acceptance Testing Framework
## [Agouti](https://github.com/sclevine/agouti): A Go Acceptance Testing Framework
Agouti allows you run WebDriver integration tests. Learn more about Agouti [here](http://agouti.org)
Agouti allows you run WebDriver integration tests. Learn more about Agouti [here](https://agouti.org)
## Set Me Up!
You'll need Golang v1.3+ (Ubuntu users: you probably have Golang v1.0 -- you'll need to upgrade!)
You'll need the Go command-line tools. Ginkgo is tested with Go 1.6+, but preferably you should get the latest. Follow the [installation instructions](https://golang.org/doc/install) if you don't have it installed.
```bash
go get github.com/onsi/ginkgo/ginkgo # installs the ginkgo CLI
go get github.com/onsi/gomega # fetches the matcher library
go get -u github.com/onsi/ginkgo/ginkgo # installs the ginkgo CLI
go get -u github.com/onsi/gomega/... # fetches the matcher library
cd path/to/package/you/want/to/test
@ -83,22 +85,22 @@ Of course, I heartily recommend [Ginkgo](https://github.com/onsi/ginkgo) and [Go
With that said, it's great to know what your options are :)
### What Golang gives you out of the box
### What Go gives you out of the box
Testing is a first class citizen in Golang, however Go's built-in testing primitives are somewhat limited: The [testing](http://golang.org/pkg/testing) package provides basic XUnit style tests and no assertion library.
Testing is a first class citizen in Go, however Go's built-in testing primitives are somewhat limited: The [testing](https://golang.org/pkg/testing) package provides basic XUnit style tests and no assertion library.
### Matcher libraries for Golang's XUnit style tests
### Matcher libraries for Go's XUnit style tests
A number of matcher libraries have been written to augment Go's built-in XUnit style tests. Here are two that have gained traction:
- [testify](https://github.com/stretchr/testify)
- [gocheck](http://labix.org/gocheck)
- [gocheck](https://labix.org/gocheck)
You can also use Ginkgo's matcher library [Gomega](https://github.com/onsi/gomega) in [XUnit style tests](http://onsi.github.io/gomega/#using-gomega-with-golangs-xunitstyle-tests)
You can also use Ginkgo's matcher library [Gomega](https://github.com/onsi/gomega) in [XUnit style tests](https://onsi.github.io/gomega/#using-gomega-with-golangs-xunitstyle-tests)
### BDD style testing frameworks
There are a handful of BDD-style testing frameworks written for Golang. Here are a few:
There are a handful of BDD-style testing frameworks written for Go. Here are a few:
- [Ginkgo](https://github.com/onsi/ginkgo) ;)
- [GoConvey](https://github.com/smartystreets/goconvey)
@ -106,10 +108,14 @@ There are a handful of BDD-style testing frameworks written for Golang. Here ar
- [Mao](https://github.com/azer/mao)
- [Zen](https://github.com/pranavraja/zen)
Finally, @shageman has [put together](https://github.com/shageman/gotestit) a comprehensive comparison of golang testing libraries.
Finally, @shageman has [put together](https://github.com/shageman/gotestit) a comprehensive comparison of Go testing libraries.
Go explore!
## License
Ginkgo is MIT-Licensed
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)

14
vendor/github.com/onsi/ginkgo/RELEASING.md generated vendored Normal file
View File

@ -0,0 +1,14 @@
A Ginkgo release is a tagged git sha and a GitHub release. To cut a release:
1. Ensure CHANGELOG.md is up to date.
- Use `git log --pretty=format:'- %s [%h]' HEAD...vX.X.X` to list all the commits since the last release
- Categorize the changes into
- Breaking Changes (requires a major version)
- New Features (minor version)
- Fixes (fix version)
- Maintenance (which in general should not be mentioned in `CHANGELOG.md` as they have no user impact)
1. Update `VERSION` in `config/config.go`
1. Create a commit with the version number as the commit message (e.g. `v1.3.0`)
1. Tag the commit with the version number as the tag name (e.g. `v1.3.0`)
1. Push the commit and tag to GitHub
1. Create a new [GitHub release](https://help.github.com/articles/creating-releases/) with the version number as the tag (e.g. `v1.3.0`). List the key changes in the release notes.

View File

@ -20,18 +20,21 @@ import (
"fmt"
)
const VERSION = "1.2.0"
const VERSION = "1.12.1"
type GinkgoConfigType struct {
RandomSeed int64
RandomizeAllSpecs bool
FocusString string
SkipString string
SkipMeasurements bool
FailOnPending bool
FailFast bool
EmitSpecProgress bool
DryRun bool
RandomSeed int64
RandomizeAllSpecs bool
RegexScansFilePath bool
FocusString string
SkipString string
SkipMeasurements bool
FailOnPending bool
FailFast bool
FlakeAttempts int
EmitSpecProgress bool
DryRun bool
DebugParallel bool
ParallelNode int
ParallelTotal int
@ -45,16 +48,19 @@ type DefaultReporterConfigType struct {
NoColor bool
SlowSpecThreshold float64
NoisyPendings bool
NoisySkippings bool
Succinct bool
Verbose bool
FullTrace bool
ReportPassed bool
ReportFile string
}
var DefaultReporterConfig = DefaultReporterConfigType{}
func processPrefix(prefix string) string {
if prefix != "" {
prefix = prefix + "."
prefix += "."
}
return prefix
}
@ -62,15 +68,24 @@ func processPrefix(prefix string) string {
func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) {
prefix = processPrefix(prefix)
flagSet.Int64Var(&(GinkgoConfig.RandomSeed), prefix+"seed", time.Now().Unix(), "The seed used to randomize the spec suite.")
flagSet.BoolVar(&(GinkgoConfig.RandomizeAllSpecs), prefix+"randomizeAllSpecs", false, "If set, ginkgo will randomize all specs together. By default, ginkgo only randomizes the top level Describe/Context groups.")
flagSet.BoolVar(&(GinkgoConfig.RandomizeAllSpecs), prefix+"randomizeAllSpecs", false, "If set, ginkgo will randomize all specs together. By default, ginkgo only randomizes the top level Describe, Context and When groups.")
flagSet.BoolVar(&(GinkgoConfig.SkipMeasurements), prefix+"skipMeasurements", false, "If set, ginkgo will skip any measurement specs.")
flagSet.BoolVar(&(GinkgoConfig.FailOnPending), prefix+"failOnPending", false, "If set, ginkgo will mark the test suite as failed if any specs are pending.")
flagSet.BoolVar(&(GinkgoConfig.FailFast), prefix+"failFast", false, "If set, ginkgo will stop running a test suite after a failure occurs.")
flagSet.BoolVar(&(GinkgoConfig.DryRun), prefix+"dryRun", false, "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v.")
flagSet.StringVar(&(GinkgoConfig.FocusString), prefix+"focus", "", "If set, ginkgo will only run specs that match this regular expression.")
flagSet.StringVar(&(GinkgoConfig.SkipString), prefix+"skip", "", "If set, ginkgo will only run specs that do not match this regular expression.")
flagSet.BoolVar(&(GinkgoConfig.RegexScansFilePath), prefix+"regexScansFilePath", false, "If set, ginkgo regex matching also will look at the file path (code location).")
flagSet.IntVar(&(GinkgoConfig.FlakeAttempts), prefix+"flakeAttempts", 1, "Make up to this many attempts to run each spec. Please note that if any of the attempts succeed, the suite will not be failed. But any failures will still be recorded.")
flagSet.BoolVar(&(GinkgoConfig.EmitSpecProgress), prefix+"progress", false, "If set, ginkgo will emit progress information as each spec runs to the GinkgoWriter.")
flagSet.BoolVar(&(GinkgoConfig.DebugParallel), prefix+"debug", false, "If set, ginkgo will emit node output to files when running in parallel.")
if includeParallelFlags {
flagSet.IntVar(&(GinkgoConfig.ParallelNode), prefix+"parallel.node", 1, "This worker node's (one-indexed) node number. For running specs in parallel.")
flagSet.IntVar(&(GinkgoConfig.ParallelTotal), prefix+"parallel.total", 1, "The total number of worker nodes. For running specs in parallel.")
@ -79,11 +94,15 @@ func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) {
}
flagSet.BoolVar(&(DefaultReporterConfig.NoColor), prefix+"noColor", false, "If set, suppress color output in default reporter.")
flagSet.Float64Var(&(DefaultReporterConfig.SlowSpecThreshold), prefix+"slowSpecThreshold", 5.0, "(in seconds) Specs that take longer to run than this threshold are flagged as slow by the default reporter (default: 5 seconds).")
flagSet.Float64Var(&(DefaultReporterConfig.SlowSpecThreshold), prefix+"slowSpecThreshold", 5.0, "(in seconds) Specs that take longer to run than this threshold are flagged as slow by the default reporter.")
flagSet.BoolVar(&(DefaultReporterConfig.NoisyPendings), prefix+"noisyPendings", true, "If set, default reporter will shout about pending tests.")
flagSet.BoolVar(&(DefaultReporterConfig.NoisySkippings), prefix+"noisySkippings", true, "If set, default reporter will shout about skipping tests.")
flagSet.BoolVar(&(DefaultReporterConfig.Verbose), prefix+"v", false, "If set, default reporter print out all specs as they begin.")
flagSet.BoolVar(&(DefaultReporterConfig.Succinct), prefix+"succinct", false, "If set, default reporter prints out a very succinct report")
flagSet.BoolVar(&(DefaultReporterConfig.FullTrace), prefix+"trace", false, "If set, default reporter prints out the full stack trace when a failure occurs")
flagSet.BoolVar(&(DefaultReporterConfig.ReportPassed), prefix+"reportPassed", false, "If set, default reporter prints out captured output of passed tests.")
flagSet.StringVar(&(DefaultReporterConfig.ReportFile), prefix+"reportFile", "", "Override the default reporter output file path.")
}
func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultReporterConfigType) []string {
@ -122,10 +141,18 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor
result = append(result, fmt.Sprintf("--%sskip=%s", prefix, ginkgo.SkipString))
}
if ginkgo.FlakeAttempts > 1 {
result = append(result, fmt.Sprintf("--%sflakeAttempts=%d", prefix, ginkgo.FlakeAttempts))
}
if ginkgo.EmitSpecProgress {
result = append(result, fmt.Sprintf("--%sprogress", prefix))
}
if ginkgo.DebugParallel {
result = append(result, fmt.Sprintf("--%sdebug", prefix))
}
if ginkgo.ParallelNode != 0 {
result = append(result, fmt.Sprintf("--%sparallel.node=%d", prefix, ginkgo.ParallelNode))
}
@ -142,6 +169,10 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor
result = append(result, fmt.Sprintf("--%sparallel.synchost=%s", prefix, ginkgo.SyncHost))
}
if ginkgo.RegexScansFilePath {
result = append(result, fmt.Sprintf("--%sregexScansFilePath", prefix))
}
if reporter.NoColor {
result = append(result, fmt.Sprintf("--%snoColor", prefix))
}
@ -154,6 +185,10 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor
result = append(result, fmt.Sprintf("--%snoisyPendings=false", prefix))
}
if !reporter.NoisySkippings {
result = append(result, fmt.Sprintf("--%snoisySkippings=false", prefix))
}
if reporter.Verbose {
result = append(result, fmt.Sprintf("--%sv", prefix))
}
@ -166,5 +201,13 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor
result = append(result, fmt.Sprintf("--%strace", prefix))
}
if reporter.ReportPassed {
result = append(result, fmt.Sprintf("--%sreportPassed", prefix))
}
if reporter.ReportFile != "" {
result = append(result, fmt.Sprintf("--%sreportFile=%s", prefix, reporter.ReportFile))
}
return result
}

View File

@ -22,9 +22,15 @@ func (t TableEntry) generateIt(itBody reflect.Value) {
return
}
values := []reflect.Value{}
for _, param := range t.Parameters {
values = append(values, reflect.ValueOf(param))
values := make([]reflect.Value, len(t.Parameters))
iBodyType := itBody.Type()
for i, param := range t.Parameters {
if param == nil {
inType := iBodyType.In(i)
values[i] = reflect.Zero(inType)
} else {
values[i] = reflect.ValueOf(param)
}
}
body := func() {

View File

@ -29,6 +29,7 @@ import (
"github.com/onsi/ginkgo/internal/writer"
"github.com/onsi/ginkgo/reporters"
"github.com/onsi/ginkgo/reporters/stenographer"
colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
"github.com/onsi/ginkgo/types"
)
@ -68,6 +69,14 @@ type GinkgoTestingT interface {
Fail()
}
//GinkgoRandomSeed returns the seed used to randomize spec execution order. It is
//useful for seeding your own pseudorandom number generators (PRNGs) to ensure
//consistent executions from run to run, where your tests contain variability (for
//example, when selecting random test data).
func GinkgoRandomSeed() int64 {
return config.GinkgoConfig.RandomSeed
}
//GinkgoParallelNode returns the parallel node number for the current ginkgo process
//The node number is 1-indexed
func GinkgoParallelNode() int {
@ -141,7 +150,8 @@ type GinkgoTestDescription struct {
FileName string
LineNumber int
Failed bool
Failed bool
Duration time.Duration
}
//CurrentGinkgoTestDescripton returns information about the current running test.
@ -161,6 +171,7 @@ func CurrentGinkgoTestDescription() GinkgoTestDescription {
FileName: subjectCodeLocation.FileName,
LineNumber: subjectCodeLocation.LineNumber,
Failed: summary.HasFailureState(),
Duration: summary.RunTime,
}
}
@ -168,6 +179,8 @@ func CurrentGinkgoTestDescription() GinkgoTestDescription {
//
//You use the Time() function to time how long the passed in body function takes to run
//You use the RecordValue() function to track arbitrary numerical measurements.
//The RecordValueWithPrecision() function can be used alternatively to provide the unit
//and resolution of the numeric measurement.
//The optional info argument is passed to the test reporter and can be used to
// provide the measurement data to a custom reporter with context.
//
@ -175,6 +188,7 @@ func CurrentGinkgoTestDescription() GinkgoTestDescription {
type Benchmarker interface {
Time(name string, body func(), info ...interface{}) (elapsedTime time.Duration)
RecordValue(name string, value float64, info ...interface{})
RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{})
}
//RunSpecs is the entry point for the Ginkgo test runner.
@ -185,13 +199,18 @@ type Benchmarker interface {
// ginkgo bootstrap
func RunSpecs(t GinkgoTestingT, description string) bool {
specReporters := []Reporter{buildDefaultReporter()}
if config.DefaultReporterConfig.ReportFile != "" {
reportFile := config.DefaultReporterConfig.ReportFile
specReporters[0] = reporters.NewJUnitReporter(reportFile)
return RunSpecsWithDefaultAndCustomReporters(t, description, specReporters)
}
return RunSpecsWithCustomReporters(t, description, specReporters)
}
//To run your tests with Ginkgo's default reporter and your custom reporter(s), replace
//RunSpecs() with this method.
func RunSpecsWithDefaultAndCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
specReporters = append([]Reporter{buildDefaultReporter()}, specReporters...)
specReporters = append(specReporters, buildDefaultReporter())
return RunSpecsWithCustomReporters(t, description, specReporters)
}
@ -205,7 +224,7 @@ func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specRepor
reporters[i] = reporter
}
passed, hasFocusedTests := globalSuite.Run(t, description, reporters, writer, config.GinkgoConfig)
if passed && hasFocusedTests {
if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
fmt.Println("PASS | FOCUSED")
os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
}
@ -215,14 +234,18 @@ func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specRepor
func buildDefaultReporter() Reporter {
remoteReportingServer := config.GinkgoConfig.StreamHost
if remoteReportingServer == "" {
stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor)
stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor, config.GinkgoConfig.FlakeAttempts > 1, colorable.NewColorableStdout())
return reporters.NewDefaultReporter(config.DefaultReporterConfig, stenographer)
} else {
return remote.NewForwardingReporter(remoteReportingServer, &http.Client{}, remote.NewOutputInterceptor())
debugFile := ""
if config.GinkgoConfig.DebugParallel {
debugFile = fmt.Sprintf("ginkgo-node-%d.log", config.GinkgoConfig.ParallelNode)
}
return remote.NewForwardingReporter(config.DefaultReporterConfig, remoteReportingServer, &http.Client{}, remote.NewOutputInterceptor(), GinkgoWriter.(*writer.Writer), debugFile)
}
}
//Skip notifies Ginkgo that the current spec should be skipped.
//Skip notifies Ginkgo that the current spec was skipped.
func Skip(message string, callerSkip ...int) {
skip := 0
if len(callerSkip) > 0 {
@ -264,9 +287,9 @@ func GinkgoRecover() {
//Describe blocks allow you to organize your specs. A Describe block can contain any number of
//BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks.
//
//In addition you can nest Describe and Context blocks. Describe and Context blocks are functionally
//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
//or method and, within that Describe, outline a number of Contexts.
//In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally
//equivalent. The difference is purely semantic -- you typically Describe the behavior of an object
//or method and, within that Describe, outline a number of Contexts and Whens.
func Describe(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1))
return true
@ -293,9 +316,9 @@ func XDescribe(text string, body func()) bool {
//Context blocks allow you to organize your specs. A Context block can contain any number of
//BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks.
//
//In addition you can nest Describe and Context blocks. Describe and Context blocks are functionally
//In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally
//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
//or method and, within that Describe, outline a number of Contexts.
//or method and, within that Describe, outline a number of Contexts and Whens.
func Context(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1))
return true
@ -319,6 +342,35 @@ func XContext(text string, body func()) bool {
return true
}
//When blocks allow you to organize your specs. A When block can contain any number of
//BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks.
//
//In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally
//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
//or method and, within that Describe, outline a number of Contexts and Whens.
func When(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1))
return true
}
//You can focus the tests within a describe block using FWhen
func FWhen(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1))
return true
}
//You can mark the tests within a describe block as pending using PWhen
func PWhen(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1))
return true
}
//You can mark the tests within a describe block as pending using XWhen
func XWhen(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1))
return true
}
//It blocks contain your test code and assertions. You cannot nest any other Ginkgo blocks
//within an It block.
//
@ -347,6 +399,32 @@ func XIt(text string, _ ...interface{}) bool {
return true
}
//Specify blocks are aliases for It blocks and allow for more natural wording in situations
//which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks
//which apply to It blocks.
func Specify(text string, body interface{}, timeout ...float64) bool {
globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
return true
}
//You can focus individual Specifys using FSpecify
func FSpecify(text string, body interface{}, timeout ...float64) bool {
globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
return true
}
//You can mark Specifys as pending using PSpecify
func PSpecify(text string, is ...interface{}) bool {
globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
return true
}
//You can mark Specifys as pending using XSpecify
func XSpecify(text string, is ...interface{}) bool {
globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
return true
}
//By allows you to better document large Its.
//
//Generally you should try to keep your Its short and to the point. This is not always possible, however,
@ -384,13 +462,13 @@ func FMeasure(text string, body interface{}, samples int) bool {
return true
}
//You can mark Maeasurements as pending using PMeasure
//You can mark Measurements as pending using PMeasure
func PMeasure(text string, _ ...interface{}) bool {
globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
return true
}
//You can mark Maeasurements as pending using XMeasure
//You can mark Measurements as pending using XMeasure
func XMeasure(text string, _ ...interface{}) bool {
globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
return true
@ -426,7 +504,7 @@ func AfterSuite(body interface{}, timeout ...float64) bool {
//until that node is done before running.
//
//SynchronizedBeforeSuite accomplishes this by taking *two* function arguments. The first is only run on parallel node #1. The second is
//run on all nodes, but *only* after the first function completes succesfully. Ginkgo also makes it possible to send data from the first function (on Node 1)
//run on all nodes, but *only* after the first function completes successfully. Ginkgo also makes it possible to send data from the first function (on Node 1)
//to the second function (on all the other nodes).
//
//The functions have the following signatures. The first function (which only runs on node 1) has the signature:
@ -517,6 +595,16 @@ func JustBeforeEach(body interface{}, timeout ...float64) bool {
return true
}
//JustAfterEach blocks are run after It blocks but *before* all AfterEach blocks. For more details,
//read the [documentation](http://onsi.github.io/ginkgo/#separating_creation_and_configuration_)
//
//Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel
func JustAfterEach(body interface{}, timeout ...float64) bool {
globalSuite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true
}
//AfterEach blocks are run after It blocks. When multiple AfterEach blocks are defined in nested
//Describe and Context blocks the innermost AfterEach blocks are run first.
//

9
vendor/github.com/onsi/ginkgo/go.mod generated vendored Normal file
View File

@ -0,0 +1,9 @@
module github.com/onsi/ginkgo
require (
github.com/nxadm/tail v1.4.4
github.com/onsi/gomega v1.7.1
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e
)
go 1.12

29
vendor/github.com/onsi/ginkgo/go.sum generated vendored Normal file
View File

@ -0,0 +1,29 @@
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -11,19 +11,35 @@ import (
func New(skip int) types.CodeLocation {
_, file, line, _ := runtime.Caller(skip + 1)
stackTrace := PruneStack(string(debug.Stack()), skip)
stackTrace := PruneStack(string(debug.Stack()), skip+1)
return types.CodeLocation{FileName: file, LineNumber: line, FullStackTrace: stackTrace}
}
// PruneStack removes references to functions that are internal to Ginkgo
// and the Go runtime from a stack string and a certain number of stack entries
// at the beginning of the stack. The stack string has the format
// as returned by runtime/debug.Stack. The leading goroutine information is
// optional and always removed if present. Beware that runtime/debug.Stack
// adds itself as first entry, so typically skip must be >= 1 to remove that
// entry.
func PruneStack(fullStackTrace string, skip int) string {
stack := strings.Split(fullStackTrace, "\n")
// Ensure that the even entries are the method names and the
// the odd entries the source code information.
if len(stack) > 0 && strings.HasPrefix(stack[0], "goroutine ") {
// Ignore "goroutine 29 [running]:" line.
stack = stack[1:]
}
// The "+1" is for skipping over the initial entry, which is
// runtime/debug.Stack() itself.
if len(stack) > 2*(skip+1) {
stack = stack[2*(skip+1):]
}
prunedStack := []string{}
re := regexp.MustCompile(`\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`)
for i := 0; i < len(stack)/2; i++ {
if !re.Match([]byte(stack[i*2])) {
// We filter out based on the source code file name.
if !re.Match([]byte(stack[i*2+1])) {
prunedStack = append(prunedStack, stack[i*2])
prunedStack = append(prunedStack, stack[i*2+1])
}

View File

@ -17,7 +17,7 @@ type benchmarker struct {
func newBenchmarker() *benchmarker {
return &benchmarker{
measurements: make(map[string]*types.SpecMeasurement, 0),
measurements: make(map[string]*types.SpecMeasurement),
}
}
@ -28,20 +28,27 @@ func (b *benchmarker) Time(name string, body func(), info ...interface{}) (elaps
b.mu.Lock()
defer b.mu.Unlock()
measurement := b.getMeasurement(name, "Fastest Time", "Slowest Time", "Average Time", "s", info...)
measurement := b.getMeasurement(name, "Fastest Time", "Slowest Time", "Average Time", "s", 3, info...)
measurement.Results = append(measurement.Results, elapsedTime.Seconds())
return
}
func (b *benchmarker) RecordValue(name string, value float64, info ...interface{}) {
measurement := b.getMeasurement(name, "Smallest", " Largest", " Average", "", info...)
b.mu.Lock()
measurement := b.getMeasurement(name, "Smallest", " Largest", " Average", "", 3, info...)
defer b.mu.Unlock()
measurement.Results = append(measurement.Results, value)
}
func (b *benchmarker) getMeasurement(name string, smallestLabel string, largestLabel string, averageLabel string, units string, info ...interface{}) *types.SpecMeasurement {
func (b *benchmarker) RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{}) {
b.mu.Lock()
measurement := b.getMeasurement(name, "Smallest", " Largest", " Average", units, precision, info...)
defer b.mu.Unlock()
measurement.Results = append(measurement.Results, value)
}
func (b *benchmarker) getMeasurement(name string, smallestLabel string, largestLabel string, averageLabel string, units string, precision int, info ...interface{}) *types.SpecMeasurement {
measurement, ok := b.measurements[name]
if !ok {
var computedInfo interface{}
@ -57,6 +64,7 @@ func (b *benchmarker) getMeasurement(name string, smallestLabel string, largestL
LargestLabel: largestLabel,
AverageLabel: averageLabel,
Units: units,
Precision: precision,
Results: make([]float64, 0),
}
b.measurements[name] = measurement

View File

@ -1,9 +1,10 @@
package leafnodes
import (
"time"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
"time"
)
type ItNode struct {

View File

@ -1,9 +1,10 @@
package leafnodes
import (
"reflect"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
"reflect"
)
type MeasureNode struct {

View File

@ -2,11 +2,12 @@ package leafnodes
import (
"fmt"
"reflect"
"time"
"github.com/onsi/ginkgo/internal/codelocation"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
"reflect"
"time"
)
type runner struct {
@ -68,7 +69,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
done := make(chan interface{}, 1)
go func() {
finished := false
finished := false
defer func() {
if e := recover(); e != nil || !finished {
@ -83,9 +84,12 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
}()
r.asyncFunc(done)
finished = true
finished = true
}()
// If this goroutine gets no CPU time before the select block,
// the <-done case may complete even if the test took longer than the timeoutThreshold.
// This can cause flaky behaviour, but we haven't seen it in the wild.
select {
case <-done:
case <-time.After(r.timeoutThreshold):
@ -96,7 +100,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
return
}
func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) {
finished := false
finished := false
defer func() {
if e := recover(); e != nil || !finished {
@ -107,7 +111,7 @@ func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure)
}()
r.syncFunc()
finished = true
finished = true
return
}

View File

@ -1,9 +1,10 @@
package leafnodes
import (
"time"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
"time"
)
type SetupNode struct {
@ -39,3 +40,9 @@ func NewJustBeforeEachNode(body interface{}, codeLocation types.CodeLocation, ti
runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeJustBeforeEach, componentIndex),
}
}
func NewJustAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, componentIndex int) *SetupNode {
return &SetupNode{
runner: newRunner(body, codeLocation, timeout, failer, types.SpecComponentTypeJustAfterEach, componentIndex),
}
}

View File

@ -1,9 +1,10 @@
package leafnodes
import (
"time"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
"time"
)
type SuiteNode interface {

View File

@ -2,11 +2,12 @@ package leafnodes
import (
"encoding/json"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
"io/ioutil"
"net/http"
"time"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
)
type synchronizedAfterSuiteNode struct {

View File

@ -3,12 +3,13 @@ package leafnodes
import (
"bytes"
"encoding/json"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
"io/ioutil"
"net/http"
"reflect"
"time"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/types"
)
type synchronizedBeforeSuiteNode struct {
@ -109,8 +110,6 @@ func (node *synchronizedBeforeSuiteNode) waitForA(syncHost string) (types.SpecSt
time.Sleep(50 * time.Millisecond)
}
return types.SpecStateFailed, failure("Shouldn't get here!")
}
func (node *synchronizedBeforeSuiteNode) Passed() bool {

View File

@ -54,11 +54,11 @@ func NewAggregator(nodeCount int, result chan bool, config config.DefaultReporte
config: config,
stenographer: stenographer,
suiteBeginnings: make(chan configAndSuite, 0),
beforeSuites: make(chan *types.SetupSummary, 0),
afterSuites: make(chan *types.SetupSummary, 0),
specCompletions: make(chan *types.SpecSummary, 0),
suiteEndings: make(chan *types.SuiteSummary, 0),
suiteBeginnings: make(chan configAndSuite),
beforeSuites: make(chan *types.SetupSummary),
afterSuites: make(chan *types.SetupSummary),
specCompletions: make(chan *types.SpecSummary),
suiteEndings: make(chan *types.SuiteSummary),
}
go aggregator.mux()
@ -125,14 +125,12 @@ func (aggregator *Aggregator) registerSuiteBeginning(configAndSuite configAndSui
aggregator.stenographer.AnnounceSuite(configAndSuite.summary.SuiteDescription, configAndSuite.config.RandomSeed, configAndSuite.config.RandomizeAllSpecs, aggregator.config.Succinct)
numberOfSpecsToRun := 0
totalNumberOfSpecs := 0
for _, configAndSuite := range aggregator.aggregatedSuiteBeginnings {
numberOfSpecsToRun += configAndSuite.summary.NumberOfSpecsThatWillBeRun
totalNumberOfSpecs += configAndSuite.summary.NumberOfTotalSpecs
if len(aggregator.aggregatedSuiteBeginnings) > 0 {
totalNumberOfSpecs = configAndSuite.summary.NumberOfSpecsBeforeParallelization
}
aggregator.stenographer.AnnounceNumberOfSpecs(numberOfSpecsToRun, totalNumberOfSpecs, aggregator.config.Succinct)
aggregator.stenographer.AnnounceTotalNumberOfSpecs(totalNumberOfSpecs, aggregator.config.Succinct)
aggregator.stenographer.AnnounceAggregatedParallelRun(aggregator.nodeCount, aggregator.config.Succinct)
aggregator.flushCompletedSpecs()
}
@ -199,17 +197,17 @@ func (aggregator *Aggregator) announceSpec(specSummary *types.SpecSummary) {
switch specSummary.State {
case types.SpecStatePassed:
if specSummary.IsMeasurement {
aggregator.stenographer.AnnounceSuccesfulMeasurement(specSummary, aggregator.config.Succinct)
aggregator.stenographer.AnnounceSuccessfulMeasurement(specSummary, aggregator.config.Succinct)
} else if specSummary.RunTime.Seconds() >= aggregator.config.SlowSpecThreshold {
aggregator.stenographer.AnnounceSuccesfulSlowSpec(specSummary, aggregator.config.Succinct)
aggregator.stenographer.AnnounceSuccessfulSlowSpec(specSummary, aggregator.config.Succinct)
} else {
aggregator.stenographer.AnnounceSuccesfulSpec(specSummary)
aggregator.stenographer.AnnounceSuccessfulSpec(specSummary)
}
case types.SpecStatePending:
aggregator.stenographer.AnnouncePendingSpec(specSummary, aggregator.config.NoisyPendings && !aggregator.config.Succinct)
case types.SpecStateSkipped:
aggregator.stenographer.AnnounceSkippedSpec(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace)
aggregator.stenographer.AnnounceSkippedSpec(specSummary, aggregator.config.Succinct || !aggregator.config.NoisySkippings, aggregator.config.FullTrace)
case types.SpecStateTimedOut:
aggregator.stenographer.AnnounceSpecTimedOut(specSummary, aggregator.config.Succinct, aggregator.config.FullTrace)
case types.SpecStatePanicked:
@ -229,7 +227,7 @@ func (aggregator *Aggregator) registerSuiteEnding(suite *types.SuiteSummary) (fi
aggregatedSuiteSummary.SuiteSucceeded = true
for _, suiteSummary := range aggregator.aggregatedSuiteEndings {
if suiteSummary.SuiteSucceeded == false {
if !suiteSummary.SuiteSucceeded {
aggregatedSuiteSummary.SuiteSucceeded = false
}
@ -239,6 +237,7 @@ func (aggregator *Aggregator) registerSuiteEnding(suite *types.SuiteSummary) (fi
aggregatedSuiteSummary.NumberOfFailedSpecs += suiteSummary.NumberOfFailedSpecs
aggregatedSuiteSummary.NumberOfPendingSpecs += suiteSummary.NumberOfPendingSpecs
aggregatedSuiteSummary.NumberOfSkippedSpecs += suiteSummary.NumberOfSkippedSpecs
aggregatedSuiteSummary.NumberOfFlakedSpecs += suiteSummary.NumberOfFlakedSpecs
}
aggregatedSuiteSummary.RunTime = time.Since(aggregator.startTime)

View File

@ -3,8 +3,14 @@ package remote
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"github.com/onsi/ginkgo/internal/writer"
"github.com/onsi/ginkgo/reporters"
"github.com/onsi/ginkgo/reporters/stenographer"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/types"
@ -30,14 +36,41 @@ type ForwardingReporter struct {
serverHost string
poster Poster
outputInterceptor OutputInterceptor
debugMode bool
debugFile *os.File
nestedReporter *reporters.DefaultReporter
}
func NewForwardingReporter(serverHost string, poster Poster, outputInterceptor OutputInterceptor) *ForwardingReporter {
return &ForwardingReporter{
func NewForwardingReporter(config config.DefaultReporterConfigType, serverHost string, poster Poster, outputInterceptor OutputInterceptor, ginkgoWriter *writer.Writer, debugFile string) *ForwardingReporter {
reporter := &ForwardingReporter{
serverHost: serverHost,
poster: poster,
outputInterceptor: outputInterceptor,
}
if debugFile != "" {
var err error
reporter.debugMode = true
reporter.debugFile, err = os.Create(debugFile)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
if !config.Verbose {
//if verbose is true then the GinkgoWriter emits to stdout. Don't _also_ redirect GinkgoWriter output as that will result in duplication.
ginkgoWriter.AndRedirectTo(reporter.debugFile)
}
outputInterceptor.StreamTo(reporter.debugFile) //This is not working
stenographer := stenographer.New(false, true, reporter.debugFile)
config.Succinct = false
config.Verbose = true
config.FullTrace = true
reporter.nestedReporter = reporters.NewDefaultReporter(config, stenographer)
}
return reporter
}
func (reporter *ForwardingReporter) post(path string, data interface{}) {
@ -56,6 +89,10 @@ func (reporter *ForwardingReporter) SpecSuiteWillBegin(conf config.GinkgoConfigT
}
reporter.outputInterceptor.StartInterceptingOutput()
if reporter.debugMode {
reporter.nestedReporter.SpecSuiteWillBegin(conf, summary)
reporter.debugFile.Sync()
}
reporter.post("/SpecSuiteWillBegin", data)
}
@ -63,10 +100,18 @@ func (reporter *ForwardingReporter) BeforeSuiteDidRun(setupSummary *types.SetupS
output, _ := reporter.outputInterceptor.StopInterceptingAndReturnOutput()
reporter.outputInterceptor.StartInterceptingOutput()
setupSummary.CapturedOutput = output
if reporter.debugMode {
reporter.nestedReporter.BeforeSuiteDidRun(setupSummary)
reporter.debugFile.Sync()
}
reporter.post("/BeforeSuiteDidRun", setupSummary)
}
func (reporter *ForwardingReporter) SpecWillRun(specSummary *types.SpecSummary) {
if reporter.debugMode {
reporter.nestedReporter.SpecWillRun(specSummary)
reporter.debugFile.Sync()
}
reporter.post("/SpecWillRun", specSummary)
}
@ -74,6 +119,10 @@ func (reporter *ForwardingReporter) SpecDidComplete(specSummary *types.SpecSumma
output, _ := reporter.outputInterceptor.StopInterceptingAndReturnOutput()
reporter.outputInterceptor.StartInterceptingOutput()
specSummary.CapturedOutput = output
if reporter.debugMode {
reporter.nestedReporter.SpecDidComplete(specSummary)
reporter.debugFile.Sync()
}
reporter.post("/SpecDidComplete", specSummary)
}
@ -81,10 +130,18 @@ func (reporter *ForwardingReporter) AfterSuiteDidRun(setupSummary *types.SetupSu
output, _ := reporter.outputInterceptor.StopInterceptingAndReturnOutput()
reporter.outputInterceptor.StartInterceptingOutput()
setupSummary.CapturedOutput = output
if reporter.debugMode {
reporter.nestedReporter.AfterSuiteDidRun(setupSummary)
reporter.debugFile.Sync()
}
reporter.post("/AfterSuiteDidRun", setupSummary)
}
func (reporter *ForwardingReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) {
reporter.outputInterceptor.StopInterceptingAndReturnOutput()
if reporter.debugMode {
reporter.nestedReporter.SpecSuiteDidEnd(summary)
reporter.debugFile.Sync()
}
reporter.post("/SpecSuiteDidEnd", summary)
}

View File

@ -1,5 +1,7 @@
package remote
import "os"
/*
The OutputInterceptor is used by the ForwardingReporter to
intercept and capture all stdin and stderr output during a test run.
@ -7,4 +9,5 @@ intercept and capture all stdin and stderr output during a test run.
type OutputInterceptor interface {
StartInterceptingOutput() error
StopInterceptingAndReturnOutput() (string, error)
StreamTo(*os.File)
}

View File

@ -1,4 +1,4 @@
// +build freebsd openbsd netbsd dragonfly darwin linux
// +build freebsd openbsd netbsd dragonfly darwin linux solaris
package remote
@ -6,7 +6,9 @@ import (
"errors"
"io/ioutil"
"os"
"syscall"
"github.com/nxadm/tail"
"golang.org/x/sys/unix"
)
func NewOutputInterceptor() OutputInterceptor {
@ -15,7 +17,10 @@ func NewOutputInterceptor() OutputInterceptor {
type outputInterceptor struct {
redirectFile *os.File
streamTarget *os.File
intercepting bool
tailer *tail.Tail
doneTailing chan bool
}
func (interceptor *outputInterceptor) StartInterceptingOutput() error {
@ -31,8 +36,22 @@ func (interceptor *outputInterceptor) StartInterceptingOutput() error {
return err
}
syscall.Dup2(int(interceptor.redirectFile.Fd()), 1)
syscall.Dup2(int(interceptor.redirectFile.Fd()), 2)
// This might call Dup3 if the dup2 syscall is not available, e.g. on
// linux/arm64 or linux/riscv64
unix.Dup2(int(interceptor.redirectFile.Fd()), 1)
unix.Dup2(int(interceptor.redirectFile.Fd()), 2)
if interceptor.streamTarget != nil {
interceptor.tailer, _ = tail.TailFile(interceptor.redirectFile.Name(), tail.Config{Follow: true})
interceptor.doneTailing = make(chan bool)
go func() {
for line := range interceptor.tailer.Lines {
interceptor.streamTarget.Write([]byte(line.Text + "\n"))
}
close(interceptor.doneTailing)
}()
}
return nil
}
@ -48,5 +67,16 @@ func (interceptor *outputInterceptor) StopInterceptingAndReturnOutput() (string,
interceptor.intercepting = false
if interceptor.streamTarget != nil {
interceptor.tailer.Stop()
interceptor.tailer.Cleanup()
<-interceptor.doneTailing
interceptor.streamTarget.Sync()
}
return string(output), err
}
func (interceptor *outputInterceptor) StreamTo(out *os.File) {
interceptor.streamTarget = out
}

View File

@ -4,6 +4,7 @@ package remote
import (
"errors"
"os"
)
func NewOutputInterceptor() OutputInterceptor {
@ -31,3 +32,5 @@ func (interceptor *outputInterceptor) StopInterceptingAndReturnOutput() (string,
return "", nil
}
func (interceptor *outputInterceptor) StreamTo(*os.File) {}

View File

@ -9,13 +9,16 @@ package remote
import (
"encoding/json"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/reporters"
"github.com/onsi/ginkgo/types"
"io/ioutil"
"net"
"net/http"
"sync"
"github.com/onsi/ginkgo/internal/spec_iterator"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/reporters"
"github.com/onsi/ginkgo/types"
)
/*
@ -29,6 +32,7 @@ type Server struct {
lock *sync.Mutex
beforeSuiteData types.RemoteBeforeSuiteData
parallelTotal int
counter int
}
//Create a new server, automatically selecting a port
@ -41,7 +45,7 @@ func NewServer(parallelTotal int) (*Server, error) {
listener: listener,
lock: &sync.Mutex{},
alives: make([]func() bool, parallelTotal),
beforeSuiteData: types.RemoteBeforeSuiteData{nil, types.RemoteBeforeSuiteStatePending},
beforeSuiteData: types.RemoteBeforeSuiteData{Data: nil, State: types.RemoteBeforeSuiteStatePending},
parallelTotal: parallelTotal,
}, nil
}
@ -63,6 +67,8 @@ func (server *Server) Start() {
//synchronization endpoints
mux.HandleFunc("/BeforeSuiteState", server.handleBeforeSuiteState)
mux.HandleFunc("/RemoteAfterSuiteData", server.handleRemoteAfterSuiteData)
mux.HandleFunc("/counter", server.handleCounter)
mux.HandleFunc("/has-counter", server.handleHasCounter) //for backward compatibility
go httpServer.Serve(server.listener)
}
@ -202,3 +208,17 @@ func (server *Server) handleRemoteAfterSuiteData(writer http.ResponseWriter, req
enc := json.NewEncoder(writer)
enc.Encode(afterSuiteData)
}
func (server *Server) handleCounter(writer http.ResponseWriter, request *http.Request) {
c := spec_iterator.Counter{}
server.lock.Lock()
c.Index = server.counter
server.counter++
server.lock.Unlock()
json.NewEncoder(writer).Encode(c)
}
func (server *Server) handleHasCounter(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte(""))
}

View File

@ -5,6 +5,8 @@ import (
"io"
"time"
"sync"
"github.com/onsi/ginkgo/internal/containernode"
"github.com/onsi/ginkgo/internal/leafnodes"
"github.com/onsi/ginkgo/types"
@ -17,9 +19,13 @@ type Spec struct {
containers []*containernode.ContainerNode
state types.SpecState
runTime time.Duration
failure types.SpecFailure
state types.SpecState
runTime time.Duration
startTime time.Time
failure types.SpecFailure
previousFailures bool
stateMutex *sync.Mutex
}
func New(subject leafnodes.SubjectNode, containers []*containernode.ContainerNode, announceProgress bool) *Spec {
@ -28,6 +34,7 @@ func New(subject leafnodes.SubjectNode, containers []*containernode.ContainerNod
containers: containers,
focused: subject.Flag() == types.FlagTypeFocused,
announceProgress: announceProgress,
stateMutex: &sync.Mutex{},
}
spec.processFlag(subject.Flag())
@ -42,28 +49,32 @@ func (spec *Spec) processFlag(flag types.FlagType) {
if flag == types.FlagTypeFocused {
spec.focused = true
} else if flag == types.FlagTypePending {
spec.state = types.SpecStatePending
spec.setState(types.SpecStatePending)
}
}
func (spec *Spec) Skip() {
spec.state = types.SpecStateSkipped
spec.setState(types.SpecStateSkipped)
}
func (spec *Spec) Failed() bool {
return spec.state == types.SpecStateFailed || spec.state == types.SpecStatePanicked || spec.state == types.SpecStateTimedOut
return spec.getState() == types.SpecStateFailed || spec.getState() == types.SpecStatePanicked || spec.getState() == types.SpecStateTimedOut
}
func (spec *Spec) Passed() bool {
return spec.state == types.SpecStatePassed
return spec.getState() == types.SpecStatePassed
}
func (spec *Spec) Flaked() bool {
return spec.getState() == types.SpecStatePassed && spec.previousFailures
}
func (spec *Spec) Pending() bool {
return spec.state == types.SpecStatePending
return spec.getState() == types.SpecStatePending
}
func (spec *Spec) Skipped() bool {
return spec.state == types.SpecStateSkipped
return spec.getState() == types.SpecStateSkipped
}
func (spec *Spec) Focused() bool {
@ -86,16 +97,21 @@ func (spec *Spec) Summary(suiteID string) *types.SpecSummary {
componentTexts[len(spec.containers)] = spec.subject.Text()
componentCodeLocations[len(spec.containers)] = spec.subject.CodeLocation()
runTime := spec.runTime
if runTime == 0 && !spec.startTime.IsZero() {
runTime = time.Since(spec.startTime)
}
return &types.SpecSummary{
IsMeasurement: spec.IsMeasurement(),
NumberOfSamples: spec.subject.Samples(),
ComponentTexts: componentTexts,
ComponentCodeLocations: componentCodeLocations,
State: spec.state,
RunTime: spec.runTime,
Failure: spec.failure,
Measurements: spec.measurementsReport(),
SuiteID: suiteID,
State: spec.getState(),
RunTime: runTime,
Failure: spec.failure,
Measurements: spec.measurementsReport(),
SuiteID: suiteID,
}
}
@ -109,33 +125,61 @@ func (spec *Spec) ConcatenatedString() string {
}
func (spec *Spec) Run(writer io.Writer) {
startTime := time.Now()
if spec.getState() == types.SpecStateFailed {
spec.previousFailures = true
}
spec.startTime = time.Now()
defer func() {
spec.runTime = time.Since(startTime)
spec.runTime = time.Since(spec.startTime)
}()
for sample := 0; sample < spec.subject.Samples(); sample++ {
spec.runSample(sample, writer)
if spec.state != types.SpecStatePassed {
if spec.getState() != types.SpecStatePassed {
return
}
}
}
func (spec *Spec) getState() types.SpecState {
spec.stateMutex.Lock()
defer spec.stateMutex.Unlock()
return spec.state
}
func (spec *Spec) setState(state types.SpecState) {
spec.stateMutex.Lock()
defer spec.stateMutex.Unlock()
spec.state = state
}
func (spec *Spec) runSample(sample int, writer io.Writer) {
spec.state = types.SpecStatePassed
spec.setState(types.SpecStatePassed)
spec.failure = types.SpecFailure{}
innerMostContainerIndexToUnwind := -1
defer func() {
for i := innerMostContainerIndexToUnwind; i >= 0; i-- {
container := spec.containers[i]
for _, justAfterEach := range container.SetupNodesOfType(types.SpecComponentTypeJustAfterEach) {
spec.announceSetupNode(writer, "JustAfterEach", container, justAfterEach)
justAfterEachState, justAfterEachFailure := justAfterEach.Run()
if justAfterEachState != types.SpecStatePassed && spec.state == types.SpecStatePassed {
spec.state = justAfterEachState
spec.failure = justAfterEachFailure
}
}
}
for i := innerMostContainerIndexToUnwind; i >= 0; i-- {
container := spec.containers[i]
for _, afterEach := range container.SetupNodesOfType(types.SpecComponentTypeAfterEach) {
spec.announceSetupNode(writer, "AfterEach", container, afterEach)
afterEachState, afterEachFailure := afterEach.Run()
if afterEachState != types.SpecStatePassed && spec.state == types.SpecStatePassed {
spec.state = afterEachState
if afterEachState != types.SpecStatePassed && spec.getState() == types.SpecStatePassed {
spec.setState(afterEachState)
spec.failure = afterEachFailure
}
}
@ -146,8 +190,10 @@ func (spec *Spec) runSample(sample int, writer io.Writer) {
innerMostContainerIndexToUnwind = i
for _, beforeEach := range container.SetupNodesOfType(types.SpecComponentTypeBeforeEach) {
spec.announceSetupNode(writer, "BeforeEach", container, beforeEach)
spec.state, spec.failure = beforeEach.Run()
if spec.state != types.SpecStatePassed {
s, f := beforeEach.Run()
spec.failure = f
spec.setState(s)
if spec.getState() != types.SpecStatePassed {
return
}
}
@ -156,15 +202,19 @@ func (spec *Spec) runSample(sample int, writer io.Writer) {
for _, container := range spec.containers {
for _, justBeforeEach := range container.SetupNodesOfType(types.SpecComponentTypeJustBeforeEach) {
spec.announceSetupNode(writer, "JustBeforeEach", container, justBeforeEach)
spec.state, spec.failure = justBeforeEach.Run()
if spec.state != types.SpecStatePassed {
s, f := justBeforeEach.Run()
spec.failure = f
spec.setState(s)
if spec.getState() != types.SpecStatePassed {
return
}
}
}
spec.announceSubject(writer, spec.subject)
spec.state, spec.failure = spec.subject.Run()
s, f := spec.subject.Run()
spec.failure = f
spec.setState(s)
}
func (spec *Spec) announceSetupNode(writer io.Writer, nodeType string, container *containernode.ContainerNode, setupNode leafnodes.BasicNode) {

View File

@ -7,15 +7,21 @@ import (
)
type Specs struct {
specs []*Spec
numberOfOriginalSpecs int
hasProgrammaticFocus bool
specs []*Spec
names []string
hasProgrammaticFocus bool
RegexScansFilePath bool
}
func NewSpecs(specs []*Spec) *Specs {
names := make([]string, len(specs))
for i, spec := range specs {
names[i] = spec.ConcatenatedString()
}
return &Specs{
specs: specs,
numberOfOriginalSpecs: len(specs),
names: names,
}
}
@ -23,10 +29,6 @@ func (e *Specs) Specs() []*Spec {
return e.specs
}
func (e *Specs) NumberOfOriginalSpecs() int {
return e.numberOfOriginalSpecs
}
func (e *Specs) HasProgrammaticFocus() bool {
return e.hasProgrammaticFocus
}
@ -35,17 +37,20 @@ func (e *Specs) Shuffle(r *rand.Rand) {
sort.Sort(e)
permutation := r.Perm(len(e.specs))
shuffledSpecs := make([]*Spec, len(e.specs))
names := make([]string, len(e.specs))
for i, j := range permutation {
shuffledSpecs[i] = e.specs[j]
names[i] = e.names[j]
}
e.specs = shuffledSpecs
e.names = names
}
func (e *Specs) ApplyFocus(description string, focusString string, skipString string) {
if focusString == "" && skipString == "" {
e.applyProgrammaticFocus()
} else {
e.applyRegExpFocus(description, focusString, skipString)
e.applyRegExpFocusAndSkip(description, focusString, skipString)
}
}
@ -67,21 +72,46 @@ func (e *Specs) applyProgrammaticFocus() {
}
}
func (e *Specs) applyRegExpFocus(description string, focusString string, skipString string) {
for _, spec := range e.specs {
// toMatch returns a byte[] to be used by regex matchers. When adding new behaviours to the matching function,
// this is the place which we append to.
func (e *Specs) toMatch(description string, i int) []byte {
if i > len(e.names) {
return nil
}
if e.RegexScansFilePath {
return []byte(
description + " " +
e.names[i] + " " +
e.specs[i].subject.CodeLocation().FileName)
} else {
return []byte(
description + " " +
e.names[i])
}
}
func (e *Specs) applyRegExpFocusAndSkip(description string, focusString string, skipString string) {
var focusFilter *regexp.Regexp
if focusString != "" {
focusFilter = regexp.MustCompile(focusString)
}
var skipFilter *regexp.Regexp
if skipString != "" {
skipFilter = regexp.MustCompile(skipString)
}
for i, spec := range e.specs {
matchesFocus := true
matchesSkip := false
toMatch := []byte(description + " " + spec.ConcatenatedString())
toMatch := e.toMatch(description, i)
if focusString != "" {
focusFilter := regexp.MustCompile(focusString)
matchesFocus = focusFilter.Match([]byte(toMatch))
if focusFilter != nil {
matchesFocus = focusFilter.Match(toMatch)
}
if skipString != "" {
skipFilter := regexp.MustCompile(skipString)
matchesSkip = skipFilter.Match([]byte(toMatch))
if skipFilter != nil {
matchesSkip = skipFilter.Match(toMatch)
}
if !matchesFocus || matchesSkip {
@ -98,15 +128,6 @@ func (e *Specs) SkipMeasurements() {
}
}
func (e *Specs) TrimForParallelization(total int, node int) {
startIndex, count := ParallelizedIndexRange(len(e.specs), total, node)
if count == 0 {
e.specs = make([]*Spec, 0)
} else {
e.specs = e.specs[startIndex : startIndex+count]
}
}
//sort.Interface
func (e *Specs) Len() int {
@ -114,9 +135,10 @@ func (e *Specs) Len() int {
}
func (e *Specs) Less(i, j int) bool {
return e.specs[i].ConcatenatedString() < e.specs[j].ConcatenatedString()
return e.names[i] < e.names[j]
}
func (e *Specs) Swap(i, j int) {
e.names[i], e.names[j] = e.names[j], e.names[i]
e.specs[i], e.specs[j] = e.specs[j], e.specs[i]
}

View File

@ -1,4 +1,4 @@
package spec
package spec_iterator
func ParallelizedIndexRange(length int, parallelTotal int, parallelNode int) (startIndex int, count int) {
if length == 0 {

View File

@ -0,0 +1,59 @@
package spec_iterator
import (
"encoding/json"
"fmt"
"net/http"
"github.com/onsi/ginkgo/internal/spec"
)
type ParallelIterator struct {
specs []*spec.Spec
host string
client *http.Client
}
func NewParallelIterator(specs []*spec.Spec, host string) *ParallelIterator {
return &ParallelIterator{
specs: specs,
host: host,
client: &http.Client{},
}
}
func (s *ParallelIterator) Next() (*spec.Spec, error) {
resp, err := s.client.Get(s.host + "/counter")
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
var counter Counter
err = json.NewDecoder(resp.Body).Decode(&counter)
if err != nil {
return nil, err
}
if counter.Index >= len(s.specs) {
return nil, ErrClosed
}
return s.specs[counter.Index], nil
}
func (s *ParallelIterator) NumberOfSpecsPriorToIteration() int {
return len(s.specs)
}
func (s *ParallelIterator) NumberOfSpecsToProcessIfKnown() (int, bool) {
return -1, false
}
func (s *ParallelIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) {
return -1, false
}

View File

@ -0,0 +1,45 @@
package spec_iterator
import (
"github.com/onsi/ginkgo/internal/spec"
)
type SerialIterator struct {
specs []*spec.Spec
index int
}
func NewSerialIterator(specs []*spec.Spec) *SerialIterator {
return &SerialIterator{
specs: specs,
index: 0,
}
}
func (s *SerialIterator) Next() (*spec.Spec, error) {
if s.index >= len(s.specs) {
return nil, ErrClosed
}
spec := s.specs[s.index]
s.index += 1
return spec, nil
}
func (s *SerialIterator) NumberOfSpecsPriorToIteration() int {
return len(s.specs)
}
func (s *SerialIterator) NumberOfSpecsToProcessIfKnown() (int, bool) {
return len(s.specs), true
}
func (s *SerialIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) {
count := 0
for _, s := range s.specs {
if !s.Skipped() && !s.Pending() {
count += 1
}
}
return count, true
}

View File

@ -0,0 +1,47 @@
package spec_iterator
import "github.com/onsi/ginkgo/internal/spec"
type ShardedParallelIterator struct {
specs []*spec.Spec
index int
maxIndex int
}
func NewShardedParallelIterator(specs []*spec.Spec, total int, node int) *ShardedParallelIterator {
startIndex, count := ParallelizedIndexRange(len(specs), total, node)
return &ShardedParallelIterator{
specs: specs,
index: startIndex,
maxIndex: startIndex + count,
}
}
func (s *ShardedParallelIterator) Next() (*spec.Spec, error) {
if s.index >= s.maxIndex {
return nil, ErrClosed
}
spec := s.specs[s.index]
s.index += 1
return spec, nil
}
func (s *ShardedParallelIterator) NumberOfSpecsPriorToIteration() int {
return len(s.specs)
}
func (s *ShardedParallelIterator) NumberOfSpecsToProcessIfKnown() (int, bool) {
return s.maxIndex - s.index, true
}
func (s *ShardedParallelIterator) NumberOfSpecsThatWillBeRunIfKnown() (int, bool) {
count := 0
for i := s.index; i < s.maxIndex; i += 1 {
if !s.specs[i].Skipped() && !s.specs[i].Pending() {
count += 1
}
}
return count, true
}

View File

@ -0,0 +1,20 @@
package spec_iterator
import (
"errors"
"github.com/onsi/ginkgo/internal/spec"
)
var ErrClosed = errors.New("no more specs to run")
type SpecIterator interface {
Next() (*spec.Spec, error)
NumberOfSpecsPriorToIteration() int
NumberOfSpecsToProcessIfKnown() (int, bool)
NumberOfSpecsThatWillBeRunIfKnown() (int, bool)
}
type Counter struct {
Index int `json:"index"`
}

View File

@ -7,6 +7,8 @@ import (
"sync"
"syscall"
"github.com/onsi/ginkgo/internal/spec_iterator"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/internal/leafnodes"
"github.com/onsi/ginkgo/internal/spec"
@ -20,7 +22,7 @@ import (
type SpecRunner struct {
description string
beforeSuiteNode leafnodes.SuiteNode
specs *spec.Specs
iterator spec_iterator.SpecIterator
afterSuiteNode leafnodes.SuiteNode
reporters []reporters.Reporter
startTime time.Time
@ -29,14 +31,15 @@ type SpecRunner struct {
writer Writer.WriterInterface
config config.GinkgoConfigType
interrupted bool
processedSpecs []*spec.Spec
lock *sync.Mutex
}
func New(description string, beforeSuiteNode leafnodes.SuiteNode, specs *spec.Specs, afterSuiteNode leafnodes.SuiteNode, reporters []reporters.Reporter, writer Writer.WriterInterface, config config.GinkgoConfigType) *SpecRunner {
func New(description string, beforeSuiteNode leafnodes.SuiteNode, iterator spec_iterator.SpecIterator, afterSuiteNode leafnodes.SuiteNode, reporters []reporters.Reporter, writer Writer.WriterInterface, config config.GinkgoConfigType) *SpecRunner {
return &SpecRunner{
description: description,
beforeSuiteNode: beforeSuiteNode,
specs: specs,
iterator: iterator,
afterSuiteNode: afterSuiteNode,
reporters: reporters,
writer: writer,
@ -53,7 +56,9 @@ func (runner *SpecRunner) Run() bool {
}
runner.reportSuiteWillBegin()
go runner.registerForInterrupts()
signalRegistered := make(chan struct{})
go runner.registerForInterrupts(signalRegistered)
<-signalRegistered
suitePassed := runner.runBeforeSuite()
@ -79,7 +84,18 @@ func (runner *SpecRunner) performDryRun() {
runner.reportBeforeSuite(summary)
}
for _, spec := range runner.specs.Specs() {
for {
spec, err := runner.iterator.Next()
if err == spec_iterator.ErrClosed {
break
}
if err != nil {
fmt.Println("failed to iterate over tests:\n" + err.Error())
break
}
runner.processedSpecs = append(runner.processedSpecs, spec)
summary := spec.Summary(runner.suiteID)
runner.reportSpecWillRun(summary)
if summary.State == types.SpecStateInvalid {
@ -130,28 +146,39 @@ func (runner *SpecRunner) runAfterSuite() bool {
func (runner *SpecRunner) runSpecs() bool {
suiteFailed := false
skipRemainingSpecs := false
for _, spec := range runner.specs.Specs() {
for {
spec, err := runner.iterator.Next()
if err == spec_iterator.ErrClosed {
break
}
if err != nil {
fmt.Println("failed to iterate over tests:\n" + err.Error())
suiteFailed = true
break
}
runner.processedSpecs = append(runner.processedSpecs, spec)
if runner.wasInterrupted() {
return suiteFailed
break
}
if skipRemainingSpecs {
spec.Skip()
}
runner.reportSpecWillRun(spec.Summary(runner.suiteID))
if !spec.Skipped() && !spec.Pending() {
runner.runningSpec = spec
spec.Run(runner.writer)
runner.runningSpec = nil
if spec.Failed() {
if passed := runner.runSpec(spec); !passed {
suiteFailed = true
}
} else if spec.Pending() && runner.config.FailOnPending {
runner.reportSpecWillRun(spec.Summary(runner.suiteID))
suiteFailed = true
runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed())
} else {
runner.reportSpecWillRun(spec.Summary(runner.suiteID))
runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed())
}
runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed())
if spec.Failed() && runner.config.FailFast {
skipRemainingSpecs = true
}
@ -160,6 +187,26 @@ func (runner *SpecRunner) runSpecs() bool {
return !suiteFailed
}
func (runner *SpecRunner) runSpec(spec *spec.Spec) (passed bool) {
maxAttempts := 1
if runner.config.FlakeAttempts > 0 {
// uninitialized configs count as 1
maxAttempts = runner.config.FlakeAttempts
}
for i := 0; i < maxAttempts; i++ {
runner.reportSpecWillRun(spec.Summary(runner.suiteID))
runner.runningSpec = spec
spec.Run(runner.writer)
runner.runningSpec = nil
runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed())
if !spec.Failed() {
return true
}
}
return false
}
func (runner *SpecRunner) CurrentSpecSummary() (*types.SpecSummary, bool) {
if runner.runningSpec == nil {
return nil, false
@ -168,9 +215,10 @@ func (runner *SpecRunner) CurrentSpecSummary() (*types.SpecSummary, bool) {
return runner.runningSpec.Summary(runner.suiteID), true
}
func (runner *SpecRunner) registerForInterrupts() {
func (runner *SpecRunner) registerForInterrupts(signalRegistered chan struct{}) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
close(signalRegistered)
<-c
signal.Stop(c)
@ -225,7 +273,7 @@ func (runner *SpecRunner) wasInterrupted() bool {
func (runner *SpecRunner) reportSuiteWillBegin() {
runner.startTime = time.Now()
summary := runner.summary(true)
summary := runner.suiteWillBeginSummary()
for _, reporter := range runner.reporters {
reporter.SpecSuiteWillBegin(runner.config, summary)
}
@ -252,6 +300,9 @@ func (runner *SpecRunner) reportSpecWillRun(summary *types.SpecSummary) {
}
func (runner *SpecRunner) reportSpecDidComplete(summary *types.SpecSummary, failed bool) {
if len(summary.CapturedOutput) == 0 {
summary.CapturedOutput = string(runner.writer.Bytes())
}
for i := len(runner.reporters) - 1; i >= 1; i-- {
runner.reporters[i].SpecDidComplete(summary)
}
@ -264,17 +315,17 @@ func (runner *SpecRunner) reportSpecDidComplete(summary *types.SpecSummary, fail
}
func (runner *SpecRunner) reportSuiteDidEnd(success bool) {
summary := runner.summary(success)
summary := runner.suiteDidEndSummary(success)
summary.RunTime = time.Since(runner.startTime)
for _, reporter := range runner.reporters {
reporter.SpecSuiteDidEnd(summary)
}
}
func (runner *SpecRunner) countSpecsSatisfying(filter func(ex *spec.Spec) bool) (count int) {
func (runner *SpecRunner) countSpecsThatRanSatisfying(filter func(ex *spec.Spec) bool) (count int) {
count = 0
for _, spec := range runner.specs.Specs() {
for _, spec := range runner.processedSpecs {
if filter(spec) {
count++
}
@ -283,28 +334,37 @@ func (runner *SpecRunner) countSpecsSatisfying(filter func(ex *spec.Spec) bool)
return count
}
func (runner *SpecRunner) summary(success bool) *types.SuiteSummary {
numberOfSpecsThatWillBeRun := runner.countSpecsSatisfying(func(ex *spec.Spec) bool {
func (runner *SpecRunner) suiteDidEndSummary(success bool) *types.SuiteSummary {
numberOfSpecsThatWillBeRun := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
return !ex.Skipped() && !ex.Pending()
})
numberOfPendingSpecs := runner.countSpecsSatisfying(func(ex *spec.Spec) bool {
numberOfPendingSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
return ex.Pending()
})
numberOfSkippedSpecs := runner.countSpecsSatisfying(func(ex *spec.Spec) bool {
numberOfSkippedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
return ex.Skipped()
})
numberOfPassedSpecs := runner.countSpecsSatisfying(func(ex *spec.Spec) bool {
numberOfPassedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
return ex.Passed()
})
numberOfFailedSpecs := runner.countSpecsSatisfying(func(ex *spec.Spec) bool {
numberOfFlakedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
return ex.Flaked()
})
numberOfFailedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
return ex.Failed()
})
if runner.beforeSuiteNode != nil && !runner.beforeSuiteNode.Passed() && !runner.config.DryRun {
var known bool
numberOfSpecsThatWillBeRun, known = runner.iterator.NumberOfSpecsThatWillBeRunIfKnown()
if !known {
numberOfSpecsThatWillBeRun = runner.iterator.NumberOfSpecsPriorToIteration()
}
numberOfFailedSpecs = numberOfSpecsThatWillBeRun
}
@ -313,12 +373,39 @@ func (runner *SpecRunner) summary(success bool) *types.SuiteSummary {
SuiteSucceeded: success,
SuiteID: runner.suiteID,
NumberOfSpecsBeforeParallelization: runner.specs.NumberOfOriginalSpecs(),
NumberOfTotalSpecs: len(runner.specs.Specs()),
NumberOfSpecsBeforeParallelization: runner.iterator.NumberOfSpecsPriorToIteration(),
NumberOfTotalSpecs: len(runner.processedSpecs),
NumberOfSpecsThatWillBeRun: numberOfSpecsThatWillBeRun,
NumberOfPendingSpecs: numberOfPendingSpecs,
NumberOfSkippedSpecs: numberOfSkippedSpecs,
NumberOfPassedSpecs: numberOfPassedSpecs,
NumberOfFailedSpecs: numberOfFailedSpecs,
NumberOfFlakedSpecs: numberOfFlakedSpecs,
}
}
func (runner *SpecRunner) suiteWillBeginSummary() *types.SuiteSummary {
numTotal, known := runner.iterator.NumberOfSpecsToProcessIfKnown()
if !known {
numTotal = -1
}
numToRun, known := runner.iterator.NumberOfSpecsThatWillBeRunIfKnown()
if !known {
numToRun = -1
}
return &types.SuiteSummary{
SuiteDescription: runner.description,
SuiteID: runner.suiteID,
NumberOfSpecsBeforeParallelization: runner.iterator.NumberOfSpecsPriorToIteration(),
NumberOfTotalSpecs: numTotal,
NumberOfSpecsThatWillBeRun: numToRun,
NumberOfPendingSpecs: -1,
NumberOfSkippedSpecs: -1,
NumberOfPassedSpecs: -1,
NumberOfFailedSpecs: -1,
NumberOfFlakedSpecs: -1,
}
}

View File

@ -2,8 +2,11 @@ package suite
import (
"math/rand"
"net/http"
"time"
"github.com/onsi/ginkgo/internal/spec_iterator"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/internal/containernode"
"github.com/onsi/ginkgo/internal/failer"
@ -52,18 +55,18 @@ func (suite *Suite) Run(t ginkgoTestingT, description string, reporters []report
r := rand.New(rand.NewSource(config.RandomSeed))
suite.topLevelContainer.Shuffle(r)
specs := suite.generateSpecs(description, config)
suite.runner = specrunner.New(description, suite.beforeSuiteNode, specs, suite.afterSuiteNode, reporters, writer, config)
iterator, hasProgrammaticFocus := suite.generateSpecsIterator(description, config)
suite.runner = specrunner.New(description, suite.beforeSuiteNode, iterator, suite.afterSuiteNode, reporters, writer, config)
suite.running = true
success := suite.runner.Run()
if !success {
t.Fail()
}
return success, specs.HasProgrammaticFocus()
return success, hasProgrammaticFocus
}
func (suite *Suite) generateSpecs(description string, config config.GinkgoConfigType) *spec.Specs {
func (suite *Suite) generateSpecsIterator(description string, config config.GinkgoConfigType) (spec_iterator.SpecIterator, bool) {
specsSlice := []*spec.Spec{}
suite.topLevelContainer.BackPropagateProgrammaticFocus()
for _, collatedNodes := range suite.topLevelContainer.Collate() {
@ -71,6 +74,7 @@ func (suite *Suite) generateSpecs(description string, config config.GinkgoConfig
}
specs := spec.NewSpecs(specsSlice)
specs.RegexScansFilePath = config.RegexScansFilePath
if config.RandomizeAllSpecs {
specs.Shuffle(rand.New(rand.NewSource(config.RandomSeed)))
@ -82,14 +86,25 @@ func (suite *Suite) generateSpecs(description string, config config.GinkgoConfig
specs.SkipMeasurements()
}
var iterator spec_iterator.SpecIterator
if config.ParallelTotal > 1 {
specs.TrimForParallelization(config.ParallelTotal, config.ParallelNode)
iterator = spec_iterator.NewParallelIterator(specs.Specs(), config.SyncHost)
resp, err := http.Get(config.SyncHost + "/has-counter")
if err != nil || resp.StatusCode != http.StatusOK {
iterator = spec_iterator.NewShardedParallelIterator(specs.Specs(), config.ParallelTotal, config.ParallelNode)
}
} else {
iterator = spec_iterator.NewSerialIterator(specs.Specs())
}
return specs
return iterator, specs.HasProgrammaticFocus()
}
func (suite *Suite) CurrentRunningSpecSummary() (*types.SpecSummary, bool) {
if !suite.running {
return nil, false
}
return suite.runner.CurrentSpecSummary()
}
@ -137,35 +152,42 @@ func (suite *Suite) PushContainerNode(text string, body func(), flag types.FlagT
func (suite *Suite) PushItNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, timeout time.Duration) {
if suite.running {
suite.failer.Fail("You may only call It from within a Describe or Context", codeLocation)
suite.failer.Fail("You may only call It from within a Describe, Context or When", codeLocation)
}
suite.currentContainer.PushSubjectNode(leafnodes.NewItNode(text, body, flag, codeLocation, timeout, suite.failer, suite.containerIndex))
}
func (suite *Suite) PushMeasureNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, samples int) {
if suite.running {
suite.failer.Fail("You may only call Measure from within a Describe or Context", codeLocation)
suite.failer.Fail("You may only call Measure from within a Describe, Context or When", codeLocation)
}
suite.currentContainer.PushSubjectNode(leafnodes.NewMeasureNode(text, body, flag, codeLocation, samples, suite.failer, suite.containerIndex))
}
func (suite *Suite) PushBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
if suite.running {
suite.failer.Fail("You may only call BeforeEach from within a Describe or Context", codeLocation)
suite.failer.Fail("You may only call BeforeEach from within a Describe, Context or When", codeLocation)
}
suite.currentContainer.PushSetupNode(leafnodes.NewBeforeEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
}
func (suite *Suite) PushJustBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
if suite.running {
suite.failer.Fail("You may only call JustBeforeEach from within a Describe or Context", codeLocation)
suite.failer.Fail("You may only call JustBeforeEach from within a Describe, Context or When", codeLocation)
}
suite.currentContainer.PushSetupNode(leafnodes.NewJustBeforeEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
}
func (suite *Suite) PushJustAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
if suite.running {
suite.failer.Fail("You may only call JustAfterEach from within a Describe or Context", codeLocation)
}
suite.currentContainer.PushSetupNode(leafnodes.NewJustAfterEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
}
func (suite *Suite) PushAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
if suite.running {
suite.failer.Fail("You may only call AfterEach from within a Describe or Context", codeLocation)
suite.failer.Fail("You may only call AfterEach from within a Describe, Context or When", codeLocation)
}
suite.currentContainer.PushSetupNode(leafnodes.NewAfterEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
}

View File

@ -50,7 +50,7 @@ func (t *ginkgoTestingTProxy) Log(args ...interface{}) {
}
func (t *ginkgoTestingTProxy) Logf(format string, args ...interface{}) {
fmt.Fprintf(t.writer, format, args...)
t.Log(fmt.Sprintf(format, args...))
}
func (t *ginkgoTestingTProxy) Failed() bool {
@ -65,7 +65,7 @@ func (t *ginkgoTestingTProxy) Skip(args ...interface{}) {
}
func (t *ginkgoTestingTProxy) Skipf(format string, args ...interface{}) {
fmt.Printf(format, args...)
t.Skip(fmt.Sprintf(format, args...))
}
func (t *ginkgoTestingTProxy) SkipNow() {

View File

@ -26,6 +26,11 @@ func (writer *FakeGinkgoWriter) DumpOutWithHeader(header string) {
writer.EventStream = append(writer.EventStream, "DUMP_WITH_HEADER: "+header)
}
func (writer *FakeGinkgoWriter) Bytes() []byte {
writer.EventStream = append(writer.EventStream, "BYTES")
return nil
}
func (writer *FakeGinkgoWriter) Write(data []byte) (n int, err error) {
return 0, nil
}

View File

@ -12,13 +12,15 @@ type WriterInterface interface {
Truncate()
DumpOut()
DumpOutWithHeader(header string)
Bytes() []byte
}
type Writer struct {
buffer *bytes.Buffer
outWriter io.Writer
lock *sync.Mutex
stream bool
buffer *bytes.Buffer
outWriter io.Writer
lock *sync.Mutex
stream bool
redirector io.Writer
}
func New(outWriter io.Writer) *Writer {
@ -30,6 +32,10 @@ func New(outWriter io.Writer) *Writer {
}
}
func (w *Writer) AndRedirectTo(writer io.Writer) {
w.redirector = writer
}
func (w *Writer) SetStream(stream bool) {
w.lock.Lock()
defer w.lock.Unlock()
@ -40,11 +46,14 @@ func (w *Writer) Write(b []byte) (n int, err error) {
w.lock.Lock()
defer w.lock.Unlock()
n, err = w.buffer.Write(b)
if w.redirector != nil {
w.redirector.Write(b)
}
if w.stream {
return w.outWriter.Write(b)
} else {
return w.buffer.Write(b)
}
return n, err
}
func (w *Writer) Truncate() {
@ -61,6 +70,15 @@ func (w *Writer) DumpOut() {
}
}
func (w *Writer) Bytes() []byte {
w.lock.Lock()
defer w.lock.Unlock()
b := w.buffer.Bytes()
copied := make([]byte, len(b))
copy(copied, b)
return copied
}
func (w *Writer) DumpOutWithHeader(header string) {
w.lock.Lock()
defer w.lock.Unlock()

View File

@ -29,9 +29,10 @@ func NewDefaultReporter(config config.DefaultReporterConfigType, stenographer st
func (reporter *DefaultReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) {
reporter.stenographer.AnnounceSuite(summary.SuiteDescription, config.RandomSeed, config.RandomizeAllSpecs, reporter.config.Succinct)
if config.ParallelTotal > 1 {
reporter.stenographer.AnnounceParallelRun(config.ParallelNode, config.ParallelTotal, summary.NumberOfTotalSpecs, summary.NumberOfSpecsBeforeParallelization, reporter.config.Succinct)
reporter.stenographer.AnnounceParallelRun(config.ParallelNode, config.ParallelTotal, reporter.config.Succinct)
} else {
reporter.stenographer.AnnounceNumberOfSpecs(summary.NumberOfSpecsThatWillBeRun, summary.NumberOfTotalSpecs, reporter.config.Succinct)
}
reporter.stenographer.AnnounceNumberOfSpecs(summary.NumberOfSpecsThatWillBeRun, summary.NumberOfTotalSpecs, reporter.config.Succinct)
}
func (reporter *DefaultReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {
@ -56,16 +57,19 @@ func (reporter *DefaultReporter) SpecDidComplete(specSummary *types.SpecSummary)
switch specSummary.State {
case types.SpecStatePassed:
if specSummary.IsMeasurement {
reporter.stenographer.AnnounceSuccesfulMeasurement(specSummary, reporter.config.Succinct)
reporter.stenographer.AnnounceSuccessfulMeasurement(specSummary, reporter.config.Succinct)
} else if specSummary.RunTime.Seconds() >= reporter.config.SlowSpecThreshold {
reporter.stenographer.AnnounceSuccesfulSlowSpec(specSummary, reporter.config.Succinct)
reporter.stenographer.AnnounceSuccessfulSlowSpec(specSummary, reporter.config.Succinct)
} else {
reporter.stenographer.AnnounceSuccesfulSpec(specSummary)
reporter.stenographer.AnnounceSuccessfulSpec(specSummary)
if reporter.config.ReportPassed {
reporter.stenographer.AnnounceCapturedOutput(specSummary.CapturedOutput)
}
}
case types.SpecStatePending:
reporter.stenographer.AnnouncePendingSpec(specSummary, reporter.config.NoisyPendings && !reporter.config.Succinct)
case types.SpecStateSkipped:
reporter.stenographer.AnnounceSkippedSpec(specSummary, reporter.config.Succinct, reporter.config.FullTrace)
reporter.stenographer.AnnounceSkippedSpec(specSummary, reporter.config.Succinct || !reporter.config.NoisySkippings, reporter.config.FullTrace)
case types.SpecStateTimedOut:
reporter.stenographer.AnnounceSpecTimedOut(specSummary, reporter.config.Succinct, reporter.config.FullTrace)
case types.SpecStatePanicked:

View File

@ -11,26 +11,37 @@ package reporters
import (
"encoding/xml"
"fmt"
"math"
"os"
"path/filepath"
"strings"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/types"
"os"
"strings"
)
type JUnitTestSuite struct {
XMLName xml.Name `xml:"testsuite"`
TestCases []JUnitTestCase `xml:"testcase"`
Name string `xml:"name,attr"`
Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"`
Errors int `xml:"errors,attr"`
Time float64 `xml:"time,attr"`
}
type JUnitTestCase struct {
Name string `xml:"name,attr"`
ClassName string `xml:"classname,attr"`
PassedMessage *JUnitPassedMessage `xml:"passed,omitempty"`
FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"`
Skipped *JUnitSkipped `xml:"skipped,omitempty"`
Time float64 `xml:"time,attr"`
SystemOut string `xml:"system-out,omitempty"`
}
type JUnitPassedMessage struct {
Message string `xml:",chardata"`
}
type JUnitFailureMessage struct {
@ -43,9 +54,10 @@ type JUnitSkipped struct {
}
type JUnitReporter struct {
suite JUnitTestSuite
filename string
testSuiteName string
suite JUnitTestSuite
filename string
testSuiteName string
ReporterConfig config.DefaultReporterConfigType
}
//NewJUnitReporter creates a new JUnit XML reporter. The XML will be stored in the passed in filename.
@ -55,12 +67,13 @@ func NewJUnitReporter(filename string) *JUnitReporter {
}
}
func (reporter *JUnitReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) {
func (reporter *JUnitReporter) SpecSuiteWillBegin(ginkgoConfig config.GinkgoConfigType, summary *types.SuiteSummary) {
reporter.suite = JUnitTestSuite{
Tests: summary.NumberOfSpecsThatWillBeRun,
Name: summary.SuiteDescription,
TestCases: []JUnitTestCase{},
}
reporter.testSuiteName = summary.SuiteDescription
reporter.ReporterConfig = config.DefaultReporterConfig
}
func (reporter *JUnitReporter) SpecWillRun(specSummary *types.SpecSummary) {
@ -74,6 +87,10 @@ func (reporter *JUnitReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary
reporter.handleSetupSummary("AfterSuite", setupSummary)
}
func failureMessage(failure types.SpecFailure) string {
return fmt.Sprintf("%s\n%s\n%s", failure.ComponentCodeLocation.String(), failure.Message, failure.Location.String())
}
func (reporter *JUnitReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) {
if setupSummary.State != types.SpecStatePassed {
testCase := JUnitTestCase{
@ -83,8 +100,9 @@ func (reporter *JUnitReporter) handleSetupSummary(name string, setupSummary *typ
testCase.FailureMessage = &JUnitFailureMessage{
Type: reporter.failureTypeForState(setupSummary.State),
Message: fmt.Sprintf("%s\n%s", setupSummary.Failure.ComponentCodeLocation.String(), setupSummary.Failure.Message),
Message: failureMessage(setupSummary.Failure),
}
testCase.SystemOut = setupSummary.CapturedOutput
testCase.Time = setupSummary.RunTime.Seconds()
reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
}
@ -95,11 +113,22 @@ func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) {
Name: strings.Join(specSummary.ComponentTexts[1:], " "),
ClassName: reporter.testSuiteName,
}
if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed {
testCase.PassedMessage = &JUnitPassedMessage{
Message: specSummary.CapturedOutput,
}
}
if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked {
testCase.FailureMessage = &JUnitFailureMessage{
Type: reporter.failureTypeForState(specSummary.State),
Message: fmt.Sprintf("%s\n%s", specSummary.Failure.ComponentCodeLocation.String(), specSummary.Failure.Message),
Message: failureMessage(specSummary.Failure),
}
if specSummary.State == types.SpecStatePanicked {
testCase.FailureMessage.Message += fmt.Sprintf("\n\nPanic: %s\n\nFull stack:\n%s",
specSummary.Failure.ForwardedPanic,
specSummary.Failure.Location.FullStackTrace)
}
testCase.SystemOut = specSummary.CapturedOutput
}
if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending {
testCase.Skipped = &JUnitSkipped{}
@ -109,19 +138,33 @@ func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) {
}
func (reporter *JUnitReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) {
reporter.suite.Time = summary.RunTime.Seconds()
reporter.suite.Tests = summary.NumberOfSpecsThatWillBeRun
reporter.suite.Time = math.Trunc(summary.RunTime.Seconds()*1000) / 1000
reporter.suite.Failures = summary.NumberOfFailedSpecs
file, err := os.Create(reporter.filename)
reporter.suite.Errors = 0
if reporter.ReporterConfig.ReportFile != "" {
reporter.filename = reporter.ReporterConfig.ReportFile
fmt.Printf("\nJUnit path was configured: %s\n", reporter.filename)
}
filePath, _ := filepath.Abs(reporter.filename)
dirPath := filepath.Dir(filePath)
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
fmt.Printf("Failed to create JUnit report file: %s\n\t%s", reporter.filename, err.Error())
fmt.Printf("\nFailed to create JUnit directory: %s\n\t%s", filePath, err.Error())
}
file, err := os.Create(filePath)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create JUnit report file: %s\n\t%s", filePath, err.Error())
}
defer file.Close()
file.WriteString(xml.Header)
encoder := xml.NewEncoder(file)
encoder.Indent(" ", " ")
err = encoder.Encode(reporter.suite)
if err != nil {
fmt.Printf("Failed to generate JUnit report\n\t%s", err.Error())
if err == nil {
fmt.Fprintf(os.Stdout, "\nJUnit report was created: %s\n", filePath)
} else {
fmt.Fprintf(os.Stderr,"\nFailed to generate JUnit report data:\n\t%s", err.Error())
}
}

View File

@ -22,24 +22,24 @@ func (s *consoleStenographer) colorize(colorCode string, format string, args ...
}
func (s *consoleStenographer) printBanner(text string, bannerCharacter string) {
fmt.Println(text)
fmt.Println(strings.Repeat(bannerCharacter, len(text)))
fmt.Fprintln(s.w, text)
fmt.Fprintln(s.w, strings.Repeat(bannerCharacter, len(text)))
}
func (s *consoleStenographer) printNewLine() {
fmt.Println("")
fmt.Fprintln(s.w, "")
}
func (s *consoleStenographer) printDelimiter() {
fmt.Println(s.colorize(grayColor, "%s", strings.Repeat("-", 30)))
fmt.Fprintln(s.w, s.colorize(grayColor, "%s", strings.Repeat("-", 30)))
}
func (s *consoleStenographer) print(indentation int, format string, args ...interface{}) {
fmt.Print(s.indent(indentation, format, args...))
fmt.Fprint(s.w, s.indent(indentation, format, args...))
}
func (s *consoleStenographer) println(indentation int, format string, args ...interface{}) {
fmt.Println(s.indent(indentation, format, args...))
fmt.Fprintln(s.w, s.indent(indentation, format, args...))
}
func (s *consoleStenographer) indent(indentation int, format string, args ...interface{}) string {

View File

@ -74,14 +74,18 @@ func (stenographer *FakeStenographer) AnnounceAggregatedParallelRun(nodes int, s
stenographer.registerCall("AnnounceAggregatedParallelRun", nodes, succinct)
}
func (stenographer *FakeStenographer) AnnounceParallelRun(node int, nodes int, specsToRun int, totalSpecs int, succinct bool) {
stenographer.registerCall("AnnounceParallelRun", node, nodes, specsToRun, totalSpecs, succinct)
func (stenographer *FakeStenographer) AnnounceParallelRun(node int, nodes int, succinct bool) {
stenographer.registerCall("AnnounceParallelRun", node, nodes, succinct)
}
func (stenographer *FakeStenographer) AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool) {
stenographer.registerCall("AnnounceNumberOfSpecs", specsToRun, total, succinct)
}
func (stenographer *FakeStenographer) AnnounceTotalNumberOfSpecs(total int, succinct bool) {
stenographer.registerCall("AnnounceTotalNumberOfSpecs", total, succinct)
}
func (stenographer *FakeStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool) {
stenographer.registerCall("AnnounceSpecRunCompletion", summary, succinct)
}
@ -101,16 +105,16 @@ func (stenographer *FakeStenographer) AnnounceCapturedOutput(output string) {
stenographer.registerCall("AnnounceCapturedOutput", output)
}
func (stenographer *FakeStenographer) AnnounceSuccesfulSpec(spec *types.SpecSummary) {
stenographer.registerCall("AnnounceSuccesfulSpec", spec)
func (stenographer *FakeStenographer) AnnounceSuccessfulSpec(spec *types.SpecSummary) {
stenographer.registerCall("AnnounceSuccessfulSpec", spec)
}
func (stenographer *FakeStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool) {
stenographer.registerCall("AnnounceSuccesfulSlowSpec", spec, succinct)
func (stenographer *FakeStenographer) AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool) {
stenographer.registerCall("AnnounceSuccessfulSlowSpec", spec, succinct)
}
func (stenographer *FakeStenographer) AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool) {
stenographer.registerCall("AnnounceSuccesfulMeasurement", spec, succinct)
func (stenographer *FakeStenographer) AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool) {
stenographer.registerCall("AnnounceSuccessfulMeasurement", spec, succinct)
}
func (stenographer *FakeStenographer) AnnouncePendingSpec(spec *types.SpecSummary, noisy bool) {

View File

@ -8,6 +8,7 @@ package stenographer
import (
"fmt"
"io"
"runtime"
"strings"
@ -35,7 +36,8 @@ const (
type Stenographer interface {
AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool)
AnnounceAggregatedParallelRun(nodes int, succinct bool)
AnnounceParallelRun(node int, nodes int, specsToRun int, totalSpecs int, succinct bool)
AnnounceParallelRun(node int, nodes int, succinct bool)
AnnounceTotalNumberOfSpecs(total int, succinct bool)
AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool)
AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool)
@ -45,9 +47,9 @@ type Stenographer interface {
AnnounceCapturedOutput(output string)
AnnounceSuccesfulSpec(spec *types.SpecSummary)
AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool)
AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool)
AnnounceSuccessfulSpec(spec *types.SpecSummary)
AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool)
AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool)
AnnouncePendingSpec(spec *types.SpecSummary, noisy bool)
AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool)
@ -59,22 +61,26 @@ type Stenographer interface {
SummarizeFailures(summaries []*types.SpecSummary)
}
func New(color bool) Stenographer {
func New(color bool, enableFlakes bool, writer io.Writer) Stenographer {
denoter := "•"
if runtime.GOOS == "windows" {
denoter = "+"
}
return &consoleStenographer{
color: color,
denoter: denoter,
cursorState: cursorStateTop,
color: color,
denoter: denoter,
cursorState: cursorStateTop,
enableFlakes: enableFlakes,
w: writer,
}
}
type consoleStenographer struct {
color bool
denoter string
cursorState cursorStateType
color bool
denoter string
cursorState cursorStateType
enableFlakes bool
w io.Writer
}
var alternatingColors = []string{defaultStyle, grayColor}
@ -92,17 +98,15 @@ func (s *consoleStenographer) AnnounceSuite(description string, randomSeed int64
s.printNewLine()
}
func (s *consoleStenographer) AnnounceParallelRun(node int, nodes int, specsToRun int, totalSpecs int, succinct bool) {
func (s *consoleStenographer) AnnounceParallelRun(node int, nodes int, succinct bool) {
if succinct {
s.print(0, "- node #%d ", node)
return
}
s.println(0,
"Parallel test node %s/%s. Assigned %s of %s specs.",
"Parallel test node %s/%s.",
s.colorize(boldStyle, "%d", node),
s.colorize(boldStyle, "%d", nodes),
s.colorize(boldStyle, "%d", specsToRun),
s.colorize(boldStyle, "%d", totalSpecs),
)
s.printNewLine()
}
@ -134,6 +138,20 @@ func (s *consoleStenographer) AnnounceNumberOfSpecs(specsToRun int, total int, s
s.printNewLine()
}
func (s *consoleStenographer) AnnounceTotalNumberOfSpecs(total int, succinct bool) {
if succinct {
s.print(0, "- %d specs ", total)
s.stream()
return
}
s.println(0,
"Will run %s specs",
s.colorize(boldStyle, "%d", total),
)
s.printNewLine()
}
func (s *consoleStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool) {
if succinct && summary.SuiteSucceeded {
s.print(0, " %s %s ", s.colorize(greenColor, "SUCCESS!"), summary.RunTime)
@ -153,11 +171,16 @@ func (s *consoleStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSumm
status = s.colorize(boldStyle+redColor, "FAIL!")
}
flakes := ""
if s.enableFlakes {
flakes = " | " + s.colorize(yellowColor+boldStyle, "%d Flaked", summary.NumberOfFlakedSpecs)
}
s.print(0,
"%s -- %s | %s | %s | %s ",
"%s -- %s | %s | %s | %s\n",
status,
s.colorize(greenColor+boldStyle, "%d Passed", summary.NumberOfPassedSpecs),
s.colorize(redColor+boldStyle, "%d Failed", summary.NumberOfFailedSpecs),
s.colorize(redColor+boldStyle, "%d Failed", summary.NumberOfFailedSpecs)+flakes,
s.colorize(yellowColor+boldStyle, "%d Pending", summary.NumberOfPendingSpecs),
s.colorize(cyanColor+boldStyle, "%d Skipped", summary.NumberOfSkippedSpecs),
)
@ -222,12 +245,12 @@ func (s *consoleStenographer) AnnounceCapturedOutput(output string) {
s.midBlock()
}
func (s *consoleStenographer) AnnounceSuccesfulSpec(spec *types.SpecSummary) {
func (s *consoleStenographer) AnnounceSuccessfulSpec(spec *types.SpecSummary) {
s.print(0, s.colorize(greenColor, s.denoter))
s.stream()
}
func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool) {
func (s *consoleStenographer) AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool) {
s.printBlockWithMessage(
s.colorize(greenColor, "%s [SLOW TEST:%.3f seconds]", s.denoter, spec.RunTime.Seconds()),
"",
@ -236,7 +259,7 @@ func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary,
)
}
func (s *consoleStenographer) AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool) {
func (s *consoleStenographer) AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool) {
s.printBlockWithMessage(
s.colorize(greenColor, "%s [MEASUREMENT]", s.denoter),
s.measurementReport(spec, succinct),
@ -506,15 +529,15 @@ func (s *consoleStenographer) measurementReport(spec *types.SpecSummary, succinc
message = append(message, fmt.Sprintf(" %s - %s: %s%s, %s: %s%s ± %s%s, %s: %s%s",
s.colorize(boldStyle, "%s", measurement.Name),
measurement.SmallestLabel,
s.colorize(greenColor, "%.3f", measurement.Smallest),
s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest),
measurement.Units,
measurement.AverageLabel,
s.colorize(cyanColor, "%.3f", measurement.Average),
s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average),
measurement.Units,
s.colorize(cyanColor, "%.3f", measurement.StdDeviation),
s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation),
measurement.Units,
measurement.LargestLabel,
s.colorize(redColor, "%.3f", measurement.Largest),
s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest),
measurement.Units,
))
}
@ -531,15 +554,15 @@ func (s *consoleStenographer) measurementReport(spec *types.SpecSummary, succinc
s.colorize(boldStyle, "%s", measurement.Name),
info,
measurement.SmallestLabel,
s.colorize(greenColor, "%.3f", measurement.Smallest),
s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest),
measurement.Units,
measurement.LargestLabel,
s.colorize(redColor, "%.3f", measurement.Largest),
s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest),
measurement.Units,
measurement.AverageLabel,
s.colorize(cyanColor, "%.3f", measurement.Average),
s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average),
measurement.Units,
s.colorize(cyanColor, "%.3f", measurement.StdDeviation),
s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation),
measurement.Units,
))
}

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,43 @@
# go-colorable
Colorable writer for windows.
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
This package is possible to handle escape sequence for ansi color on windows.
## Too Bad!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png)
## So Good!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png)
## Usage
```go
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
logrus.SetOutput(colorable.NewColorableStdout())
logrus.Info("succeeded")
logrus.Warn("not correct")
logrus.Error("something error")
logrus.Fatal("panic")
```
You can compile above code on non-windows OSs.
## Installation
```
$ go get github.com/mattn/go-colorable
```
# License
MIT
# Author
Yasuhiro Matsumoto (a.k.a mattn)

View File

@ -0,0 +1,24 @@
// +build !windows
package colorable
import (
"io"
"os"
)
func NewColorable(file *os.File) io.Writer {
if file == nil {
panic("nil passed instead of *os.File to NewColorable()")
}
return file
}
func NewColorableStdout() io.Writer {
return os.Stdout
}
func NewColorableStderr() io.Writer {
return os.Stderr
}

View File

@ -0,0 +1,783 @@
package colorable
import (
"bytes"
"fmt"
"io"
"math"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty"
)
const (
foregroundBlue = 0x1
foregroundGreen = 0x2
foregroundRed = 0x4
foregroundIntensity = 0x8
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
backgroundBlue = 0x10
backgroundGreen = 0x20
backgroundRed = 0x40
backgroundIntensity = 0x80
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
)
type wchar uint16
type short int16
type dword uint32
type word uint16
type coord struct {
x short
y short
}
type smallRect struct {
left short
top short
right short
bottom short
}
type consoleScreenBufferInfo struct {
size coord
cursorPosition coord
attributes word
window smallRect
maximumWindowSize coord
}
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
)
type Writer struct {
out io.Writer
handle syscall.Handle
lastbuf bytes.Buffer
oldattr word
}
func NewColorable(file *os.File) io.Writer {
if file == nil {
panic("nil passed instead of *os.File to NewColorable()")
}
if isatty.IsTerminal(file.Fd()) {
var csbi consoleScreenBufferInfo
handle := syscall.Handle(file.Fd())
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
return &Writer{out: file, handle: handle, oldattr: csbi.attributes}
} else {
return file
}
}
func NewColorableStdout() io.Writer {
return NewColorable(os.Stdout)
}
func NewColorableStderr() io.Writer {
return NewColorable(os.Stderr)
}
var color256 = map[int]int{
0: 0x000000,
1: 0x800000,
2: 0x008000,
3: 0x808000,
4: 0x000080,
5: 0x800080,
6: 0x008080,
7: 0xc0c0c0,
8: 0x808080,
9: 0xff0000,
10: 0x00ff00,
11: 0xffff00,
12: 0x0000ff,
13: 0xff00ff,
14: 0x00ffff,
15: 0xffffff,
16: 0x000000,
17: 0x00005f,
18: 0x000087,
19: 0x0000af,
20: 0x0000d7,
21: 0x0000ff,
22: 0x005f00,
23: 0x005f5f,
24: 0x005f87,
25: 0x005faf,
26: 0x005fd7,
27: 0x005fff,
28: 0x008700,
29: 0x00875f,
30: 0x008787,
31: 0x0087af,
32: 0x0087d7,
33: 0x0087ff,
34: 0x00af00,
35: 0x00af5f,
36: 0x00af87,
37: 0x00afaf,
38: 0x00afd7,
39: 0x00afff,
40: 0x00d700,
41: 0x00d75f,
42: 0x00d787,
43: 0x00d7af,
44: 0x00d7d7,
45: 0x00d7ff,
46: 0x00ff00,
47: 0x00ff5f,
48: 0x00ff87,
49: 0x00ffaf,
50: 0x00ffd7,
51: 0x00ffff,
52: 0x5f0000,
53: 0x5f005f,
54: 0x5f0087,
55: 0x5f00af,
56: 0x5f00d7,
57: 0x5f00ff,
58: 0x5f5f00,
59: 0x5f5f5f,
60: 0x5f5f87,
61: 0x5f5faf,
62: 0x5f5fd7,
63: 0x5f5fff,
64: 0x5f8700,
65: 0x5f875f,
66: 0x5f8787,
67: 0x5f87af,
68: 0x5f87d7,
69: 0x5f87ff,
70: 0x5faf00,
71: 0x5faf5f,
72: 0x5faf87,
73: 0x5fafaf,
74: 0x5fafd7,
75: 0x5fafff,
76: 0x5fd700,
77: 0x5fd75f,
78: 0x5fd787,
79: 0x5fd7af,
80: 0x5fd7d7,
81: 0x5fd7ff,
82: 0x5fff00,
83: 0x5fff5f,
84: 0x5fff87,
85: 0x5fffaf,
86: 0x5fffd7,
87: 0x5fffff,
88: 0x870000,
89: 0x87005f,
90: 0x870087,
91: 0x8700af,
92: 0x8700d7,
93: 0x8700ff,
94: 0x875f00,
95: 0x875f5f,
96: 0x875f87,
97: 0x875faf,
98: 0x875fd7,
99: 0x875fff,
100: 0x878700,
101: 0x87875f,
102: 0x878787,
103: 0x8787af,
104: 0x8787d7,
105: 0x8787ff,
106: 0x87af00,
107: 0x87af5f,
108: 0x87af87,
109: 0x87afaf,
110: 0x87afd7,
111: 0x87afff,
112: 0x87d700,
113: 0x87d75f,
114: 0x87d787,
115: 0x87d7af,
116: 0x87d7d7,
117: 0x87d7ff,
118: 0x87ff00,
119: 0x87ff5f,
120: 0x87ff87,
121: 0x87ffaf,
122: 0x87ffd7,
123: 0x87ffff,
124: 0xaf0000,
125: 0xaf005f,
126: 0xaf0087,
127: 0xaf00af,
128: 0xaf00d7,
129: 0xaf00ff,
130: 0xaf5f00,
131: 0xaf5f5f,
132: 0xaf5f87,
133: 0xaf5faf,
134: 0xaf5fd7,
135: 0xaf5fff,
136: 0xaf8700,
137: 0xaf875f,
138: 0xaf8787,
139: 0xaf87af,
140: 0xaf87d7,
141: 0xaf87ff,
142: 0xafaf00,
143: 0xafaf5f,
144: 0xafaf87,
145: 0xafafaf,
146: 0xafafd7,
147: 0xafafff,
148: 0xafd700,
149: 0xafd75f,
150: 0xafd787,
151: 0xafd7af,
152: 0xafd7d7,
153: 0xafd7ff,
154: 0xafff00,
155: 0xafff5f,
156: 0xafff87,
157: 0xafffaf,
158: 0xafffd7,
159: 0xafffff,
160: 0xd70000,
161: 0xd7005f,
162: 0xd70087,
163: 0xd700af,
164: 0xd700d7,
165: 0xd700ff,
166: 0xd75f00,
167: 0xd75f5f,
168: 0xd75f87,
169: 0xd75faf,
170: 0xd75fd7,
171: 0xd75fff,
172: 0xd78700,
173: 0xd7875f,
174: 0xd78787,
175: 0xd787af,
176: 0xd787d7,
177: 0xd787ff,
178: 0xd7af00,
179: 0xd7af5f,
180: 0xd7af87,
181: 0xd7afaf,
182: 0xd7afd7,
183: 0xd7afff,
184: 0xd7d700,
185: 0xd7d75f,
186: 0xd7d787,
187: 0xd7d7af,
188: 0xd7d7d7,
189: 0xd7d7ff,
190: 0xd7ff00,
191: 0xd7ff5f,
192: 0xd7ff87,
193: 0xd7ffaf,
194: 0xd7ffd7,
195: 0xd7ffff,
196: 0xff0000,
197: 0xff005f,
198: 0xff0087,
199: 0xff00af,
200: 0xff00d7,
201: 0xff00ff,
202: 0xff5f00,
203: 0xff5f5f,
204: 0xff5f87,
205: 0xff5faf,
206: 0xff5fd7,
207: 0xff5fff,
208: 0xff8700,
209: 0xff875f,
210: 0xff8787,
211: 0xff87af,
212: 0xff87d7,
213: 0xff87ff,
214: 0xffaf00,
215: 0xffaf5f,
216: 0xffaf87,
217: 0xffafaf,
218: 0xffafd7,
219: 0xffafff,
220: 0xffd700,
221: 0xffd75f,
222: 0xffd787,
223: 0xffd7af,
224: 0xffd7d7,
225: 0xffd7ff,
226: 0xffff00,
227: 0xffff5f,
228: 0xffff87,
229: 0xffffaf,
230: 0xffffd7,
231: 0xffffff,
232: 0x080808,
233: 0x121212,
234: 0x1c1c1c,
235: 0x262626,
236: 0x303030,
237: 0x3a3a3a,
238: 0x444444,
239: 0x4e4e4e,
240: 0x585858,
241: 0x626262,
242: 0x6c6c6c,
243: 0x767676,
244: 0x808080,
245: 0x8a8a8a,
246: 0x949494,
247: 0x9e9e9e,
248: 0xa8a8a8,
249: 0xb2b2b2,
250: 0xbcbcbc,
251: 0xc6c6c6,
252: 0xd0d0d0,
253: 0xdadada,
254: 0xe4e4e4,
255: 0xeeeeee,
}
func (w *Writer) Write(data []byte) (n int, err error) {
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
er := bytes.NewBuffer(data)
loop:
for {
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
if r1 == 0 {
break loop
}
c1, _, err := er.ReadRune()
if err != nil {
break loop
}
if c1 != 0x1b {
fmt.Fprint(w.out, string(c1))
continue
}
c2, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
break loop
}
if c2 != 0x5b {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
continue
}
var buf bytes.Buffer
var m rune
for {
c, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
w.lastbuf.Write(buf.Bytes())
break loop
}
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
m = c
break
}
buf.Write([]byte(string(c)))
}
var csbi consoleScreenBufferInfo
switch m {
case 'A':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.y -= short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'B':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.y += short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'C':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x -= short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'D':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
if n, err = strconv.Atoi(buf.String()); err == nil {
var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x += short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
}
case 'E':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x = 0
csbi.cursorPosition.y += short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'F':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x = 0
csbi.cursorPosition.y -= short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'G':
n, err = strconv.Atoi(buf.String())
if err != nil {
continue
}
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
csbi.cursorPosition.x = short(n)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'H':
token := strings.Split(buf.String(), ";")
if len(token) != 2 {
continue
}
n1, err := strconv.Atoi(token[0])
if err != nil {
continue
}
n2, err := strconv.Atoi(token[1])
if err != nil {
continue
}
csbi.cursorPosition.x = short(n2)
csbi.cursorPosition.x = short(n1)
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
case 'J':
n, err := strconv.Atoi(buf.String())
if err != nil {
continue
}
var cursor coord
switch n {
case 0:
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
case 1:
cursor = coord{x: csbi.window.left, y: csbi.window.top}
case 2:
cursor = coord{x: csbi.window.left, y: csbi.window.top}
}
var count, written dword
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
case 'K':
n, err := strconv.Atoi(buf.String())
if err != nil {
continue
}
var cursor coord
switch n {
case 0:
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
case 1:
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
case 2:
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
}
var count, written dword
count = dword(csbi.size.x - csbi.cursorPosition.x)
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
case 'm':
attr := csbi.attributes
cs := buf.String()
if cs == "" {
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
continue
}
token := strings.Split(cs, ";")
for i := 0; i < len(token); i += 1 {
ns := token[i]
if n, err = strconv.Atoi(ns); err == nil {
switch {
case n == 0 || n == 100:
attr = w.oldattr
case 1 <= n && n <= 5:
attr |= foregroundIntensity
case n == 7:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
case 22 == n || n == 25 || n == 25:
attr |= foregroundIntensity
case n == 27:
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
case 30 <= n && n <= 37:
attr = (attr & backgroundMask)
if (n-30)&1 != 0 {
attr |= foregroundRed
}
if (n-30)&2 != 0 {
attr |= foregroundGreen
}
if (n-30)&4 != 0 {
attr |= foregroundBlue
}
case n == 38: // set foreground color.
if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
if n256, err := strconv.Atoi(token[i+2]); err == nil {
if n256foreAttr == nil {
n256setup()
}
attr &= backgroundMask
attr |= n256foreAttr[n256]
i += 2
}
} else {
attr = attr & (w.oldattr & backgroundMask)
}
case n == 39: // reset foreground color.
attr &= backgroundMask
attr |= w.oldattr & foregroundMask
case 40 <= n && n <= 47:
attr = (attr & foregroundMask)
if (n-40)&1 != 0 {
attr |= backgroundRed
}
if (n-40)&2 != 0 {
attr |= backgroundGreen
}
if (n-40)&4 != 0 {
attr |= backgroundBlue
}
case n == 48: // set background color.
if i < len(token)-2 && token[i+1] == "5" {
if n256, err := strconv.Atoi(token[i+2]); err == nil {
if n256backAttr == nil {
n256setup()
}
attr &= foregroundMask
attr |= n256backAttr[n256]
i += 2
}
} else {
attr = attr & (w.oldattr & foregroundMask)
}
case n == 49: // reset foreground color.
attr &= foregroundMask
attr |= w.oldattr & backgroundMask
case 90 <= n && n <= 97:
attr = (attr & backgroundMask)
attr |= foregroundIntensity
if (n-90)&1 != 0 {
attr |= foregroundRed
}
if (n-90)&2 != 0 {
attr |= foregroundGreen
}
if (n-90)&4 != 0 {
attr |= foregroundBlue
}
case 100 <= n && n <= 107:
attr = (attr & foregroundMask)
attr |= backgroundIntensity
if (n-100)&1 != 0 {
attr |= backgroundRed
}
if (n-100)&2 != 0 {
attr |= backgroundGreen
}
if (n-100)&4 != 0 {
attr |= backgroundBlue
}
}
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
}
}
}
}
return len(data) - w.lastbuf.Len(), nil
}
type consoleColor struct {
rgb int
red bool
green bool
blue bool
intensity bool
}
func (c consoleColor) foregroundAttr() (attr word) {
if c.red {
attr |= foregroundRed
}
if c.green {
attr |= foregroundGreen
}
if c.blue {
attr |= foregroundBlue
}
if c.intensity {
attr |= foregroundIntensity
}
return
}
func (c consoleColor) backgroundAttr() (attr word) {
if c.red {
attr |= backgroundRed
}
if c.green {
attr |= backgroundGreen
}
if c.blue {
attr |= backgroundBlue
}
if c.intensity {
attr |= backgroundIntensity
}
return
}
var color16 = []consoleColor{
consoleColor{0x000000, false, false, false, false},
consoleColor{0x000080, false, false, true, false},
consoleColor{0x008000, false, true, false, false},
consoleColor{0x008080, false, true, true, false},
consoleColor{0x800000, true, false, false, false},
consoleColor{0x800080, true, false, true, false},
consoleColor{0x808000, true, true, false, false},
consoleColor{0xc0c0c0, true, true, true, false},
consoleColor{0x808080, false, false, false, true},
consoleColor{0x0000ff, false, false, true, true},
consoleColor{0x00ff00, false, true, false, true},
consoleColor{0x00ffff, false, true, true, true},
consoleColor{0xff0000, true, false, false, true},
consoleColor{0xff00ff, true, false, true, true},
consoleColor{0xffff00, true, true, false, true},
consoleColor{0xffffff, true, true, true, true},
}
type hsv struct {
h, s, v float32
}
func (a hsv) dist(b hsv) float32 {
dh := a.h - b.h
switch {
case dh > 0.5:
dh = 1 - dh
case dh < -0.5:
dh = -1 - dh
}
ds := a.s - b.s
dv := a.v - b.v
return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
}
func toHSV(rgb int) hsv {
r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
float32((rgb&0x00FF00)>>8)/256.0,
float32(rgb&0x0000FF)/256.0
min, max := minmax3f(r, g, b)
h := max - min
if h > 0 {
if max == r {
h = (g - b) / h
if h < 0 {
h += 6
}
} else if max == g {
h = 2 + (b-r)/h
} else {
h = 4 + (r-g)/h
}
}
h /= 6.0
s := max - min
if max != 0 {
s /= max
}
v := max
return hsv{h: h, s: s, v: v}
}
type hsvTable []hsv
func toHSVTable(rgbTable []consoleColor) hsvTable {
t := make(hsvTable, len(rgbTable))
for i, c := range rgbTable {
t[i] = toHSV(c.rgb)
}
return t
}
func (t hsvTable) find(rgb int) consoleColor {
hsv := toHSV(rgb)
n := 7
l := float32(5.0)
for i, p := range t {
d := hsv.dist(p)
if d < l {
l, n = d, i
}
}
return color16[n]
}
func minmax3f(a, b, c float32) (min, max float32) {
if a < b {
if b < c {
return a, c
} else if a < c {
return a, b
} else {
return c, b
}
} else {
if a < c {
return b, c
} else if b < c {
return b, a
} else {
return c, a
}
}
}
var n256foreAttr []word
var n256backAttr []word
func n256setup() {
n256foreAttr = make([]word, 256)
n256backAttr = make([]word, 256)
t := toHSVTable(color16)
for i, rgb := range color256 {
c := t.find(rgb)
n256foreAttr[i] = c.foregroundAttr()
n256backAttr[i] = c.backgroundAttr()
}
}

View File

@ -0,0 +1,57 @@
package colorable
import (
"bytes"
"fmt"
"io"
)
type NonColorable struct {
out io.Writer
lastbuf bytes.Buffer
}
func NewNonColorable(w io.Writer) io.Writer {
return &NonColorable{out: w}
}
func (w *NonColorable) Write(data []byte) (n int, err error) {
er := bytes.NewBuffer(data)
loop:
for {
c1, _, err := er.ReadRune()
if err != nil {
break loop
}
if c1 != 0x1b {
fmt.Fprint(w.out, string(c1))
continue
}
c2, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
break loop
}
if c2 != 0x5b {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
continue
}
var buf bytes.Buffer
for {
c, _, err := er.ReadRune()
if err != nil {
w.lastbuf.WriteRune(c1)
w.lastbuf.WriteRune(c2)
w.lastbuf.Write(buf.Bytes())
break loop
}
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
break
}
buf.Write([]byte(string(c)))
}
}
return len(data) - w.lastbuf.Len(), nil
}

View File

@ -0,0 +1,9 @@
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
MIT License (Expat)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,37 @@
# go-isatty
isatty for golang
## Usage
```go
package main
import (
"fmt"
"github.com/mattn/go-isatty"
"os"
)
func main() {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println("Is Terminal")
} else {
fmt.Println("Is Not Terminal")
}
}
```
## Installation
```
$ go get github.com/mattn/go-isatty
```
# License
MIT
# Author
Yasuhiro Matsumoto (a.k.a mattn)

View File

@ -0,0 +1,2 @@
// Package isatty implements interface to isatty
package isatty

View File

@ -0,0 +1,9 @@
// +build appengine
package isatty
// IsTerminal returns true if the file descriptor is terminal which
// is always false on on appengine classic which is a sandboxed PaaS.
func IsTerminal(fd uintptr) bool {
return false
}

View File

@ -0,0 +1,18 @@
// +build darwin freebsd openbsd netbsd
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TIOCGETA
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View File

@ -0,0 +1,18 @@
// +build linux
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View File

@ -0,0 +1,16 @@
// +build solaris
// +build !appengine
package isatty
import (
"golang.org/x/sys/unix"
)
// IsTerminal returns true if the given file descriptor is a terminal.
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
func IsTerminal(fd uintptr) bool {
var termio unix.Termio
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
return err == nil
}

View File

@ -0,0 +1,19 @@
// +build windows
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}

View File

@ -10,10 +10,11 @@ package reporters
import (
"fmt"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/types"
"io"
"strings"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/types"
)
const (
@ -21,8 +22,9 @@ const (
)
type TeamCityReporter struct {
writer io.Writer
testSuiteName string
writer io.Writer
testSuiteName string
ReporterConfig config.DefaultReporterConfigType
}
func NewTeamCityReporter(writer io.Writer) *TeamCityReporter {
@ -33,7 +35,7 @@ func NewTeamCityReporter(writer io.Writer) *TeamCityReporter {
func (reporter *TeamCityReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) {
reporter.testSuiteName = escape(summary.SuiteDescription)
fmt.Fprintf(reporter.writer, "%s[testSuiteStarted name='%s']", messageId, reporter.testSuiteName)
fmt.Fprintf(reporter.writer, "%s[testSuiteStarted name='%s']\n", messageId, reporter.testSuiteName)
}
func (reporter *TeamCityReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {
@ -47,38 +49,50 @@ func (reporter *TeamCityReporter) AfterSuiteDidRun(setupSummary *types.SetupSumm
func (reporter *TeamCityReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) {
if setupSummary.State != types.SpecStatePassed {
testName := escape(name)
fmt.Fprintf(reporter.writer, "%s[testStarted name='%s']", messageId, testName)
message := escape(setupSummary.Failure.ComponentCodeLocation.String())
details := escape(setupSummary.Failure.Message)
fmt.Fprintf(reporter.writer, "%s[testFailed name='%s' message='%s' details='%s']", messageId, testName, message, details)
fmt.Fprintf(reporter.writer, "%s[testStarted name='%s']\n", messageId, testName)
message := reporter.failureMessage(setupSummary.Failure)
details := reporter.failureDetails(setupSummary.Failure)
fmt.Fprintf(reporter.writer, "%s[testFailed name='%s' message='%s' details='%s']\n", messageId, testName, message, details)
durationInMilliseconds := setupSummary.RunTime.Seconds() * 1000
fmt.Fprintf(reporter.writer, "%s[testFinished name='%s' duration='%v']", messageId, testName, durationInMilliseconds)
fmt.Fprintf(reporter.writer, "%s[testFinished name='%s' duration='%v']\n", messageId, testName, durationInMilliseconds)
}
}
func (reporter *TeamCityReporter) SpecWillRun(specSummary *types.SpecSummary) {
testName := escape(strings.Join(specSummary.ComponentTexts[1:], " "))
fmt.Fprintf(reporter.writer, "%s[testStarted name='%s']", messageId, testName)
fmt.Fprintf(reporter.writer, "%s[testStarted name='%s']\n", messageId, testName)
}
func (reporter *TeamCityReporter) SpecDidComplete(specSummary *types.SpecSummary) {
testName := escape(strings.Join(specSummary.ComponentTexts[1:], " "))
if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed {
details := escape(specSummary.CapturedOutput)
fmt.Fprintf(reporter.writer, "%s[testPassed name='%s' details='%s']\n", messageId, testName, details)
}
if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked {
message := escape(specSummary.Failure.ComponentCodeLocation.String())
details := escape(specSummary.Failure.Message)
fmt.Fprintf(reporter.writer, "%s[testFailed name='%s' message='%s' details='%s']", messageId, testName, message, details)
message := reporter.failureMessage(specSummary.Failure)
details := reporter.failureDetails(specSummary.Failure)
fmt.Fprintf(reporter.writer, "%s[testFailed name='%s' message='%s' details='%s']\n", messageId, testName, message, details)
}
if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending {
fmt.Fprintf(reporter.writer, "%s[testIgnored name='%s']", messageId, testName)
fmt.Fprintf(reporter.writer, "%s[testIgnored name='%s']\n", messageId, testName)
}
durationInMilliseconds := specSummary.RunTime.Seconds() * 1000
fmt.Fprintf(reporter.writer, "%s[testFinished name='%s' duration='%v']", messageId, testName, durationInMilliseconds)
fmt.Fprintf(reporter.writer, "%s[testFinished name='%s' duration='%v']\n", messageId, testName, durationInMilliseconds)
}
func (reporter *TeamCityReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) {
fmt.Fprintf(reporter.writer, "%s[testSuiteFinished name='%s']", messageId, reporter.testSuiteName)
fmt.Fprintf(reporter.writer, "%s[testSuiteFinished name='%s']\n", messageId, reporter.testSuiteName)
}
func (reporter *TeamCityReporter) failureMessage(failure types.SpecFailure) string {
return escape(failure.ComponentCodeLocation.String())
}
func (reporter *TeamCityReporter) failureDetails(failure types.SpecFailure) string {
return escape(fmt.Sprintf("%s\n%s", failure.Message, failure.Location.String()))
}
func escape(output string) string {

View File

@ -1,9 +1,25 @@
package types
import "time"
import (
"strconv"
"time"
)
const GINKGO_FOCUS_EXIT_CODE = 197
/*
SuiteSummary represents the a summary of the test suite and is passed to both
Reporter.SpecSuiteWillBegin
Reporter.SpecSuiteDidEnd
this is unfortunate as these two methods should receive different objects. When running in parallel
each node does not deterministically know how many specs it will end up running.
Unfortunately making such a change would break backward compatibility.
Until Ginkgo 2.0 comes out we will continue to reuse this struct but populate unknown fields
with -1.
*/
type SuiteSummary struct {
SuiteDescription string
SuiteSucceeded bool
@ -16,7 +32,10 @@ type SuiteSummary struct {
NumberOfSkippedSpecs int
NumberOfPassedSpecs int
NumberOfFailedSpecs int
RunTime time.Duration
// Flaked specs are those that failed initially, but then passed on a
// subsequent try.
NumberOfFlakedSpecs int
RunTime time.Duration
}
type SpecSummary struct {
@ -100,6 +119,17 @@ type SpecMeasurement struct {
LargestLabel string
AverageLabel string
Units string
Precision int
}
func (s SpecMeasurement) PrecisionFmt() string {
if s.Precision == 0 {
return "%f"
}
str := strconv.Itoa(s.Precision)
return "%." + str + "f"
}
type SpecState uint
@ -129,6 +159,7 @@ const (
SpecComponentTypeAfterSuite
SpecComponentTypeBeforeEach
SpecComponentTypeJustBeforeEach
SpecComponentTypeJustAfterEach
SpecComponentTypeAfterEach
SpecComponentTypeIt
SpecComponentTypeMeasure