pax_global_header 0000666 0000000 0000000 00000000064 14274752762 0014532 g ustar 00root root 0000000 0000000 52 comment=f8631511001c69a5d2fdae986f87405b9152a68d
gotestsum-1.8.2/ 0000775 0000000 0000000 00000000000 14274752762 0013574 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/.circleci/ 0000775 0000000 0000000 00000000000 14274752762 0015427 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/.circleci/config.yml 0000664 0000000 0000000 00000010151 14274752762 0017415 0 ustar 00root root 0000000 0000000 version: 2.1
orbs:
go: gotest/tools@0.0.14
workflows:
ci:
jobs:
- go/test:
name: test-go-1.16
gotestsum-format: testname
executor:
name: go/golang
tag: 1.16-alpine
- go/test:
name: test-go-1.17
gotestsum-format: testname
executor:
name: go/golang
tag: 1.17-alpine
- go/test:
name: test-go-1.18
gotestsum-format: testname
executor:
name: go/golang
tag: 1.18-alpine
- go/test:
name: test-go-1.19
gotestsum-format: testname
executor:
name: go/golang
tag: 1.19-alpine
- go/test:
name: test-windows
executor: windows
pre-steps:
- run: |
git config --global core.autocrlf false
git config --global core.symlinks true
- run: |
choco upgrade golang
echo 'export PATH="$PATH:/c/Program Files/Go/bin"' > $BASH_ENV
- run: go version
- lint
- build
- run
- update-windows-golden:
filters:
branches: {ignore: '/.*/'}
- build:
name: release
publish: true
filters:
tags: {only: '/v[0-9]+(\.[0-9]+)*/'}
branches: {ignore: '/.*/'}
executors:
windows:
machine:
image: windows-server-2019-vs2019:stable
resource_class: windows.medium
shell: bash.exe
commands:
install-goreleaser:
description: Install goreleaser
steps:
- run:
name: Install goreleaser
command: |
wget https://github.com/goreleaser/goreleaser/releases/download/v1.7.0/goreleaser_Linux_x86_64.tar.gz
echo "e74934e7571991522324642ac7b032310f04baf192ce2a54db1dc323b97bcd7d goreleaser_Linux_x86_64.tar.gz" > checksum.txt
sha256sum -c checksum.txt
tar -xf goreleaser_Linux_x86_64.tar.gz
mkdir -p ./bin
mv goreleaser ./bin
jobs:
build:
parameters:
publish:
type: boolean
default: false
executor:
name: go/golang
tag: 1.19-alpine
steps:
- go/install: {package: git}
- go/install-ssh
- checkout
- go/mod-download
- go/mod-tidy-check
- install-goreleaser
- unless:
condition: << parameters.publish >>
steps:
run:
name: build binaries
command: bin/goreleaser --rm-dist --snapshot --config .project/goreleaser.yml
- when:
condition: << parameters.publish >>
steps:
run:
name: build and publish binaries
command: bin/goreleaser --rm-dist --skip-validate --config .project/goreleaser.yml
- store_artifacts:
path: ./dist
destination: dist
run:
executor: go/golang
steps:
- go/install: {package: git}
- go/install-ssh
- checkout
- go/mod-download
- run: |
mkdir -p dist
go build -o dist/gotestsum .
- run: dist/gotestsum
lint:
executor:
name: go/golang
tag: 1.19-alpine
steps:
- checkout
- run: go mod download
- run:
name: Install golangci-lint
command: |
mkdir -p /go/bin
download=https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh
wget -O- -q "$download" | sh -s -- -b /go/bin/ v1.48.0
- run:
name: Lint
command: |
golangci-lint run -v --concurrency 2 --config .project/golangci-lint.yml
update-windows-golden:
executor: windows
steps:
- checkout
- go/install-gotestsum
- run: |
git config --global core.autocrlf false
git config --global core.symlinks true
- run: |
choco upgrade golang
go version
- run: |
/go/bin/gotestsum ./testjson ./internal/junitxml -test.update-golden
- store_artifacts:
path: testjson/testdata/
destination: golden
gotestsum-1.8.2/.gitignore 0000664 0000000 0000000 00000000040 14274752762 0015556 0 ustar 00root root 0000000 0000000 vendor/
dist/
junit.xml
.plsdo/
gotestsum-1.8.2/.project/ 0000775 0000000 0000000 00000000000 14274752762 0015320 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/.project/Dockerfile 0000664 0000000 0000000 00000001111 14274752762 0017304 0 ustar 00root root 0000000 0000000
ARG GOLANG_VERSION
FROM golang:${GOLANG_VERSION:-1.18-alpine} as golang
RUN apk add -U curl git bash
ENV CGO_ENABLED=0 \
PS1="# " \
GO111MODULE=on
ARG UID=1000
RUN adduser --uid=${UID} --disabled-password devuser
USER ${UID}:${UID}
FROM golang as tools
RUN wget -O- -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s && \
mv bin/golangci-lint /go/bin
FROM golang as dev
COPY --from=tools /go/bin/golangci-lint /usr/bin/golangci-lint
FROM dev as dev-with-source
COPY . .
gotestsum-1.8.2/.project/docs/ 0000775 0000000 0000000 00000000000 14274752762 0016250 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/.project/docs/running-without-go.md 0000664 0000000 0000000 00000001757 14274752762 0022370 0 ustar 00root root 0000000 0000000 # Running without Go
`gotestsum` may be run without Go as long as the package to be tested has
already been compiled using `go test -c`, and the `test2json` tool is available.
The `test2json` tool can be compiled from the Go source tree so that it can be distributed to the environment that needs it.
```sh
GOVERSION=1.17.6
OS=$(uname -s | sed 's/.*/\L&/')
mkdir -p gopath
GOPATH=$(realpath gopath)
HOME=$(realpath ./)
curl -L --silent https://go.dev/dl/go${GOVERSION}.${OS}-amd64.tar.gz | tar xz -C ./
env HOME=$HOME GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GOPATH=$GOPATH ./go/bin/go build -o test2json -ldflags="-s -w" cmd/test2json
mv test2json /usr/local/bin/test2json
```
Or if you have Go installed already:
```sh
env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o test2json -ldflags="-s -w" cmd/test2json
mv test2json /usr/local/bin/test2json
```
Example: running without a Go installation
```
export GOVERSION=1.13
gotestsum --raw-command -- test2json -t -p pkgname ./binary.test -test.v
```
gotestsum-1.8.2/.project/golangci-lint.yml 0000664 0000000 0000000 00000002150 14274752762 0020570 0 ustar 00root root 0000000 0000000 linters-settings:
gocyclo:
min-complexity: 12
goconst:
min-len: 2
min-occurrences: 4
lll:
line-length: 120
issues:
exclude-use-default: false
exclude-rules:
- linters: [revive]
text: 'should have comment .*or be unexported'
- linters: [stylecheck]
text: 'ST1000: at least one file in a package should have a package comment'
- linters: [errcheck]
text: 'Error return value of `.*\.WriteString` is not checked'
- linters: [unparam]
text: 'result .* is always'
- linters: [unparam]
text: 'always receives'
# Remove once go1.16 is dropped
- linters: staticcheck
text: 'env.Patch is deprecated'
linters:
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- errcheck
- gocognit
- goconst
- gocyclo
- gofmt
- goimports
- gosimple
- govet
- ineffassign
- interfacer
- lll
- misspell
- nakedret
- prealloc
- revive
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
gotestsum-1.8.2/.project/goreleaser.yml 0000664 0000000 0000000 00000001164 14274752762 0020175 0 ustar 00root root 0000000 0000000
project_name: gotestsum
release:
github:
owner: gotestyourself
name: gotestsum
builds:
- binary: gotestsum
goos:
- darwin
- windows
- linux
goarch:
- amd64
- arm64
- arm
- s390x
- ppc64le
env: [CGO_ENABLED=0]
ldflags: ["-s -w -X gotest.tools/gotestsum/cmd.version={{.Version}}"]
ignore:
- goos: darwin
goarch: s390x
- goos: darwin
goarch: ppc64le
- goos: windows
goarch: s390x
- goos: windows
goarch: ppc64le
checksum:
name_template: '{{ .ProjectName }}-{{ .Version }}-checksums.txt'
gotestsum-1.8.2/LICENSE 0000664 0000000 0000000 00000026136 14274752762 0014611 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
gotestsum-1.8.2/NOTICE 0000664 0000000 0000000 00000001114 14274752762 0014475 0 ustar 00root root 0000000 0000000 Copyright The gotestsum Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
gotestsum-1.8.2/README.md 0000664 0000000 0000000 00000034141 14274752762 0015056 0 ustar 00root root 0000000 0000000 # gotestsum
`gotestsum` runs tests using `go test -json`, prints formatted test output, and a summary of the test run.
It is designed to work well for both local development, and for automation like CI.
## Install
Download a binary from [releases](https://github.com/gotestyourself/gotestsum/releases), or build from
source with `go install gotest.tools/gotestsum@latest`. With `go` version before 1.17, use `go get gotest.tools/gotestsum`.
## Documentation
**Core features**
- [Output Format](#output-format) from compact to verbose, with color highlighting.
- [Summary](#summary) of the test run.
- [Add `go test` flags](#custom-go-test-command), or
[run a compiled test binary](#executing-a-compiled-test-binary).
**CI and Automation**
- [`--junitfile`](#junit-xml-output) - write a JUnit XML file for integration with CI systems.
- [`--jsonfile`](#json-file-output) - write the `test2json` output in a file.
- [`--rerun-fails`](#re-running-failed-tests) - run failed tests again to save time when dealing with flaky test suites.
**Local Development**
- [`--watch`](#run-tests-when-a-file-is-saved) - when a file is saved, run the tests for the package that includes the file.
- [`--post-run-command`](#post-run-command) - run a command after the tests, can be used for desktop notification.
- [`gotestsum tool slowest`](#finding-and-skipping-slow-tests) - find the slowest tests, also update slow tests to be skipepd with `-short`.
### Output Format
The `--format` flag or `GOTESTSUM_FORMAT` environment variable set the format that
is used to print the test names, and possibly test output, as the tests run. Most
outputs use color to highlight pass, fail, or skip.
Commonly used formats (see `--help` for a full list):
* `dots` - print a character for each test.
* `pkgname` (default) - print a line for each package.
* `testname` - print a line for each test and package.
* `standard-quiet` - the standard `go test` format.
* `standard-verbose` - the standard `go test -v` format.
Have an idea for a new format?
Please [share it on github](https://github.com/gotestyourself/gotestsum/issues/new)!
#### Demo
A demonstration of three `--format` options.

[Source](https://github.com/gotestyourself/gotestsum/tree/readme-demo/scripts)
### Summary
Following the formatted output is a summary of the test run. The summary includes:
* The test output, and elapsed time, for any test that fails or is skipped.
* The build errors for any package that fails to build.
* A `DONE` line with a count of tests run, tests skipped, tests failed, package build errors,
and the elapsed time including time to build.
```
DONE 101 tests[, 3 skipped][, 2 failures][, 1 error] in 0.103s
```
To hide parts of the summary use `--hide-summary section`.
**Example: hide skipped tests in the summary**
```
gotestsum --hide-summary=skipped
```
**Example: hide everything except the DONE line**
```
gotestsum --hide-summary=skipped,failed,errors,output
# or
gotestsum --hide-summary=all
```
**Example: hide test output in the summary, only print names of failed and skipped tests
and errors**
```
gotestsum --hide-summary=output
```
### JUnit XML output
When the `--junitfile` flag or `GOTESTSUM_JUNITFILE` environment variable are set
to a file path, `gotestsum` will write a test report, in JUnit XML format, to the file.
This file can be used to integrate with CI systems.
```
gotestsum --junitfile unit-tests.xml
```
If the package names in the `testsuite.name` or `testcase.classname` fields do not
work with your CI system these values can be customized using the
`--junitfile-testsuite-name`, or `--junitfile-testcase-classname` flags. These flags
accept the following values:
* `short` - the base name of the package (the single term specified by the
package statement).
* `relative` - a package path relative to the root of the repository
* `full` - the full package path (default)
Note: If Go is not installed, or the `go` binary is not in `PATH`, the `GOVERSION`
environment variable can be set to remove the "failed to lookup go version for junit xml"
warning.
### JSON file output
When the `--jsonfile` flag or `GOTESTSUM_JSONFILE` environment variable are set
to a file path, `gotestsum` will write a line-delimited JSON file with all the
[test2json](https://golang.org/cmd/test2json/#hdr-Output_Format)
output that was written by `go test -json`. This file can be used to compare test
runs, or find flaky tests.
```
gotestsum --jsonfile test-output.log
```
### Post Run Command
The `--post-run-command` flag may be used to execute a command after the
test run has completed. The binary will be run with the following environment
variables set:
```
GOTESTSUM_FORMAT # gotestsum format (ex: short)
GOTESTSUM_JSONFILE # path to the jsonfile, empty if no file path was given
GOTESTSUM_JUNITFILE # path to the junit.xml file, empty if no file path was given
TESTS_ERRORS # number of errors
TESTS_FAILED # number of failed tests
TESTS_SKIPPED # number of skipped tests
TESTS_TOTAL # number of tests run
```
To get more details about the test run, such as failure messages or the full list of failed
tests, run `gotestsum` with either a `--jsonfile` or `--junitfile` and parse the
file from the post-run-command. The
[gotestsum/testjson](https://pkg.go.dev/gotest.tools/gotestsum/testjson?tab=doc)
package may be used to parse the JSON file output.
**Example: desktop notifications**
First install the example notification command with `go get gotest.tools/gotestsum/contrib/notify`.
The command will be downloaded to `$GOPATH/bin` as `notify`. Note that this
example `notify` command only works on macOS with
[terminal-notifer](https://github.com/julienXX/terminal-notifier) installed.
```
gotestsum --post-run-command notify
```
**Example: command with flags**
Possitional arguments or command line flags can be passed to the `--post-run-command` by
quoting the whole command.
```
gotestsum --post-run-command "notify me --date"
```
### Re-running failed tests
When the `--rerun-fails` flag is set, `gotestsum` will re-run any failed tests.
The tests will be re-run until each passes once, or the number of attempts
exceeds the maximum attempts. Maximum attempts defaults to 2, and can be changed
with `--rerun-fails=n`.
To avoid re-running tests when there are real failures, the re-run will be
skipped when there are too many test failures. By default this value is 10, and
can be changed with `--rerun-fails-max-failures=n`.
Note that using `--rerun-fails` may require the use of other flags, depending on
how you specify args to `go test`:
* when used with `--raw-command` the re-run will pass additional arguments to
the command. The first arg is a `-test.run` flag with a regex that matches the test to re-run,
and second is the name of a go package. These additional args can be passed to `go test`,
or a test binary.
* when used with any `go test` args (anything after `--` on the command line), the list of
packages to test must be specified as a space separated list using the `--packages` arg.
**Example**
```
gotestsum --rerun-fails --packages="./..." -- -count=2
```
* if any of the `go test` args should be passed to the test binary, instead of
`go test` itself, the `-args` flag must be used to separate the two groups of
arguments. `-args` is a special flag that is understood by `go test` to indicate
that any following args should be passed directly to the test binary.
**Example**
```
gotestsum --rerun-fails --packages="./..." -- -count=2 -args -update-golden
```
### Custom `go test` command
By default `gotestsum` runs tests using the command `go test -json ./...`. You
can change the command with positional arguments after a `--`. You can change just the
test directory value (which defaults to `./...`) by setting the `TEST_DIRECTORY`
environment variable.
You can use `--debug` to echo the command before it is run.
**Example: set build tags**
```
gotestsum -- -tags=integration ./...
```
**Example: run tests in a single package**
```
gotestsum -- ./io/http
```
**Example: enable coverage**
```
gotestsum -- -coverprofile=cover.out ./...
```
**Example: run a script instead of `go test`**
```
gotestsum --raw-command -- ./scripts/run_tests.sh
```
Note: when using `--raw-command`, the script must follow a few rules about
stdout and stderr output:
* The stdout produced by the script must only contain the `test2json` output, or
`gotestsum` will fail. If it isn't possible to change the script to avoid
non-JSON output, you can use `--ignore-non-json-output-lines` (added in version 1.7.0)
to ignore non-JSON lines and write them to `gotestsum`'s stderr instead.
* Any stderr produced by the script will be considered an error (this behaviour
is necessary because package build errors are only reported by writting to
stderr, not the `test2json` stdout). Any stderr produced by tests is not
considered an error (it will be in the `test2json` stdout).
**Example: accept intput from stdin**
```
cat out.json | gotestsum --raw-command -- cat
```
**Example: run tests with profiling enabled**
Using a `profile.sh` script like this:
```sh
#!/usr/bin/env bash
set -eu
for pkg in $(go list "$@"); do
dir="$(go list -f '{{ .Dir }}' $pkg)"
go test -json -cpuprofile="$dir/cpu.profile" "$pkg"
done
```
You can run:
```
gotestsum --raw-command ./profile.sh ./...
```
**Example: using `TEST_DIRECTORY`**
```
TEST_DIRECTORY=./io/http gotestsum
```
### Executing a compiled test binary
`gotestsum` supports executing a compiled test binary (created with `go test -c`) by running
it as a custom command.
The `-json` flag is handled by `go test` itself, it is not available when using a
compiled test binary, so `go tool test2json` must be used to get the output
that `gotestsum` expects.
**Example: running `./binary.test`**
```
gotestsum --raw-command -- go tool test2json -t -p pkgname ./binary.test -test.v
```
`pkgname` is the name of the package being tested, it will show up in the test
output. `./binary.test` is the path to the compiled test binary. The `-test.v`
must be included so that `go tool test2json` receives all the output.
To execute a test binary without installing Go, see
[running without go](./.project/docs/running-without-go.md).
### Finding and skipping slow tests
`gotestsum tool slowest` reads [test2json output][testjson],
from a file or stdin, and prints the names and elapsed time of slow tests.
The tests are sorted from slowest to fastest.
`gotestsum tool slowest` can also rewrite the source of tests slower than the
threshold, making it possible to optionally skip them.
The [test2json output][testjson] can be created with `gotestsum --jsonfile` or `go test -json`.
See `gotestsum tool slowest --help`.
**Example: printing a list of tests slower than 500 milliseconds**
```
$ gotestsum --format dots --jsonfile json.log
[.]····↷··↷·
$ gotestsum tool slowest --jsonfile json.log --threshold 500ms
gotest.tools/example TestSomething 1.34s
gotest.tools/example TestSomethingElse 810ms
```
**Example: skipping slow tests with `go test --short`**
Any test slower than 200 milliseconds will be modified to add:
```go
if testing.Short() {
t.Skip("too slow for testing.Short")
}
```
```sh
go test -json -short ./... | gotestsum tool slowest --skip-stmt "testing.Short" --threshold 200ms
```
Use `git diff` to see the file changes.
The next time tests are run using `--short` all the slow tests will be skipped.
[testjson]: https://golang.org/cmd/test2json/
### Run tests when a file is saved
When the `--watch` flag is set, `gotestsum` will watch directories using
[file system notifications](https://pkg.go.dev/github.com/fsnotify/fsnotify).
When a Go file in one of those directories is modified, `gotestsum` will run the
tests for the package which contains the changed file. By default all
directories with at least one file with a `.go` extension, under the current
directory will be watched. Use the `--packages` flag to specify a different list.
If `--watch` is used with a command line that includes the name of one or more
packages as command line arguments (ex: `gotestsum --watch -- ./...` or
`gotestsum --watch -- ./extrapkg`), the
tests in those packages will also be run when any file changes.
While in watch mode, pressing some keys will perform an action:
* `r` will run tests for the previous event.
Added in version 1.6.1.
* `u` will run tests for the previous event, with the `-update` flag added.
Many [golden](https://gotest.tools/v3/golden) packages use this flag to automatically
update expected values of tests.
Added in version 1.8.1.
* `d` will run tests for the previous event using `dlv test`, allowing you to
debug a test failure using [delve]. A breakpoint will automatically be added at
the first line of any tests which failed in the previous run. Additional
breakpoints can be added with [`runtime.Breakpoint`](https://golang.org/pkg/runtime/#Breakpoint)
or by using the delve command prompt.
Added in version 1.6.1.
* `a` will run tests for all packages, by using `./...` as the package selector.
Added in version 1.7.0.
* `l` will scan the directory list again, and if there are any new directories
which contain a file with a `.go` extension, they will be added to the watch
list.
Added in version 1.7.0.
Note that [delve] must be installed in order to use debug (`d`).
[delve]: https://github.com/go-delve/delve
**Example: run tests for a package when any file in that package is saved**
```
gotestsum --watch --format testname
```
## Development
[](https://pkg.go.dev/gotest.tools/gotestsum?tab=subdirectories)
[](https://circleci.com/gh/gotestyourself/gotestsum/tree/main)
[](https://goreportcard.com/report/gotest.tools/gotestsum)
Pull requests and bug reports are welcome! Please open an issue first for any
big changes.
## Thanks
This package is heavily influenced by the [pytest](https://docs.pytest.org) test runner for `python`.
gotestsum-1.8.2/cmd/ 0000775 0000000 0000000 00000000000 14274752762 0014337 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/cmd.go 0000664 0000000 0000000 00000000400 14274752762 0015423 0 ustar 00root root 0000000 0000000 package cmd
// Next splits args into the next positional argument and any remaining args.
func Next(args []string) (string, []string) {
switch len(args) {
case 0:
return "", nil
case 1:
return args[0], nil
default:
return args[0], args[1:]
}
}
gotestsum-1.8.2/cmd/flags.go 0000664 0000000 0000000 00000005027 14274752762 0015766 0 ustar 00root root 0000000 0000000 package cmd
import (
"encoding/csv"
"fmt"
"path"
"strings"
"github.com/dnephin/pflag"
"github.com/google/shlex"
"gotest.tools/gotestsum/internal/junitxml"
"gotest.tools/gotestsum/testjson"
)
type hideSummaryValue struct {
value testjson.Summary
}
func newHideSummaryValue() *hideSummaryValue {
return &hideSummaryValue{value: testjson.SummarizeAll}
}
func readAsCSV(val string) ([]string, error) {
if val == "" {
return nil, nil
}
return csv.NewReader(strings.NewReader(val)).Read()
}
func (s *hideSummaryValue) Set(val string) error {
v, err := readAsCSV(val)
if err != nil {
return err
}
for _, item := range v {
summary, ok := testjson.NewSummary(item)
if !ok {
return fmt.Errorf("value must be one or more of: %s",
testjson.SummarizeAll.String())
}
s.value -= summary
}
return nil
}
func (s *hideSummaryValue) Type() string {
return "summary"
}
func (s *hideSummaryValue) String() string {
// flip all the bits, since the flag value is the negative of what is stored
return (testjson.SummarizeAll ^ s.value).String()
}
var junitFieldFormatValues = "full, relative, short"
type junitFieldFormatValue struct {
value junitxml.FormatFunc
}
func (f *junitFieldFormatValue) Set(val string) error {
switch val {
case "full":
return nil
case "relative":
f.value = testjson.RelativePackagePath
return nil
case "short":
f.value = path.Base
return nil
}
return fmt.Errorf("invalid value: %v, must be one of: "+junitFieldFormatValues, val)
}
func (f *junitFieldFormatValue) Type() string {
return "field-format"
}
func (f *junitFieldFormatValue) String() string {
return "full"
}
func (f *junitFieldFormatValue) Value() junitxml.FormatFunc {
if f == nil {
return nil
}
return f.value
}
type commandValue struct {
original string
command []string
}
func (c *commandValue) String() string {
return c.original
}
func (c *commandValue) Set(raw string) error {
var err error
c.command, err = shlex.Split(raw)
c.original = raw
return err
}
func (c *commandValue) Type() string {
return "command"
}
func (c *commandValue) Value() []string {
if c == nil {
return nil
}
return c.command
}
var _ pflag.Value = (*stringSlice)(nil)
// stringSlice is a flag.Value which populates the string slice by splitting
// the raw flag value on whitespace.
type stringSlice []string
func (s *stringSlice) String() string {
return strings.Join(*s, " ")
}
func (s *stringSlice) Set(raw string) error {
*s = append(*s, strings.Fields(raw)...)
return nil
}
func (s *stringSlice) Type() string {
return "list"
}
gotestsum-1.8.2/cmd/flags_test.go 0000664 0000000 0000000 00000001650 14274752762 0017023 0 ustar 00root root 0000000 0000000 package cmd
import (
"testing"
"gotest.tools/v3/assert"
)
func TestNoSummaryValue_SetAndString(t *testing.T) {
t.Run("none", func(t *testing.T) {
assert.Equal(t, newHideSummaryValue().String(), "none")
})
t.Run("one", func(t *testing.T) {
value := newHideSummaryValue()
assert.NilError(t, value.Set("output"))
assert.Equal(t, value.String(), "output")
})
t.Run("some", func(t *testing.T) {
value := newHideSummaryValue()
assert.NilError(t, value.Set("errors,failed"))
assert.Equal(t, value.String(), "failed,errors")
})
t.Run("bad value", func(t *testing.T) {
value := newHideSummaryValue()
assert.ErrorContains(t, value.Set("bogus"), "must be one or more of")
})
}
func TestStringSlice(t *testing.T) {
value := "one \ntwo three\n\tfour\t five \n"
var v []string
ss := (*stringSlice)(&v)
assert.NilError(t, ss.Set(value))
assert.DeepEqual(t, v, []string{"one", "two", "three", "four", "five"})
}
gotestsum-1.8.2/cmd/handler.go 0000664 0000000 0000000 00000006273 14274752762 0016313 0 ustar 00root root 0000000 0000000 package cmd
import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"gotest.tools/gotestsum/internal/junitxml"
"gotest.tools/gotestsum/internal/log"
"gotest.tools/gotestsum/testjson"
)
type eventHandler struct {
formatter testjson.EventFormatter
err io.Writer
jsonFile io.WriteCloser
maxFails int
}
func (h *eventHandler) Err(text string) error {
_, _ = h.err.Write([]byte(text + "\n"))
// always return nil, no need to stop scanning if the stderr write fails
return nil
}
func (h *eventHandler) Event(event testjson.TestEvent, execution *testjson.Execution) error {
// ignore artificial events with no raw Bytes()
if h.jsonFile != nil && len(event.Bytes()) > 0 {
_, err := h.jsonFile.Write(append(event.Bytes(), '\n'))
if err != nil {
return fmt.Errorf("failed to write JSON file: %w", err)
}
}
err := h.formatter.Format(event, execution)
if err != nil {
return fmt.Errorf("failed to format event: %w", err)
}
if h.maxFails > 0 && len(execution.Failed()) >= h.maxFails {
return fmt.Errorf("ending test run because max failures was reached")
}
return nil
}
func (h *eventHandler) Close() error {
if h.jsonFile != nil {
if err := h.jsonFile.Close(); err != nil {
log.Errorf("Failed to close JSON file: %v", err)
}
}
return nil
}
var _ testjson.EventHandler = &eventHandler{}
func newEventHandler(opts *options) (*eventHandler, error) {
formatter := testjson.NewEventFormatter(opts.stdout, opts.format)
if formatter == nil {
return nil, fmt.Errorf("unknown format %s", opts.format)
}
handler := &eventHandler{
formatter: formatter,
err: opts.stderr,
maxFails: opts.maxFails,
}
var err error
if opts.jsonFile != "" {
_ = os.MkdirAll(filepath.Dir(opts.jsonFile), 0o755)
handler.jsonFile, err = os.Create(opts.jsonFile)
if err != nil {
return handler, fmt.Errorf("failed to open JSON file: %w", err)
}
}
return handler, nil
}
func writeJUnitFile(opts *options, execution *testjson.Execution) error {
if opts.junitFile == "" {
return nil
}
_ = os.MkdirAll(filepath.Dir(opts.junitFile), 0o755)
junitFile, err := os.Create(opts.junitFile)
if err != nil {
return fmt.Errorf("failed to open JUnit file: %v", err)
}
defer func() {
if err := junitFile.Close(); err != nil {
log.Errorf("Failed to close JUnit file: %v", err)
}
}()
return junitxml.Write(junitFile, execution, junitxml.Config{
ProjectName: opts.junitProjectName,
FormatTestSuiteName: opts.junitTestSuiteNameFormat.Value(),
FormatTestCaseClassname: opts.junitTestCaseClassnameFormat.Value(),
})
}
func postRunHook(opts *options, execution *testjson.Execution) error {
command := opts.postRunHookCmd.Value()
if len(command) == 0 {
return nil
}
cmd := exec.Command(command[0], command[1:]...)
cmd.Stdout = opts.stdout
cmd.Stderr = opts.stderr
cmd.Env = append(
os.Environ(),
"GOTESTSUM_JSONFILE="+opts.jsonFile,
"GOTESTSUM_JUNITFILE="+opts.junitFile,
fmt.Sprintf("TESTS_TOTAL=%d", execution.Total()),
fmt.Sprintf("TESTS_FAILED=%d", len(execution.Failed())),
fmt.Sprintf("TESTS_SKIPPED=%d", len(execution.Skipped())),
fmt.Sprintf("TESTS_ERRORS=%d", len(execution.Errors())),
)
// TODO: send a more detailed report to stdin?
return cmd.Run()
}
gotestsum-1.8.2/cmd/handler_test.go 0000664 0000000 0000000 00000006136 14274752762 0017350 0 ustar 00root root 0000000 0000000 package cmd
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"gotest.tools/gotestsum/testjson"
"gotest.tools/v3/assert"
"gotest.tools/v3/env"
"gotest.tools/v3/fs"
"gotest.tools/v3/golden"
)
func TestPostRunHook(t *testing.T) {
command := &commandValue{}
err := command.Set("go run ./testdata/postrunhook/main.go")
assert.NilError(t, err)
buf := new(bytes.Buffer)
opts := &options{
postRunHookCmd: command,
jsonFile: "events.json",
junitFile: "junit.xml",
stdout: buf,
}
env.Patch(t, "GOTESTSUM_FORMAT", "short")
exec := newExecFromTestData(t)
err = postRunHook(opts, exec)
assert.NilError(t, err)
golden.Assert(t, buf.String(), "post-run-hook-expected")
}
func newExecFromTestData(t *testing.T) *testjson.Execution {
t.Helper()
f, err := os.Open("../testjson/testdata/input/go-test-json.out")
assert.NilError(t, err)
defer f.Close() // nolint: errcheck
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: f,
Stderr: strings.NewReader(""),
})
assert.NilError(t, err)
return exec
}
type bufferCloser struct {
bytes.Buffer
}
func (bufferCloser) Close() error { return nil }
func TestEventHandler_Event_WithMissingActionFail(t *testing.T) {
buf := new(bufferCloser)
errBuf := new(bytes.Buffer)
format := testjson.NewEventFormatter(errBuf, "testname")
source := golden.Get(t, "../../testjson/testdata/input/go-test-json-missing-test-fail.out")
cfg := testjson.ScanConfig{
Stdout: bytes.NewReader(source),
Handler: &eventHandler{jsonFile: buf, formatter: format},
}
_, err := testjson.ScanTestOutput(cfg)
assert.NilError(t, err)
assert.Equal(t, buf.String(), string(source))
// confirm the artificial event was sent to the handler by checking the output
// of the formatter.
golden.Assert(t, errBuf.String(), "event-handler-missing-test-fail-expected")
}
func TestEventHandler_Event_MaxFails(t *testing.T) {
format := testjson.NewEventFormatter(ioutil.Discard, "testname")
source := golden.Get(t, "../../testjson/testdata/input/go-test-json.out")
cfg := testjson.ScanConfig{
Stdout: bytes.NewReader(source),
Handler: &eventHandler{formatter: format, maxFails: 2},
}
_, err := testjson.ScanTestOutput(cfg)
assert.Error(t, err, "ending test run because max failures was reached")
}
func TestNewEventHandler_CreatesDirectory(t *testing.T) {
dir := fs.NewDir(t, t.Name())
jsonFile := filepath.Join(dir.Path(), "new-path", "log.json")
opts := &options{
stdout: new(bytes.Buffer),
format: "testname",
jsonFile: jsonFile,
}
_, err := newEventHandler(opts)
assert.NilError(t, err)
_, err = os.Stat(jsonFile)
assert.NilError(t, err)
}
func TestWriteJunitFile_CreatesDirectory(t *testing.T) {
dir := fs.NewDir(t, t.Name())
junitFile := filepath.Join(dir.Path(), "new-path", "junit.xml")
opts := &options{
junitFile: junitFile,
junitTestCaseClassnameFormat: &junitFieldFormatValue{},
junitTestSuiteNameFormat: &junitFieldFormatValue{},
}
exec := &testjson.Execution{}
err := writeJUnitFile(opts, exec)
assert.NilError(t, err)
_, err = os.Stat(junitFile)
assert.NilError(t, err)
}
gotestsum-1.8.2/cmd/internal/ 0000775 0000000 0000000 00000000000 14274752762 0016153 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/internal/signalhandlerdriver/ 0000775 0000000 0000000 00000000000 14274752762 0022202 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/internal/signalhandlerdriver/main.go 0000664 0000000 0000000 00000001376 14274752762 0023464 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"io/ioutil"
"os"
"os/signal"
"strconv"
"syscall"
"time"
)
func main() {
if len(os.Args) < 2 {
log("missing required filename argument")
os.Exit(1)
}
pid := []byte(strconv.Itoa(os.Getpid()))
if err := ioutil.WriteFile(os.Args[1], pid, 0644); err != nil {
log("failed to write file:", err.Error())
os.Exit(1)
}
c := make(chan os.Signal, 1)
signal.Notify(c)
var s os.Signal
select {
case s = <-c:
case <-time.After(time.Minute):
log("timeout waiting for signal")
os.Exit(1)
}
log("Received signal:", s)
switch n := s.(type) {
case syscall.Signal:
os.Exit(100 + int(n))
default:
log("failed to parse signal number")
os.Exit(3)
}
}
func log(v ...interface{}) {
fmt.Fprintln(os.Stderr, v...)
}
gotestsum-1.8.2/cmd/main.go 0000664 0000000 0000000 00000032306 14274752762 0015616 0 ustar 00root root 0000000 0000000 package cmd
import (
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"os/signal"
"strings"
"sync/atomic"
"syscall"
"github.com/dnephin/pflag"
"github.com/fatih/color"
"gotest.tools/gotestsum/internal/log"
"gotest.tools/gotestsum/testjson"
)
var version = "dev"
func Run(name string, args []string) error {
flags, opts := setupFlags(name)
switch err := flags.Parse(args); {
case err == pflag.ErrHelp:
return nil
case err != nil:
usage(os.Stderr, name, flags)
return err
}
opts.args = flags.Args()
setupLogging(opts)
switch {
case opts.version:
fmt.Fprintf(os.Stdout, "gotestsum version %s\n", version)
return nil
case opts.watch:
return runWatcher(opts)
}
return run(opts)
}
func setupFlags(name string) (*pflag.FlagSet, *options) {
opts := &options{
hideSummary: newHideSummaryValue(),
junitTestCaseClassnameFormat: &junitFieldFormatValue{},
junitTestSuiteNameFormat: &junitFieldFormatValue{},
postRunHookCmd: &commandValue{},
stdout: color.Output,
stderr: color.Error,
}
flags := pflag.NewFlagSet(name, pflag.ContinueOnError)
flags.SetInterspersed(false)
flags.Usage = func() {
usage(os.Stdout, name, flags)
}
flags.StringVarP(&opts.format, "format", "f",
lookEnvWithDefault("GOTESTSUM_FORMAT", "short"),
"print format of test input")
flags.BoolVar(&opts.rawCommand, "raw-command", false,
"don't prepend 'go test -json' to the 'go test' command")
flags.BoolVar(&opts.ignoreNonJSONOutputLines, "ignore-non-json-output-lines", false,
"write non-JSON 'go test' output lines to stderr instead of failing")
flags.Lookup("ignore-non-json-output-lines").Hidden = true
flags.StringVar(&opts.jsonFile, "jsonfile",
lookEnvWithDefault("GOTESTSUM_JSONFILE", ""),
"write all TestEvents to file")
flags.BoolVar(&opts.noColor, "no-color", defaultNoColor, "disable color output")
flags.Var(opts.hideSummary, "no-summary",
"do not print summary of: "+testjson.SummarizeAll.String())
flags.Lookup("no-summary").Hidden = true
flags.Var(opts.hideSummary, "hide-summary",
"hide sections of the summary: "+testjson.SummarizeAll.String())
flags.Var(opts.postRunHookCmd, "post-run-command",
"command to run after the tests have completed")
flags.BoolVar(&opts.watch, "watch", false,
"watch go files, and run tests when a file is modified")
flags.IntVar(&opts.maxFails, "max-fails", 0,
"end the test run after this number of failures")
flags.StringVar(&opts.junitFile, "junitfile",
lookEnvWithDefault("GOTESTSUM_JUNITFILE", ""),
"write a JUnit XML file")
flags.Var(opts.junitTestSuiteNameFormat, "junitfile-testsuite-name",
"format the testsuite name field as: "+junitFieldFormatValues)
flags.Var(opts.junitTestCaseClassnameFormat, "junitfile-testcase-classname",
"format the testcase classname field as: "+junitFieldFormatValues)
flags.StringVar(&opts.junitProjectName, "junitfile-project-name",
lookEnvWithDefault("GOTESTSUM_JUNITFILE_PROJECT_NAME", ""),
"name of the project used in the junit.xml file")
flags.IntVar(&opts.rerunFailsMaxAttempts, "rerun-fails", 0,
"rerun failed tests until they all pass, or attempts exceeds maximum. Defaults to max 2 reruns when enabled.")
flags.Lookup("rerun-fails").NoOptDefVal = "2"
flags.IntVar(&opts.rerunFailsMaxInitialFailures, "rerun-fails-max-failures", 10,
"do not rerun any tests if the initial run has more than this number of failures")
flags.Var((*stringSlice)(&opts.packages), "packages",
"space separated list of package to test")
flags.StringVar(&opts.rerunFailsReportFile, "rerun-fails-report", "",
"write a report to the file, of the tests that were rerun")
flags.BoolVar(&opts.rerunFailsOnlyRootCases, "rerun-fails-only-root-testcases", false,
"rerun only root testcaes, instead of only subtests")
flags.Lookup("rerun-fails-only-root-testcases").Hidden = true
flags.BoolVar(&opts.debug, "debug", false, "enabled debug logging")
flags.BoolVar(&opts.version, "version", false, "show version and exit")
return flags, opts
}
func usage(out io.Writer, name string, flags *pflag.FlagSet) {
fmt.Fprintf(out, `Usage:
%[1]s [flags] [--] [go test flags]
%[1]s [command]
Flags:
`, name)
flags.SetOutput(out)
flags.PrintDefaults()
fmt.Fprint(out, `
Formats:
dots print a character for each test
dots-v2 experimental dots format, one package per line
pkgname print a line for each package
pkgname-and-test-fails print a line for each package and failed test output
testname print a line for each test and package
standard-quiet standard go test format
standard-verbose standard go test -v format
Commands:
tool tools for working with test2json output
help print this help next
`)
}
func lookEnvWithDefault(key, defValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defValue
}
type options struct {
args []string
format string
debug bool
rawCommand bool
ignoreNonJSONOutputLines bool
jsonFile string
junitFile string
postRunHookCmd *commandValue
noColor bool
hideSummary *hideSummaryValue
junitTestSuiteNameFormat *junitFieldFormatValue
junitTestCaseClassnameFormat *junitFieldFormatValue
junitProjectName string
rerunFailsMaxAttempts int
rerunFailsMaxInitialFailures int
rerunFailsReportFile string
rerunFailsOnlyRootCases bool
packages []string
watch bool
maxFails int
version bool
// shims for testing
stdout io.Writer
stderr io.Writer
}
func (o options) Validate() error {
if o.rerunFailsMaxAttempts > 0 && len(o.args) > 0 && !o.rawCommand && len(o.packages) == 0 {
return fmt.Errorf(
"when go test args are used with --rerun-fails-max-attempts " +
"the list of packages to test must be specified by the --packages flag")
}
return nil
}
var defaultNoColor = func() bool {
if os.Getenv("GITHUB_ACTIONS") == "true" {
return false
}
return color.NoColor
}()
func setupLogging(opts *options) {
if opts.debug {
log.SetLevel(log.DebugLevel)
}
color.NoColor = opts.noColor
}
func run(opts *options) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := opts.Validate(); err != nil {
return err
}
goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, rerunOpts{}))
if err != nil {
return err
}
handler, err := newEventHandler(opts)
if err != nil {
return err
}
defer handler.Close() // nolint: errcheck
cfg := testjson.ScanConfig{
Stdout: goTestProc.stdout,
Stderr: goTestProc.stderr,
Handler: handler,
Stop: cancel,
IgnoreNonJSONOutputLines: opts.ignoreNonJSONOutputLines,
}
exec, err := testjson.ScanTestOutput(cfg)
if err != nil {
return finishRun(opts, exec, err)
}
exitErr := goTestProc.cmd.Wait()
if signum := atomic.LoadInt32(&goTestProc.signal); signum != 0 {
return finishRun(opts, exec, exitError{num: signalExitCode + int(signum)})
}
if exitErr == nil || opts.rerunFailsMaxAttempts == 0 {
return finishRun(opts, exec, exitErr)
}
if err := hasErrors(exitErr, exec); err != nil {
return finishRun(opts, exec, err)
}
failed := len(rerunFailsFilter(opts)(exec.Failed()))
if failed > opts.rerunFailsMaxInitialFailures {
err := fmt.Errorf(
"number of test failures (%d) exceeds maximum (%d) set by --rerun-fails-max-failures",
failed, opts.rerunFailsMaxInitialFailures)
return finishRun(opts, exec, err)
}
cfg = testjson.ScanConfig{Execution: exec, Handler: handler}
exitErr = rerunFailed(ctx, opts, cfg)
if err := writeRerunFailsReport(opts, exec); err != nil {
return err
}
return finishRun(opts, exec, exitErr)
}
func finishRun(opts *options, exec *testjson.Execution, exitErr error) error {
testjson.PrintSummary(opts.stdout, exec, opts.hideSummary.value)
if err := writeJUnitFile(opts, exec); err != nil {
return fmt.Errorf("failed to write junit file: %w", err)
}
if err := postRunHook(opts, exec); err != nil {
return fmt.Errorf("post run command failed: %w", err)
}
return exitErr
}
func goTestCmdArgs(opts *options, rerunOpts rerunOpts) []string {
if opts.rawCommand {
var result []string
result = append(result, opts.args...)
result = append(result, rerunOpts.Args()...)
return result
}
args := opts.args
result := []string{"go", "test"}
if len(args) == 0 {
result = append(result, "-json")
if rerunOpts.runFlag != "" {
result = append(result, rerunOpts.runFlag)
}
return append(result, cmdArgPackageList(opts, rerunOpts, "./...")...)
}
if boolArgIndex("json", args) < 0 {
result = append(result, "-json")
}
if rerunOpts.runFlag != "" {
// Remove any existing run arg, it needs to be replaced with our new one
// and duplicate args are not allowed by 'go test'.
runIndex, runIndexEnd := argIndex("run", args)
if runIndex >= 0 && runIndexEnd < len(args) {
args = append(args[:runIndex], args[runIndexEnd+1:]...)
}
result = append(result, rerunOpts.runFlag)
}
pkgArgIndex := findPkgArgPosition(args)
result = append(result, args[:pkgArgIndex]...)
result = append(result, cmdArgPackageList(opts, rerunOpts)...)
result = append(result, args[pkgArgIndex:]...)
return result
}
func cmdArgPackageList(opts *options, rerunOpts rerunOpts, defPkgList ...string) []string {
switch {
case rerunOpts.pkg != "":
return []string{rerunOpts.pkg}
case len(opts.packages) > 0:
return opts.packages
case os.Getenv("TEST_DIRECTORY") != "":
return []string{os.Getenv("TEST_DIRECTORY")}
default:
return defPkgList
}
}
func boolArgIndex(flag string, args []string) int {
for i, arg := range args {
if arg == "-"+flag || arg == "--"+flag {
return i
}
}
return -1
}
func argIndex(flag string, args []string) (start, end int) {
for i, arg := range args {
if arg == "-"+flag || arg == "--"+flag {
return i, i + 1
}
if strings.HasPrefix(arg, "-"+flag+"=") || strings.HasPrefix(arg, "--"+flag+"=") {
return i, i
}
}
return -1, -1
}
// The package list is before the -args flag, or at the end of the args list
// if the -args flag is not in args.
// The -args flag is a 'go test' flag that indicates that all subsequent
// args should be passed to the test binary. It requires that the list of
// packages comes before -args, so we re-use it as a placeholder in the case
// where some args must be passed to the test binary.
func findPkgArgPosition(args []string) int {
if i := boolArgIndex("args", args); i >= 0 {
return i
}
return len(args)
}
type proc struct {
cmd waiter
stdout io.Reader
stderr io.Reader
// signal is atomically set to the signal value when a signal is received
// by newSignalHandler.
signal int32
}
type waiter interface {
Wait() error
}
func startGoTest(ctx context.Context, args []string) (*proc, error) {
if len(args) == 0 {
return nil, errors.New("missing command to run")
}
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
cmd.Stdin = os.Stdin
p := proc{cmd: cmd}
log.Debugf("exec: %s", cmd.Args)
var err error
p.stdout, err = cmd.StdoutPipe()
if err != nil {
return nil, err
}
p.stderr, err = cmd.StderrPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to run %s: %w", strings.Join(cmd.Args, " "), err)
}
log.Debugf("go test pid: %d", cmd.Process.Pid)
ctx, cancel := context.WithCancel(ctx)
newSignalHandler(ctx, cmd.Process.Pid, &p)
p.cmd = &cancelWaiter{cancel: cancel, wrapped: p.cmd}
return &p, nil
}
// ExitCodeWithDefault returns the ExitStatus of a process from the error returned by
// exec.Run(). If the exit status is not available an error is returned.
func ExitCodeWithDefault(err error) int {
if err == nil {
return 0
}
if exiterr, ok := err.(exitCoder); ok {
if code := exiterr.ExitCode(); code != -1 {
return code
}
}
return 127
}
type exitCoder interface {
ExitCode() int
}
func IsExitCoder(err error) bool {
_, ok := err.(exitCoder)
return ok
}
type exitError struct {
num int
}
func (e exitError) Error() string {
return fmt.Sprintf("exit code %d", e.num)
}
func (e exitError) ExitCode() int {
return e.num
}
// signalExitCode is the base value added to a signal number to produce the
// exit code value. This matches the behaviour of bash.
const signalExitCode = 128
func newSignalHandler(ctx context.Context, pid int, p *proc) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
defer signal.Stop(c)
select {
case <-ctx.Done():
return
case s := <-c:
atomic.StoreInt32(&p.signal, int32(s.(syscall.Signal)))
proc, err := os.FindProcess(pid)
if err != nil {
log.Errorf("failed to find pid of 'go test': %v", err)
return
}
if err := proc.Signal(s); err != nil {
log.Errorf("failed to interrupt 'go test': %v", err)
return
}
}
}()
}
// cancelWaiter wraps a waiter to cancel the context after the wrapped
// Wait exits.
type cancelWaiter struct {
cancel func()
wrapped waiter
}
func (w *cancelWaiter) Wait() error {
err := w.wrapped.Wait()
w.cancel()
return err
}
gotestsum-1.8.2/cmd/main_e2e_test.go 0000664 0000000 0000000 00000015064 14274752762 0017412 0 ustar 00root root 0000000 0000000 package cmd
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
"time"
"gotest.tools/gotestsum/internal/text"
"gotest.tools/v3/assert"
"gotest.tools/v3/env"
"gotest.tools/v3/fs"
"gotest.tools/v3/golden"
"gotest.tools/v3/icmd"
"gotest.tools/v3/poll"
"gotest.tools/v3/skip"
)
func TestMain(m *testing.M) {
code := m.Run()
binaryFixture.Cleanup()
os.Exit(code)
}
func TestE2E_RerunFails(t *testing.T) {
if testing.Short() {
t.Skip("too slow for short run")
}
type testCase struct {
name string
args []string
expectedErr string
}
fn := func(t *testing.T, tc testCase) {
tmpFile := fs.NewFile(t, t.Name()+"-seedfile", fs.WithContent("0"))
defer tmpFile.Remove()
envVars := osEnviron()
envVars["TEST_SEEDFILE"] = tmpFile.Path()
defer env.PatchAll(t, envVars)()
flags, opts := setupFlags("gotestsum")
assert.NilError(t, flags.Parse(tc.args))
opts.args = flags.Args()
bufStdout := new(bytes.Buffer)
opts.stdout = bufStdout
bufStderr := new(bytes.Buffer)
opts.stderr = bufStderr
err := run(opts)
if tc.expectedErr != "" {
assert.Error(t, err, tc.expectedErr)
} else {
assert.NilError(t, err)
}
out := text.ProcessLines(t, bufStdout,
text.OpRemoveSummaryLineElapsedTime,
text.OpRemoveTestElapsedTime,
filepath.ToSlash, // for windows
)
golden.Assert(t, out, "e2e/expected/"+expectedFilename(t.Name()))
}
var testCases = []testCase{
{
name: "reruns until success",
args: []string{
"-f=testname",
"--rerun-fails=4",
"--packages=./testdata/e2e/flaky/",
"--", "-count=1", "-tags=testdata",
},
},
{
name: "reruns continues to fail",
args: []string{
"-f=testname",
"--rerun-fails=2",
"--packages=./testdata/e2e/flaky/",
"--", "-count=1", "-tags=testdata",
},
expectedErr: "exit status 1",
},
{
name: "first run has errors, abort rerun",
args: []string{
"-f=testname",
"--rerun-fails=2",
"--packages=../testjson/internal/broken",
"--", "-count=1", "-tags=stubpkg",
},
expectedErr: "rerun aborted because previous run had errors",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fn(t, tc)
})
}
}
// osEnviron returns os.Environ() as a map, with any GOTESTSUM_ env vars removed
// so that they do not alter the test results.
func osEnviron() map[string]string {
e := env.ToMap(os.Environ())
for k := range e {
if strings.HasPrefix(k, "GOTESTSUM_") {
delete(e, k)
}
}
return e
}
func expectedFilename(name string) string {
ver := runtime.Version()
switch {
case isPreGo114(ver):
return name + "-go1.13"
default:
return name
}
}
// go1.14.6 changed how it prints messages from tests. go1.14.{0-5} used a format
// that was different from both go1.14.6 and previous versions of Go. These tests
// no longer support that format.
func isPreGo114(ver string) bool {
prefix := "go1.1"
if !strings.HasPrefix(ver, prefix) || len(ver) < len(prefix)+1 {
return false
}
switch ver[len(prefix)] {
case '0', '1', '2', '3':
return true
}
return false
}
var binaryFixture pkgFixtureFile
type pkgFixtureFile struct {
filename string
once sync.Once
cleanup func()
}
func (p *pkgFixtureFile) Path() string {
return p.filename
}
func (p *pkgFixtureFile) Do(f func() string) {
p.once.Do(func() {
p.filename = f()
p.cleanup = func() {
os.RemoveAll(p.filename) // nolint: errcheck
}
})
}
func (p *pkgFixtureFile) Cleanup() {
if p.cleanup != nil {
p.cleanup()
}
}
// compileBinary once the first time this function is called. Subsequent calls
// will return the path to the compiled binary. The binary is removed when all
// the tests in this package have completed.
func compileBinary(t *testing.T) string {
t.Helper()
if testing.Short() {
t.Skip("too slow for short run")
}
binaryFixture.Do(func() string {
tmpDir, err := ioutil.TempDir("", "gotestsum-binary")
assert.NilError(t, err)
path := filepath.Join(tmpDir, "gotestsum")
result := icmd.RunCommand("go", "build", "-o", path, "..")
result.Assert(t, icmd.Success)
return path
})
if binaryFixture.Path() == "" {
t.Skip("previous attempt to compile the binary failed")
}
return binaryFixture.Path()
}
func TestE2E_SignalHandler(t *testing.T) {
skip.If(t, runtime.GOOS == "windows", "test timeout waiting for pidfile")
bin := compileBinary(t)
tmpDir := fs.NewDir(t, t.Name())
defer tmpDir.Remove()
driver := tmpDir.Join("driver")
target := filepath.FromSlash("./internal/signalhandlerdriver/")
icmd.RunCommand("go", "build", "-o", driver, target).
Assert(t, icmd.Success)
pidFile := tmpDir.Join("pidfile")
args := []string{"--raw-command", "--", driver, pidFile}
result := icmd.StartCmd(icmd.Command(bin, args...))
poll.WaitOn(t, poll.FileExists(pidFile), poll.WithTimeout(time.Second))
assert.NilError(t, result.Cmd.Process.Signal(os.Interrupt))
icmd.WaitOnCmd(2*time.Second, result)
result.Assert(t, icmd.Expected{ExitCode: 130})
}
func TestE2E_MaxFails_EndTestRun(t *testing.T) {
if testing.Short() {
t.Skip("too slow for short run")
}
tmpFile := fs.NewFile(t, t.Name()+"-seedfile", fs.WithContent("0"))
defer tmpFile.Remove()
envVars := osEnviron()
envVars["TEST_SEEDFILE"] = tmpFile.Path()
defer env.PatchAll(t, envVars)()
flags, opts := setupFlags("gotestsum")
args := []string{"--max-fails=2", "--packages=./testdata/e2e/flaky/", "--", "-tags=testdata"}
assert.NilError(t, flags.Parse(args))
opts.args = flags.Args()
bufStdout := new(bytes.Buffer)
opts.stdout = bufStdout
bufStderr := new(bytes.Buffer)
opts.stderr = bufStderr
err := run(opts)
assert.Error(t, err, "ending test run because max failures was reached")
out := text.ProcessLines(t, bufStdout,
text.OpRemoveSummaryLineElapsedTime,
text.OpRemoveTestElapsedTime,
filepath.ToSlash, // for windows
)
golden.Assert(t, out, "e2e/expected/"+t.Name())
}
func TestE2E_IgnoresWarnings(t *testing.T) {
if testing.Short() {
t.Skip("too slow for short run")
}
flags, opts := setupFlags("gotestsum")
args := []string{
"--rerun-fails=1",
"--packages=./testdata/e2e/ignore_warnings/",
"--format=testname",
"--", "-tags=testdata", "-cover", "-coverpkg=./cmd/internal",
}
assert.NilError(t, flags.Parse(args))
opts.args = flags.Args()
bufStdout := new(bytes.Buffer)
opts.stdout = bufStdout
bufStderr := new(bytes.Buffer)
opts.stderr = bufStderr
err := run(opts)
assert.Error(t, err, "exit status 1")
out := text.ProcessLines(t, bufStdout,
text.OpRemoveSummaryLineElapsedTime,
text.OpRemoveTestElapsedTime,
filepath.ToSlash, // for windows
)
golden.Assert(t, out, "e2e/expected/"+t.Name())
}
gotestsum-1.8.2/cmd/main_test.go 0000664 0000000 0000000 00000031316 14274752762 0016655 0 ustar 00root root 0000000 0000000 package cmd
import (
"bytes"
"encoding/json"
"os"
"os/exec"
"strings"
"testing"
"gotest.tools/gotestsum/testjson"
"gotest.tools/v3/assert"
"gotest.tools/v3/assert/cmp"
"gotest.tools/v3/env"
"gotest.tools/v3/golden"
)
func TestUsage_WithFlagsFromSetupFlags(t *testing.T) {
defer env.PatchAll(t, nil)()
name := "gotestsum"
flags, _ := setupFlags(name)
buf := new(bytes.Buffer)
usage(buf, name, flags)
golden.Assert(t, buf.String(), "gotestsum-help-text")
}
func TestOptions_Validate_FromFlags(t *testing.T) {
type testCase struct {
name string
args []string
expected string
}
fn := func(t *testing.T, tc testCase) {
flags, opts := setupFlags("gotestsum")
err := flags.Parse(tc.args)
assert.NilError(t, err)
opts.args = flags.Args()
err = opts.Validate()
if tc.expected == "" {
assert.NilError(t, err)
return
}
assert.ErrorContains(t, err, tc.expected, "opts: %#v", opts)
}
var testCases = []testCase{
{
name: "no flags",
},
{
name: "rerun flag, raw command",
args: []string{"--rerun-fails", "--raw-command", "--", "./test-all"},
},
{
name: "rerun flag, no go-test args",
args: []string{"--rerun-fails", "--"},
},
{
name: "rerun flag, go-test args, no packages flag",
args: []string{"--rerun-fails", "--", "./..."},
expected: "the list of packages to test must be specified by the --packages flag",
},
{
name: "rerun flag, go-test args, with packages flag",
args: []string{"--rerun-fails", "--packages", "./...", "--", "--foo"},
},
{
name: "rerun flag, no go-test args, with packages flag",
args: []string{"--rerun-fails", "--packages", "./..."},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fn(t, tc)
})
}
}
func TestGoTestCmdArgs(t *testing.T) {
type testCase struct {
opts *options
rerunOpts rerunOpts
env []string
expected []string
}
run := func(t *testing.T, name string, tc testCase) {
t.Helper()
runCase(t, name, func(t *testing.T) {
defer env.PatchAll(t, env.ToMap(tc.env))()
actual := goTestCmdArgs(tc.opts, tc.rerunOpts)
assert.DeepEqual(t, actual, tc.expected)
})
}
run(t, "raw command", testCase{
opts: &options{
rawCommand: true,
args: []string{"./script", "-test.timeout=20m"},
},
expected: []string{"./script", "-test.timeout=20m"},
})
run(t, "no args", testCase{
opts: &options{},
expected: []string{"go", "test", "-json", "./..."},
})
run(t, "no args, with rerunPackageList arg", testCase{
opts: &options{
packages: []string{"./pkg"},
},
expected: []string{"go", "test", "-json", "./pkg"},
})
run(t, "TEST_DIRECTORY env var no args", testCase{
opts: &options{},
env: []string{"TEST_DIRECTORY=testdir"},
expected: []string{"go", "test", "-json", "testdir"},
})
run(t, "TEST_DIRECTORY env var with args", testCase{
opts: &options{
args: []string{"-tags=integration"},
},
env: []string{"TEST_DIRECTORY=testdir"},
expected: []string{"go", "test", "-json", "-tags=integration", "testdir"},
})
run(t, "no -json arg", testCase{
opts: &options{
args: []string{"-timeout=2m", "./pkg"},
},
expected: []string{"go", "test", "-json", "-timeout=2m", "./pkg"},
})
run(t, "with -json arg", testCase{
opts: &options{
args: []string{"-json", "-timeout=2m", "./pkg"},
},
expected: []string{"go", "test", "-json", "-timeout=2m", "./pkg"},
})
run(t, "raw command, with rerunOpts", testCase{
opts: &options{
rawCommand: true,
args: []string{"./script", "-test.timeout=20m"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"./script", "-test.timeout=20m", "-run=TestOne|TestTwo", "./fails"},
})
run(t, "no args, with rerunOpts", testCase{
opts: &options{},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "./fails"},
})
run(t, "TEST_DIRECTORY env var, no args, with rerunOpts", testCase{
opts: &options{},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
env: []string{"TEST_DIRECTORY=testdir"},
// TEST_DIRECTORY should be overridden by rerun opts
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "./fails"},
})
run(t, "TEST_DIRECTORY env var, with args, with rerunOpts", testCase{
opts: &options{
args: []string{"-tags=integration"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
env: []string{"TEST_DIRECTORY=testdir"},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "-tags=integration", "./fails"},
})
run(t, "no -json arg, with rerunOpts", testCase{
opts: &options{
args: []string{"-timeout=2m"},
packages: []string{"./pkg"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "-timeout=2m", "./fails"},
})
run(t, "with -json arg, with rerunOpts", testCase{
opts: &options{
args: []string{"-json", "-timeout=2m"},
packages: []string{"./pkg"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-run=TestOne|TestTwo", "-json", "-timeout=2m", "./fails"},
})
run(t, "with args, with reunFailsPackageList args, with rerunOpts", testCase{
opts: &options{
args: []string{"-timeout=2m"},
packages: []string{"./pkg1", "./pkg2", "./pkg3"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "-timeout=2m", "./fails"},
})
run(t, "with args, with reunFailsPackageList", testCase{
opts: &options{
args: []string{"-timeout=2m"},
packages: []string{"./pkg1", "./pkg2", "./pkg3"},
},
expected: []string{"go", "test", "-json", "-timeout=2m", "./pkg1", "./pkg2", "./pkg3"},
})
run(t, "reunFailsPackageList args, with rerunOpts ", testCase{
opts: &options{
packages: []string{"./pkg1", "./pkg2", "./pkg3"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "./fails"},
})
run(t, "reunFailsPackageList args, with rerunOpts, with -args ", testCase{
opts: &options{
args: []string{"before", "-args", "after"},
packages: []string{"./pkg1"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "before", "./fails", "-args", "after"},
})
run(t, "reunFailsPackageList args, with rerunOpts, with -args at end", testCase{
opts: &options{
args: []string{"before", "-args"},
packages: []string{"./pkg1"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "before", "./fails", "-args"},
})
run(t, "reunFailsPackageList args, with -args at start", testCase{
opts: &options{
args: []string{"-args", "after"},
packages: []string{"./pkg1"},
},
expected: []string{"go", "test", "-json", "./pkg1", "-args", "after"},
})
run(t, "-run arg at start, with rerunOpts ", testCase{
opts: &options{
args: []string{"-run=TestFoo", "-args"},
packages: []string{"./pkg"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "./fails", "-args"},
})
run(t, "-run arg in middle, with rerunOpts ", testCase{
opts: &options{
args: []string{"-count", "1", "--run", "TestFoo", "-args"},
packages: []string{"./pkg"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "-count", "1", "./fails", "-args"},
})
run(t, "-run arg at end with missing value, with rerunOpts ", testCase{
opts: &options{
args: []string{"-count", "1", "-run"},
packages: []string{"./pkg"},
},
rerunOpts: rerunOpts{
runFlag: "-run=TestOne|TestTwo",
pkg: "./fails",
},
expected: []string{"go", "test", "-json", "-run=TestOne|TestTwo", "-count", "1", "-run", "./fails"},
})
}
func runCase(t *testing.T, name string, fn func(t *testing.T)) {
t.Helper()
t.Run(name, func(t *testing.T) {
t.Helper()
t.Log("case:", name)
fn(t)
})
}
func TestRun_RerunFails_WithTooManyInitialFailures(t *testing.T) {
jsonFailed := `{"Package": "pkg", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "fail"}
{"Package": "pkg", "Test": "TestTwo", "Action": "run"}
{"Package": "pkg", "Test": "TestTwo", "Action": "fail"}
{"Package": "pkg", "Action": "fail"}
`
fn := func(args []string) *proc {
return &proc{
cmd: fakeWaiter{result: newExitCode("failed", 1)},
stdout: strings.NewReader(jsonFailed),
stderr: bytes.NewReader(nil),
}
}
reset := patchStartGoTestFn(fn)
defer reset()
out := new(bytes.Buffer)
opts := &options{
rawCommand: true,
args: []string{"./test.test"},
format: "testname",
rerunFailsMaxAttempts: 3,
rerunFailsMaxInitialFailures: 1,
stdout: out,
stderr: os.Stderr,
hideSummary: newHideSummaryValue(),
}
err := run(opts)
assert.ErrorContains(t, err, "number of test failures (2) exceeds maximum (1)", out.String())
}
func TestRun_RerunFails_BuildErrorPreventsRerun(t *testing.T) {
jsonFailed := `{"Package": "pkg", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "fail"}
{"Package": "pkg", "Test": "TestTwo", "Action": "run"}
{"Package": "pkg", "Test": "TestTwo", "Action": "fail"}
{"Package": "pkg", "Action": "fail"}
`
fn := func(args []string) *proc {
return &proc{
cmd: fakeWaiter{result: newExitCode("failed", 1)},
stdout: strings.NewReader(jsonFailed),
stderr: strings.NewReader("anything here is an error\n"),
}
}
reset := patchStartGoTestFn(fn)
defer reset()
out := new(bytes.Buffer)
opts := &options{
rawCommand: true,
args: []string{"./test.test"},
format: "testname",
rerunFailsMaxAttempts: 3,
rerunFailsMaxInitialFailures: 1,
stdout: out,
stderr: os.Stderr,
hideSummary: newHideSummaryValue(),
}
err := run(opts)
assert.ErrorContains(t, err, "rerun aborted because previous run had errors", out.String())
}
// type checking of os/exec.ExitError is done in a test file so that users
// installing from source can continue to use versions prior to go1.12.
var _ exitCoder = &exec.ExitError{}
func TestRun_RerunFails_PanicPreventsRerun(t *testing.T) {
jsonFailed := `{"Package": "pkg", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "output","Output":"panic: something went wrong\n"}
{"Package": "pkg", "Action": "fail"}
`
fn := func(args []string) *proc {
return &proc{
cmd: fakeWaiter{result: newExitCode("failed", 1)},
stdout: strings.NewReader(jsonFailed),
stderr: bytes.NewReader(nil),
}
}
reset := patchStartGoTestFn(fn)
defer reset()
out := new(bytes.Buffer)
opts := &options{
rawCommand: true,
args: []string{"./test.test"},
format: "testname",
rerunFailsMaxAttempts: 3,
rerunFailsMaxInitialFailures: 1,
stdout: out,
stderr: os.Stderr,
hideSummary: newHideSummaryValue(),
}
err := run(opts)
assert.ErrorContains(t, err, "rerun aborted because previous run had a suspected panic", out.String())
}
func TestRun_InputFromStdin(t *testing.T) {
stdin := os.Stdin
t.Cleanup(func() { os.Stdin = stdin })
r, w, err := os.Pipe()
assert.NilError(t, err)
t.Cleanup(func() { _ = r.Close() })
os.Stdin = r
go func() {
defer func() { _ = w.Close() }()
e := json.NewEncoder(w)
for _, event := range []testjson.TestEvent{
{Action: "run", Package: "pkg"},
{Action: "run", Package: "pkg", Test: "TestOne"},
{Action: "fail", Package: "pkg", Test: "TestOne"},
{Action: "fail", Package: "pkg"},
} {
assert.Check(t, e.Encode(event))
}
}()
stdout := new(bytes.Buffer)
err = run(&options{
args: []string{"cat"},
format: "testname",
hideSummary: newHideSummaryValue(),
rawCommand: true,
stdout: stdout,
stderr: os.Stderr,
})
assert.NilError(t, err)
assert.Assert(t, cmp.Contains(stdout.String(), "DONE 1"))
}
gotestsum-1.8.2/cmd/rerunfails.go 0000664 0000000 0000000 00000011023 14274752762 0017035 0 ustar 00root root 0000000 0000000 package cmd
import (
"context"
"fmt"
"os"
"sort"
"gotest.tools/gotestsum/testjson"
)
type rerunOpts struct {
runFlag string
pkg string
}
func (o rerunOpts) Args() []string {
var result []string
if o.runFlag != "" {
result = append(result, o.runFlag)
}
if o.pkg != "" {
result = append(result, o.pkg)
}
return result
}
func newRerunOptsFromTestCase(tc testjson.TestCase) rerunOpts {
return rerunOpts{
runFlag: goTestRunFlagForTestCase(tc.Test),
pkg: tc.Package,
}
}
type testCaseFilter func([]testjson.TestCase) []testjson.TestCase
func rerunFailsFilter(o *options) testCaseFilter {
if o.rerunFailsOnlyRootCases {
return func(tcs []testjson.TestCase) []testjson.TestCase {
return tcs
}
}
return testjson.FilterFailedUnique
}
func rerunFailed(ctx context.Context, opts *options, scanConfig testjson.ScanConfig) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
tcFilter := rerunFailsFilter(opts)
rec := newFailureRecorderFromExecution(scanConfig.Execution)
for attempts := 0; rec.count() > 0 && attempts < opts.rerunFailsMaxAttempts; attempts++ {
testjson.PrintSummary(opts.stdout, scanConfig.Execution, testjson.SummarizeNone)
opts.stdout.Write([]byte("\n")) // nolint: errcheck
nextRec := newFailureRecorder(scanConfig.Handler)
for _, tc := range tcFilter(rec.failures) {
goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, newRerunOptsFromTestCase(tc)))
if err != nil {
return err
}
cfg := testjson.ScanConfig{
RunID: attempts + 1,
Stdout: goTestProc.stdout,
Stderr: goTestProc.stderr,
Handler: nextRec,
Execution: scanConfig.Execution,
Stop: cancel,
}
if _, err := testjson.ScanTestOutput(cfg); err != nil {
return err
}
exitErr := goTestProc.cmd.Wait()
if exitErr != nil {
nextRec.lastErr = exitErr
}
if err := hasErrors(exitErr, scanConfig.Execution); err != nil {
return err
}
}
rec = nextRec
}
return rec.lastErr
}
// startGoTestFn is a shim for testing
var startGoTestFn = startGoTest
func hasErrors(err error, exec *testjson.Execution) error {
switch {
case len(exec.Errors()) > 0:
return fmt.Errorf("rerun aborted because previous run had errors")
// Exit code 0 and 1 are expected.
case ExitCodeWithDefault(err) > 1:
return fmt.Errorf("unexpected go test exit code: %v", err)
case exec.HasPanic():
return fmt.Errorf("rerun aborted because previous run had a suspected panic and some test may not have run")
default:
return nil
}
}
type failureRecorder struct {
testjson.EventHandler
failures []testjson.TestCase
lastErr error
}
func newFailureRecorder(handler testjson.EventHandler) *failureRecorder {
return &failureRecorder{EventHandler: handler}
}
func newFailureRecorderFromExecution(exec *testjson.Execution) *failureRecorder {
return &failureRecorder{failures: exec.Failed()}
}
func (r *failureRecorder) Event(event testjson.TestEvent, execution *testjson.Execution) error {
if !event.PackageEvent() && event.Action == testjson.ActionFail {
pkg := execution.Package(event.Package)
tc := pkg.LastFailedByName(event.Test)
r.failures = append(r.failures, tc)
}
return r.EventHandler.Event(event, execution)
}
func (r *failureRecorder) count() int {
return len(r.failures)
}
func goTestRunFlagForTestCase(test testjson.TestName) string {
if test.IsSubTest() {
root, sub := test.Split()
return "-test.run=^" + root + "$/^" + sub + "$"
}
return "-test.run=^" + test.Name() + "$"
}
func writeRerunFailsReport(opts *options, exec *testjson.Execution) error {
if opts.rerunFailsMaxAttempts == 0 || opts.rerunFailsReportFile == "" {
return nil
}
type testCaseCounts struct {
total int
failed int
}
names := []string{}
results := map[string]testCaseCounts{}
for _, failure := range exec.Failed() {
name := failure.Package + "." + failure.Test.Name()
if _, ok := results[name]; ok {
continue
}
names = append(names, name)
pkg := exec.Package(failure.Package)
counts := testCaseCounts{}
for _, tc := range pkg.Failed {
if tc.Test == failure.Test {
counts.total++
counts.failed++
}
}
for _, tc := range pkg.Passed {
if tc.Test == failure.Test {
counts.total++
}
}
// Skipped tests are not counted, but presumably skipped tests can not fail
results[name] = counts
}
fh, err := os.Create(opts.rerunFailsReportFile)
if err != nil {
return err
}
sort.Strings(names)
for _, name := range names {
counts := results[name]
fmt.Fprintf(fh, "%s: %d runs, %d failures\n", name, counts.total, counts.failed)
}
return nil
}
gotestsum-1.8.2/cmd/rerunfails_test.go 0000664 0000000 0000000 00000011005 14274752762 0020074 0 ustar 00root root 0000000 0000000 package cmd
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"strings"
"testing"
"gotest.tools/gotestsum/testjson"
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
"gotest.tools/v3/golden"
)
func TestWriteRerunFailsReport(t *testing.T) {
reportFile := fs.NewFile(t, t.Name())
defer reportFile.Remove()
opts := &options{
rerunFailsReportFile: reportFile.Path(),
rerunFailsMaxAttempts: 4,
}
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: bytes.NewReader(golden.Get(t, "go-test-json-flaky-rerun.out")),
})
assert.NilError(t, err)
err = writeRerunFailsReport(opts, exec)
assert.NilError(t, err)
raw, err := ioutil.ReadFile(reportFile.Path())
assert.NilError(t, err)
golden.Assert(t, string(raw), t.Name()+"-expected")
}
func TestWriteRerunFailsReport_HandlesMissingActionRunEvents(t *testing.T) {
reportFile := fs.NewFile(t, t.Name())
defer reportFile.Remove()
opts := &options{
rerunFailsReportFile: reportFile.Path(),
rerunFailsMaxAttempts: 4,
}
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: bytes.NewReader(golden.Get(t, "go-test-missing-run-events.out")),
})
assert.NilError(t, err)
err = writeRerunFailsReport(opts, exec)
assert.NilError(t, err)
raw, err := ioutil.ReadFile(reportFile.Path())
assert.NilError(t, err)
golden.Assert(t, string(raw), t.Name()+"-expected")
}
func TestGoTestRunFlagFromTestCases(t *testing.T) {
type testCase struct {
input string
expected string
}
fn := func(t *testing.T, tc testCase) {
actual := goTestRunFlagForTestCase(testjson.TestName(tc.input))
assert.Equal(t, actual, tc.expected)
}
var testCases = map[string]testCase{
"root test case": {
input: "TestOne",
expected: "-test.run=^TestOne$",
},
"sub test case": {
input: "TestOne/SubtestA",
expected: "-test.run=^TestOne$/^SubtestA$",
},
}
for name := range testCases {
t.Run(name, func(t *testing.T) {
fn(t, testCases[name])
})
}
}
func TestRerunFailed_ReturnsAnErrorWhenTheLastTestIsSuccessful(t *testing.T) {
type result struct {
out string
err error
}
jsonFailed := `{"Package": "pkg", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "fail"}
{"Package": "pkg", "Action": "fail"}
`
events := []result{
{out: jsonFailed, err: newExitCode("run-failed-1", 1)},
{out: jsonFailed, err: newExitCode("run-failed-2", 1)},
{out: jsonFailed, err: newExitCode("run-failed-3", 1)},
{
out: `{"Package": "pkg", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "pass"}
{"Package": "pkg", "Action": "pass"}
`,
},
}
fn := func(args []string) *proc {
next := events[0]
events = events[1:]
return &proc{
cmd: fakeWaiter{result: next.err},
stdout: strings.NewReader(next.out),
stderr: bytes.NewReader(nil),
}
}
reset := patchStartGoTestFn(fn)
defer reset()
stdout := new(bytes.Buffer)
ctx := context.Background()
opts := &options{
rerunFailsMaxInitialFailures: 10,
rerunFailsMaxAttempts: 2,
stdout: stdout,
}
cfg := testjson.ScanConfig{
Execution: newExecutionWithTwoFailures(t),
Handler: noopHandler{},
}
err := rerunFailed(ctx, opts, cfg)
assert.Error(t, err, "run-failed-3")
}
func patchStartGoTestFn(f func(args []string) *proc) func() {
orig := startGoTestFn
startGoTestFn = func(ctx context.Context, args []string) (*proc, error) {
return f(args), nil
}
return func() {
startGoTestFn = orig
}
}
func newExecutionWithTwoFailures(t *testing.T) *testjson.Execution {
t.Helper()
out := `{"Package": "pkg", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "run"}
{"Package": "pkg", "Test": "TestOne", "Action": "fail"}
{"Package": "pkg", "Test": "TestTwo", "Action": "run"}
{"Package": "pkg", "Test": "TestTwo", "Action": "fail"}
{"Package": "pkg", "Action": "fail"}
`
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: strings.NewReader(out),
Stderr: strings.NewReader(""),
})
assert.NilError(t, err)
return exec
}
type fakeWaiter struct {
result error
}
func (f fakeWaiter) Wait() error {
return f.result
}
type exitCodeError struct {
error
code int
}
func (e exitCodeError) ExitCode() int {
return e.code
}
func newExitCode(msg string, code int) error {
return exitCodeError{error: fmt.Errorf(msg), code: code}
}
type noopHandler struct{}
func (s noopHandler) Event(testjson.TestEvent, *testjson.Execution) error {
return nil
}
func (s noopHandler) Err(string) error {
return nil
}
gotestsum-1.8.2/cmd/testdata/ 0000775 0000000 0000000 00000000000 14274752762 0016150 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/TestWriteRerunFailsReport-expected 0000664 0000000 0000000 00000000354 14274752762 0025015 0 ustar 00root root 0000000 0000000 gotest.tools/gotestsum/testdata/e2e/flaky.TestFailsOften: 4 runs, 3 failures
gotest.tools/gotestsum/testdata/e2e/flaky.TestFailsRarely: 2 runs, 1 failures
gotest.tools/gotestsum/testdata/e2e/flaky.TestFailsSometimes: 3 runs, 2 failures
gotestsum-1.8.2/cmd/testdata/TestWriteRerunFailsReport_HandlesMissingActionRunEvents-expected 0000664 0000000 0000000 00000000476 14274752762 0033022 0 ustar 00root root 0000000 0000000 github.com/hashicorp/consul/test/integration/connect/envoy.TestEnvoy: 5 runs, 5 failures
github.com/hashicorp/consul/test/integration/connect/envoy.TestEnvoy/case-ent-cross-namespaces: 3 runs, 3 failures
github.com/hashicorp/consul/test/integration/connect/envoy.TestEnvoy/case-ent-intra-namespace: 3 runs, 3 failures
gotestsum-1.8.2/cmd/testdata/e2e/ 0000775 0000000 0000000 00000000000 14274752762 0016623 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/e2e/expected/ 0000775 0000000 0000000 00000000000 14274752762 0020424 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_IgnoresWarnings 0000664 0000000 0000000 00000001101 14274752762 0024752 0 ustar 00root root 0000000 0000000 === RUN TestIgnoreWarnings
--- FAIL: TestIgnoreWarnings
FAIL cmd/testdata/e2e/ignore_warnings.TestIgnoreWarnings
coverage: [no statements]
FAIL cmd/testdata/e2e/ignore_warnings
DONE 1 tests, 1 failure
=== RUN TestIgnoreWarnings
--- FAIL: TestIgnoreWarnings
FAIL cmd/testdata/e2e/ignore_warnings.TestIgnoreWarnings (re-run 1)
coverage: [no statements]
FAIL cmd/testdata/e2e/ignore_warnings
=== Failed
=== FAIL: cmd/testdata/e2e/ignore_warnings TestIgnoreWarnings
=== FAIL: cmd/testdata/e2e/ignore_warnings TestIgnoreWarnings (re-run 1)
DONE 2 runs, 2 tests, 2 failures
gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_MaxFails_EndTestRun 0000664 0000000 0000000 00000000346 14274752762 0025464 0 ustar 00root root 0000000 0000000
=== Failed
=== FAIL: cmd/testdata/e2e/flaky TestFailsRarely
SEED: 0
flaky_test.go:51: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsSometimes
SEED: 0
flaky_test.go:58: not this time
DONE 3 tests, 2 failures
gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_RerunFails/ 0000775 0000000 0000000 00000000000 14274752762 0023771 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_RerunFails/first_run_has_errors,_abort_rerun 0000664 0000000 0000000 00000000146 14274752762 0032635 0 ustar 00root root 0000000 0000000
=== Errors
../testjson/internal/broken/broken.go:5:21: undefined: somepackage
DONE 0 tests, 1 error
first_run_has_errors,_abort_rerun-go1.13 0000664 0000000 0000000 00000000146 14274752762 0033464 0 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_RerunFails
=== Errors
../testjson/internal/broken/broken.go:5:21: undefined: somepackage
DONE 0 tests, 1 error
gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_RerunFails/reruns_continues_to_fail 0000664 0000000 0000000 00000005126 14274752762 0031022 0 ustar 00root root 0000000 0000000 PASS cmd/testdata/e2e/flaky.TestAlwaysPasses
=== RUN TestFailsRarely
SEED: 0
flaky_test.go:51: not this time
--- FAIL: TestFailsRarely
FAIL cmd/testdata/e2e/flaky.TestFailsRarely
=== RUN TestFailsSometimes
SEED: 0
flaky_test.go:58: not this time
--- FAIL: TestFailsSometimes
FAIL cmd/testdata/e2e/flaky.TestFailsSometimes
PASS cmd/testdata/e2e/flaky.TestFailsOften/subtest_always_passes
=== RUN TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail
=== RUN TestFailsOften
SEED: 0
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften
PASS cmd/testdata/e2e/flaky.TestFailsOftenDoesNotPrefixMatch
PASS cmd/testdata/e2e/flaky.TestFailsSometimesDoesNotPrefixMatch
FAIL cmd/testdata/e2e/flaky
DONE 8 tests, 4 failures
PASS cmd/testdata/e2e/flaky.TestFailsRarely (re-run 1)
PASS cmd/testdata/e2e/flaky
PASS cmd/testdata/e2e/flaky.TestFailsSometimes (re-run 1)
PASS cmd/testdata/e2e/flaky
=== RUN TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 1)
=== RUN TestFailsOften
SEED: 3
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 1)
FAIL cmd/testdata/e2e/flaky
DONE 2 runs, 12 tests, 6 failures
=== RUN TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 2)
=== RUN TestFailsOften
SEED: 4
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 2)
FAIL cmd/testdata/e2e/flaky
=== Failed
=== FAIL: cmd/testdata/e2e/flaky TestFailsRarely
SEED: 0
flaky_test.go:51: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsSometimes
SEED: 0
flaky_test.go:58: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften
SEED: 0
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 1)
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 1)
SEED: 3
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 2)
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 2)
SEED: 4
DONE 3 runs, 14 tests, 8 failures
gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_RerunFails/reruns_continues_to_fail-go1.13 0000664 0000000 0000000 00000005156 14274752762 0031733 0 ustar 00root root 0000000 0000000 PASS cmd/testdata/e2e/flaky.TestAlwaysPasses
=== RUN TestFailsRarely
SEED: 0
--- FAIL: TestFailsRarely
flaky_test.go:51: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsRarely
=== RUN TestFailsSometimes
SEED: 0
--- FAIL: TestFailsSometimes
flaky_test.go:58: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsSometimes
PASS cmd/testdata/e2e/flaky.TestFailsOften/subtest_always_passes
=== RUN TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail
=== RUN TestFailsOften
SEED: 0
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften
PASS cmd/testdata/e2e/flaky.TestFailsOftenDoesNotPrefixMatch
PASS cmd/testdata/e2e/flaky.TestFailsSometimesDoesNotPrefixMatch
FAIL cmd/testdata/e2e/flaky
DONE 8 tests, 4 failures
PASS cmd/testdata/e2e/flaky.TestFailsRarely (re-run 1)
PASS cmd/testdata/e2e/flaky
PASS cmd/testdata/e2e/flaky.TestFailsSometimes (re-run 1)
PASS cmd/testdata/e2e/flaky
=== RUN TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 1)
=== RUN TestFailsOften
SEED: 3
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 1)
FAIL cmd/testdata/e2e/flaky
DONE 2 runs, 12 tests, 6 failures
=== RUN TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 2)
=== RUN TestFailsOften
SEED: 4
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 2)
FAIL cmd/testdata/e2e/flaky
=== Failed
=== FAIL: cmd/testdata/e2e/flaky TestFailsRarely
SEED: 0
flaky_test.go:51: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsSometimes
SEED: 0
flaky_test.go:58: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften
SEED: 0
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 1)
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 1)
SEED: 3
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 2)
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 2)
SEED: 4
DONE 3 runs, 14 tests, 8 failures
gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_RerunFails/reruns_until_success 0000664 0000000 0000000 00000006555 14274752762 0030210 0 ustar 00root root 0000000 0000000 PASS cmd/testdata/e2e/flaky.TestAlwaysPasses
=== RUN TestFailsRarely
SEED: 0
flaky_test.go:51: not this time
--- FAIL: TestFailsRarely
FAIL cmd/testdata/e2e/flaky.TestFailsRarely
=== RUN TestFailsSometimes
SEED: 0
flaky_test.go:58: not this time
--- FAIL: TestFailsSometimes
FAIL cmd/testdata/e2e/flaky.TestFailsSometimes
PASS cmd/testdata/e2e/flaky.TestFailsOften/subtest_always_passes
=== RUN TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail
=== RUN TestFailsOften
SEED: 0
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften
PASS cmd/testdata/e2e/flaky.TestFailsOftenDoesNotPrefixMatch
PASS cmd/testdata/e2e/flaky.TestFailsSometimesDoesNotPrefixMatch
FAIL cmd/testdata/e2e/flaky
DONE 8 tests, 4 failures
PASS cmd/testdata/e2e/flaky.TestFailsRarely (re-run 1)
PASS cmd/testdata/e2e/flaky
PASS cmd/testdata/e2e/flaky.TestFailsSometimes (re-run 1)
PASS cmd/testdata/e2e/flaky
=== RUN TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 1)
=== RUN TestFailsOften
SEED: 3
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 1)
FAIL cmd/testdata/e2e/flaky
DONE 2 runs, 12 tests, 6 failures
=== RUN TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 2)
=== RUN TestFailsOften
SEED: 4
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 2)
FAIL cmd/testdata/e2e/flaky
DONE 3 runs, 14 tests, 8 failures
=== RUN TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 3)
=== RUN TestFailsOften
SEED: 5
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 3)
FAIL cmd/testdata/e2e/flaky
DONE 4 runs, 16 tests, 10 failures
PASS cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 4)
PASS cmd/testdata/e2e/flaky.TestFailsOften (re-run 4)
PASS cmd/testdata/e2e/flaky
=== Failed
=== FAIL: cmd/testdata/e2e/flaky TestFailsRarely
SEED: 0
flaky_test.go:51: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsSometimes
SEED: 0
flaky_test.go:58: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften
SEED: 0
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 1)
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 1)
SEED: 3
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 2)
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 2)
SEED: 4
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 3)
flaky_test.go:68: not this time
--- FAIL: TestFailsOften/subtest_may_fail
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 3)
SEED: 5
DONE 5 runs, 18 tests, 10 failures
gotestsum-1.8.2/cmd/testdata/e2e/expected/TestE2E_RerunFails/reruns_until_success-go1.13 0000664 0000000 0000000 00000006615 14274752762 0031113 0 ustar 00root root 0000000 0000000 PASS cmd/testdata/e2e/flaky.TestAlwaysPasses
=== RUN TestFailsRarely
SEED: 0
--- FAIL: TestFailsRarely
flaky_test.go:51: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsRarely
=== RUN TestFailsSometimes
SEED: 0
--- FAIL: TestFailsSometimes
flaky_test.go:58: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsSometimes
PASS cmd/testdata/e2e/flaky.TestFailsOften/subtest_always_passes
=== RUN TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail
=== RUN TestFailsOften
SEED: 0
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften
PASS cmd/testdata/e2e/flaky.TestFailsOftenDoesNotPrefixMatch
PASS cmd/testdata/e2e/flaky.TestFailsSometimesDoesNotPrefixMatch
FAIL cmd/testdata/e2e/flaky
DONE 8 tests, 4 failures
PASS cmd/testdata/e2e/flaky.TestFailsRarely (re-run 1)
PASS cmd/testdata/e2e/flaky
PASS cmd/testdata/e2e/flaky.TestFailsSometimes (re-run 1)
PASS cmd/testdata/e2e/flaky
=== RUN TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 1)
=== RUN TestFailsOften
SEED: 3
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 1)
FAIL cmd/testdata/e2e/flaky
DONE 2 runs, 12 tests, 6 failures
=== RUN TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 2)
=== RUN TestFailsOften
SEED: 4
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 2)
FAIL cmd/testdata/e2e/flaky
DONE 3 runs, 14 tests, 8 failures
=== RUN TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
FAIL cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 3)
=== RUN TestFailsOften
SEED: 5
--- FAIL: TestFailsOften
FAIL cmd/testdata/e2e/flaky.TestFailsOften (re-run 3)
FAIL cmd/testdata/e2e/flaky
DONE 4 runs, 16 tests, 10 failures
PASS cmd/testdata/e2e/flaky.TestFailsOften/subtest_may_fail (re-run 4)
PASS cmd/testdata/e2e/flaky.TestFailsOften (re-run 4)
PASS cmd/testdata/e2e/flaky
=== Failed
=== FAIL: cmd/testdata/e2e/flaky TestFailsRarely
SEED: 0
flaky_test.go:51: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsSometimes
SEED: 0
flaky_test.go:58: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften
SEED: 0
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 1)
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 1)
SEED: 3
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 2)
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 2)
SEED: 4
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften/subtest_may_fail (re-run 3)
--- FAIL: TestFailsOften/subtest_may_fail
flaky_test.go:68: not this time
=== FAIL: cmd/testdata/e2e/flaky TestFailsOften (re-run 3)
SEED: 5
DONE 5 runs, 18 tests, 10 failures
gotestsum-1.8.2/cmd/testdata/e2e/flaky/ 0000775 0000000 0000000 00000000000 14274752762 0017731 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/e2e/flaky/flaky_test.go 0000664 0000000 0000000 00000002476 14274752762 0022436 0 ustar 00root root 0000000 0000000 // +build testdata
package flaky
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"sync"
"testing"
)
var seed int
var seedfile = seedFile()
var once = new(sync.Once)
func setup(t *testing.T) {
once.Do(func() {
raw, err := ioutil.ReadFile(seedfile)
if err != nil {
t.Fatalf("failed to read seed: %v", err)
}
n, err := strconv.ParseInt(string(raw), 10, 64)
if err != nil {
t.Fatalf("failed to parse seed: %v", err)
}
seed = int(n)
err = ioutil.WriteFile(seedfile, []byte(strconv.Itoa(seed+1)), 0644)
if err != nil {
t.Fatalf("failed to write seed: %v", err)
}
})
fmt.Fprintln(os.Stderr, "SEED: ", seed)
}
func seedFile() string {
if name, ok := os.LookupEnv("TEST_SEEDFILE"); ok {
return name
}
return "/tmp/gotestsum-flaky-seedfile"
}
func TestAlwaysPasses(t *testing.T) {
}
func TestFailsRarely(t *testing.T) {
setup(t)
if seed%20 != 1 {
t.Fatal("not this time")
}
}
func TestFailsSometimes(t *testing.T) {
setup(t)
if seed%4 != 2 {
t.Fatal("not this time")
}
}
func TestFailsOften(t *testing.T) {
setup(t)
t.Run("subtest always passes", func(t *testing.T) {})
t.Run("subtest may fail", func(t *testing.T) {
if seed%20 != 6 {
t.Fatal("not this time")
}
})
}
func TestFailsOftenDoesNotPrefixMatch(t *testing.T) {}
func TestFailsSometimesDoesNotPrefixMatch(t *testing.T) {}
gotestsum-1.8.2/cmd/testdata/e2e/ignore_warnings/ 0000775 0000000 0000000 00000000000 14274752762 0022016 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/e2e/ignore_warnings/ignore_warnings.go 0000664 0000000 0000000 00000000030 14274752762 0025531 0 ustar 00root root 0000000 0000000 package ignore_warnings
gotestsum-1.8.2/cmd/testdata/e2e/ignore_warnings/ignore_warnings_test.go 0000664 0000000 0000000 00000000137 14274752762 0026600 0 ustar 00root root 0000000 0000000 package ignore_warnings
import "testing"
func TestIgnoreWarnings(t *testing.T) {
t.Fail()
}
gotestsum-1.8.2/cmd/testdata/event-handler-missing-test-fail-expected 0000664 0000000 0000000 00000002405 14274752762 0025764 0 ustar 00root root 0000000 0000000 FAIL gotest.tools/v3/poll
=== RUN TestWaitOn_WithCompare
panic: runtime error: index out of range [1] with length 1
goroutine 7 [running]:
gotest.tools/v3/internal/assert.ArgsFromComparisonCall(0xc0000552a0, 0x1, 0x1, 0x1, 0x0, 0x0)
/home/daniel/pers/code/gotest.tools/internal/assert/result.go:102 +0x9f
gotest.tools/v3/internal/assert.runComparison(0x6bcb80, 0xc00000e180, 0x67dee8, 0xc00007a9f0, 0x0, 0x0, 0x0, 0x7f7f4fb6d108)
/home/daniel/pers/code/gotest.tools/internal/assert/result.go:34 +0x2b1
gotest.tools/v3/internal/assert.Eval(0x6bcb80, 0xc00000e180, 0x67dee8, 0x627660, 0xc00007a9f0, 0x0, 0x0, 0x0, 0x642c60)
/home/daniel/pers/code/gotest.tools/internal/assert/assert.go:56 +0x2e4
gotest.tools/v3/poll.Compare(0xc00007a9f0, 0x6b74a0, 0x618a60)
/home/daniel/pers/code/gotest.tools/poll/poll.go:151 +0x81
gotest.tools/v3/poll.TestWaitOn_WithCompare.func1(0x6be4c0, 0xc00016c240, 0xc00016c240, 0x6be4c0)
/home/daniel/pers/code/gotest.tools/poll/poll_test.go:81 +0x58
gotest.tools/v3/poll.WaitOn.func1(0xc00001e3c0, 0x67df50, 0x6c1960, 0xc00016c240)
/home/daniel/pers/code/gotest.tools/poll/poll.go:125 +0x62
created by gotest.tools/v3/poll.WaitOn
/home/daniel/pers/code/gotest.tools/poll/poll.go:124 +0x16f
FAIL gotest.tools/v3/poll.TestWaitOn_WithCompare (-1.00s)
gotestsum-1.8.2/cmd/testdata/go-test-json-flaky-rerun.out 0000664 0000000 0000000 00000031116 14274752762 0023471 0 ustar 00root root 0000000 0000000 {"Time":"2020-06-21T21:12:10.815884042-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestAlwaysPasses"}
{"Time":"2020-06-21T21:12:10.816009964-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestAlwaysPasses","Output":"=== RUN TestAlwaysPasses\n"}
{"Time":"2020-06-21T21:12:10.816024167-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestAlwaysPasses","Output":"--- PASS: TestAlwaysPasses (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.81603019-04:00","Action":"pass","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestAlwaysPasses","Elapsed":0}
{"Time":"2020-06-21T21:12:10.81604135-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely"}
{"Time":"2020-06-21T21:12:10.816045457-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Output":"=== RUN TestFailsRarely\n"}
{"Time":"2020-06-21T21:12:10.816049373-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Output":"SEED: 0\n"}
{"Time":"2020-06-21T21:12:10.816063218-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Output":" TestFailsRarely: flaky_test.go:51: not this time\n"}
{"Time":"2020-06-21T21:12:10.816068373-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Output":"--- FAIL: TestFailsRarely (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.816072877-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Elapsed":0}
{"Time":"2020-06-21T21:12:10.81607599-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes"}
{"Time":"2020-06-21T21:12:10.81607897-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"=== RUN TestFailsSometimes\n"}
{"Time":"2020-06-21T21:12:10.816082575-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"SEED: 0\n"}
{"Time":"2020-06-21T21:12:10.816086165-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":" TestFailsSometimes: flaky_test.go:58: not this time\n"}
{"Time":"2020-06-21T21:12:10.8160903-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"--- FAIL: TestFailsSometimes (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.816094667-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Elapsed":0}
{"Time":"2020-06-21T21:12:10.816098149-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften"}
{"Time":"2020-06-21T21:12:10.816101281-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"=== RUN TestFailsOften\n"}
{"Time":"2020-06-21T21:12:10.81610482-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"SEED: 0\n"}
{"Time":"2020-06-21T21:12:10.816108597-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":" TestFailsOften: flaky_test.go:65: not this time\n"}
{"Time":"2020-06-21T21:12:10.816112698-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"--- FAIL: TestFailsOften (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.816116235-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Elapsed":0}
{"Time":"2020-06-21T21:12:10.816119536-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOftenDoesNotPrefixMatch"}
{"Time":"2020-06-21T21:12:10.816122935-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOftenDoesNotPrefixMatch","Output":"=== RUN TestFailsOftenDoesNotPrefixMatch\n"}
{"Time":"2020-06-21T21:12:10.816127081-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOftenDoesNotPrefixMatch","Output":"--- PASS: TestFailsOftenDoesNotPrefixMatch (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.816131578-04:00","Action":"pass","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOftenDoesNotPrefixMatch","Elapsed":0}
{"Time":"2020-06-21T21:12:10.816135262-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimesDoesNotPrefixMatch"}
{"Time":"2020-06-21T21:12:10.816139844-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimesDoesNotPrefixMatch","Output":"=== RUN TestFailsSometimesDoesNotPrefixMatch\n"}
{"Time":"2020-06-21T21:12:10.816149774-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimesDoesNotPrefixMatch","Output":"--- PASS: TestFailsSometimesDoesNotPrefixMatch (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.816155804-04:00","Action":"pass","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimesDoesNotPrefixMatch","Elapsed":0}
{"Time":"2020-06-21T21:12:10.816159612-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"FAIL\n"}
{"Time":"2020-06-21T21:12:10.816211999-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"FAIL\tgotest.tools/gotestsum/testdata/e2e/flaky\t0.001s\n"}
{"Time":"2020-06-21T21:12:10.816224311-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Elapsed":0.001}
{"Time":"2020-06-21T21:12:10.985779906-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely"}
{"Time":"2020-06-21T21:12:10.985890459-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Output":"=== RUN TestFailsRarely\n"}
{"Time":"2020-06-21T21:12:10.985902826-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Output":"SEED: 1\n"}
{"Time":"2020-06-21T21:12:10.985911982-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Output":"--- PASS: TestFailsRarely (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.985916034-04:00","Action":"pass","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsRarely","Elapsed":0}
{"Time":"2020-06-21T21:12:10.985923087-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes"}
{"Time":"2020-06-21T21:12:10.985926869-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"=== RUN TestFailsSometimes\n"}
{"Time":"2020-06-21T21:12:10.985930857-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"SEED: 1\n"}
{"Time":"2020-06-21T21:12:10.985934726-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":" TestFailsSometimes: flaky_test.go:58: not this time\n"}
{"Time":"2020-06-21T21:12:10.985939499-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"--- FAIL: TestFailsSometimes (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.985943642-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Elapsed":0}
{"Time":"2020-06-21T21:12:10.985945782-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften"}
{"Time":"2020-06-21T21:12:10.985947733-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"=== RUN TestFailsOften\n"}
{"Time":"2020-06-21T21:12:10.985949865-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"SEED: 1\n"}
{"Time":"2020-06-21T21:12:10.985952001-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":" TestFailsOften: flaky_test.go:65: not this time\n"}
{"Time":"2020-06-21T21:12:10.985954479-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"--- FAIL: TestFailsOften (0.00s)\n"}
{"Time":"2020-06-21T21:12:10.985956607-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Elapsed":0}
{"Time":"2020-06-21T21:12:10.985958719-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"FAIL\n"}
{"Time":"2020-06-21T21:12:10.986048472-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"FAIL\tgotest.tools/gotestsum/testdata/e2e/flaky\t0.001s\n"}
{"Time":"2020-06-21T21:12:10.986068802-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Elapsed":0.001}
{"Time":"2020-06-21T21:12:11.147554767-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes"}
{"Time":"2020-06-21T21:12:11.147676606-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"=== RUN TestFailsSometimes\n"}
{"Time":"2020-06-21T21:12:11.14768948-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"SEED: 2\n"}
{"Time":"2020-06-21T21:12:11.147700275-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Output":"--- PASS: TestFailsSometimes (0.00s)\n"}
{"Time":"2020-06-21T21:12:11.147705201-04:00","Action":"pass","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsSometimes","Elapsed":0}
{"Time":"2020-06-21T21:12:11.147710442-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften"}
{"Time":"2020-06-21T21:12:11.147712827-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"=== RUN TestFailsOften\n"}
{"Time":"2020-06-21T21:12:11.147715318-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"SEED: 2\n"}
{"Time":"2020-06-21T21:12:11.147717797-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":" TestFailsOften: flaky_test.go:65: not this time\n"}
{"Time":"2020-06-21T21:12:11.147721095-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"--- FAIL: TestFailsOften (0.00s)\n"}
{"Time":"2020-06-21T21:12:11.147723329-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Elapsed":0}
{"Time":"2020-06-21T21:12:11.147725838-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"FAIL\n"}
{"Time":"2020-06-21T21:12:11.14783256-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"FAIL\tgotest.tools/gotestsum/testdata/e2e/flaky\t0.001s\n"}
{"Time":"2020-06-21T21:12:11.147849384-04:00","Action":"fail","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Elapsed":0.001}
{"Time":"2020-06-21T21:12:11.226137617-04:00","Action":"run","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften"}
{"Time":"2020-06-21T21:12:11.226247164-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"=== RUN TestFailsOften\n"}
{"Time":"2020-06-21T21:12:11.226256816-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"SEED: 14\n"}
{"Time":"2020-06-21T21:12:11.226264297-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Output":"--- PASS: TestFailsOften (0.00s)\n"}
{"Time":"2020-06-21T21:12:11.226266905-04:00","Action":"pass","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Test":"TestFailsOften","Elapsed":0}
{"Time":"2020-06-21T21:12:11.22627268-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"PASS\n"}
{"Time":"2020-06-21T21:12:11.226275849-04:00","Action":"output","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Output":"ok \tgotest.tools/gotestsum/testdata/e2e/flaky\t(cached)\n"}
{"Time":"2020-06-21T21:12:11.226279592-04:00","Action":"pass","Package":"gotest.tools/gotestsum/testdata/e2e/flaky","Elapsed":0}
gotestsum-1.8.2/cmd/testdata/go-test-missing-run-events.out 0000664 0000000 0000000 00000045344 14274752762 0024050 0 ustar 00root root 0000000 0000000 {"Time":"2020-10-22T00:18:23.836961241Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy"}
{"Time":"2020-10-22T00:19:07.176212704Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-basic"}
{"Time":"2020-10-22T00:19:30.595333272Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-centralconf"}
{"Time":"2020-10-22T00:20:00.782717543Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-dc-failover-gateways-none"}
{"Time":"2020-10-22T00:21:02.358098165Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-dc-failover-gateways-remote"}
{"Time":"2020-10-22T00:22:04.420054524Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-defaultsubset"}
{"Time":"2020-10-22T00:22:38.212414238Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-features"}
{"Time":"2020-10-22T00:23:09.301536045Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-subset-onlypassing"}
{"Time":"2020-10-22T00:23:41.435709605Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-subset-redirect"}
{"Time":"2020-10-22T00:24:16.86716551Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-svc-failover"}
{"Time":"2020-10-22T00:25:01.571200729Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-svc-redirect-http"}
{"Time":"2020-10-22T00:25:32.020127536Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-svc-redirect-tcp"}
{"Time":"2020-10-22T00:26:00.566029647Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-router-features"}
{"Time":"2020-10-22T00:26:34.976920948Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-splitter-features"}
{"Time":"2020-10-22T00:27:09.988011217Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-consul-exec"}
{"Time":"2020-10-22T00:27:36.461488754Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-dogstatsd-udp"}
{"Time":"2020-10-22T00:28:03.709314906Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-cross-namespaces"}
{"Time":"2020-10-22T00:28:54.659486846Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-gateways-local"}
{"Time":"2020-10-22T00:29:46.702588149Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-gateways-remote"}
{"Time":"2020-10-22T00:30:31.2432353Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-intra-namespace"}
{"Time":"2020-10-22T00:31:23.487327584Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-namespace-discovery-chain"}
{"Time":"2020-10-22T00:32:21.2756168Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-gateway-without-services"}
{"Time":"2020-10-22T00:32:47.742941478Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-gateways-local"}
{"Time":"2020-10-22T00:33:42.557718152Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-gateways-remote"}
{"Time":"2020-10-22T00:34:23.708756405Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-grpc"}
{"Time":"2020-10-22T00:34:51.466793964Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-http"}
{"Time":"2020-10-22T00:35:17.958659061Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-http-badauthz"}
{"Time":"2020-10-22T00:35:45.024612888Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-grpc"}
{"Time":"2020-10-22T00:36:16.626166436Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-http"}
{"Time":"2020-10-22T00:36:49.578681889Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-multiple-services"}
{"Time":"2020-10-22T00:37:20.475516355Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-simple"}
{"Time":"2020-10-22T00:37:52.651634624Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-tls"}
{"Time":"2020-10-22T00:38:23.194366198Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-mesh-gateways-resolver"}
{"Time":"2020-10-22T00:39:16.834144235Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-l7-intentions"}
{"Time":"2020-10-22T00:39:43.921073741Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-multidc-rsa-ca"}
{"Time":"2020-10-22T00:40:23.323700733Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-prometheus"}
{"Time":"2020-10-22T00:40:47.546267846Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-stats-proxy"}
{"Time":"2020-10-22T00:41:11.955878675Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-statsd-udp"}
{"Time":"2020-10-22T00:41:40.749502998Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-hostnames"}
{"Time":"2020-10-22T00:42:05.839131501Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-simple"}
{"Time":"2020-10-22T00:42:33.162515422Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-subsets"}
{"Time":"2020-10-22T00:43:01.841085827Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-without-services"}
{"Time":"2020-10-22T00:43:24.22276926Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-upstream-config"}
{"Time":"2020-10-22T00:43:49.179125398Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-wanfed-gw"}
{"Time":"2020-10-22T00:44:46.641753773Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-zipkin"}
{"Time":"2020-10-22T00:45:20.108579041Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-badauthz","Elapsed":38.97}
{"Time":"2020-10-22T00:45:20.108606Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-basic","Elapsed":23.42}
{"Time":"2020-10-22T00:45:20.108618156Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-centralconf","Elapsed":30.19}
{"Time":"2020-10-22T00:45:20.108629906Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-dc-failover-gateways-none","Elapsed":61.58}
{"Time":"2020-10-22T00:45:20.108640546Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-dc-failover-gateways-remote","Elapsed":62.06}
{"Time":"2020-10-22T00:45:20.108652253Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-defaultsubset","Elapsed":33.79}
{"Time":"2020-10-22T00:45:20.108662762Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-features","Elapsed":31.09}
{"Time":"2020-10-22T00:45:20.108672795Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-subset-onlypassing","Elapsed":32.13}
{"Time":"2020-10-22T00:45:20.108683532Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-subset-redirect","Elapsed":35.43}
{"Time":"2020-10-22T00:45:20.108693772Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-svc-failover","Elapsed":44.7}
{"Time":"2020-10-22T00:45:20.108706751Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-svc-redirect-http","Elapsed":30.45}
{"Time":"2020-10-22T00:45:20.10871706Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-resolver-svc-redirect-tcp","Elapsed":28.55}
{"Time":"2020-10-22T00:45:20.10872787Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-router-features","Elapsed":34.41}
{"Time":"2020-10-22T00:45:20.108737871Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-cfg-splitter-features","Elapsed":35.01}
{"Time":"2020-10-22T00:45:20.108748902Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-consul-exec","Elapsed":26.47}
{"Time":"2020-10-22T00:45:20.108759692Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-dogstatsd-udp","Elapsed":27.25}
{"Time":"2020-10-22T00:45:20.108770081Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-cross-namespaces","Elapsed":50.95}
{"Time":"2020-10-22T00:45:20.108780941Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-gateways-local","Elapsed":52.04}
{"Time":"2020-10-22T00:45:20.108791687Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-gateways-remote","Elapsed":44.54}
{"Time":"2020-10-22T00:45:20.108802026Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-intra-namespace","Elapsed":52.24}
{"Time":"2020-10-22T00:45:20.108812518Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-namespace-discovery-chain","Elapsed":57.79}
{"Time":"2020-10-22T00:45:20.108823218Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-gateway-without-services","Elapsed":26.47}
{"Time":"2020-10-22T00:45:20.108834724Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-gateways-local","Elapsed":54.81}
{"Time":"2020-10-22T00:45:20.108845012Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-gateways-remote","Elapsed":41.15}
{"Time":"2020-10-22T00:45:20.108856989Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-grpc","Elapsed":27.76}
{"Time":"2020-10-22T00:45:20.108867426Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-http","Elapsed":26.49}
{"Time":"2020-10-22T00:45:20.108878346Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-http-badauthz","Elapsed":27.07}
{"Time":"2020-10-22T00:45:20.108888697Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-grpc","Elapsed":31.6}
{"Time":"2020-10-22T00:45:20.108898988Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-http","Elapsed":32.95}
{"Time":"2020-10-22T00:45:20.108910108Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-multiple-services","Elapsed":30.9}
{"Time":"2020-10-22T00:45:20.108920326Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-simple","Elapsed":32.18}
{"Time":"2020-10-22T00:45:20.10893017Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-gateway-tls","Elapsed":30.54}
{"Time":"2020-10-22T00:45:20.108940126Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ingress-mesh-gateways-resolver","Elapsed":53.64}
{"Time":"2020-10-22T00:45:20.108949868Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-l7-intentions","Elapsed":27.09}
{"Time":"2020-10-22T00:45:20.108959852Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-multidc-rsa-ca","Elapsed":39.4}
{"Time":"2020-10-22T00:45:20.108969973Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-prometheus","Elapsed":24.22}
{"Time":"2020-10-22T00:45:20.108979797Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-stats-proxy","Elapsed":24.41}
{"Time":"2020-10-22T00:45:20.108991334Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-statsd-udp","Elapsed":28.79}
{"Time":"2020-10-22T00:45:20.109001312Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-hostnames","Elapsed":25.09}
{"Time":"2020-10-22T00:45:20.109011226Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-simple","Elapsed":27.32}
{"Time":"2020-10-22T00:45:20.109021026Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-subsets","Elapsed":28.68}
{"Time":"2020-10-22T00:45:20.109031199Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-terminating-gateway-without-services","Elapsed":22.38}
{"Time":"2020-10-22T00:45:20.109041152Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-upstream-config","Elapsed":24.96}
{"Time":"2020-10-22T00:45:20.109050975Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-wanfed-gw","Elapsed":57.46}
{"Time":"2020-10-22T00:45:20.109060353Z","Action":"pass","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-zipkin","Elapsed":31.09}
{"Time":"2020-10-22T00:45:20.109064921Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy","Elapsed":1616.27}
{"Time":"2020-10-22T00:45:20.109148885Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Elapsed":1616.276}
{"Time":"2020-10-22T00:45:20.673179245Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy"}
{"Time":"2020-10-22T00:46:10.218981797Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-cross-namespaces","Elapsed":44.32}
{"Time":"2020-10-22T00:46:10.218989964Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy","Elapsed":49.55}
{"Time":"2020-10-22T00:46:10.219542662Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Elapsed":49.549}
{"Time":"2020-10-22T00:46:10.784497983Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy"}
{"Time":"2020-10-22T00:47:14.789854054Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-intra-namespace","Elapsed":58.85}
{"Time":"2020-10-22T00:47:14.789862576Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy","Elapsed":64.01}
{"Time":"2020-10-22T00:47:14.790364204Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Elapsed":64.009}
{"Time":"2020-10-22T00:47:15.354865459Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy"}
{"Time":"2020-10-22T00:48:04.07040834Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-cross-namespaces","Elapsed":43.64}
{"Time":"2020-10-22T00:48:04.070414141Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy","Elapsed":48.72}
{"Time":"2020-10-22T00:48:04.071038715Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Elapsed":48.719}
{"Time":"2020-10-22T00:48:04.708139615Z","Action":"run","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy"}
{"Time":"2020-10-22T00:48:58.010967512Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy/case-ent-intra-namespace","Elapsed":48.1}
{"Time":"2020-10-22T00:48:58.010973222Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Test":"TestEnvoy","Elapsed":53.3}
{"Time":"2020-10-22T00:48:58.011515541Z","Action":"fail","Package":"github.com/hashicorp/consul/test/integration/connect/envoy","Elapsed":53.306}
gotestsum-1.8.2/cmd/testdata/gotestsum-help-text 0000664 0000000 0000000 00000004770 14274752762 0022045 0 ustar 00root root 0000000 0000000 Usage:
gotestsum [flags] [--] [go test flags]
gotestsum [command]
Flags:
--debug enabled debug logging
-f, --format string print format of test input (default "short")
--hide-summary summary hide sections of the summary: skipped,failed,errors,output (default none)
--jsonfile string write all TestEvents to file
--junitfile string write a JUnit XML file
--junitfile-project-name string name of the project used in the junit.xml file
--junitfile-testcase-classname field-format format the testcase classname field as: full, relative, short (default full)
--junitfile-testsuite-name field-format format the testsuite name field as: full, relative, short (default full)
--max-fails int end the test run after this number of failures
--no-color disable color output (default true)
--packages list space separated list of package to test
--post-run-command command command to run after the tests have completed
--raw-command don't prepend 'go test -json' to the 'go test' command
--rerun-fails int[=2] rerun failed tests until they all pass, or attempts exceeds maximum. Defaults to max 2 reruns when enabled.
--rerun-fails-max-failures int do not rerun any tests if the initial run has more than this number of failures (default 10)
--rerun-fails-report string write a report to the file, of the tests that were rerun
--version show version and exit
--watch watch go files, and run tests when a file is modified
Formats:
dots print a character for each test
dots-v2 experimental dots format, one package per line
pkgname print a line for each package
pkgname-and-test-fails print a line for each package and failed test output
testname print a line for each test and package
standard-quiet standard go test format
standard-verbose standard go test -v format
Commands:
tool tools for working with test2json output
help print this help next
gotestsum-1.8.2/cmd/testdata/post-run-hook-expected 0000664 0000000 0000000 00000000222 14274752762 0022413 0 ustar 00root root 0000000 0000000 GOTESTSUM_FORMAT=short
GOTESTSUM_JSONFILE=events.json
GOTESTSUM_JUNITFILE=junit.xml
TESTS_ERRORS=0
TESTS_FAILED=13
TESTS_SKIPPED=5
TESTS_TOTAL=59
gotestsum-1.8.2/cmd/testdata/postrunhook/ 0000775 0000000 0000000 00000000000 14274752762 0020543 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/testdata/postrunhook/main.go 0000664 0000000 0000000 00000000753 14274752762 0022023 0 ustar 00root root 0000000 0000000 package main
import (
"errors"
"fmt"
"os"
"sort"
"strings"
)
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
}
func run() error {
environ := os.Environ()
sort.Strings(environ)
for _, v := range environ {
for _, prefix := range []string{"TESTS_", "GOTESTSUM_"} {
if strings.HasPrefix(v, prefix) {
fmt.Println(v)
}
}
}
err := os.Getenv("TEST_STUB_ERROR")
if err != "" {
return errors.New(err)
}
return nil
}
gotestsum-1.8.2/cmd/tool/ 0000775 0000000 0000000 00000000000 14274752762 0015314 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/tool/cmd.go 0000664 0000000 0000000 00000001162 14274752762 0016406 0 ustar 00root root 0000000 0000000 package tool
import (
"fmt"
"os"
"gotest.tools/gotestsum/cmd"
"gotest.tools/gotestsum/cmd/tool/slowest"
)
// Run one of the tool commands.
func Run(name string, args []string) error {
next, rest := cmd.Next(args)
switch next {
case "":
fmt.Println(usage(name))
return nil
case "slowest":
return slowest.Run(name+" "+next, rest)
default:
fmt.Fprintln(os.Stderr, usage(name))
return fmt.Errorf("invalid command: %v %v", name, next)
}
}
func usage(name string) string {
return fmt.Sprintf(`Usage: %s COMMAND [flags]
Commands: slowest
Use '%s COMMAND --help' for command specific help.
`, name, name)
}
gotestsum-1.8.2/cmd/tool/slowest/ 0000775 0000000 0000000 00000000000 14274752762 0017014 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/tool/slowest/ast.go 0000664 0000000 0000000 00000011115 14274752762 0020131 0 ustar 00root root 0000000 0000000 package slowest
import (
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"os"
"strings"
"golang.org/x/tools/go/packages"
"gotest.tools/gotestsum/internal/log"
"gotest.tools/gotestsum/testjson"
)
func writeTestSkip(tcs []testjson.TestCase, skipStmt ast.Stmt) error {
fset := token.NewFileSet()
cfg := packages.Config{
Mode: modeAll(),
Tests: true,
Fset: fset,
BuildFlags: buildFlags(),
}
pkgNames, index := testNamesByPkgName(tcs)
pkgs, err := packages.Load(&cfg, pkgNames...)
if err != nil {
return fmt.Errorf("failed to load packages: %v", err)
}
for _, pkg := range pkgs {
if len(pkg.Errors) > 0 {
return errPkgLoad(pkg)
}
tcs, ok := index[normalizePkgName(pkg.PkgPath)]
if !ok {
log.Debugf("skipping %v, no slow tests", pkg.PkgPath)
continue
}
log.Debugf("rewriting %v for %d test cases", pkg.PkgPath, len(tcs))
for _, file := range pkg.Syntax {
path := fset.File(file.Pos()).Name()
log.Debugf("looking for test cases in: %v", path)
if !rewriteAST(file, tcs, skipStmt) {
continue
}
if err := writeFile(path, file, fset); err != nil {
return fmt.Errorf("failed to write ast to file %v: %v", path, err)
}
}
}
return errTestCasesNotFound(index)
}
// normalizePkgName removes the _test suffix from a package name. External test
// packages (those named package_test) may contain tests, but the test2json output
// always uses the non-external package name. The _test suffix must be removed
// so that any slow tests in an external test package can be found.
func normalizePkgName(name string) string {
return strings.TrimSuffix(name, "_test")
}
func writeFile(path string, file *ast.File, fset *token.FileSet) error {
fh, err := os.Create(path)
if err != nil {
return err
}
defer func() {
if err := fh.Close(); err != nil {
log.Errorf("Failed to close file %v: %v", path, err)
}
}()
return format.Node(fh, fset, file)
}
func parseSkipStatement(text string) (ast.Stmt, error) {
switch text {
case "default", "testing.Short":
text = `
if testing.Short() {
t.Skip("too slow for testing.Short")
}
`
}
// Add some required boilerplate around the statement to make it a valid file
text = "package stub\nfunc Stub() {\n" + text + "\n}\n"
file, err := parser.ParseFile(token.NewFileSet(), "fragment", text, 0)
if err != nil {
return nil, err
}
stmt := file.Decls[0].(*ast.FuncDecl).Body.List[0]
return stmt, nil
}
func rewriteAST(file *ast.File, testNames set, skipStmt ast.Stmt) bool {
var modified bool
for _, decl := range file.Decls {
fd, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
name := fd.Name.Name // TODO: can this be nil?
if _, ok := testNames[name]; !ok {
continue
}
fd.Body.List = append([]ast.Stmt{skipStmt}, fd.Body.List...)
modified = true
delete(testNames, name)
}
return modified
}
type set map[string]struct{}
// testNamesByPkgName strips subtest names from test names, then builds
// and returns a slice of all the packages names, and a mapping of package name
// to set of failed tests in that package.
//
// subtests are removed because the AST lookup currently only works for top-level
// functions, not t.Run subtests.
func testNamesByPkgName(tcs []testjson.TestCase) ([]string, map[string]set) {
var pkgs []string
index := make(map[string]set)
for _, tc := range tcs {
testName := tc.Test.Name()
if tc.Test.IsSubTest() {
root, _ := tc.Test.Split()
testName = root
}
if len(index[tc.Package]) == 0 {
pkgs = append(pkgs, tc.Package)
index[tc.Package] = make(map[string]struct{})
}
index[tc.Package][testName] = struct{}{}
}
return pkgs, index
}
func errPkgLoad(pkg *packages.Package) error {
buf := new(strings.Builder)
for _, err := range pkg.Errors {
buf.WriteString("\n" + err.Error())
}
return fmt.Errorf("failed to load package %v %v", pkg.PkgPath, buf.String())
}
func errTestCasesNotFound(index map[string]set) error {
var missed []string
for pkg, tcs := range index {
for tc := range tcs {
missed = append(missed, fmt.Sprintf("%v.%v", pkg, tc))
}
}
if len(missed) == 0 {
return nil
}
return fmt.Errorf("failed to find source for test cases:\n%v", strings.Join(missed, "\n"))
}
func modeAll() packages.LoadMode {
mode := packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
mode = mode | packages.NeedImports | packages.NeedDeps
mode = mode | packages.NeedTypes | packages.NeedTypesSizes
mode = mode | packages.NeedSyntax | packages.NeedTypesInfo
return mode
}
func buildFlags() []string {
flags := os.Getenv("GOFLAGS")
if len(flags) == 0 {
return nil
}
return strings.Split(os.Getenv("GOFLAGS"), " ")
}
gotestsum-1.8.2/cmd/tool/slowest/ast_test.go 0000664 0000000 0000000 00000000720 14274752762 0021170 0 ustar 00root root 0000000 0000000 package slowest
import (
"bytes"
"go/format"
"go/token"
"testing"
"gotest.tools/v3/assert"
)
func TestParseSkipStatement_Preset_testingShort(t *testing.T) {
stmt, err := parseSkipStatement("testing.Short")
assert.NilError(t, err)
expected := `if testing.Short() {
t.Skip("too slow for testing.Short")
}`
buf := new(bytes.Buffer)
err = format.Node(buf, token.NewFileSet(), stmt)
assert.NilError(t, err)
assert.DeepEqual(t, buf.String(), expected)
}
gotestsum-1.8.2/cmd/tool/slowest/slowest.go 0000664 0000000 0000000 00000007776 14274752762 0021064 0 ustar 00root root 0000000 0000000 package slowest
import (
"fmt"
"io"
"io/ioutil"
"os"
"time"
"github.com/dnephin/pflag"
"gotest.tools/gotestsum/internal/aggregate"
"gotest.tools/gotestsum/internal/log"
"gotest.tools/gotestsum/testjson"
)
// Run the command
func Run(name string, args []string) error {
flags, opts := setupFlags(name)
switch err := flags.Parse(args); {
case err == pflag.ErrHelp:
return nil
case err != nil:
usage(os.Stderr, name, flags)
return err
}
return run(opts)
}
func setupFlags(name string) (*pflag.FlagSet, *options) {
opts := &options{}
flags := pflag.NewFlagSet(name, pflag.ContinueOnError)
flags.SetInterspersed(false)
flags.Usage = func() {
usage(os.Stdout, name, flags)
}
flags.StringVar(&opts.jsonfile, "jsonfile", os.Getenv("GOTESTSUM_JSONFILE"),
"path to test2json output, defaults to stdin")
flags.DurationVar(&opts.threshold, "threshold", 100*time.Millisecond,
"test cases with elapsed time greater than threshold are slow tests")
flags.StringVar(&opts.skipStatement, "skip-stmt", "",
"add this go statement to slow tests, instead of printing the list of slow tests")
flags.BoolVar(&opts.debug, "debug", false,
"enable debug logging.")
return flags, opts
}
func usage(out io.Writer, name string, flags *pflag.FlagSet) {
fmt.Fprintf(out, `Usage:
%[1]s [flags]
Read a json file and print or update tests which are slower than threshold.
The json file may be created with 'gotestsum --jsonfile' or 'go test -json'.
If a TestCase appears more than once in the json file, it will only appear once
in the output, and the median value of all the elapsed times will be used.
By default this command will print the list of tests slower than threshold to stdout.
The list will be sorted from slowest to fastest.
If --skip-stmt is set, instead of printing the list to stdout, the AST for the
Go source code in the working directory tree will be modified. The value of
--skip-stmt will be added to Go test files as the first statement in all the test
functions which are slower than threshold.
The --skip-stmt flag may be set to the name of a predefined statement, or to
Go source code which will be parsed as a go/ast.Stmt. Currently there is only one
predefined statement, --skip-stmt=testing.Short, which uses this Go statement:
if testing.Short() {
t.Skip("too slow for testing.Short")
}
Alternatively, a custom --skip-stmt may be provided as a string:
skip_stmt='
if os.GetEnv("TEST_FAST") != "" {
t.Skip("too slow for TEST_FAST")
}
'
go test -json -short ./... | %[1]s --skip-stmt "$skip_stmt"
Note that this tool does not add imports, so using a custom statement may require
you to add imports to the file.
Go build flags, such as build tags, may be set using the GOFLAGS environment
variable, following the same rules as the go toolchain. See
https://golang.org/cmd/go/#hdr-Environment_variables.
Flags:
`, name)
flags.SetOutput(out)
flags.PrintDefaults()
}
type options struct {
threshold time.Duration
jsonfile string
skipStatement string
debug bool
}
func run(opts *options) error {
if opts.debug {
log.SetLevel(log.DebugLevel)
}
in, err := jsonfileReader(opts.jsonfile)
if err != nil {
return fmt.Errorf("failed to read jsonfile: %v", err)
}
defer func() {
if err := in.Close(); err != nil {
log.Errorf("Failed to close file %v: %v", opts.jsonfile, err)
}
}()
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{Stdout: in})
if err != nil {
return fmt.Errorf("failed to scan testjson: %v", err)
}
tcs := aggregate.Slowest(exec, opts.threshold)
if opts.skipStatement != "" {
skipStmt, err := parseSkipStatement(opts.skipStatement)
if err != nil {
return fmt.Errorf("failed to parse skip expr: %v", err)
}
return writeTestSkip(tcs, skipStmt)
}
for _, tc := range tcs {
fmt.Printf("%s %s %v\n", tc.Package, tc.Test, tc.Elapsed)
}
return nil
}
func jsonfileReader(v string) (io.ReadCloser, error) {
switch v {
case "", "-":
return ioutil.NopCloser(os.Stdin), nil
default:
return os.Open(v)
}
}
gotestsum-1.8.2/cmd/tool/slowest/slowest_test.go 0000664 0000000 0000000 00000000546 14274752762 0022107 0 ustar 00root root 0000000 0000000 package slowest
import (
"bytes"
"testing"
"gotest.tools/v3/env"
"gotest.tools/v3/golden"
)
func TestUsage_WithFlagsFromSetupFlags(t *testing.T) {
defer env.PatchAll(t, nil)()
name := "gotestsum tool slowest"
flags, _ := setupFlags(name)
buf := new(bytes.Buffer)
usage(buf, name, flags)
golden.Assert(t, buf.String(), "cmd-flags-help-text")
}
gotestsum-1.8.2/cmd/tool/slowest/testdata/ 0000775 0000000 0000000 00000000000 14274752762 0020625 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/cmd/tool/slowest/testdata/cmd-flags-help-text 0000664 0000000 0000000 00000003734 14274752762 0024324 0 ustar 00root root 0000000 0000000 Usage:
gotestsum tool slowest [flags]
Read a json file and print or update tests which are slower than threshold.
The json file may be created with 'gotestsum --jsonfile' or 'go test -json'.
If a TestCase appears more than once in the json file, it will only appear once
in the output, and the median value of all the elapsed times will be used.
By default this command will print the list of tests slower than threshold to stdout.
The list will be sorted from slowest to fastest.
If --skip-stmt is set, instead of printing the list to stdout, the AST for the
Go source code in the working directory tree will be modified. The value of
--skip-stmt will be added to Go test files as the first statement in all the test
functions which are slower than threshold.
The --skip-stmt flag may be set to the name of a predefined statement, or to
Go source code which will be parsed as a go/ast.Stmt. Currently there is only one
predefined statement, --skip-stmt=testing.Short, which uses this Go statement:
if testing.Short() {
t.Skip("too slow for testing.Short")
}
Alternatively, a custom --skip-stmt may be provided as a string:
skip_stmt='
if os.GetEnv("TEST_FAST") != "" {
t.Skip("too slow for TEST_FAST")
}
'
go test -json -short ./... | gotestsum tool slowest --skip-stmt "$skip_stmt"
Note that this tool does not add imports, so using a custom statement may require
you to add imports to the file.
Go build flags, such as build tags, may be set using the GOFLAGS environment
variable, following the same rules as the go toolchain. See
https://golang.org/cmd/go/#hdr-Environment_variables.
Flags:
--debug enable debug logging.
--jsonfile string path to test2json output, defaults to stdin
--skip-stmt string add this go statement to slow tests, instead of printing the list of slow tests
--threshold duration test cases with elapsed time greater than threshold are slow tests (default 100ms)
gotestsum-1.8.2/cmd/watch.go 0000664 0000000 0000000 00000006121 14274752762 0015774 0 ustar 00root root 0000000 0000000 package cmd
import (
"bufio"
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"gotest.tools/gotestsum/internal/filewatcher"
"gotest.tools/gotestsum/testjson"
)
func runWatcher(opts *options) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
w := &watchRuns{opts: *opts}
return filewatcher.Watch(ctx, opts.packages, w.run)
}
type watchRuns struct {
opts options
prevExec *testjson.Execution
}
func (w *watchRuns) run(event filewatcher.Event) error {
if event.Debug {
path, cleanup, err := delveInitFile(w.prevExec)
if err != nil {
return fmt.Errorf("failed to write delve init file: %w", err)
}
defer cleanup()
o := delveOpts{
pkgPath: event.PkgPath,
args: w.opts.args,
initFilePath: path,
}
if err := runDelve(o); !IsExitCoder(err) {
return fmt.Errorf("delve failed: %w", err)
}
return nil
}
opts := w.opts // shallow copy opts
opts.packages = append([]string{}, opts.packages...)
opts.packages = append(opts.packages, event.PkgPath)
opts.packages = append(opts.packages, event.Args...)
var err error
if w.prevExec, err = runSingle(&opts); !IsExitCoder(err) {
return err
}
return nil
}
// runSingle is similar to run. It doesn't support rerun-fails. It may be
// possible to share runSingle with run, but the defer close on the handler
// would require at least 3 return values, so for now it is a copy.
func runSingle(opts *options) (*testjson.Execution, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := opts.Validate(); err != nil {
return nil, err
}
goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, rerunOpts{}))
if err != nil {
return nil, err
}
handler, err := newEventHandler(opts)
if err != nil {
return nil, err
}
defer handler.Close() // nolint: errcheck
cfg := testjson.ScanConfig{
Stdout: goTestProc.stdout,
Stderr: goTestProc.stderr,
Handler: handler,
Stop: cancel,
}
exec, err := testjson.ScanTestOutput(cfg)
if err != nil {
return exec, finishRun(opts, exec, err)
}
err = goTestProc.cmd.Wait()
return exec, finishRun(opts, exec, err)
}
func delveInitFile(exec *testjson.Execution) (string, func(), error) {
fh, err := ioutil.TempFile("", "gotestsum-delve-init")
if err != nil {
return "", nil, err
}
remove := func() {
os.Remove(fh.Name()) // nolint: errcheck
}
buf := bufio.NewWriter(fh)
for _, tc := range exec.Failed() {
fmt.Fprintf(buf, "break %s\n", tc.Test.Name())
}
buf.WriteString("continue\n")
if err := buf.Flush(); err != nil {
remove()
return "", nil, err
}
return fh.Name(), remove, nil
}
type delveOpts struct {
pkgPath string
args []string
initFilePath string
}
func runDelve(opts delveOpts) error {
pkg := opts.pkgPath
args := []string{"dlv", "test", "--wd", pkg}
args = append(args, "--output", "gotestsum-watch-debug.test")
args = append(args, "--init", opts.initFilePath)
args = append(args, pkg, "--")
args = append(args, opts.args...)
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
gotestsum-1.8.2/contrib/ 0000775 0000000 0000000 00000000000 14274752762 0015234 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/contrib/notify/ 0000775 0000000 0000000 00000000000 14274752762 0016544 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/contrib/notify/notify-macos.go 0000664 0000000 0000000 00000002057 14274752762 0021507 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"log"
"os"
"os/exec"
"strconv"
)
func main() {
total := envInt("TOTAL")
skipped := envInt("SKIPPED")
failed := envInt("FAILED")
errors := envInt("ERRORS")
emoji := "✅"
title := "Passed"
switch {
case errors > 0:
emoji = "⚠️"
title = "Errored"
case failed > 0:
emoji = "❌"
title = "Failed"
case skipped > 0:
title = "Passed with skipped"
}
subtitle := fmt.Sprintf("%d Tests Run", total)
if errors > 0 {
subtitle += fmt.Sprintf(", %d Errored", errors)
}
if failed > 0 {
subtitle += fmt.Sprintf(", %d Failed", failed)
}
if skipped > 0 {
subtitle += fmt.Sprintf(", %d Skipped", skipped)
}
args := []string{
"-title", emoji + " " + title,
"-group", "gotestsum",
"-subtitle", subtitle,
}
log.Printf("terminal-notifier %#v", args)
err := exec.Command("terminal-notifier", args...).Run()
if err != nil {
log.Fatalf("Failed to exec: %v", err)
}
}
func envInt(name string) int {
val := os.Getenv("TESTS_" + name)
n, err := strconv.Atoi(val)
if err != nil {
return 0
}
return n
}
gotestsum-1.8.2/do 0000775 0000000 0000000 00000004547 14274752762 0014136 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
declare -A help
binary() {
mkdir -p dist
go build -o dist/gotestsum .
}
binary-static() {
echo "building static binary: dist/gotestsum"
CGO_ENABLED=0 binary
}
update-golden() {
gotestsum -- ./... -update
}
lint() {
golangci-lint run -v --config .project/golangci-lint.yml
}
go-mod-tidy() {
go mod tidy
git diff --stat --exit-code go.mod go.sum
}
help[shell]='Run a shell in a golang docker container.
Env vars:
GOLANG_VERSION - the docker image tag used to build the image.
'
shell() {
local image; image="$(_docker-build-dev)"
docker run \
--tty --interactive --rm \
-v "$PWD:/work" \
-v ~/.cache/go-build:/root/.cache/go-build \
-v ~/go/pkg/mod:/go/pkg/mod \
-w /work \
"$image" \
"${@-bash}"
}
_docker-build-dev() {
set -e
local idfile=".plsdo/docker-build-dev-image-id-${GOLANG_VERSION-default}"
local dockerfile=.project/Dockerfile
local tag=gotest.tools/gotestsum/builder
if [ -f "$idfile" ] && [ "$dockerfile" -ot "$idfile" ]; then
cat "$idfile"
return 0
fi
mkdir -p .plsdo
>&2 docker build \
--iidfile "$idfile" \
--file "$dockerfile" \
--build-arg "UID=$UID" \
--build-arg GOLANG_VERSION \
--target "dev" \
.plsdo
cat "$idfile"
}
help[godoc]="Run godoc locally to preview package documentation."
godoc() {
local url; url="http://localhost:6060/pkg/$(go list)/"
command -v xdg-open && xdg-open "$url" &
command -v open && open "$url" &
command godoc -http=:6060
}
help[list]="Print the list of tasks"
list() {
declare -F | awk '{print $3}' | grep -v '^_'
}
_plsdo_help() {
local topic="${1-}"
# print help for the topic
if [ -n "$topic" ]; then
if ! command -v "$topic" > /dev/null ; then
>&2 echo "No such task: $topic"
return 1
fi
printf "\nUsage:\n %s %s\n\n%s\n" "$0" "$topic" "${help[$topic]-}"
return 0
fi
# print list of tasks and their help line.
[ -n "${banner-}" ] && echo "$banner" && echo
for i in $(list); do
printf "%-12s\t%s\n" "$i" "${help[$i]-}" | head -1
done
}
_plsdo_run() {
case "${1-}" in
""|help)
_plsdo_help "${2-}" ;;
*)
"$@" ;;
esac
}
_plsdo_run "$@"
gotestsum-1.8.2/go.mod 0000664 0000000 0000000 00000000777 14274752762 0014715 0 ustar 00root root 0000000 0000000 module gotest.tools/gotestsum
require (
github.com/dnephin/pflag v1.0.7
github.com/fatih/color v1.13.0
github.com/fsnotify/fsnotify v1.5.4
github.com/google/go-cmp v0.5.8
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/mattn/go-colorable v0.1.12 // indirect
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467
golang.org/x/tools v0.1.11
gotest.tools/v3 v3.3.0
)
go 1.13
gotestsum-1.8.2/go.sum 0000664 0000000 0000000 00000015321 14274752762 0014731 0 ustar 00root root 0000000 0000000 github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk=
github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
gotestsum-1.8.2/internal/ 0000775 0000000 0000000 00000000000 14274752762 0015410 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/internal/aggregate/ 0000775 0000000 0000000 00000000000 14274752762 0017336 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/internal/aggregate/slowest.go 0000664 0000000 0000000 00000003621 14274752762 0021367 0 ustar 00root root 0000000 0000000 package aggregate
import (
"sort"
"time"
"gotest.tools/gotestsum/testjson"
)
// Slowest returns a slice of all tests with an elapsed time greater than
// threshold. The slice is sorted by Elapsed time in descending order (slowest
// test first).
//
// If there are multiple runs of a TestCase, all of them will be represented
// by a single TestCase with the median elapsed time in the returned slice.
func Slowest(exec *testjson.Execution, threshold time.Duration) []testjson.TestCase {
if threshold == 0 {
return nil
}
pkgs := exec.Packages()
tests := make([]testjson.TestCase, 0, len(pkgs))
for _, pkg := range pkgs {
pkgTests := ByElapsed(exec.Package(pkg).TestCases(), median)
tests = append(tests, pkgTests...)
}
sort.Slice(tests, func(i, j int) bool {
return tests[i].Elapsed > tests[j].Elapsed
})
end := sort.Search(len(tests), func(i int) bool {
return tests[i].Elapsed < threshold
})
return tests[:end]
}
// ByElapsed maps all test cases by name, and if there is more than one
// instance of a TestCase, uses fn to select the elapsed time for the group.
//
// All cases are assumed to be part of the same package.
func ByElapsed(cases []testjson.TestCase, fn func(times []time.Duration) time.Duration) []testjson.TestCase {
if len(cases) <= 1 {
return cases
}
pkg := cases[0].Package
// nolint: prealloc // size is not predictable
m := make(map[testjson.TestName][]time.Duration)
for _, tc := range cases {
m[tc.Test] = append(m[tc.Test], tc.Elapsed)
}
result := make([]testjson.TestCase, 0, len(m))
for name, timing := range m {
result = append(result, testjson.TestCase{
Package: pkg,
Test: name,
Elapsed: fn(timing),
})
}
return result
}
func median(times []time.Duration) time.Duration {
switch len(times) {
case 0:
return 0
case 1:
return times[0]
}
sort.Slice(times, func(i, j int) bool {
return times[i] < times[j]
})
return times[len(times)/2]
}
gotestsum-1.8.2/internal/aggregate/slowest_test.go 0000664 0000000 0000000 00000003306 14274752762 0022426 0 ustar 00root root 0000000 0000000 package aggregate
import (
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp/cmpopts"
"gotest.tools/gotestsum/testjson"
"gotest.tools/v3/assert"
)
func TestByElapsed_WithMedian(t *testing.T) {
cases := []testjson.TestCase{
{Test: "TestOne", Package: "pkg", Elapsed: time.Second},
{Test: "TestTwo", Package: "pkg", Elapsed: 2 * time.Second},
{Test: "TestOne", Package: "pkg", Elapsed: 3 * time.Second},
{Test: "TestTwo", Package: "pkg", Elapsed: 4 * time.Second},
{Test: "TestOne", Package: "pkg", Elapsed: 5 * time.Second},
{Test: "TestTwo", Package: "pkg", Elapsed: 6 * time.Second},
}
actual := ByElapsed(cases, median)
expected := []testjson.TestCase{
{Test: "TestOne", Package: "pkg", Elapsed: 3 * time.Second},
{Test: "TestTwo", Package: "pkg", Elapsed: 4 * time.Second},
}
assert.DeepEqual(t, actual, expected,
cmpopts.SortSlices(func(x, y testjson.TestCase) bool {
return strings.Compare(x.Test.Name(), y.Test.Name()) == -1
}),
cmpopts.IgnoreUnexported(testjson.TestCase{}))
}
func TestMedian(t *testing.T) {
var testcases = []struct {
name string
times []time.Duration
expected time.Duration
}{
{
name: "one item slice",
times: []time.Duration{time.Minute},
expected: time.Minute,
},
{
name: "odd number of items",
times: []time.Duration{time.Millisecond, time.Hour, time.Second},
expected: time.Second,
},
{
name: "even number of items",
times: []time.Duration{time.Second, time.Millisecond, time.Microsecond, time.Hour},
expected: time.Second,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
actual := median(tc.times)
assert.Equal(t, actual, tc.expected)
})
}
}
gotestsum-1.8.2/internal/dotwriter/ 0000775 0000000 0000000 00000000000 14274752762 0017433 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/internal/dotwriter/LICENSE 0000664 0000000 0000000 00000002070 14274752762 0020437 0 ustar 00root root 0000000 0000000 MIT License
===========
Copyright (c) 2015, Greg Osuri
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.
gotestsum-1.8.2/internal/dotwriter/README 0000664 0000000 0000000 00000000316 14274752762 0020313 0 ustar 00root root 0000000 0000000 This package contains a striped down and modified version of
https://github.com/gosuri/uilive. The original package did not work with
terminal colors, and had some bits that were unnecessary for gotestsum.
gotestsum-1.8.2/internal/dotwriter/writer.go 0000664 0000000 0000000 00000001574 14274752762 0021305 0 ustar 00root root 0000000 0000000 /*
Package dotwriter implements a buffered Writer for updating progress on the
terminal.
*/
package dotwriter
import (
"bytes"
"io"
)
// ESC is the ASCII code for escape character
const ESC = 27
// Writer buffers writes until Flush is called. Flush clears previously written
// lines before writing new lines from the buffer.
type Writer struct {
out io.Writer
buf bytes.Buffer
lineCount int
}
// New returns a new Writer
func New(out io.Writer) *Writer {
return &Writer{out: out}
}
// Flush the buffer, writing all buffered lines to out
func (w *Writer) Flush() error {
if w.buf.Len() == 0 {
return nil
}
w.clearLines(w.lineCount)
w.lineCount = bytes.Count(w.buf.Bytes(), []byte{'\n'})
_, err := w.out.Write(w.buf.Bytes())
w.buf.Reset()
return err
}
// Write saves buf to a buffer
func (w *Writer) Write(buf []byte) (int, error) {
return w.buf.Write(buf)
}
gotestsum-1.8.2/internal/dotwriter/writer_posix.go 0000664 0000000 0000000 00000000432 14274752762 0022517 0 ustar 00root root 0000000 0000000 //go:build !windows
// +build !windows
package dotwriter
import (
"fmt"
"strings"
)
// clear the line and move the cursor up
var clear = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC)
func (w *Writer) clearLines(count int) {
_, _ = fmt.Fprint(w.out, strings.Repeat(clear, count))
}
gotestsum-1.8.2/internal/dotwriter/writer_windows.go 0000664 0000000 0000000 00000003007 14274752762 0023050 0 ustar 00root root 0000000 0000000 // +build windows
package dotwriter
import (
"fmt"
"io"
"strings"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
)
// clear the line and move the cursor up
var clear = fmt.Sprintf("%c[%dA%c[2K\r", ESC, 0, ESC)
type dword uint32
type coord struct {
x int16
y int16
}
type fdWriter interface {
io.Writer
Fd() uintptr
}
func (w *Writer) clearLines(count int) {
f, ok := w.out.(fdWriter)
if ok && !isConsole(f.Fd()) {
ok = false
}
if !ok {
_, _ = fmt.Fprint(w.out, strings.Repeat(clear, count))
return
}
fd := f.Fd()
var csbi windows.ConsoleScreenBufferInfo
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &csbi); err != nil {
return
}
for i := 0; i < count; i++ {
// move the cursor up
csbi.CursorPosition.Y--
_, _, _ = procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&csbi.CursorPosition))))
// clear the line
cursor := coord{
x: csbi.Window.Left,
y: csbi.Window.Top + csbi.CursorPosition.Y,
}
var count, w dword
count = dword(csbi.Size.X)
_, _, _ = procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w)))
}
}
func isConsole(fd uintptr) bool {
var mode uint32
err := windows.GetConsoleMode(windows.Handle(fd), &mode)
return err == nil
}
gotestsum-1.8.2/internal/filewatcher/ 0000775 0000000 0000000 00000000000 14274752762 0017705 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/internal/filewatcher/term_aix.go 0000664 0000000 0000000 00000000207 14274752762 0022043 0 ustar 00root root 0000000 0000000 //go:build aix
// +build aix
package filewatcher
import "golang.org/x/sys/unix"
const tcGet = unix.TCGETA
const tcSet = unix.TCSETA
gotestsum-1.8.2/internal/filewatcher/term_bsd.go 0000664 0000000 0000000 00000000337 14274752762 0022036 0 ustar 00root root 0000000 0000000 //go:build darwin || dragonfly || freebsd || netbsd || openbsd
// +build darwin dragonfly freebsd netbsd openbsd
package filewatcher
import "golang.org/x/sys/unix"
const tcGet = unix.TIOCGETA
const tcSet = unix.TIOCSETA
gotestsum-1.8.2/internal/filewatcher/term_linux.go 0000664 0000000 0000000 00000000151 14274752762 0022417 0 ustar 00root root 0000000 0000000 package filewatcher
import "golang.org/x/sys/unix"
const tcGet = unix.TCGETS
const tcSet = unix.TCSETS
gotestsum-1.8.2/internal/filewatcher/term_unix.go 0000664 0000000 0000000 00000005012 14274752762 0022244 0 ustar 00root root 0000000 0000000 //go:build !windows && !aix
// +build !windows,!aix
package filewatcher
import (
"bufio"
"context"
"fmt"
"io"
"os"
"golang.org/x/sys/unix"
"gotest.tools/gotestsum/internal/log"
)
type terminal struct {
ch chan Event
reset func()
}
func newTerminal() *terminal {
h := &terminal{ch: make(chan Event)}
h.Start()
return h
}
// Start the terminal is non-blocking read mode. The terminal can be reset to
// normal mode by calling Reset.
func (r *terminal) Start() {
if r == nil {
return
}
fd := int(os.Stdin.Fd())
reset, err := enableNonBlockingRead(fd)
if err != nil {
log.Warnf("failed to put terminal (fd %d) into raw mode: %v", fd, err)
return
}
r.reset = reset
}
func enableNonBlockingRead(fd int) (func(), error) {
term, err := unix.IoctlGetTermios(fd, tcGet)
if err != nil {
return nil, err
}
state := *term
reset := func() {
if err := unix.IoctlSetTermios(fd, tcSet, &state); err != nil {
log.Debugf("failed to reset fd %d: %v", fd, err)
}
}
term.Lflag &^= unix.ECHO | unix.ICANON
term.Cc[unix.VMIN] = 1
term.Cc[unix.VTIME] = 0
if err := unix.IoctlSetTermios(fd, tcSet, term); err != nil {
reset()
return nil, err
}
return reset, nil
}
var stdin io.Reader = os.Stdin
// Monitor the terminal for key presses. If the key press is associated with an
// action, an event will be sent to channel returned by Events.
func (r *terminal) Monitor(ctx context.Context) {
if r == nil {
return
}
in := bufio.NewReader(stdin)
for {
char, err := in.ReadByte()
if err != nil {
log.Warnf("failed to read input: %v", err)
return
}
log.Debugf("received byte %v (%v)", char, string(char))
chResume := make(chan struct{})
switch char {
case 'r':
r.ch <- Event{resume: chResume, useLastPath: true}
case 'd':
r.ch <- Event{resume: chResume, useLastPath: true, Debug: true}
case 'a':
r.ch <- Event{resume: chResume, PkgPath: "./..."}
case 'l':
r.ch <- Event{resume: chResume, reloadPaths: true}
case 'u':
r.ch <- Event{resume: chResume, useLastPath: true, Args: []string{"-update"}}
case '\n':
fmt.Println()
continue
default:
continue
}
select {
case <-ctx.Done():
return
case <-chResume:
}
}
}
// Events returns a channel which will receive events when keys are pressed.
// When an event is received, the caller must close the resume channel to
// resume monitoring for events.
func (r *terminal) Events() <-chan Event {
if r == nil {
return nil
}
return r.ch
}
func (r *terminal) Reset() {
if r != nil && r.reset != nil {
r.reset()
}
}
gotestsum-1.8.2/internal/filewatcher/term_windows.go 0000664 0000000 0000000 00000000424 14274752762 0022755 0 ustar 00root root 0000000 0000000 package filewatcher
import "context"
type terminal struct{}
func newTerminal() *terminal {
return nil
}
func (r *terminal) Monitor(context.Context) {}
func (r *terminal) Events() <-chan Event {
return nil
}
func (r *terminal) Start() {}
func (r *terminal) Reset() {}
gotestsum-1.8.2/internal/filewatcher/watch.go 0000664 0000000 0000000 00000013773 14274752762 0021355 0 ustar 00root root 0000000 0000000 //go:build !aix
// +build !aix
package filewatcher
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/fsnotify/fsnotify"
"gotest.tools/gotestsum/internal/log"
)
const maxDepth = 7
type Event struct {
// PkgPath of the package that triggered the event.
PkgPath string
// Args will be appended to the command line args for 'go test'.
Args []string
// Debug runs the tests with delve.
Debug bool
// resume the Watch goroutine when this channel is closed. Used to block
// the Watch goroutine while tests are running.
resume chan struct{}
// reloadPaths will cause the watched path list to be reloaded, to watch
// new directories.
reloadPaths bool
// useLastPath when true will use the PkgPath from the previous run.
useLastPath bool
}
// Watch dirs for filesystem events, and run tests when .go files are saved.
// nolint: gocyclo
func Watch(ctx context.Context, dirs []string, run func(Event) error) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return fmt.Errorf("failed to create file watcher: %w", err)
}
defer watcher.Close() // nolint: errcheck // always returns nil error
if err := loadPaths(watcher, dirs); err != nil {
return err
}
timer := time.NewTimer(maxIdleTime)
defer timer.Stop()
term := newTerminal()
defer term.Reset()
go term.Monitor(ctx)
h := &fsEventHandler{last: time.Now(), fn: run}
for {
select {
case <-ctx.Done():
return nil
case <-timer.C:
return fmt.Errorf("exceeded idle timeout while watching files")
case event := <-term.Events():
resetTimer(timer)
if event.reloadPaths {
if err := loadPaths(watcher, dirs); err != nil {
return err
}
close(event.resume)
continue
}
term.Reset()
if err := h.runTests(event); err != nil {
return fmt.Errorf("failed to rerun tests for %v: %v", event.PkgPath, err)
}
term.Start()
close(event.resume)
case event := <-watcher.Events:
resetTimer(timer)
log.Debugf("handling event %v", event)
if handleDirCreated(watcher, event) {
continue
}
if err := h.handleEvent(event); err != nil {
return fmt.Errorf("failed to run tests for %v: %v", event.Name, err)
}
case err := <-watcher.Errors:
return fmt.Errorf("failed while watching files: %v", err)
}
}
}
const maxIdleTime = time.Hour
func resetTimer(timer *time.Timer) {
if !timer.Stop() {
<-timer.C
}
timer.Reset(maxIdleTime)
}
func loadPaths(watcher *fsnotify.Watcher, dirs []string) error {
toWatch := findAllDirs(dirs, maxDepth)
fmt.Printf("Watching %v directories. Use Ctrl-c to to stop a run or exit.\n", len(toWatch))
for _, dir := range toWatch {
if err := watcher.Add(dir); err != nil {
return fmt.Errorf("failed to watch directory %v: %w", dir, err)
}
}
return nil
}
func findAllDirs(dirs []string, maxDepth int) []string {
if len(dirs) == 0 {
dirs = []string{"./..."}
}
var output []string // nolint: prealloc
for _, dir := range dirs {
const recur = "/..."
if strings.HasSuffix(dir, recur) {
dir = strings.TrimSuffix(dir, recur)
output = append(output, findSubDirs(dir, maxDepth)...)
continue
}
output = append(output, dir)
}
return output
}
func findSubDirs(rootDir string, maxDepth int) []string {
var output []string
// add root dir depth so that maxDepth is relative to the root dir
maxDepth += pathDepth(rootDir)
walker := func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Warnf("failed to watch %v: %v", path, err)
return nil
}
if !info.IsDir() {
return nil
}
if pathDepth(path) > maxDepth || exclude(path) {
log.Debugf("Ignoring %v because of max depth or exclude list", path)
return filepath.SkipDir
}
if !hasGoFiles(path) {
log.Debugf("Ignoring %v because it has no .go files", path)
return nil
}
output = append(output, path)
return nil
}
// nolint: errcheck // error is handled by walker func
filepath.Walk(rootDir, walker)
return output
}
func pathDepth(path string) int {
return strings.Count(filepath.Clean(path), string(filepath.Separator))
}
// return true if path is vendor, testdata, or starts with a dot
func exclude(path string) bool {
base := filepath.Base(path)
switch {
case strings.HasPrefix(base, ".") && len(base) > 1:
return true
case base == "vendor" || base == "testdata":
return true
}
return false
}
func hasGoFiles(path string) bool {
fh, err := os.Open(path)
if err != nil {
return false
}
defer fh.Close() // nolint: errcheck // fh is opened read-only
for {
names, err := fh.Readdirnames(20)
switch {
case err == io.EOF:
return false
case err != nil:
log.Warnf("failed to read directory %v: %v", path, err)
return false
}
for _, name := range names {
if strings.HasSuffix(name, ".go") {
return true
}
}
}
}
func handleDirCreated(watcher *fsnotify.Watcher, event fsnotify.Event) (handled bool) {
if event.Op&fsnotify.Create != fsnotify.Create {
return false
}
fileInfo, err := os.Stat(event.Name)
if err != nil {
log.Warnf("failed to stat %s: %s", event.Name, err)
return false
}
if !fileInfo.IsDir() {
return false
}
if err := watcher.Add(event.Name); err != nil {
log.Warnf("failed to watch new directory %v: %v", event.Name, err)
}
return true
}
type fsEventHandler struct {
last time.Time
lastPath string
fn func(opts Event) error
}
var floodThreshold = 250 * time.Millisecond
func (h *fsEventHandler) handleEvent(event fsnotify.Event) error {
if event.Op&(fsnotify.Write|fsnotify.Create) == 0 {
return nil
}
if !strings.HasSuffix(event.Name, ".go") {
return nil
}
if time.Since(h.last) < floodThreshold {
log.Debugf("skipping event received less than %v after the previous", floodThreshold)
return nil
}
return h.runTests(Event{PkgPath: "./" + filepath.Dir(event.Name)})
}
func (h *fsEventHandler) runTests(opts Event) error {
if opts.useLastPath {
opts.PkgPath = h.lastPath
}
fmt.Printf("\nRunning tests in %v\n", opts.PkgPath)
if err := h.fn(opts); err != nil {
return err
}
h.last = time.Now()
h.lastPath = opts.PkgPath
return nil
}
gotestsum-1.8.2/internal/filewatcher/watch_test.go 0000664 0000000 0000000 00000007522 14274752762 0022407 0 ustar 00root root 0000000 0000000 package filewatcher
import (
"fmt"
"path/filepath"
"testing"
"time"
"github.com/fsnotify/fsnotify"
"gotest.tools/v3/assert"
"gotest.tools/v3/env"
"gotest.tools/v3/fs"
)
func TestFSEventHandler_HandleEvent(t *testing.T) {
type testCase struct {
name string
last time.Time
expectedRun bool
event fsnotify.Event
}
fn := func(t *testing.T, tc testCase) {
var ran bool
run := func(opts Event) error {
ran = true
return nil
}
h := fsEventHandler{last: tc.last, fn: run}
err := h.handleEvent(tc.event)
assert.NilError(t, err)
assert.Equal(t, ran, tc.expectedRun)
if tc.expectedRun {
assert.Assert(t, !h.last.IsZero())
}
}
var testCases = []testCase{
{
name: "Op is rename",
event: fsnotify.Event{Op: fsnotify.Rename, Name: "file_test.go"},
},
{
name: "Op is remove",
event: fsnotify.Event{Op: fsnotify.Remove, Name: "file_test.go"},
},
{
name: "Op is chmod",
event: fsnotify.Event{Op: fsnotify.Chmod, Name: "file_test.go"},
},
{
name: "Op is write+chmod",
event: fsnotify.Event{Op: fsnotify.Write | fsnotify.Chmod, Name: "file_test.go"},
expectedRun: true,
},
{
name: "Op is write",
event: fsnotify.Event{Op: fsnotify.Write, Name: "file_test.go"},
expectedRun: true,
},
{
name: "Op is create",
event: fsnotify.Event{Op: fsnotify.Create, Name: "file_test.go"},
expectedRun: true,
},
{
name: "file is not a go file",
event: fsnotify.Event{Op: fsnotify.Write, Name: "readme.md"},
},
{
name: "under flood threshold",
event: fsnotify.Event{Op: fsnotify.Create, Name: "file_test.go"},
last: time.Now(),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fn(t, tc)
})
}
}
func TestHasGoFiles(t *testing.T) {
t.Run("none", func(t *testing.T) {
tmpDir := fs.NewDir(t, t.Name(), fs.WithFile("readme.md", ""))
defer tmpDir.Remove()
assert.Assert(t, !hasGoFiles(tmpDir.Path()))
})
t.Run("empty", func(t *testing.T) {
tmpDir := fs.NewDir(t, t.Name())
defer tmpDir.Remove()
assert.Assert(t, !hasGoFiles(tmpDir.Path()))
})
t.Run("some go files", func(t *testing.T) {
tmpDir := fs.NewDir(t, t.Name(), fs.WithFile("main.go", ""))
defer tmpDir.Remove()
assert.Assert(t, hasGoFiles(tmpDir.Path()))
})
t.Run("many go files", func(t *testing.T) {
tmpDir := fs.NewDir(t, t.Name())
for i := 0; i < 47; i++ {
fs.Apply(t, tmpDir, fs.WithFile(fmt.Sprintf("file%d.go", i), ""))
}
defer tmpDir.Remove()
assert.Assert(t, hasGoFiles(tmpDir.Path()))
})
}
func TestFindAllDirs(t *testing.T) {
goFile := fs.WithFile("file.go", "")
dirOne := fs.NewDir(t, t.Name(),
goFile,
fs.WithFile("not-a-dir", ""),
fs.WithDir("no-go-files"),
fs.WithDir(".starts-with-dot", goFile))
defer dirOne.Remove()
var path string
for i := 1; i <= 10; i++ {
path = filepath.Join(path, fmt.Sprintf("%d", i))
var ops []fs.PathOp
if i != 4 && i != 5 {
ops = []fs.PathOp{goFile}
}
fs.Apply(t, dirOne, fs.WithDir(path, ops...))
}
dirTwo := fs.NewDir(t, t.Name(),
goFile,
// subdir should be ignored, dirTwo is used without /... suffix
fs.WithDir("subdir", goFile))
defer dirTwo.Remove()
dirs := findAllDirs([]string{dirOne.Path() + "/...", dirTwo.Path()}, maxDepth)
expected := []string{
dirOne.Path(),
dirOne.Join("1"),
dirOne.Join("1/2"),
dirOne.Join("1/2/3"),
dirOne.Join("1/2/3/4/5/6"),
dirOne.Join("1/2/3/4/5/6/7"),
dirTwo.Path(),
}
assert.DeepEqual(t, dirs, expected)
}
func TestFindAllDirs_DefaultPath(t *testing.T) {
goFile := fs.WithFile("file.go", "")
dirOne := fs.NewDir(t, t.Name(),
goFile,
fs.WithDir("a", goFile),
fs.WithDir("b", goFile))
defer dirOne.Remove()
defer env.ChangeWorkingDir(t, dirOne.Path())()
dirs := findAllDirs([]string{}, maxDepth)
expected := []string{".", "a", "b"}
assert.DeepEqual(t, dirs, expected)
}
gotestsum-1.8.2/internal/filewatcher/watch_unix_test.go 0000664 0000000 0000000 00000004420 14274752762 0023444 0 ustar 00root root 0000000 0000000 //go:build !windows && !aix
// +build !windows,!aix
package filewatcher
import (
"context"
"io"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
)
func TestWatch(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
dir := fs.NewDir(t, t.Name())
r, w := io.Pipe()
patchStdin(t, r)
patchFloodThreshold(t, 0)
chEvents := make(chan Event, 1)
capture := func(event Event) error {
chEvents <- event
return nil
}
go func() {
err := Watch(ctx, []string{dir.Path()}, capture)
assert.Check(t, err)
}()
t.Run("run all tests", func(t *testing.T) {
_, err := w.Write([]byte("a"))
assert.NilError(t, err)
event := <-chEvents
expected := Event{PkgPath: "./..."}
assert.DeepEqual(t, event, expected, cmpEvent)
})
t.Run("run tests on file change", func(t *testing.T) {
fs.Apply(t, dir, fs.WithFile("file.go", ""))
event := <-chEvents
expected := Event{PkgPath: "./" + dir.Path()}
assert.DeepEqual(t, event, expected, cmpEvent)
t.Run("and rerun", func(t *testing.T) {
_, err := w.Write([]byte("r"))
assert.NilError(t, err)
event := <-chEvents
expected := Event{PkgPath: "./" + dir.Path(), useLastPath: true}
assert.DeepEqual(t, event, expected, cmpEvent)
})
t.Run("and debug", func(t *testing.T) {
_, err := w.Write([]byte("d"))
assert.NilError(t, err)
event := <-chEvents
expected := Event{
PkgPath: "./" + dir.Path(),
useLastPath: true,
Debug: true,
}
assert.DeepEqual(t, event, expected, cmpEvent)
})
t.Run("and update", func(t *testing.T) {
_, err := w.Write([]byte("u"))
assert.NilError(t, err)
event := <-chEvents
expected := Event{
PkgPath: "./" + dir.Path(),
Args: []string{"-update"},
useLastPath: true,
}
assert.DeepEqual(t, event, expected, cmpEvent)
})
})
}
var cmpEvent = cmp.Options{
cmp.AllowUnexported(Event{}),
cmpopts.IgnoreTypes(make(chan struct{})),
}
func patchStdin(t *testing.T, in io.Reader) {
orig := stdin
stdin = in
t.Cleanup(func() {
stdin = orig
})
}
func patchFloodThreshold(t *testing.T, d time.Duration) {
orig := floodThreshold
floodThreshold = d
t.Cleanup(func() {
floodThreshold = orig
})
}
gotestsum-1.8.2/internal/filewatcher/watch_unsupported.go 0000664 0000000 0000000 00000000435 14274752762 0024014 0 ustar 00root root 0000000 0000000 //go:build aix
// +build aix
package filewatcher
import (
"fmt"
"runtime"
)
type Event struct {
PkgPath string
Debug bool
}
func Watch(dirs []string, run func(Event) error) error {
return fmt.Errorf("file watching is not supported on %v/%v", runtime.GOOS, runtime.GOARCH)
}
gotestsum-1.8.2/internal/junitxml/ 0000775 0000000 0000000 00000000000 14274752762 0017262 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/internal/junitxml/report.go 0000664 0000000 0000000 00000014045 14274752762 0021130 0 ustar 00root root 0000000 0000000 /*Package junitxml creates a JUnit XML report from a testjson.Execution.
*/
package junitxml
import (
"encoding/xml"
"fmt"
"io"
"os"
"os/exec"
"strings"
"time"
"gotest.tools/gotestsum/internal/log"
"gotest.tools/gotestsum/testjson"
)
// JUnitTestSuites is a collection of JUnit test suites.
type JUnitTestSuites struct {
XMLName xml.Name `xml:"testsuites"`
Name string `xml:"name,attr,omitempty"`
Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"`
Errors int `xml:"errors,attr"`
Time string `xml:"time,attr"`
Suites []JUnitTestSuite
}
// JUnitTestSuite is a single JUnit test suite which may contain many
// testcases.
type JUnitTestSuite struct {
XMLName xml.Name `xml:"testsuite"`
Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"`
Time string `xml:"time,attr"`
Name string `xml:"name,attr"`
Properties []JUnitProperty `xml:"properties>property,omitempty"`
TestCases []JUnitTestCase
Timestamp string `xml:"timestamp,attr"`
}
// JUnitTestCase is a single test case with its result.
type JUnitTestCase struct {
XMLName xml.Name `xml:"testcase"`
Classname string `xml:"classname,attr"`
Name string `xml:"name,attr"`
Time string `xml:"time,attr"`
SkipMessage *JUnitSkipMessage `xml:"skipped,omitempty"`
Failure *JUnitFailure `xml:"failure,omitempty"`
}
// JUnitSkipMessage contains the reason why a testcase was skipped.
type JUnitSkipMessage struct {
Message string `xml:"message,attr"`
}
// JUnitProperty represents a key/value pair used to define properties.
type JUnitProperty struct {
Name string `xml:"name,attr"`
Value string `xml:"value,attr"`
}
// JUnitFailure contains data related to a failed test.
type JUnitFailure struct {
Message string `xml:"message,attr"`
Type string `xml:"type,attr"`
Contents string `xml:",chardata"`
}
// Config used to write a junit XML document.
type Config struct {
ProjectName string
FormatTestSuiteName FormatFunc
FormatTestCaseClassname FormatFunc
// This is used for tests to have a consistent timestamp
customTimestamp string
customElapsed string
}
// FormatFunc converts a string from one format into another.
type FormatFunc func(string) string
// Write creates an XML document and writes it to out.
func Write(out io.Writer, exec *testjson.Execution, cfg Config) error {
if err := write(out, generate(exec, cfg)); err != nil {
return fmt.Errorf("failed to write JUnit XML: %v", err)
}
return nil
}
func generate(exec *testjson.Execution, cfg Config) JUnitTestSuites {
cfg = configWithDefaults(cfg)
version := goVersion()
suites := JUnitTestSuites{
Name: cfg.ProjectName,
Tests: exec.Total(),
Failures: len(exec.Failed()),
Errors: len(exec.Errors()),
Time: formatDurationAsSeconds(time.Since(exec.Started())),
}
if cfg.customElapsed != "" {
suites.Time = cfg.customElapsed
}
for _, pkgname := range exec.Packages() {
pkg := exec.Package(pkgname)
junitpkg := JUnitTestSuite{
Name: cfg.FormatTestSuiteName(pkgname),
Tests: pkg.Total,
Time: formatDurationAsSeconds(pkg.Elapsed()),
Properties: packageProperties(version),
TestCases: packageTestCases(pkg, cfg.FormatTestCaseClassname),
Failures: len(pkg.Failed),
Timestamp: cfg.customTimestamp,
}
if cfg.customTimestamp == "" {
junitpkg.Timestamp = exec.Started().Format(time.RFC3339)
}
suites.Suites = append(suites.Suites, junitpkg)
}
return suites
}
func configWithDefaults(cfg Config) Config {
noop := func(v string) string {
return v
}
if cfg.FormatTestSuiteName == nil {
cfg.FormatTestSuiteName = noop
}
if cfg.FormatTestCaseClassname == nil {
cfg.FormatTestCaseClassname = noop
}
return cfg
}
func formatDurationAsSeconds(d time.Duration) string {
return fmt.Sprintf("%f", d.Seconds())
}
func packageProperties(goVersion string) []JUnitProperty {
return []JUnitProperty{
{Name: "go.version", Value: goVersion},
}
}
// goVersion returns the version as reported by the go binary in PATH. This
// version will not be the same as runtime.Version, which is always the version
// of go used to build the gotestsum binary.
//
// To skip the os/exec call set the GOVERSION environment variable to the
// desired value.
func goVersion() string {
if version, ok := os.LookupEnv("GOVERSION"); ok {
return version
}
log.Debugf("exec: go version")
cmd := exec.Command("go", "version")
out, err := cmd.Output()
if err != nil {
log.Warnf("Failed to lookup go version for junit xml: %v", err)
return "unknown"
}
return strings.TrimPrefix(strings.TrimSpace(string(out)), "go version ")
}
func packageTestCases(pkg *testjson.Package, formatClassname FormatFunc) []JUnitTestCase {
cases := []JUnitTestCase{}
if pkg.TestMainFailed() {
jtc := newJUnitTestCase(testjson.TestCase{Test: "TestMain"}, formatClassname)
jtc.Failure = &JUnitFailure{
Message: "Failed",
Contents: pkg.Output(0),
}
cases = append(cases, jtc)
}
for _, tc := range pkg.Failed {
jtc := newJUnitTestCase(tc, formatClassname)
jtc.Failure = &JUnitFailure{
Message: "Failed",
Contents: strings.Join(pkg.OutputLines(tc), ""),
}
cases = append(cases, jtc)
}
for _, tc := range pkg.Skipped {
jtc := newJUnitTestCase(tc, formatClassname)
jtc.SkipMessage = &JUnitSkipMessage{
Message: strings.Join(pkg.OutputLines(tc), ""),
}
cases = append(cases, jtc)
}
for _, tc := range pkg.Passed {
jtc := newJUnitTestCase(tc, formatClassname)
cases = append(cases, jtc)
}
return cases
}
func newJUnitTestCase(tc testjson.TestCase, formatClassname FormatFunc) JUnitTestCase {
return JUnitTestCase{
Classname: formatClassname(tc.Package),
Name: tc.Test.Name(),
Time: formatDurationAsSeconds(tc.Elapsed),
}
}
func write(out io.Writer, suites JUnitTestSuites) error {
doc, err := xml.MarshalIndent(suites, "", "\t")
if err != nil {
return err
}
_, err = out.Write([]byte(xml.Header))
if err != nil {
return err
}
_, err = out.Write(doc)
return err
}
gotestsum-1.8.2/internal/junitxml/report_test.go 0000664 0000000 0000000 00000002457 14274752762 0022173 0 ustar 00root root 0000000 0000000 package junitxml
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"runtime"
"testing"
"time"
"gotest.tools/gotestsum/testjson"
"gotest.tools/v3/assert"
"gotest.tools/v3/env"
"gotest.tools/v3/golden"
)
func TestWrite(t *testing.T) {
out := new(bytes.Buffer)
exec := createExecution(t)
env.Patch(t, "GOVERSION", "go7.7.7")
err := Write(out, exec, Config{
ProjectName: "test",
customTimestamp: new(time.Time).Format(time.RFC3339),
customElapsed: "2.1",
})
assert.NilError(t, err)
golden.Assert(t, out.String(), "junitxml-report.golden")
}
func createExecution(t *testing.T) *testjson.Execution {
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: readTestData(t, "out"),
Stderr: readTestData(t, "err"),
})
assert.NilError(t, err)
return exec
}
func readTestData(t *testing.T, stream string) io.Reader {
raw, err := ioutil.ReadFile("../../testjson/testdata/input/go-test-json." + stream)
assert.NilError(t, err)
return bytes.NewReader(raw)
}
func TestGoVersion(t *testing.T) {
t.Run("unknown", func(t *testing.T) {
env.Patch(t, "PATH", "/bogus")
assert.Equal(t, goVersion(), "unknown")
})
t.Run("current version", func(t *testing.T) {
expected := fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH)
assert.Equal(t, goVersion(), expected)
})
}
gotestsum-1.8.2/internal/junitxml/testdata/ 0000775 0000000 0000000 00000000000 14274752762 0021073 5 ustar 00root root 0000000 0000000 gotestsum-1.8.2/internal/junitxml/testdata/junitxml-report.golden 0000664 0000000 0000000 00000030404 14274752762 0025451 0 ustar 00root root 0000000 0000000
sometimes main can exit 2
FAIL gotest.tools/gotestsum/testjson/internal/badmain 0.001s
=== RUN TestNestedParallelFailures/a
=== PAUSE TestNestedParallelFailures/a
=== CONT TestNestedParallelFailures/a
fails_test.go:50: failed sub a
--- FAIL: TestNestedParallelFailures/a (0.00s)
=== RUN TestNestedParallelFailures/d
=== PAUSE TestNestedParallelFailures/d
=== CONT TestNestedParallelFailures/d
fails_test.go:50: failed sub d
--- FAIL: TestNestedParallelFailures/d (0.00s)
=== RUN TestNestedParallelFailures/c
=== PAUSE TestNestedParallelFailures/c
=== CONT TestNestedParallelFailures/c
fails_test.go:50: failed sub c
--- FAIL: TestNestedParallelFailures/c (0.00s)
=== RUN TestNestedParallelFailures/b
=== PAUSE TestNestedParallelFailures/b
=== CONT TestNestedParallelFailures/b
fails_test.go:50: failed sub b
--- FAIL: TestNestedParallelFailures/b (0.00s)
=== RUN TestNestedParallelFailures
--- FAIL: TestNestedParallelFailures (0.00s)
=== RUN TestParallelTheFirst
=== PAUSE TestParallelTheFirst
=== CONT TestParallelTheFirst
fails_test.go:29: failed the first
--- FAIL: TestParallelTheFirst (0.01s)
=== RUN TestParallelTheThird
=== PAUSE TestParallelTheThird
=== CONT TestParallelTheThird
fails_test.go:41: failed the third
--- FAIL: TestParallelTheThird (0.00s)
=== RUN TestParallelTheSecond
=== PAUSE TestParallelTheSecond
=== CONT TestParallelTheSecond
fails_test.go:35: failed the second
--- FAIL: TestParallelTheSecond (0.01s)
=== RUN TestFailed
fails_test.go:34: this failed
--- FAIL: TestFailed (0.00s)
=== RUN TestFailedWithStderr
this is stderr
fails_test.go:43: also failed
--- FAIL: TestFailedWithStderr (0.00s)
=== RUN TestNestedWithFailure/c
fails_test.go:65: failed
--- FAIL: TestNestedWithFailure/c (0.00s)
=== RUN TestNestedWithFailure
--- FAIL: TestNestedWithFailure (0.00s)