├── .github
└── workflows
│ ├── ci.yaml
│ ├── lint.yaml
│ └── release.yaml
├── .golangci.yaml
├── .goreleaser.yaml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── go.mod
├── go.sum
├── internal
├── app
│ ├── app.go
│ ├── console_writer.go
│ ├── table.go
│ ├── table_failed.go
│ ├── table_summary.go
│ └── table_tests.go
└── utils
│ ├── utils.go
│ └── utils_test.go
├── main.go
├── parse
├── event.go
├── event_test.go
├── package.go
├── package_slice.go
├── process.go
├── process_options.go
└── test.go
├── scripts
└── release-notes.sh
└── tests
├── cached_test.go
├── cover_test.go
├── follow_test.go
├── outcome_test.go
├── package_start_test.go
├── panic_test.go
├── prescan_test.go
├── race_test.go
├── sort_test.go
├── summary_counts_test.go
└── testdata
├── cached
├── test_01.jsonl
└── test_02.jsonl
├── cover
├── test_01.jsonl
├── test_02.jsonl
└── test_03.jsonl
├── elapsed_test.jsonl
├── failed
├── test_01.jsonl
├── test_02.jsonl
├── test_03.jsonl
└── test_04.jsonl
├── follow-verbose
├── test_01.golden
├── test_01.jsonl
├── test_02.golden
├── test_02.jsonl
├── test_03.golden
├── test_03.jsonl
├── test_04.golden
├── test_04.jsonl
├── test_05.golden
├── test_05.jsonl
├── test_06.golden
└── test_06.jsonl
├── follow
├── test_01.golden
└── test_01.jsonl
├── go120_start_action.jsonl
├── metrics_test.jsonl
├── outcome
├── test_01.jsonl
├── test_02.jsonl
├── test_03.jsonl
├── test_04.jsonl
├── test_05.jsonl
├── test_06.jsonl
├── test_07.jsonl
└── test_08.jsonl
├── panic
├── test_01.jsonl
├── test_02.jsonl
├── test_03.jsonl
├── test_04.jsonl
├── test_05.jsonl
└── test_06.jsonl
├── prescan
├── test_01.txt
├── test_02.txt
├── test_03.txt
└── test_04.txt
└── race
├── test_01.jsonl
├── test_02.jsonl
├── test_03.jsonl
├── test_04.jsonl
├── test_05.jsonl
├── test_06.jsonl
├── test_07.jsonl
└── test_08.jsonl
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types: [opened, synchronize, reopened]
9 |
10 | jobs:
11 | build:
12 | name: Build and test
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | # go-version: ['oldstable', 'stable', '1.23.0-rc.2']
17 | go-version: ['oldstable', 'stable']
18 | env:
19 | VERBOSE: 1
20 |
21 | steps:
22 | - name: Checkout code
23 | uses: actions/checkout@v4
24 | - name: Set up Go
25 | uses: actions/setup-go@v5
26 | with:
27 | go-version: ${{ matrix.go-version }}
28 | - name: Build
29 | run: go build -v .
30 | - name: Run tests with GITHUB_STEP_SUMMARY
31 | shell: bash
32 | # Note the use of || true. This so the job doesn't fail at that line. We want to preserve -follow
33 | # as part of the test output, but not output it to the summary page, which is done in the proceeding
34 | # command when we parse the output.jsonl file.
35 | run: |
36 | go test -v -count=1 -race ./... -json -coverpkg github.com/mfridman/tparse/parse \
37 | | tee output.jsonl | ./tparse -notests -follow -all || true
38 | ./tparse -format markdown -file output.jsonl -all -slow 20 > $GITHUB_STEP_SUMMARY
39 | - name: Run tparse w/ std lib
40 | run: go test -count=1 fmt strings bytes bufio crypto log mime sort slices -json -cover | ./tparse -follow -all
41 | - name: Install GoReleaser
42 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.go-version == 'stable'
43 | uses: goreleaser/goreleaser-action@v6
44 | with:
45 | install-only: true
46 | distribution: goreleaser
47 | version: "~> v2"
48 | - name: Gorelease dry-run
49 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.go-version == 'stable'
50 | run: |
51 | goreleaser release --skip=publish --snapshot --fail-fast --clean
52 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yaml:
--------------------------------------------------------------------------------
1 | name: golangci
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | golangci:
15 | name: lint
16 | runs-on: ubuntu-latest
17 | steps:
18 | - name: Checkout code
19 | uses: actions/checkout@v4
20 | - uses: actions/setup-go@v5
21 | with:
22 | go-version: 'stable'
23 | - name: golangci-lint
24 | uses: golangci/golangci-lint-action@v5
25 | with:
26 | version: latest
27 | github-token: ${{ secrets.GITHUB_TOKEN }}
28 | args: --timeout=2m --verbose
29 | annotations: false
30 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: goreleaser
2 | on:
3 | push:
4 | tags:
5 | - '*'
6 | permissions:
7 | contents: write
8 | jobs:
9 | goreleaser:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | with:
14 | fetch-depth: 0
15 | - run: git fetch --force --tags
16 | - uses: actions/setup-go@v5
17 | with:
18 | go-version: 'stable'
19 | - name: Generate release notes
20 | continue-on-error: true
21 | run: ./scripts/release-notes.sh ${{github.ref_name}} > ${{runner.temp}}/release_notes.txt
22 | - name: Run GoReleaser
23 | uses: goreleaser/goreleaser-action@v6
24 | with:
25 | distribution: goreleaser
26 | version: "~> v2"
27 | args: release --clean --release-notes=${{runner.temp}}/release_notes.txt
28 | env:
29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30 |
--------------------------------------------------------------------------------
/.golangci.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | linters:
3 | enable:
4 | # check when errors are compared without errors.Is
5 | - errorlint
6 |
7 | # check imports order and makes it always deterministic.
8 | - gci
9 |
10 | # Very Basic spell error checker
11 | - misspell
12 |
13 | # Fast, configurable, extensible, flexible, and beautiful linter for Go.
14 | # Drop-in replacement of golint.
15 | - revive
16 |
17 | # make sure to use t.Helper() when needed
18 | - thelper
19 |
20 | # ensure that lint exceptions have explanations. Consider the case below:
21 | - nolintlint
22 |
23 | # detect duplicated words in code
24 | - dupword
25 |
26 | # mirror suggests rewrites to avoid unnecessary []byte/string conversion
27 | - mirror
28 |
29 | # testify checks good usage of github.com/stretchr/testify.
30 | - testifylint
31 |
32 | linters-settings:
33 | dupword:
34 | # Keywords used to ignore detection.
35 | # Default: []
36 | ignore:
37 | - "FAIL" # "FAIL FAIL" is tolerated
38 |
39 | nolintlint:
40 | # Disable to ensure that all nolint directives actually have an effect.
41 | # Default: false
42 | allow-unused: true # too many false positive reported
43 | # Exclude following linters from requiring an explanation.
44 | # Default: []
45 | allow-no-explanation: []
46 | # Enable to require an explanation of nonzero length
47 | # after each nolint directive.
48 | # Default: false
49 | require-explanation: true
50 | # Enable to require nolint directives to mention the specific
51 | # linter being suppressed.
52 | # Default: false
53 | require-specific: true
54 |
55 | revive:
56 | rules:
57 | - name: bare-return
58 | - name: blank-imports
59 | - name: comment-spacings
60 | - name: context-as-argument
61 | arguments:
62 | - allowTypesBefore: "*testing.T"
63 | - name: context-keys-type
64 | - name: defer
65 | arguments:
66 | - ["call-chain", "loop"]
67 | - name: dot-imports
68 | - name: early-return
69 | - name: empty-block
70 | - name: error-return
71 | - name: error-strings
72 | - name: error-naming
73 | - name: errorf
74 | - name: exported
75 | arguments:
76 | # enables checking public methods of private types
77 | - "checkPrivateReceivers"
78 | # make error messages clearer
79 | - "sayRepetitiveInsteadOfStutters"
80 | - name: if-return
81 | - name: import-shadowing
82 | - name: increment-decrement
83 | - name: indent-error-flow
84 | - name: exported
85 | - name: var-naming
86 | - name: var-declaration
87 | - name: package-comments
88 | - name: range
89 | - name: receiver-naming
90 | - name: redefines-builtin-id
91 | - name: superfluous-else
92 | - name: time-naming
93 | - name: time-equal
94 | - name: unexported-return
95 | - name: use-any
96 | - name: unreachable-code
97 | - name: unhandled-error
98 | arguments:
99 | - "fmt.Print.*"
100 | - "fmt.Fprint.*"
101 | - "bytes.Buffer.Write.*"
102 | - "strings.Builder.Write.*"
103 | - name: unused-parameter
104 | - name: unused-receiver
105 | - name: useless-break
106 |
107 | # define the import orders
108 | gci:
109 | sections:
110 | # Standard section: captures all standard packages.
111 | - standard
112 | # Default section: catchall that is not standard or custom
113 | - default
114 | # Custom section: groups all imports with the specified Prefix.
115 | - prefix(github.com/mfridman)
116 |
117 |
--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json
2 | #
3 | # See https://goreleaser.com/customization/ for more information.
4 | version: 2
5 | project_name: tparse
6 |
7 | before:
8 | hooks:
9 | - go mod tidy
10 | builds:
11 | - env:
12 | - CGO_ENABLED=0
13 | binary: tparse
14 | main: main.go
15 | goos:
16 | - linux
17 | - darwin
18 | # - windows
19 | goarch:
20 | - amd64
21 | - arm64
22 | ldflags:
23 | # The v prefix is stripped by goreleaser, so we need to add it back.
24 | # https://goreleaser.com/customization/templates/#fnref:version-prefix
25 | - "-s -w -X main.version=v{{ .Version }}"
26 |
27 | archives:
28 | - format: binary
29 | name_template: >-
30 | {{ .ProjectName }}_{{- tolower .Os }}_{{- if eq .Arch "amd64" }}x86_64{{- else }}{{ .Arch }}{{ end }}
31 | checksum:
32 | name_template: "checksums.txt"
33 | snapshot:
34 | name_template: "{{ incpatch .Version }}-next"
35 | changelog:
36 | use: github-native
37 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project
6 | adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [Unreleased]
9 |
10 | ## [v0.17.0]
11 |
12 | - Deprecate github.com/mfridman/buildversion, and use std lib `debug.ReadBuildInfo()` instead. In
13 | go1.24 this is handled automatically, from the [release notes](https://go.dev/doc/go1.24):
14 |
15 | > The go build command now sets the main module’s version in the compiled binary based on the
16 | > version control system tag and/or commit. A +dirty suffix will be appended if there are
17 | > uncommitted changes. Use the -buildvcs=false flag to omit version control information from the
18 | > binary.
19 |
20 | - Handle changes in go1.24 related to build output. `tparse` will pipe the build output to stderr
21 |
22 | > Furthermore, `go test -json` now reports build output and failures in JSON, interleaved with
23 | > test result JSON. These are distinguished by new Action types, but if they cause problems in a
24 | > test integration system, you can revert to the text build output with GODEBUG setting
25 | > gotestjsonbuildtext=1.
26 |
27 | ## [v0.16.0]
28 |
29 | - Add a `-follow-output` flag to allow writing go test output directly into a file. This will be
30 | useful (especially in CI jobs) for outputting overly verbose testing output into a file instead of
31 | the standard stream. (#134)
32 |
33 | | flag combination | `go test` output destination |
34 | | ------------------------ | ---------------------------- |
35 | | No flags | Discard output |
36 | | `-follow` | Write to stdout |
37 | | `-follow-output` | Write to file |
38 | | `-follow -follow-output` | Write to file |
39 |
40 | - Use [charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) for table rendering.
41 | - This will allow for more control over the output and potentially more features in the future.
42 | (#136)
43 | - Minor changes to the output format are expected, but the overall content should remain the same.
44 | If you have any feedback, please let me know.
45 |
46 | ## [v0.15.0]
47 |
48 | - Add `-trimpath` flag, which removes the path prefix from package names in the output, simplifying
49 | their display. See #128 for examples.
50 | - There's a special case for `-trimpath=auto` which will automatically determine the prefix based
51 | on the longest common prefix of all package paths.
52 |
53 | ## [v0.14.0]
54 |
55 | - Modify `--follow` behavior by minimizing noisy output. (#122)
56 |
57 | > [!TIP]
58 | >
59 | > If you want the existing behavior, I added a `--follow-verbose` flag. But please do let me know if
60 | > this affected you, as I plan to remove this before cutting a `v1.0.0`. Thank you!
61 |
62 | ## [v0.13.3]
63 |
64 | - General housekeeping and dependency updates.
65 |
66 | ## [v0.13.2]
67 |
68 | - Add partial support for `-compare`. A feature that displays the coverage difference against a
69 | previous run. See description for more details
70 | https://github.com/mfridman/tparse/pull/101#issue-1857786730 and the initial issue #92.
71 | - Fix unstable common package prefix logic #104
72 |
73 | ## [v0.13.1] - 2023-08-04
74 |
75 | - Fix failing GoReleaser GitHub action (release notes location).
76 |
77 | Summary from [v0.13.0](https://github.com/mfridman/tparse/releases/tag/v0.13.0)
78 |
79 | - Start a [CHANGELOG.md](https://github.com/mfridman/tparse/blob/main/CHANGELOG.md) for user-facing
80 | change.
81 | - Add [GoReleaser](https://goreleaser.com/) to automate the release process. Pre-built binaries are
82 | available for each release, currently Linux and macOS. If there is demand, can also add Windows.
83 |
84 | ## [v0.13.0] - 2023-08-04
85 |
86 | - Start a [CHANGELOG.md](https://github.com/mfridman/tparse/blob/main/CHANGELOG.md) for user-facing
87 | change.
88 | - Add [GoReleaser](https://goreleaser.com/) to automate the release process. Pre-built binaries are
89 | available for each release, currently Linux and macOS. If there is demand, can also add Windows.
90 |
91 | [Unreleased]: https://github.com/mfridman/tparse/compare/v0.17.0...HEAD
92 | [v0.17.0]: https://github.com/mfridman/tparse/compare/v0.16.0...v0.17.0
93 | [v0.16.0]: https://github.com/mfridman/tparse/compare/v0.15.0...v0.16.0
94 | [v0.15.0]: https://github.com/mfridman/tparse/compare/v0.14.0...v0.15.0
95 | [v0.14.0]: https://github.com/mfridman/tparse/compare/v0.13.3...v0.14.0
96 | [v0.13.3]: https://github.com/mfridman/tparse/compare/v0.13.2...v0.13.3
97 | [v0.13.2]: https://github.com/mfridman/tparse/compare/v0.13.1...v0.13.2
98 | [v0.13.1]: https://github.com/mfridman/tparse/compare/v0.13.0...v0.13.1
99 | [v0.13.0]: https://github.com/mfridman/tparse/releases/tag/v0.13.0
100 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Michael Fridman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ROOT := github.com/mfridman/tparse
2 | GOPATH ?= $(shell go env GOPATH)
3 | TOOLS_BIN = $(GOPATH)/bin
4 |
5 | .PHONY: vet
6 | vet:
7 | @go vet ./...
8 |
9 | .PHONY: lint
10 | lint: tools
11 | @golangci-lint run ./... --fix
12 |
13 | .PHONY: tools
14 | tools:
15 | @which golangci-lint >/dev/null 2>&1 || \
16 | (echo "Installing latest golangci-lint" && \
17 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
18 | sh -s -- -b "$(TOOLS_BIN)")
19 |
20 | .PHONY: tools-update
21 | tools-update:
22 | @echo "Updating golangci-lint to latest version"
23 | @rm -f "$(TOOLS_BIN)/golangci-lint"
24 | @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
25 | sh -s -- -b "$(TOOLS_BIN)"
26 | @echo "golangci-lint updated successfully to latest version"
27 |
28 | .PHONY: tools-version
29 | tools-version:
30 | @echo "Current tool versions:"
31 | @echo "golangci-lint: $$(golangci-lint --version 2>/dev/null || echo 'not installed')"
32 |
33 | .PHONY: release
34 | release:
35 | @goreleaser --rm-dist
36 |
37 | .PHONY: build
38 | build:
39 | @go build -o $$GOBIN/tparse ./
40 |
41 | .PHONY: clean
42 | clean:
43 | @find . -type f -name '*.FAIL' -delete
44 |
45 | .PHONY: test
46 | test:
47 | @go test -count=1 ./...
48 |
49 | test-tparse:
50 | @go test -race -count=1 ./internal/... -json -cover | go run main.go -trimpath=auto -sort=elapsed
51 | @go test -race -count=1 ./tests/... -json -cover -coverpkg=./parse | go run main.go -trimpath=github.com/mfridman/tparse/ -sort=elapsed
52 |
53 | # dogfooding :)
54 | test-tparse-full:
55 | go test -race -count=1 -v ./... -json | go run main.go -all -smallscreen -notests -sort=elapsed
56 |
57 | coverage:
58 | go test ./tests/... -coverpkg=./parse -covermode=count -coverprofile=count.out
59 | go tool cover -html=count.out
60 |
61 | search-todo:
62 | @echo "Searching for TODOs in Go files..."
63 | @rg '// TODO\(mf\):' --glob '*.go' || echo "No TODOs found."
64 |
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tparse [](https://github.com/mfridman/tparse)
2 |
3 | A command line tool for analyzing and summarizing `go test` output.
4 |
5 | > [!TIP]
6 | >
7 | > Don't forget to run `go test` with the `-json` flag.
8 |
9 | Pass | Fail
10 | :-------------------------:|:-------------------------:
11 |
|
12 |
13 | By default, `tparse` will always return test failures and panics, if any, followed by a package-level summary table.
14 |
15 | To get additional info on passed tests run `tparse` with `-pass` flag. Tests are grouped by package and sorted by elapsed time in descending order (longest to shortest).
16 |
17 | ### [But why?!](#but-why) for more info.
18 |
19 | ## Installation
20 |
21 | go install github.com/mfridman/tparse@latest
22 |
23 | Or download the latest pre-built binary [here](https://github.com/mfridman/tparse/releases/latest).
24 |
25 | ## Usage
26 |
27 | Once `tparse` is installed there are 2 ways to use it:
28 |
29 | 1. Run `go test` as normal, but add `-json` flag and pipe output to `tparse`.
30 |
31 | ```
32 | set -o pipefail && go test fmt -json | tparse -all
33 | ```
34 |
35 | 2. Save the output of `go test` with `-json` flag into a file and call `tparse` with `-file` option.
36 |
37 | ```
38 | go test fmt -json > fmt.out
39 | tparse -all -file=fmt.out
40 | ```
41 |
42 | Tip: run `tparse -h` to get usage and options.
43 |
44 | ## But why?!
45 |
46 | `go test` is awesome, but verbose. Sometimes you just want readily available failures, grouped by package, printed with a dash of color.
47 |
48 | `tparse` attempts to do just that; return failed tests and panics, if any, followed by a single package-level summary. No more searching for the literal string: "--- FAIL".
49 |
50 | But, let's take it a bit further. With `-all` (`-pass` and `-skip` combined) you can get additional info, such as skipped tests and elapsed time of each passed test.
51 |
52 | `tparse` comes with a `-follow` flag to print raw output. Yep, go test pipes JSON, it's parsed and the output is printed back out as if you ran go test without `-json` flag. Eliminating the need for `tee /dev/tty` between pipes.
53 |
54 | The default print order is:
55 | - `go test` output (if adding `-follow` flag)
56 | - passed/skipped table (if adding `-all`, `-skip` or `-pass` flag)
57 | - failed tests and panics
58 | - summary
59 |
60 | For narrow displays the `-smallscreen` flag may be useful, dividing a long test name and making it vertical heavy:
61 |
62 | ```
63 | TestSubtests/an_awesome_but_long/subtest_for_the/win
64 |
65 | TestSubtests
66 | /an_awesome_but_long
67 | /subtest_for_the
68 | /win
69 | ```
70 |
71 | `tparse` aims to be a simple alternative to one-liner bash functions.
72 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/mfridman/tparse
2 |
3 | go 1.23.0
4 |
5 | toolchain go1.24.2
6 |
7 | require (
8 | github.com/charmbracelet/lipgloss v1.1.0
9 | github.com/muesli/termenv v0.16.0
10 | github.com/stretchr/testify v1.9.0
11 | )
12 |
13 | require (
14 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
15 | github.com/charmbracelet/colorprofile v0.3.1 // indirect
16 | github.com/charmbracelet/x/ansi v0.9.2 // indirect
17 | github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
18 | github.com/charmbracelet/x/term v0.2.1 // indirect
19 | github.com/davecgh/go-spew v1.1.1 // indirect
20 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
21 | github.com/mattn/go-isatty v0.0.20 // indirect
22 | github.com/mattn/go-runewidth v0.0.16 // indirect
23 | github.com/pmezard/go-difflib v1.0.0 // indirect
24 | github.com/rivo/uniseg v0.4.7 // indirect
25 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
26 | golang.org/x/sys v0.33.0 // indirect
27 | gopkg.in/yaml.v3 v3.0.1 // indirect
28 | )
29 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
2 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
3 | github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
4 | github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
5 | github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
6 | github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
7 | github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
8 | github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
9 | github.com/charmbracelet/x/ansi v0.9.2 h1:92AGsQmNTRMzuzHEYfCdjQeUzTrgE1vfO5/7fEVoXdY=
10 | github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
11 | github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
12 | github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
13 | github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
14 | github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
15 | github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
16 | github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
17 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
18 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
19 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
20 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
21 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
22 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
23 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
24 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
25 | github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
26 | github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
27 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
28 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
29 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
30 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
31 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
32 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
33 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
34 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
35 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
36 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
37 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
38 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
39 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
40 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
41 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
42 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
43 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
44 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
45 |
--------------------------------------------------------------------------------
/internal/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "io"
7 | "os"
8 |
9 | "github.com/mfridman/tparse/parse"
10 | )
11 |
12 | type Options struct {
13 | // Output is used to write the final output, such as the tables, summary, etc.
14 | Output io.Writer
15 | // DisableColor will disable all colors.
16 | DisableColor bool
17 | // Format will set the output format for tables.
18 | Format OutputFormat
19 | // Sorter will set the sort order for the table.
20 | Sorter parse.PackageSorter
21 | // ShowNoTests will display packages containing no test files or empty test files.
22 | ShowNoTests bool
23 | // FileName will read test output from a file.
24 | FileName string
25 |
26 | // Test table options
27 | TestTableOptions TestTableOptions
28 | SummaryTableOptions SummaryTableOptions
29 |
30 | // FollowOutput will follow the raw output as go test is running.
31 | FollowOutput bool // Output to stdout
32 | FollowOutputWriter io.WriteCloser // Output to a file, takes precedence over FollowOutput
33 | FollowOutputVerbose bool
34 |
35 | // Progress will print a single summary line for each package once the package has completed.
36 | // Useful for long running test suites. Maybe used with FollowOutput or on its own.
37 | //
38 | // This will output to stdout.
39 | Progress bool
40 | ProgressOutput io.Writer
41 |
42 | // DisableTableOutput will disable all table output. This is used for testing.
43 | DisableTableOutput bool
44 |
45 | //
46 | // Experimental
47 | //
48 |
49 | // Compare includes a diff of a previous test output file in the summary table.
50 | Compare string
51 | }
52 |
53 | func Run(option Options) (int, error) {
54 | var reader io.ReadCloser
55 | var err error
56 | if option.FileName != "" {
57 | if reader, err = os.Open(option.FileName); err != nil {
58 | return 1, err
59 | }
60 | } else {
61 | if reader, err = newPipeReader(); err != nil {
62 | return 1, errors.New("stdin must be a pipe, or use -file to open a go test output file")
63 | }
64 | }
65 | defer reader.Close()
66 |
67 | if option.FollowOutputWriter != nil {
68 | defer option.FollowOutputWriter.Close()
69 | }
70 |
71 | summary, err := parse.Process(
72 | reader,
73 | parse.WithFollowOutput(option.FollowOutput),
74 | parse.WithFollowVersboseOutput(option.FollowOutputVerbose),
75 | parse.WithWriter(option.FollowOutputWriter),
76 | parse.WithProgress(option.Progress),
77 | parse.WithProgressOutput(option.ProgressOutput),
78 | )
79 | if err != nil {
80 | return 1, err
81 | }
82 | if len(summary.Packages) == 0 {
83 | return 1, fmt.Errorf("found no go test packages")
84 | }
85 | // Useful for tests that don't need tparse table output. Very useful for testing output from
86 | // [parse.Process]
87 | if !option.DisableTableOutput {
88 | display(option.Output, summary, option)
89 | }
90 | return summary.ExitCode(), nil
91 | }
92 |
93 | func newPipeReader() (io.ReadCloser, error) {
94 | finfo, err := os.Stdin.Stat()
95 | if err != nil {
96 | return nil, err
97 | }
98 | // Check file mode bits to test for named pipe as stdin.
99 | if finfo.Mode()&os.ModeNamedPipe != 0 {
100 | return os.Stdin, nil
101 | }
102 | return nil, errors.New("stdin must be a pipe")
103 | }
104 |
105 | func display(w io.Writer, summary *parse.GoTestSummary, option Options) {
106 | // Best effort to open the compare against file, if it exists.
107 | var warnings []string
108 | defer func() {
109 | for _, w := range warnings {
110 | fmt.Fprintf(os.Stderr, "warning: %s\n", w)
111 | }
112 | }()
113 | var against *parse.GoTestSummary
114 | if option.Compare != "" {
115 | // TODO(mf): cleanup, this is messy.
116 | f, err := os.Open(option.Compare)
117 | if err != nil {
118 | warnings = append(warnings, fmt.Sprintf("failed to open against file: %s", option.Compare))
119 | } else {
120 | defer f.Close()
121 | against, err = parse.Process(f)
122 | if err != nil {
123 | warnings = append(warnings, fmt.Sprintf("failed to parse against file: %s", option.Compare))
124 | }
125 | }
126 | }
127 |
128 | cw := newConsoleWriter(w, option.Format, option.DisableColor)
129 | // Sort packages by name ASC.
130 | packages := summary.GetSortedPackages(option.Sorter)
131 | // Only print the tests table if either pass or skip is true.
132 | if option.TestTableOptions.Pass || option.TestTableOptions.Skip {
133 | if option.Format == OutputFormatMarkdown {
134 | cw.testsTableMarkdown(packages, option.TestTableOptions)
135 | } else {
136 | cw.testsTable(packages, option.TestTableOptions)
137 | }
138 | }
139 | // Failures (if any) and summary table are always printed.
140 | cw.printFailed(packages)
141 | cw.summaryTable(packages, option.ShowNoTests, option.SummaryTableOptions, against)
142 | }
143 |
--------------------------------------------------------------------------------
/internal/app/console_writer.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "io"
5 |
6 | "github.com/charmbracelet/lipgloss"
7 | "github.com/muesli/termenv"
8 | )
9 |
10 | type OutputFormat int
11 |
12 | const (
13 | // OutputFormatBasic is a normal table without a border
14 | OutputFormatPlain OutputFormat = iota + 1
15 | // OutputFormatBasic is a normal table with border
16 | OutputFormatBasic
17 | // OutputFormatBasic is a markdown-rendered table
18 | OutputFormatMarkdown
19 | )
20 |
21 | type consoleWriter struct {
22 | format OutputFormat
23 | w io.Writer
24 |
25 | red colorOptionFunc
26 | green colorOptionFunc
27 | yellow colorOptionFunc
28 | }
29 |
30 | type colorOptionFunc func(s string) string
31 |
32 | // newColor is a helper function to set the base color.
33 | func newColor(color lipgloss.TerminalColor) colorOptionFunc {
34 | return func(text string) string {
35 | return lipgloss.NewStyle().Foreground(color).Render(text)
36 | }
37 | }
38 |
39 | // newMarkdownColor is a helper function to set the base color for markdown.
40 | func newMarkdownColor(s string) colorOptionFunc {
41 | return func(text string) string {
42 | return s + " " + text
43 | }
44 | }
45 |
46 | func noColor() colorOptionFunc {
47 | return func(text string) string { return text }
48 | }
49 |
50 | func newConsoleWriter(w io.Writer, format OutputFormat, disableColor bool) *consoleWriter {
51 | if format == 0 {
52 | format = OutputFormatBasic
53 | }
54 | cw := &consoleWriter{
55 | w: w,
56 | format: format,
57 | }
58 | cw.red = noColor()
59 | cw.green = noColor()
60 | cw.yellow = noColor()
61 |
62 | if !disableColor {
63 | // NOTE(mf): GitHub Actions CI env (and probably others) do not have an
64 | // interactive TTY, and tparse through termenv will degrade to the
65 | // "best available option" .. which is no colors. We can work around this by
66 | // setting a color profile explicitly instead of relying on termenv to auto-detect.
67 | // Ref: https://github.com/charmbracelet/lipgloss/issues/74
68 | // Ref: https://github.com/mfridman/tparse/issues/76
69 | lipgloss.SetColorProfile(termenv.TrueColor)
70 |
71 | switch format {
72 | case OutputFormatMarkdown:
73 | cw.green = newMarkdownColor("🟢")
74 | cw.yellow = newMarkdownColor("🟡")
75 | cw.red = newMarkdownColor("🔴")
76 | default:
77 | cw.green = newColor(lipgloss.Color("10"))
78 | cw.yellow = newColor(lipgloss.Color("11"))
79 | cw.red = newColor(lipgloss.Color("9"))
80 | }
81 | }
82 | return cw
83 | }
84 |
--------------------------------------------------------------------------------
/internal/app/table.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "github.com/charmbracelet/lipgloss"
5 | "github.com/charmbracelet/lipgloss/table"
6 | )
7 |
8 | func newTable(
9 | format OutputFormat,
10 | override func(style lipgloss.Style, row, col int) lipgloss.Style,
11 | ) *table.Table {
12 | tbl := table.New()
13 | switch format {
14 | case OutputFormatPlain:
15 | tbl.Border(lipgloss.HiddenBorder()).BorderTop(false).BorderBottom(false)
16 | case OutputFormatMarkdown:
17 | tbl.Border(markdownBorder).BorderBottom(false).BorderTop(false)
18 | case OutputFormatBasic:
19 | tbl.Border(lipgloss.RoundedBorder())
20 | }
21 | return tbl.StyleFunc(func(row, col int) lipgloss.Style {
22 | // Default style, may be overridden.
23 | style := lipgloss.NewStyle().PaddingLeft(1).PaddingRight(1).Align(lipgloss.Center)
24 | if override != nil {
25 | style = override(style, row, col)
26 | }
27 | return style
28 | })
29 | }
30 |
31 | var markdownBorder = lipgloss.Border{
32 | Top: "-",
33 | Bottom: "-",
34 | Left: "|",
35 | Right: "|",
36 | TopLeft: "", // empty for markdown
37 | TopRight: "", // empty for markdown
38 | BottomLeft: "", // empty for markdown
39 | BottomRight: "", // empty for markdown
40 | MiddleLeft: "|",
41 | MiddleRight: "|",
42 | Middle: "|",
43 | MiddleTop: "|",
44 | MiddleBottom: "|",
45 | }
46 |
--------------------------------------------------------------------------------
/internal/app/table_failed.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "fmt"
5 | "sort"
6 | "strings"
7 |
8 | "github.com/charmbracelet/lipgloss"
9 |
10 | "github.com/mfridman/tparse/parse"
11 | )
12 |
13 | // printFailed prints all failed tests, grouping them by package. Packages are sorted.
14 | // Panic is an exception.
15 | func (c *consoleWriter) printFailed(packages []*parse.Package) {
16 | for _, pkg := range packages {
17 | if pkg.HasPanic {
18 | // TODO(mf): document why panics are handled separately. A panic may or may
19 | // not be associated with tests, so we print it at the package level.
20 | output := c.prepareStyledPanic(pkg.Summary.Package, pkg.Summary.Test, pkg.PanicEvents)
21 | fmt.Fprintln(c.w, output)
22 | continue
23 | }
24 | failedTests := pkg.TestsByAction(parse.ActionFail)
25 | if len(failedTests) == 0 {
26 | continue
27 | }
28 | styledPackageHeader := c.styledHeader(
29 | pkg.Summary.Action.String(),
30 | pkg.Summary.Package,
31 | )
32 | fmt.Fprintln(c.w, styledPackageHeader)
33 | fmt.Fprintln(c.w)
34 | /*
35 | Failed tests are all the individual tests, where the subtests are not separated.
36 |
37 | We need to sort the tests by name to ensure they are grouped together
38 | */
39 | sort.Slice(failedTests, func(i, j int) bool {
40 | return failedTests[i].Name < failedTests[j].Name
41 | })
42 |
43 | divider := lipgloss.NewStyle().
44 | BorderStyle(lipgloss.NormalBorder()).
45 | BorderTop(true).
46 | Faint(c.format != OutputFormatMarkdown).
47 | Width(96)
48 |
49 | /*
50 | Note, some output such as the "--- FAIL: " line is prefixed
51 | with spaces. Unfortunately when dumping this in markdown format
52 | it renders as an code block.
53 |
54 | "To produce a code block in Markdown, simply indent every line of the
55 | block by at least 4 spaces or 1 tab."
56 | Ref. https://daringfireball.net/projects/markdown/syntax
57 |
58 | Example:
59 | --- FAIL: Test (0.05s)
60 | --- FAIL: Test/test_01 (0.01s)
61 | --- FAIL: Test/test_01/sort (0.00s)
62 |
63 | This is why we wrap the entire test output in a code block.
64 | */
65 |
66 | if c.format == OutputFormatMarkdown {
67 | fmt.Fprintln(c.w, fencedCodeBlock)
68 | }
69 | var key string
70 | for i, t := range failedTests {
71 | // Add top divider to all tests except first one.
72 | base, _, _ := cut(t.Name, "/")
73 | if i > 0 && key != base {
74 | fmt.Fprintln(c.w, divider.String())
75 | }
76 | key = base
77 | fmt.Fprintln(c.w, c.prepareStyledTest(t))
78 | }
79 | if c.format == OutputFormatMarkdown {
80 | fmt.Fprint(c.w, fencedCodeBlock+"\n\n")
81 | }
82 | }
83 | }
84 |
85 | const (
86 | fencedCodeBlock string = "```"
87 | )
88 |
89 | // copied directly from strings.Cut (go1.18) to support older Go versions.
90 | // In the future, replace this with the upstream function.
91 | func cut(s, sep string) (before, after string, found bool) {
92 | if i := strings.Index(s, sep); i >= 0 {
93 | return s[:i], s[i+len(sep):], true
94 | }
95 | return s, "", false
96 | }
97 |
98 | func (c *consoleWriter) prepareStyledPanic(
99 | packageName string,
100 | testName string,
101 | panicEvents []*parse.Event,
102 | ) string {
103 | if testName != "" {
104 | packageName = packageName + " • " + testName
105 | }
106 | styledPackageHeader := c.styledHeader("PANIC", packageName)
107 | // TODO(mf): can we pass this panic stack to another package and either by default,
108 | // or optionally, build human-readable panic output with:
109 | // https://github.com/maruel/panicparse
110 | var rows strings.Builder
111 | for _, e := range panicEvents {
112 | if e.Output == "" {
113 | continue
114 | }
115 | rows.WriteString(e.Output)
116 | }
117 | return lipgloss.JoinVertical(lipgloss.Left, styledPackageHeader, rows.String())
118 | }
119 |
120 | func (c *consoleWriter) styledHeader(status, packageName string) string {
121 | status = c.red(strings.ToUpper(status))
122 | packageName = strings.TrimSpace(packageName)
123 |
124 | if c.format == OutputFormatMarkdown {
125 | msg := fmt.Sprintf("## %s • %s", status, packageName)
126 | return msg
127 | // TODO(mf): an alternative implementation is to add 2 horizontal lines above and below
128 | // the package header output.
129 | //
130 | // var divider string
131 | // for i := 0; i < len(msg); i++ {
132 | // divider += "─"
133 | // }
134 | // return fmt.Sprintf("%s\n%s\n%s", divider, msg, divider)
135 | }
136 | /*
137 | Need to rethink how to best support multiple output formats across
138 | CI, local terminal development and markdown
139 |
140 | See https://github.com/mfridman/tparse/issues/71
141 | */
142 | headerStyle := lipgloss.NewStyle().
143 | BorderStyle(lipgloss.ThickBorder()).
144 | BorderForeground(lipgloss.Color("103"))
145 | statusStyle := lipgloss.NewStyle().
146 | PaddingLeft(3).
147 | PaddingRight(2).
148 | Foreground(lipgloss.Color("9"))
149 | packageNameStyle := lipgloss.NewStyle().
150 | PaddingRight(3)
151 | headerRow := lipgloss.JoinHorizontal(
152 | lipgloss.Left,
153 | statusStyle.Render(status),
154 | packageNameStyle.Render("package: "+packageName),
155 | )
156 | return headerStyle.Render(headerRow)
157 | }
158 |
159 | const (
160 | failLine = "--- FAIL: "
161 | )
162 |
163 | func (c *consoleWriter) prepareStyledTest(t *parse.Test) string {
164 | t.SortEvents()
165 |
166 | var rows, headerRows strings.Builder
167 | for _, e := range t.Events {
168 | // Only add events that have output information. Skip everything else.
169 | // Note, since we know about all the output, we can bubble "--- Fail" to the top
170 | // of the output so it's trivial to spot the failing test name and elapsed time.
171 | if e.Action != parse.ActionOutput {
172 | continue
173 | }
174 | if strings.Contains(e.Output, failLine) {
175 | header := strings.TrimSuffix(e.Output, "\n")
176 | // go test prefixes too much padding to the "--- FAIL: " output lines.
177 | // Let's cut the padding by half, being careful to preserve the fail
178 | // line and the proceeding output.
179 | before, after, ok := cut(header, failLine)
180 | var pad string
181 | if ok {
182 | var n int
183 | for _, r := range before {
184 | if r == 32 {
185 | n++
186 | }
187 | }
188 | for i := 0; i < n/2; i++ {
189 | pad += " "
190 | }
191 | }
192 | header = pad + failLine + after
193 |
194 | // Avoid colorizing markdown output so it renders properly, otherwise add a subtle
195 | // red color to the test headers.
196 | if c.format != OutputFormatMarkdown {
197 | header = lipgloss.NewStyle().Foreground(lipgloss.Color("1")).Render(header)
198 | }
199 | headerRows.WriteString(header)
200 | continue
201 | }
202 |
203 | if e.Output != "" {
204 | rows.WriteString(e.Output)
205 | }
206 | }
207 | out := headerRows.String()
208 | if rows.Len() > 0 {
209 | out += "\n\n" + rows.String()
210 | }
211 | return out
212 | }
213 |
--------------------------------------------------------------------------------
/internal/app/table_summary.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "fmt"
5 | "path"
6 | "strconv"
7 | "strings"
8 |
9 | "github.com/charmbracelet/lipgloss"
10 | "github.com/charmbracelet/lipgloss/table"
11 |
12 | "github.com/mfridman/tparse/internal/utils"
13 | "github.com/mfridman/tparse/parse"
14 | )
15 |
16 | type SummaryTableOptions struct {
17 | // For narrow screens, remove common prefix and trim long package names vertically. Example:
18 | // github.com/mfridman/tparse/app
19 | // github.com/mfridman/tparse/internal/seed-up-down-to-zero
20 | //
21 | // tparse/app
22 | // tparse
23 | // /seed-up-down-to-zero
24 | Trim bool
25 |
26 | // TrimPath is the path prefix to trim from the package name.
27 | TrimPath string
28 | }
29 |
30 | func (c *consoleWriter) summaryTable(
31 | packages []*parse.Package,
32 | showNoTests bool,
33 | options SummaryTableOptions,
34 | against *parse.GoTestSummary,
35 | ) {
36 | tbl := newTable(c.format, func(style lipgloss.Style, row, col int) lipgloss.Style {
37 | switch row {
38 | case table.HeaderRow:
39 | default:
40 | if col == 2 {
41 | // Package name
42 | style = style.Align(lipgloss.Left)
43 | }
44 | }
45 | return style
46 | })
47 | header := summaryRow{
48 | status: "Status",
49 | elapsed: "Elapsed",
50 | packageName: "Package",
51 | cover: "Cover",
52 | pass: "Pass",
53 | fail: "Fail",
54 | skip: "Skip",
55 | }
56 | tbl.Headers(header.toRow()...)
57 | data := table.NewStringData()
58 |
59 | // Capture as separate slices because notests are optional when passed tests are available.
60 | // The only exception is if passed=0 and notests=1, then we display them regardless. This
61 | // is almost always the user matching on the wrong package.
62 | var passed, notests []summaryRow
63 |
64 | names := make([]string, 0, len(packages))
65 | for _, pkg := range packages {
66 | names = append(names, pkg.Summary.Package)
67 | }
68 | packagePrefix := utils.FindLongestCommonPrefix(names)
69 |
70 | for _, pkg := range packages {
71 | elapsed := strconv.FormatFloat(pkg.Summary.Elapsed, 'f', 2, 64) + "s"
72 | if pkg.Cached {
73 | elapsed = "(cached)"
74 | }
75 | packageName := pkg.Summary.Package
76 | packageName = shortenPackageName(packageName, packagePrefix, 32, options.Trim, options.TrimPath)
77 | if pkg.HasPanic {
78 | row := summaryRow{
79 | status: c.red("PANIC"),
80 | elapsed: elapsed,
81 | packageName: packageName,
82 | cover: "--", pass: "--", fail: "--", skip: "--",
83 | }
84 | data.Append(row.toRow())
85 | continue
86 | }
87 | if pkg.HasFailedBuildOrSetup {
88 | row := summaryRow{
89 | status: c.red("FAIL"),
90 | elapsed: elapsed,
91 | packageName: packageName + "\n[" + pkg.Summary.Output + "]",
92 | cover: "--", pass: "--", fail: "--", skip: "--",
93 | }
94 | data.Append(row.toRow())
95 | continue
96 | }
97 | if pkg.NoTestFiles {
98 | row := summaryRow{
99 | status: c.yellow("NOTEST"),
100 | elapsed: elapsed,
101 | packageName: packageName + "\n[no test files]",
102 | cover: "--", pass: "--", fail: "--", skip: "--",
103 | }
104 | notests = append(notests, row)
105 | continue
106 | }
107 | if pkg.NoTests {
108 | // This should capture cases where packages truly have no tests, but empty files.
109 | if len(pkg.NoTestSlice) == 0 {
110 | row := summaryRow{
111 | status: c.yellow("NOTEST"),
112 | elapsed: elapsed,
113 | packageName: packageName + "\n[no tests to run]",
114 | cover: "--", pass: "--", fail: "--", skip: "--",
115 | }
116 | notests = append(notests, row)
117 | continue
118 | }
119 | // This should capture cases where packages have a mixture of empty and non-empty test files.
120 | var ss []string
121 | for i, t := range pkg.NoTestSlice {
122 | i++
123 | ss = append(ss, fmt.Sprintf("%d.%s", i, t.Test))
124 | }
125 | packageName := fmt.Sprintf("%s\n[no tests to run]\n%s", packageName, strings.Join(ss, "\n"))
126 | row := summaryRow{
127 | status: c.yellow("NOTEST"),
128 | elapsed: elapsed,
129 | packageName: packageName,
130 | cover: "--", pass: "--", fail: "--", skip: "--",
131 | }
132 | notests = append(notests, row)
133 |
134 | if len(pkg.TestsByAction(parse.ActionPass)) == len(pkg.NoTestSlice) {
135 | continue
136 | }
137 | }
138 | // TODO(mf): refactor this
139 | // Separate cover colorization from the delta output.
140 | coverage := "--"
141 | if pkg.Cover {
142 | coverage = fmt.Sprintf("%.1f%%", pkg.Coverage)
143 | if against != nil {
144 | againstP, ok := against.Packages[pkg.Summary.Package]
145 | if ok {
146 | var sign string
147 | if pkg.Coverage > againstP.Coverage {
148 | sign = "+"
149 | }
150 | coverage = fmt.Sprintf("%s (%s)", coverage, sign+strconv.FormatFloat(pkg.Coverage-againstP.Coverage, 'f', 1, 64)+"%")
151 | } else {
152 | coverage = fmt.Sprintf("%s (-)", coverage)
153 | }
154 | }
155 | // Showing coverage for a package that failed is a bit odd.
156 | //
157 | // Only colorize the coverage when everything passed AND the output is not markdown.
158 | if pkg.Summary.Action == parse.ActionPass && c.format != OutputFormatMarkdown {
159 | switch cover := pkg.Coverage; {
160 | case cover > 0.0 && cover <= 50.0:
161 | coverage = c.red(coverage)
162 | case pkg.Coverage > 50.0 && pkg.Coverage < 80.0:
163 | coverage = c.yellow(coverage)
164 | case pkg.Coverage >= 80.0:
165 | coverage = c.green(coverage)
166 | }
167 | }
168 | }
169 |
170 | status := strings.ToUpper(pkg.Summary.Action.String())
171 | switch pkg.Summary.Action {
172 | case parse.ActionPass:
173 | status = c.green(status)
174 | case parse.ActionSkip:
175 | status = c.yellow(status)
176 | case parse.ActionFail:
177 | status = c.red(status)
178 | }
179 | row := summaryRow{
180 | status: status,
181 | elapsed: elapsed,
182 | packageName: packageName,
183 | cover: coverage,
184 | pass: strconv.Itoa(len(pkg.TestsByAction(parse.ActionPass))),
185 | fail: strconv.Itoa(len(pkg.TestsByAction(parse.ActionFail))),
186 | skip: strconv.Itoa(len(pkg.TestsByAction(parse.ActionSkip))),
187 | }
188 | passed = append(passed, row)
189 | }
190 |
191 | if data.Rows() == 0 && len(passed) == 0 && len(notests) == 0 {
192 | return
193 | }
194 | for _, r := range passed {
195 | data.Append(r.toRow())
196 | }
197 |
198 | // Only display the "no tests to run" cases if users want to see them when passed
199 | // tests are available.
200 | // An exception is made if there are no passed tests and only a single no test files
201 | // package. This is almost always because the user forgot to match one or more packages.
202 | if showNoTests || (len(passed) == 0 && len(notests) == 1) {
203 | for _, r := range notests {
204 | data.Append(r.toRow())
205 | }
206 | }
207 |
208 | fmt.Fprintln(c.w, tbl.Data(data).Render())
209 | }
210 |
211 | type summaryRow struct {
212 | status string
213 | elapsed string
214 | packageName string
215 | cover string
216 | pass string
217 | fail string
218 | skip string
219 | }
220 |
221 | func (r summaryRow) toRow() []string {
222 | return []string{
223 | r.status,
224 | r.elapsed,
225 | r.packageName,
226 | r.cover,
227 | r.pass,
228 | r.fail,
229 | r.skip,
230 | }
231 | }
232 |
233 | func shortenPackageName(
234 | name string,
235 | prefix string,
236 | maxLength int,
237 | trim bool,
238 | trimPath string,
239 | ) string {
240 | if trimPath == "auto" {
241 | name = strings.TrimPrefix(name, prefix)
242 | } else if trimPath != "" {
243 | name = strings.TrimPrefix(name, trimPath)
244 | }
245 | if !trim {
246 | return name
247 | }
248 |
249 | if prefix == "" {
250 | dir, name := path.Split(name)
251 | // For SIV-style imports show the last non-versioned path identifier.
252 | // Example: github.com/foo/bar/helper/v3 returns helper/v3
253 | if dir != "" && versionMajorRe.MatchString(name) {
254 | _, subpath := path.Split(path.Clean(dir))
255 | name = path.Join(subpath, name)
256 | }
257 | return name
258 | }
259 |
260 | name = strings.TrimPrefix(name, prefix)
261 | name = strings.TrimLeft(name, "/")
262 | name = shortenTestName(name, true, maxLength)
263 |
264 | return name
265 | }
266 |
--------------------------------------------------------------------------------
/internal/app/table_tests.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "fmt"
5 | "regexp"
6 | "sort"
7 | "strconv"
8 | "strings"
9 |
10 | "github.com/charmbracelet/lipgloss"
11 | "github.com/charmbracelet/lipgloss/table"
12 |
13 | "github.com/mfridman/tparse/internal/utils"
14 | "github.com/mfridman/tparse/parse"
15 | )
16 |
17 | var (
18 | versionMajorRe = regexp.MustCompile(`(?m)v[0-9]+`)
19 | )
20 |
21 | type TestTableOptions struct {
22 | // Display passed or skipped tests. If both are true this is equivalent to all.
23 | Pass, Skip bool
24 | // For narrow screens, trim long test identifiers vertically. Example:
25 | // TestNoVersioning/seed-up-down-to-zero
26 | //
27 | // TestNoVersioning
28 | // /seed-up-down-to-zero
29 | Trim bool
30 |
31 | // TrimPath is the path prefix to trim from the package name.
32 | TrimPath string
33 |
34 | // Display up to N slow tests for each package, tests are sorted by
35 | // calculated the elapsed time for the given test.
36 | Slow int
37 | }
38 |
39 | type packageTests struct {
40 | skippedCount int
41 | skipped []*parse.Test
42 | passedCount int
43 | passed []*parse.Test
44 | }
45 |
46 | func (c *consoleWriter) testsTable(packages []*parse.Package, option TestTableOptions) {
47 | // Print passed tests, sorted by elapsed DESC. Grouped by alphabetically sorted packages.
48 | tbl := newTable(c.format, func(style lipgloss.Style, row, col int) lipgloss.Style {
49 | switch row {
50 | case table.HeaderRow:
51 | default:
52 | if col == 2 || col == 3 {
53 | // Test name and package name
54 | style = style.Align(lipgloss.Left)
55 | }
56 | }
57 | return style
58 | })
59 | header := testRow{
60 | status: "Status",
61 | elapsed: "Elapsed",
62 | testName: "Test",
63 | packageName: "Package",
64 | }
65 | tbl.Headers(header.toRow()...)
66 | data := table.NewStringData()
67 |
68 | names := make([]string, 0, len(packages))
69 | for _, pkg := range packages {
70 | names = append(names, pkg.Summary.Package)
71 | }
72 | packagePrefix := utils.FindLongestCommonPrefix(names)
73 |
74 | for i, pkg := range packages {
75 | // Discard packages where we cannot generate a sensible test summary.
76 | if pkg.NoTestFiles || pkg.NoTests || pkg.HasPanic {
77 | continue
78 | }
79 | pkgTests := getTestsFromPackages(pkg, option)
80 | all := make([]*parse.Test, 0, len(pkgTests.passed)+len(pkgTests.skipped))
81 | all = append(all, pkgTests.passed...)
82 | all = append(all, pkgTests.skipped...)
83 |
84 | for _, t := range all {
85 | // TODO(mf): why are we sorting this?
86 | t.SortEvents()
87 |
88 | testName := shortenTestName(t.Name, option.Trim, 32)
89 |
90 | status := strings.ToUpper(t.Status().String())
91 | switch t.Status() {
92 | case parse.ActionPass:
93 | status = c.green(status)
94 | case parse.ActionSkip:
95 | status = c.yellow(status)
96 | case parse.ActionFail:
97 | status = c.red(status)
98 | }
99 |
100 | packageName := shortenPackageName(t.Package, packagePrefix, 16, option.Trim, option.TrimPath)
101 |
102 | row := testRow{
103 | status: status,
104 | elapsed: strconv.FormatFloat(t.Elapsed(), 'f', 2, 64),
105 | testName: testName,
106 | packageName: packageName,
107 | }
108 | data.Append(row.toRow())
109 | }
110 | if i != (len(packages) - 1) {
111 | // Add a blank row between packages.
112 | data.Append(testRow{}.toRow())
113 | }
114 | }
115 |
116 | if data.Rows() > 0 {
117 | fmt.Fprintln(c.w, tbl.Data(data).Render())
118 | }
119 | }
120 |
121 | func (c *consoleWriter) testsTableMarkdown(packages []*parse.Package, option TestTableOptions) {
122 | for _, pkg := range packages {
123 | // Print passed tests, sorted by elapsed DESC. Grouped by alphabetically sorted packages.
124 | tbl := newTable(c.format, func(style lipgloss.Style, row, col int) lipgloss.Style {
125 | switch row {
126 | case table.HeaderRow:
127 | default:
128 | if col == 2 {
129 | // Test name
130 | style = style.Align(lipgloss.Left)
131 | }
132 | }
133 | return style
134 | })
135 | header := []string{
136 | "Status",
137 | "Elapsed",
138 | "Test",
139 | }
140 | tbl.Headers(header...)
141 | data := table.NewStringData()
142 |
143 | // Discard packages where we cannot generate a sensible test summary.
144 | if pkg.NoTestFiles || pkg.NoTests || pkg.HasPanic {
145 | continue
146 | }
147 | pkgTests := getTestsFromPackages(pkg, option)
148 | all := make([]*parse.Test, 0, len(pkgTests.passed)+len(pkgTests.skipped))
149 | all = append(all, pkgTests.passed...)
150 | all = append(all, pkgTests.skipped...)
151 |
152 | for _, t := range all {
153 | // TODO(mf): why are we sorting this?
154 | t.SortEvents()
155 |
156 | testName := shortenTestName(t.Name, option.Trim, 32)
157 |
158 | status := strings.ToUpper(t.Status().String())
159 | switch t.Status() {
160 | case parse.ActionPass:
161 | status = c.green(status)
162 | case parse.ActionSkip:
163 | status = c.yellow(status)
164 | case parse.ActionFail:
165 | status = c.red(status)
166 | }
167 | data.Append([]string{
168 | status,
169 | strconv.FormatFloat(t.Elapsed(), 'f', 2, 64),
170 | testName,
171 | })
172 | }
173 | if data.Rows() > 0 {
174 | fmt.Fprintf(c.w, "## 📦 Package **`%s`**\n", pkg.Summary.Package)
175 | fmt.Fprintln(c.w)
176 |
177 | msg := fmt.Sprintf("Tests: ✓ %d passed | %d skipped\n",
178 | pkgTests.passedCount,
179 | pkgTests.skippedCount,
180 | )
181 | if option.Slow > 0 && option.Slow < pkgTests.passedCount {
182 | msg += fmt.Sprintf("↓ Slowest %d passed tests shown (of %d)\n",
183 | option.Slow,
184 | pkgTests.passedCount,
185 | )
186 | }
187 | fmt.Fprint(c.w, msg)
188 |
189 | fmt.Fprintln(c.w)
190 | fmt.Fprintln(c.w, "")
191 | fmt.Fprintln(c.w)
192 | fmt.Fprintln(c.w, "Click for test summary
")
193 | fmt.Fprintln(c.w)
194 | fmt.Fprintln(c.w, tbl.Data(data).Render())
195 | fmt.Fprintln(c.w, " ")
196 | fmt.Fprintln(c.w)
197 | }
198 | fmt.Fprintln(c.w)
199 | }
200 | }
201 |
202 | func getTestsFromPackages(pkg *parse.Package, option TestTableOptions) *packageTests {
203 | tests := &packageTests{}
204 | skipped := pkg.TestsByAction(parse.ActionSkip)
205 | tests.skippedCount = len(skipped)
206 | passed := pkg.TestsByAction(parse.ActionPass)
207 | tests.passedCount = len(passed)
208 | if option.Skip {
209 | tests.skipped = append(tests.skipped, skipped...)
210 | }
211 | if option.Pass {
212 | tests.passed = append(tests.passed, passed...)
213 | // Order passed tests within a package by elapsed time DESC (longest on top).
214 | sort.Slice(tests.passed, func(i, j int) bool {
215 | return tests.passed[i].Elapsed() > tests.passed[j].Elapsed()
216 | })
217 | // Optional, display only the slowest N tests by elapsed time.
218 | if option.Slow > 0 && len(tests.passed) > option.Slow {
219 | tests.passed = tests.passed[:option.Slow]
220 | }
221 | }
222 | return tests
223 | }
224 |
225 | func shortenTestName(s string, trim bool, maxLength int) string {
226 | var testName strings.Builder
227 | testName.WriteString(s)
228 | if trim && testName.Len() > maxLength && strings.Count(testName.String(), "/") > 0 {
229 | testName.Reset()
230 | ss := strings.Split(s, "/")
231 | testName.WriteString(ss[0] + "\n")
232 | for i, s := range ss[1:] {
233 | testName.WriteString(" /")
234 | for len(s) > maxLength {
235 | testName.WriteString(s[:maxLength-2] + " …\n ")
236 | s = s[maxLength-2:]
237 | }
238 | testName.WriteString(s)
239 | if i != len(ss[1:])-1 {
240 | testName.WriteString("\n")
241 | }
242 | }
243 | }
244 | return testName.String()
245 | }
246 |
247 | type testRow struct {
248 | status string
249 | elapsed string
250 | testName string
251 | packageName string
252 | }
253 |
254 | func (r testRow) toRow() []string {
255 | return []string{
256 | r.status,
257 | r.elapsed,
258 | r.testName,
259 | r.packageName,
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/internal/utils/utils.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "io"
5 | "sort"
6 | "strings"
7 | )
8 |
9 | // FindLongestCommonPrefix finds the longest common path prefix of a set of paths. For example,
10 | // given the following:
11 | //
12 | // github.com/owner/repo/cmd/foo
13 | // github.com/owner/repo/cmd/bar
14 | //
15 | // The longest common prefix is: github.com/owner/repo/cmd/ (note the trailing slash is included).
16 | func FindLongestCommonPrefix(paths []string) string {
17 | if len(paths) < 2 {
18 | return ""
19 | }
20 | // Sort the paths to optimize comparison.
21 | sort.Strings(paths)
22 |
23 | first, last := paths[0], paths[len(paths)-1]
24 | if first == last {
25 | return first
26 | }
27 |
28 | // Find the common prefix between the first and last sorted paths.
29 | commonPrefixLength := 0
30 | minLength := min(len(first), len(last))
31 | for commonPrefixLength < minLength && first[commonPrefixLength] == last[commonPrefixLength] {
32 | commonPrefixLength++
33 | }
34 |
35 | // Ensure the common prefix ends at a boundary.
36 | commonPrefix := first[:commonPrefixLength]
37 | if n := strings.LastIndex(commonPrefix, "/"); n != -1 {
38 | return commonPrefix[:n+1]
39 | }
40 | return ""
41 | }
42 |
43 | // DiscardCloser is an io.Writer that implements io.Closer by doing nothing.
44 | //
45 | // https://github.com/golang/go/issues/22823
46 | type WriteNopCloser struct {
47 | io.Writer
48 | }
49 |
50 | func (WriteNopCloser) Close() error {
51 | return nil
52 | }
53 |
--------------------------------------------------------------------------------
/internal/utils/utils_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "testing"
4 |
5 | func TestFindLongestCommonPrefix(t *testing.T) {
6 | t.Parallel()
7 |
8 | tests := []struct {
9 | paths []string
10 | want string
11 | }{
12 | {
13 | paths: []string{},
14 | want: "",
15 | },
16 | {
17 | paths: []string{
18 | "github.com/user/project/pkg",
19 | },
20 | want: "",
21 | },
22 | {
23 | paths: []string{
24 | "github.com/user/project/pkg",
25 | "github.com/user/project/pkg",
26 | "github.com/user/project/pkg",
27 | },
28 | want: "github.com/user/project/pkg",
29 | },
30 | {
31 | paths: []string{
32 | "github.com/user/project/pkg",
33 | "github.com/user/project/cmd",
34 | },
35 | want: "github.com/user/project/",
36 | },
37 | {
38 | paths: []string{
39 | "github.com/user/project/pkg",
40 | "bitbucket.org/user/project/cmd",
41 | },
42 | want: "",
43 | },
44 | {
45 | paths: []string{
46 | "github.com/user/project/pkg",
47 | "github.com/user/project/cmd",
48 | "github.com/user/project/cmd/subcmd",
49 | "github.com/nonuser/project/cmd/subcmd",
50 | },
51 | want: "github.com/",
52 | },
53 | {
54 | paths: []string{
55 | "github.com/foo/bar/baz/qux",
56 | "github.com/foo/bar/baz",
57 | "github.com/foo/bar/baz/qux/quux",
58 | "github.com/foo/bar/baz/qux/quux/corge",
59 | "github.com/foo/bar/baz/foo",
60 | "github.com/foo/bar/baz/foo/bar",
61 | },
62 | want: "github.com/foo/bar/",
63 | },
64 | {
65 | paths: []string{
66 | "/",
67 | },
68 | want: "",
69 | },
70 | {
71 | paths: []string{
72 | "/",
73 | "/",
74 | },
75 | want: "/",
76 | },
77 | {
78 | paths: []string{
79 | "/abc",
80 | "/abc",
81 | },
82 | want: "/abc",
83 | },
84 | {
85 | paths: []string{
86 | "foo/bar/foo",
87 | "foo/foo/foo",
88 | },
89 | want: "foo/",
90 | },
91 | }
92 | for _, tt := range tests {
93 | t.Run("", func(t *testing.T) {
94 | actual := FindLongestCommonPrefix(tt.paths)
95 | if actual != tt.want {
96 | t.Errorf("want %s, got %s", tt.want, actual)
97 | }
98 | })
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "log"
9 | "os"
10 | "runtime/debug"
11 |
12 | "github.com/mfridman/tparse/internal/app"
13 | "github.com/mfridman/tparse/internal/utils"
14 | "github.com/mfridman/tparse/parse"
15 | )
16 |
17 | // Flags.
18 | var (
19 | vPtr = flag.Bool("v", false, "")
20 | versionPtr = flag.Bool("version", false, "")
21 | hPtr = flag.Bool("h", false, "")
22 | helpPtr = flag.Bool("help", false, "")
23 | allPtr = flag.Bool("all", false, "")
24 | passPtr = flag.Bool("pass", false, "")
25 | skipPtr = flag.Bool("skip", false, "")
26 | showNoTestsPtr = flag.Bool("notests", false, "")
27 | smallScreenPtr = flag.Bool("smallscreen", false, "")
28 | noColorPtr = flag.Bool("nocolor", false, "")
29 | slowPtr = flag.Int("slow", 0, "")
30 | fileNamePtr = flag.String("file", "", "")
31 | formatPtr = flag.String("format", "", "")
32 | followPtr = flag.Bool("follow", false, "")
33 | followOutputPtr = flag.String("follow-output", "", "")
34 | sortPtr = flag.String("sort", "name", "")
35 | progressPtr = flag.Bool("progress", false, "")
36 | comparePtr = flag.String("compare", "", "")
37 | trimPathPtr = flag.String("trimpath", "", "")
38 | // Undocumented flags
39 | followVerbosePtr = flag.Bool("follow-verbose", false, "")
40 |
41 | // Legacy flags
42 | noBordersPtr = flag.Bool("noborders", false, "")
43 | )
44 |
45 | var usage = `Usage:
46 | go test ./... -json | tparse [options...]
47 | go test [packages...] -json | tparse [options...]
48 | go test [packages...] -json > pkgs.out ; tparse [options...] -file pkgs.out
49 |
50 | Options:
51 | -h Show help.
52 | -v Show version.
53 | -all Display table event for pass and skip. (Failed items always displayed)
54 | -pass Display table for passed tests.
55 | -skip Display table for skipped tests.
56 | -notests Display packages containing no test files or empty test files.
57 | -smallscreen Split subtest names vertically to fit on smaller screens.
58 | -slow Number of slowest tests to display. Default is 0, display all.
59 | -sort Sort table output by attribute [name, elapsed, cover]. Default is name.
60 | -nocolor Disable all colors. (NO_COLOR also supported)
61 | -format The output format for tables [basic, plain, markdown]. Default is basic.
62 | -file Read test output from a file.
63 | -follow Follow raw output from go test to stdout.
64 | -follow-output Write raw output from go test to a file (takes precedence over -follow).
65 | -progress Print a single summary line for each package. Useful for long running test suites.
66 | -compare Compare against a previous test output file. (experimental)
67 | -trimpath Remove path prefix from package names in output, simplifying their display.
68 | `
69 |
70 | var version string
71 |
72 | func main() {
73 | log.SetFlags(0)
74 | flag.Usage = func() {
75 | fmt.Fprint(flag.CommandLine.Output(), usage)
76 | }
77 | flag.Parse()
78 |
79 | if *vPtr || *versionPtr {
80 | if info, ok := debug.ReadBuildInfo(); ok {
81 | version = info.Main.Version
82 | }
83 | fmt.Fprintf(os.Stdout, "tparse version: %s\n", version)
84 | return
85 | }
86 | if *hPtr || *helpPtr {
87 | fmt.Print(usage)
88 | return
89 | }
90 | var format app.OutputFormat
91 | switch *formatPtr {
92 | case "basic":
93 | format = app.OutputFormatBasic
94 | case "plain":
95 | format = app.OutputFormatPlain
96 | case "markdown":
97 | format = app.OutputFormatMarkdown
98 | case "":
99 | // This was an existing flag, let's try to avoid breaking users.
100 | format = app.OutputFormatBasic
101 | if *noBordersPtr {
102 | format = app.OutputFormatPlain
103 | }
104 | default:
105 | fmt.Fprintf(os.Stderr, "invalid option:%q. The -format flag must be one of: basic, plain or markdown\n", *formatPtr)
106 | return
107 | }
108 | var sorter parse.PackageSorter
109 | switch *sortPtr {
110 | case "name":
111 | sorter = parse.SortByPackageName
112 | case "elapsed":
113 | sorter = parse.SortByElapsed
114 | case "cover":
115 | sorter = parse.SortByCoverage
116 | default:
117 | fmt.Fprintf(os.Stderr, "invalid option:%q. The -sort flag must be one of: name, elapsed or cover\n", *sortPtr)
118 | return
119 | }
120 |
121 | if *allPtr {
122 | *passPtr = true
123 | *skipPtr = true
124 | }
125 | // Show colors by default.
126 | var disableColor bool
127 | if _, ok := os.LookupEnv("NO_COLOR"); ok || *noColorPtr {
128 | disableColor = true
129 | }
130 |
131 | var followOutput io.WriteCloser
132 | switch {
133 | case *followOutputPtr != "":
134 | var err error
135 | followOutput, err = os.Create(*followOutputPtr)
136 | if err != nil {
137 | fmt.Fprintln(os.Stderr, err)
138 | return
139 | }
140 | *followPtr = true
141 | case *followPtr, *followVerbosePtr:
142 | followOutput = os.Stdout
143 | default:
144 | // If no follow flags are set, we should not write to followOutput.
145 | followOutput = utils.WriteNopCloser{Writer: io.Discard}
146 | }
147 | // TODO(mf): we should marry the options with the flags to avoid having to do this.
148 | options := app.Options{
149 | Output: os.Stdout,
150 | DisableColor: disableColor,
151 | FollowOutput: *followPtr,
152 | FollowOutputWriter: followOutput,
153 | FollowOutputVerbose: *followVerbosePtr,
154 | FileName: *fileNamePtr,
155 | TestTableOptions: app.TestTableOptions{
156 | Pass: *passPtr,
157 | Skip: *skipPtr,
158 | Trim: *smallScreenPtr,
159 | TrimPath: *trimPathPtr,
160 | Slow: *slowPtr,
161 | },
162 | SummaryTableOptions: app.SummaryTableOptions{
163 | Trim: *smallScreenPtr,
164 | TrimPath: *trimPathPtr,
165 | },
166 | Format: format,
167 | Sorter: sorter,
168 | ShowNoTests: *showNoTestsPtr,
169 | Progress: *progressPtr,
170 | ProgressOutput: os.Stdout,
171 | Compare: *comparePtr,
172 |
173 | // Do not expose publicly.
174 | DisableTableOutput: false,
175 | }
176 | exitCode, err := app.Run(options)
177 | if err != nil {
178 | msg := err.Error()
179 | if errors.Is(err, parse.ErrNotParsable) {
180 | msg = "no parsable events: Make sure to run go test with -json flag"
181 | }
182 | fmt.Fprintln(os.Stderr, msg)
183 | }
184 | os.Exit(exitCode)
185 | }
186 |
--------------------------------------------------------------------------------
/parse/event.go:
--------------------------------------------------------------------------------
1 | package parse
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "regexp"
7 | "strconv"
8 | "strings"
9 | "time"
10 | )
11 |
12 | var (
13 | coverRe = regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1}\%`)
14 | failedBuildOrSetupRe = regexp.MustCompile(`^FAIL(.*)\[(build failed|setup failed)\]`)
15 | )
16 |
17 | // Event represents a single line of JSON output from go test with the -json flag.
18 | //
19 | // For more info see, https://golang.org/cmd/test2json and
20 | // https://github.com/golang/go/blob/master/src/cmd/internal/test2json/test2json.go
21 | type Event struct {
22 | // Action can be one of: run, pause, cont, pass, bench, fail, output, skip
23 | //
24 | // Added in go1.20:
25 | // - start
26 | //
27 | // Added in go1.24:
28 | // - build-fail
29 | // - build-output
30 | Action Action
31 |
32 | // Portion of the test's output (standard output and standard error merged together)
33 | Output string
34 |
35 | // Time at which the event occurred, encodes as an RFC3339-format string.
36 | // It is conventionally omitted for cached test results.
37 | Time time.Time
38 |
39 | // The Package field, if present, specifies the package being tested.
40 | // When the go command runs parallel tests in -json mode, events from
41 | // different tests are interlaced; the Package field allows readers to separate them.
42 | Package string
43 |
44 | // The Test field, if present, specifies the test, example, or benchmark
45 | // function that caused the event. Events for the overall package test do not set Test.
46 | Test string
47 |
48 | // Elapsed is time elapsed (in seconds) for the specific test or
49 | // the overall package test that passed or failed.
50 | Elapsed float64
51 |
52 | // FailedBuild is the package ID that is the root cause of a build failure for this test. This
53 | // will be reported in the final "fail" event's FailedBuild field.
54 | FailedBuild string
55 |
56 | // BuildEvent specific fields.
57 | //
58 | // TODO(mf): Unfortunately the output has both BuildEvent and TestEvent interleaved in the
59 | // output so for now we just combine them. But in the future pre-v1 we'll want to improve this.
60 | //
61 | // type BuildEvent struct {
62 | // ImportPath string
63 | // Action string
64 | // Output string
65 | // }
66 | ImportPath string
67 | }
68 |
69 | func (e *Event) String() string {
70 | return fmt.Sprintf(
71 | "%-6s - %s - %s elapsed[%.2f] - time[%s]\n%v",
72 | strings.ToUpper(e.Action.String()),
73 | e.Package,
74 | e.Test,
75 | e.Elapsed,
76 | e.Time.Format(time.StampMicro),
77 | e.Output,
78 | )
79 | }
80 |
81 | // NewEvent attempts to decode data into an Event.
82 | func NewEvent(data []byte) (*Event, error) {
83 | var e Event
84 | if err := json.Unmarshal(data, &e); err != nil {
85 | return nil, err
86 | }
87 | return &e, nil
88 | }
89 |
90 | // DiscardOutput reports whether to discard output that belongs to one of
91 | // the output update actions:
92 | // === RUN
93 | // === PAUSE
94 | // === CONT
95 | // If output is none one of the above return false.
96 | func (e *Event) DiscardOutput() bool {
97 | for i := range updates {
98 | if strings.HasPrefix(e.Output, updates[i]) {
99 | return true
100 | }
101 | }
102 | return false
103 | }
104 |
105 | func (e *Event) DiscardEmptyTestOutput() bool {
106 | return e.Action == ActionOutput && e.Test == ""
107 | }
108 |
109 | // Prefixes for the different types of test updates. See:
110 | //
111 | // https://github.com/golang/go/blob/38cfb3be9d486833456276777155980d1ec0823e/src/cmd/internal/test2json/test2json.go#L160-L168
112 | const (
113 | updatePrefixRun = "=== RUN "
114 | updatePrefixPause = "=== PAUSE "
115 | updatePrefixCont = "=== CONT "
116 | updatePrefixName = "=== NAME "
117 | updatePrefixPass = "=== PASS "
118 | updatePrefixFail = "=== FAIL "
119 | updatePrefixSkip = "=== SKIP "
120 | )
121 |
122 | var updates = []string{
123 | updatePrefixRun,
124 | updatePrefixPause,
125 | updatePrefixCont,
126 | }
127 |
128 | // Prefix for the different types of test results. See
129 | //
130 | // https://github.com/golang/go/blob/38cfb3be9d486833456276777155980d1ec0823e/src/cmd/internal/test2json/test2json.go#L170-L175
131 | const (
132 | resultPrefixPass = "--- PASS: "
133 | resultPrefixFail = "--- FAIL: "
134 | resultPrefixSkip = "--- SKIP: "
135 | resultPrefixBench = "--- BENCH: "
136 | )
137 |
138 | // BigResult reports whether the test output is a big pass or big fail
139 | //
140 | // https://github.com/golang/go/blob/38cfb3be9d486833456276777155980d1ec0823e/src/cmd/internal/test2json/test2json.go#L146-L150
141 | const (
142 | bigPass = "PASS"
143 | bigFail = "FAIL"
144 | )
145 |
146 | // Let's try using the LastLine method to report the package result.
147 | // If there are issues with LastLine() we can switch to this method.
148 | //
149 | // BigResult reports whether the package passed or failed.
150 | // func (e *Event) BigResult() bool {
151 | // return e.Test == "" && (e.Output == "PASS\n" || e.Output == "FAIL\n")
152 | // }
153 |
154 | // LastLine reports whether the event is the final emitted output line summarizing the package run.
155 | //
156 | // ok github.com/astromail/rover/tests 0.583s
157 | // {Time:2018-10-14 11:45:03.489687 -0400 EDT Action:pass Output: Package:github.com/astromail/rover/tests Test: Elapsed:0.584}
158 | //
159 | // FAIL github.com/astromail/rover/tests 0.534s
160 | // {Time:2018-10-14 11:45:23.916729 -0400 EDT Action:fail Output: Package:github.com/astromail/rover/tests Test: Elapsed:0.53}
161 | func (e *Event) LastLine() bool {
162 | return e.Test == "" && e.Output == "" && (e.Action == ActionPass || e.Action == ActionFail)
163 | }
164 |
165 | // NoTestFiles reports special event case for packages containing no test files:
166 | // "? \tpackage\t[no test files]\n"
167 | func (e *Event) NoTestFiles() bool {
168 | return strings.HasPrefix(e.Output, "? \t") && strings.HasSuffix(e.Output, "[no test files]\n")
169 | }
170 |
171 | // NoTestsToRun reports special event case for no tests to run:
172 | // "ok \tgithub.com/some/awesome/module\t4.543s [no tests to run]\n"
173 | func (e *Event) NoTestsToRun() bool {
174 | return strings.HasPrefix(e.Output, "ok \t") && strings.HasSuffix(e.Output, "[no tests to run]\n")
175 | }
176 |
177 | // NoTestsWarn whether the event is a test that identifies as: "testing: warning: no tests to run\n"
178 | //
179 | // NOTE: can be found in a package or test event. Must check for non-empty test name in the event.
180 | func (e *Event) NoTestsWarn() bool {
181 | return e.Test != "" && e.Output == "testing: warning: no tests to run\n"
182 | }
183 |
184 | // IsCached reports special event case for cached packages:
185 | // "ok \tgithub.com/mfridman/tparse/tests\t(cached)\n"
186 | // "ok \tgithub.com/mfridman/srfax\t(cached)\tcoverage: 28.8% of statements\n"
187 | func (e *Event) IsCached() bool {
188 | return strings.HasPrefix(e.Output, "ok \t") && strings.Contains(e.Output, "\t(cached)")
189 | }
190 |
191 | // Cover reports special event case for package coverage:
192 | // "ok \tgithub.com/mfridman/srfax\t(cached)\tcoverage: 28.8% of statements\n"
193 | // "ok \tgithub.com/mfridman/srfax\t0.027s\tcoverage: 28.8% of statements\n"
194 | // "ok \tgithub.com/mfridman/tparse/tests\t0.516s\tcoverage: 34.5% of statements in ./...\n"
195 | func (e *Event) Cover() (float64, bool) {
196 | var f float64
197 | var err error
198 | if strings.Contains(e.Output, "coverage:") && strings.Contains(e.Output, "of statements") {
199 | s := coverRe.FindString(e.Output)
200 | f, err = strconv.ParseFloat(strings.TrimRight(s, "%"), 64)
201 | if err != nil {
202 | return f, false
203 | }
204 | return f, true
205 | }
206 | return f, false
207 | }
208 |
209 | // IsRace indicates a race event has been detected.
210 | func (e *Event) IsRace() bool {
211 | return strings.HasPrefix(e.Output, "WARNING: DATA RACE")
212 | }
213 |
214 | // IsPanic indicates a panic event has been detected.
215 | func (e *Event) IsPanic() bool {
216 | // Let's see how this goes. If a user has this in one of their output lines, I think it's
217 | // defensible to suggest updating their output.
218 | if strings.HasPrefix(e.Output, "panic: ") {
219 | return true
220 | }
221 | // The golang/go test suite occasionally outputs these keywords along with "as expected":
222 | // time_test.go:1359: panic in goroutine 7, as expected, with "runtime error: racy use of timers"
223 | if strings.Contains(e.Output, "runtime error:") && !strings.Contains(e.Output, "as expected") {
224 | return true
225 | }
226 | return false
227 |
228 | }
229 |
230 | // Action is one of a fixed set of actions describing a single emitted event.
231 | type Action string
232 |
233 | // Prefixed with Action for convenience.
234 | const (
235 | ActionRun Action = "run" // test has started running
236 | ActionPause Action = "pause" // test has been paused
237 | ActionCont Action = "cont" // the test has continued running
238 | ActionPass Action = "pass" // test passed
239 | ActionBench Action = "bench" // benchmark printed log output but did not fail
240 | ActionFail Action = "fail" // test or benchmark failed
241 | ActionOutput Action = "output" // test printed output
242 | ActionSkip Action = "skip" // test was skipped or the package contained no tests
243 |
244 | // Added in go1.20 to denote the beginning of each test program's execution.
245 | ActionStart Action = "start" // the start at the beginning of each test program's execution
246 |
247 | // Added in go1.24 to denote a build failure.
248 | ActionBuildFail Action = "build-fail" // the build failed
249 | ActionBuildOutput Action = "build-output" // the toolchain printed output
250 | )
251 |
252 | func (a Action) String() string {
253 | return string(a)
254 | }
255 |
--------------------------------------------------------------------------------
/parse/package.go:
--------------------------------------------------------------------------------
1 | package parse
2 |
3 | import "time"
4 |
5 | // Package is the representation of a single package being tested. The
6 | // summary field is an event that contains all relevant information about the
7 | // package, namely Package (name), Elapsed and Action (big pass or fail).
8 | type Package struct {
9 | Summary *Event
10 | Tests []*Test
11 |
12 | // StartTime is the time the package started running. This is only available
13 | // in go1.20 and above.
14 | StartTime time.Time
15 |
16 | // NoTestFiles indicates whether the package contains tests: [no test files]
17 | // This only occurs at the package level
18 | NoTestFiles bool
19 |
20 | // NoTests indicates a package contains one or more files with no tests. This doesn't
21 | // necessarily mean the file is empty or that the package doesn't have any tests.
22 | // Unfortunately go test marks the package summary with [no tests to run].
23 | NoTests bool
24 | // NoTestSlice holds events that contain "testing: warning: no tests to run" and
25 | // a non-empty test name.
26 | NoTestSlice []*Event
27 |
28 | // Cached indicates whether the test result was obtained from the cache.
29 | Cached bool
30 |
31 | // Cover reports whether the package contains coverage (go test run with -cover)
32 | Cover bool
33 | Coverage float64
34 |
35 | // HasPanic marks the entire package as panicked. Game over.
36 | HasPanic bool
37 | // Once a package has been marked HasPanic all subsequent events are added to PanicEvents.
38 | PanicEvents []*Event
39 |
40 | // HasDataRace marks the entire package as having a data race.
41 | HasDataRace bool
42 | // DataRaceTests captures an individual test names as having a data race.
43 | DataRaceTests []string
44 |
45 | // HasFailedBuildOrSetup marks the package as having a failed build or setup.
46 | // Example: [build failed] or [setup failed]
47 | HasFailedBuildOrSetup bool
48 | }
49 |
50 | // newPackage initializes and returns a Package.
51 | func newPackage() *Package {
52 | return &Package{
53 | Summary: &Event{},
54 | Tests: []*Test{},
55 | }
56 | }
57 |
58 | // AddEvent adds the event to a test based on test name.
59 | func (p *Package) AddEvent(event *Event) {
60 | var t *Test
61 | if t = p.GetTest(event.Test); t == nil {
62 | // Test does not exist, add it to pkg.
63 | t = &Test{
64 | Name: event.Test,
65 | Package: event.Package,
66 | }
67 | p.Tests = append(p.Tests, t)
68 | }
69 |
70 | t.Events = append(t.Events, event)
71 | }
72 |
73 | // GetTest returns a test based on given name, if no test is found
74 | // return nil
75 | func (p *Package) GetTest(name string) *Test {
76 | for _, t := range p.Tests {
77 | if t.Name == name {
78 | return t
79 | }
80 | }
81 | return nil
82 | }
83 |
84 | // TestsByAction returns all tests that identify as one of the following
85 | // actions: pass, skip or fail.
86 | //
87 | // An empty slice if returned if there are no tests.
88 | func (p *Package) TestsByAction(action Action) []*Test {
89 | var tests []*Test
90 | for _, t := range p.Tests {
91 | if t.Status() == action {
92 | tests = append(tests, t)
93 | }
94 | }
95 | return tests
96 | }
97 |
--------------------------------------------------------------------------------
/parse/package_slice.go:
--------------------------------------------------------------------------------
1 | package parse
2 |
3 | import (
4 | "sort"
5 | )
6 |
7 | type PackageSorter func([]*Package) sort.Interface
8 | type PackageSlice []*Package
9 |
10 | type byCoverage struct{ PackageSlice }
11 | type byElapsed struct{ PackageSlice }
12 |
13 | // SortByPackageName sorts packages in ascending alphabetical order.
14 | func SortByPackageName(packages []*Package) sort.Interface { return PackageSlice(packages) }
15 | func (packages PackageSlice) Len() int { return len(packages) }
16 | func (packages PackageSlice) Swap(i, j int) {
17 | packages[i], packages[j] = packages[j], packages[i]
18 | }
19 | func (packages PackageSlice) Less(i, j int) bool {
20 | return packages[i].Summary.Package < packages[j].Summary.Package
21 | }
22 |
23 | // SortByCoverage sorts packages in descending order of code coverage.
24 | func SortByCoverage(packages []*Package) sort.Interface { return byCoverage{packages} }
25 | func (packages byCoverage) Less(i, j int) bool {
26 | return packages.PackageSlice[i].Coverage > packages.PackageSlice[j].Coverage
27 | }
28 |
29 | // SortByElapsed sorts packages in descending order of elapsed time per package.
30 | func SortByElapsed(packages []*Package) sort.Interface { return byElapsed{packages} }
31 | func (packages byElapsed) Less(i, j int) bool {
32 | return packages.PackageSlice[i].Summary.Elapsed > packages.PackageSlice[j].Summary.Elapsed
33 | }
34 |
--------------------------------------------------------------------------------
/parse/process_options.go:
--------------------------------------------------------------------------------
1 | package parse
2 |
3 | import (
4 | "io"
5 | )
6 |
7 | type options struct {
8 | w io.Writer
9 | follow bool
10 | followVerbose bool
11 | debug bool
12 |
13 | progress bool
14 | progressOutput io.Writer
15 | }
16 |
17 | type OptionsFunc func(o *options)
18 |
19 | func WithFollowOutput(b bool) OptionsFunc {
20 | return func(o *options) { o.follow = b }
21 | }
22 |
23 | func WithFollowVersboseOutput(b bool) OptionsFunc {
24 | return func(o *options) { o.followVerbose = b }
25 | }
26 |
27 | func WithWriter(w io.Writer) OptionsFunc {
28 | return func(o *options) { o.w = w }
29 | }
30 |
31 | func WithDebug() OptionsFunc {
32 | return func(o *options) { o.debug = true }
33 | }
34 |
35 | func WithProgress(b bool) OptionsFunc {
36 | return func(o *options) { o.progress = b }
37 | }
38 |
39 | func WithProgressOutput(w io.Writer) OptionsFunc {
40 | return func(o *options) { o.progressOutput = w }
41 | }
42 |
--------------------------------------------------------------------------------
/parse/test.go:
--------------------------------------------------------------------------------
1 | package parse
2 |
3 | import (
4 | "sort"
5 | )
6 |
7 | // Test represents a single, unique, package test.
8 | type Test struct {
9 | Name string
10 | Package string
11 | Events []*Event
12 | }
13 |
14 | // Elapsed indicates how long a given test ran (in seconds), by scanning for the largest
15 | // elapsed value from all events.
16 | func (t *Test) Elapsed() float64 {
17 | var f float64
18 | for _, e := range t.Events {
19 | if e.Elapsed > f {
20 | f = e.Elapsed
21 | }
22 | }
23 | return f
24 | }
25 |
26 | // Status reports the outcome of the test represented as a single Action: pass, fail or skip.
27 | func (t *Test) Status() Action {
28 | // sort by time and scan for an action in reverse order.
29 | // The first action we come across (in reverse order) is
30 | // the outcome of the test, which will be one of pass|fail|skip.
31 | t.SortEvents()
32 | for i := len(t.Events) - 1; i >= 0; i-- {
33 | switch t.Events[i].Action {
34 | case ActionPass:
35 | return ActionPass
36 | case ActionSkip:
37 | return ActionSkip
38 | case ActionFail:
39 | return ActionFail
40 | }
41 | }
42 | return ActionFail
43 | }
44 |
45 | // SortEvents sorts test events by elapsed time in ascending order, i.e., oldest to newest.
46 | func (t *Test) SortEvents() {
47 | sort.Slice(t.Events, func(i, j int) bool {
48 | return t.Events[i].Time.Before(t.Events[j].Time)
49 | })
50 | }
51 |
--------------------------------------------------------------------------------
/scripts/release-notes.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -euo pipefail
4 |
5 | # Check if the required argument is provided
6 | if [ $# -lt 1 ]; then
7 | echo "Usage: $0 []"
8 | exit 1
9 | fi
10 |
11 | version="$1"
12 | changelog_file="${2:-CHANGELOG.md}"
13 |
14 | # Check if the changelog file exists
15 | if [ ! -f "$changelog_file" ]; then
16 | echo "Error: $changelog_file does not exist"
17 | exit 1
18 | fi
19 |
20 | CAPTURE=0
21 | items=""
22 | # Read the changelog file line by line
23 | while IFS= read -r LINE; do
24 | # Stop capturing when we reach the next version sections
25 | if [[ "${LINE}" == "##"* ]] && [[ "${CAPTURE}" -eq 1 ]]; then
26 | break
27 | fi
28 | # Stop capturing when we reach the Unreleased section
29 | if [[ "${LINE}" == "[Unreleased]"* ]]; then
30 | break
31 | fi
32 | # Start capturing when we reach the specified version section
33 | if [[ "${LINE}" == "## [${version}]"* ]] && [[ "${CAPTURE}" -eq 0 ]]; then
34 | CAPTURE=1
35 | continue
36 | fi
37 | # Capture the lines between the specified version and the next version
38 | if [[ "${CAPTURE}" -eq 1 ]]; then
39 | # Ignore empty lines
40 | if [[ -z "${LINE}" ]]; then
41 | continue
42 | fi
43 | items+="$(echo "${LINE}" | xargs -0)"
44 | # Add a newline between each item
45 | if [[ -n "$items" ]]; then
46 | items+=$'\n'
47 | fi
48 | fi
49 | done <"${changelog_file}"
50 |
51 | if [[ -n "$items" ]]; then
52 | cat < f && !pkg.Cover {
81 | t.Fatalf("got %v, want package %q to have cover field marked as true when coverage %v>%v",
82 | pkg.Cover,
83 | name,
84 | pkg.Coverage,
85 | f,
86 | )
87 | }
88 | })
89 | }
90 | })
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/tests/follow_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "os"
7 | "path/filepath"
8 | "testing"
9 |
10 | "github.com/stretchr/testify/assert"
11 |
12 | "github.com/mfridman/tparse/internal/app"
13 | "github.com/mfridman/tparse/internal/utils"
14 | "github.com/mfridman/tparse/parse"
15 | )
16 |
17 | func TestFollow(t *testing.T) {
18 | t.Parallel()
19 |
20 | t.Run("follow_verbose", func(t *testing.T) {
21 | base := filepath.Join("testdata", "follow-verbose")
22 |
23 | tt := []struct {
24 | fileName string
25 | err error
26 | exitCode int
27 | }{
28 | // race detected
29 | {"test_01", nil, 1},
30 | {"test_02", nil, 0},
31 | {"test_03", nil, 0},
32 | {"test_04", nil, 0},
33 | {"test_05", parse.ErrNotParsable, 1},
34 | // build failure in one package
35 | {"test_06", nil, 2},
36 | }
37 | for _, tc := range tt {
38 | t.Run(tc.fileName, func(t *testing.T) {
39 | buf := bytes.NewBuffer(nil)
40 | inputFile := filepath.Join(base, tc.fileName+".jsonl")
41 | options := app.Options{
42 | FileName: inputFile,
43 | FollowOutput: true,
44 | FollowOutputWriter: utils.WriteNopCloser{Writer: buf},
45 | FollowOutputVerbose: true,
46 | DisableTableOutput: true,
47 | }
48 | gotExitCode, err := app.Run(options)
49 | if err != nil && !errors.Is(err, tc.err) {
50 | t.Fatal(err)
51 | }
52 | assert.Equal(t, gotExitCode, tc.exitCode)
53 | goldenFile := filepath.Join(base, tc.fileName+".golden")
54 | want, err := os.ReadFile(goldenFile)
55 | if err != nil {
56 | t.Fatal(err)
57 | }
58 | checkGolden(t, inputFile, goldenFile, buf.Bytes(), want)
59 | })
60 | }
61 | })
62 |
63 | t.Run("follow_no_verbose", func(t *testing.T) {
64 | base := filepath.Join("testdata", "follow")
65 |
66 | tt := []struct {
67 | fileName string
68 | err error
69 | exitCode int
70 | }{
71 | {"test_01", nil, 0},
72 | }
73 | for _, tc := range tt {
74 | t.Run(tc.fileName, func(t *testing.T) {
75 | buf := bytes.NewBuffer(nil)
76 | inputFile := filepath.Join(base, tc.fileName+".jsonl")
77 | options := app.Options{
78 | FileName: inputFile,
79 | FollowOutput: true,
80 | FollowOutputWriter: utils.WriteNopCloser{Writer: buf},
81 | DisableTableOutput: true,
82 | }
83 | gotExitCode, err := app.Run(options)
84 | if err != nil && !errors.Is(err, tc.err) {
85 | t.Fatal(err)
86 | }
87 | assert.Equal(t, gotExitCode, tc.exitCode)
88 | goldenFile := filepath.Join(base, tc.fileName+".golden")
89 | want, err := os.ReadFile(goldenFile)
90 | if err != nil {
91 | t.Fatal(err)
92 | }
93 | checkGolden(t, inputFile, goldenFile, buf.Bytes(), want)
94 | })
95 | }
96 | })
97 | }
98 |
99 | func checkGolden(
100 | t *testing.T,
101 | inputFile, goldenFile string,
102 | got, want []byte,
103 | ) {
104 | t.Helper()
105 | if !bytes.Equal(got, want) {
106 | t.Error("input does not match expected output; diff files in follow dir suffixed with .FAIL to debug")
107 | t.Logf("diff %v %v",
108 | "tests/"+goldenFile+".FAIL",
109 | "tests/"+inputFile+".FAIL",
110 | )
111 | if err := os.WriteFile(goldenFile+".FAIL", got, 0644); err != nil {
112 | t.Fatal(err)
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/tests/outcome_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/assert"
9 | "github.com/stretchr/testify/require"
10 |
11 | "github.com/mfridman/tparse/parse"
12 | )
13 |
14 | func TestFinalOutcome(t *testing.T) {
15 | t.Parallel()
16 | // key is the package name, action reports the final outcome of the package.
17 | type registry map[string]parse.Action
18 |
19 | base := filepath.Join("testdata", "outcome")
20 |
21 | tt := []struct {
22 | fileName string
23 | exitCode int
24 | registry
25 | }{
26 | {"test_01.jsonl", 1, registry{
27 | "github.com/mfridman/tparse/tests": parse.ActionFail,
28 | }},
29 | {"test_02.jsonl", 1, registry{
30 | "github.com/astromail/rover/tests": parse.ActionFail,
31 | "github.com/astromail/rover/cmd/roverd": parse.ActionPass,
32 | "github.com/astromail/rover/smtp": parse.ActionPass,
33 | "github.com/astromail/rover/storage": parse.ActionPass,
34 | "github.com/astromail/rover/errors": parse.ActionPass,
35 | "github.com/astromail/rover/storage/badger": parse.ActionPass,
36 | "github.com/astromail/rover": parse.ActionPass,
37 | }},
38 | {"test_03.jsonl", 0, registry{
39 | "fmt": parse.ActionPass,
40 | }},
41 | {"test_04.jsonl", 0, registry{
42 | "github.com/astromail/rover/tests": parse.ActionPass,
43 | }},
44 | {"test_05.jsonl", 0, registry{
45 | "github.com/astromail/rover/tests": parse.ActionPass,
46 | }},
47 | {"test_06.jsonl", 0, registry{
48 | "fmt": parse.ActionPass,
49 | }},
50 | {"test_07.jsonl", 0, registry{
51 | "debug/errorcause": parse.ActionPass,
52 | }},
53 | {"test_08.jsonl", 0, registry{
54 | "github.com/awesome/pkg": parse.ActionPass,
55 | }},
56 | }
57 | for _, tc := range tt {
58 | t.Run(tc.fileName, func(t *testing.T) {
59 | f, err := os.Open(filepath.Join(base, tc.fileName))
60 | if err != nil {
61 | t.Fatal(err)
62 | }
63 | summary, err := parse.Process(f)
64 | require.NoError(t, err)
65 | assert.Equal(t, len(summary.Packages), len(tc.registry))
66 | assert.Equal(t, summary.ExitCode(), tc.exitCode)
67 |
68 | for name, pkg := range summary.Packages {
69 | want, ok := tc.registry[name]
70 | if !ok {
71 | t.Log("currently registered packages:")
72 | for k := range tc.registry {
73 | t.Log(k)
74 | }
75 | t.Fatalf("got unmapped package name %q. Check input file and record all unique package names in registry", name)
76 | }
77 | if pkg.Summary.Action != want {
78 | t.Fatalf("failed package summary action: got: %q, want: %q", pkg.Summary.Action, want)
79 | }
80 | if len(pkg.Tests) == 0 && pkg.Summary.Action != parse.ActionPass {
81 | t.Fatalf("zero test should always return pass: got: %q, want: %q", pkg.Summary.Action, parse.ActionPass)
82 | }
83 | if (pkg.NoTestFiles || pkg.NoTests) && pkg.Summary.Action != parse.ActionPass {
84 | t.Fatalf("packages marked as [no tests to run] or [no test files] should always return pass: got: %q, want: %q",
85 | pkg.Summary.Action,
86 | parse.ActionPass,
87 | )
88 | }
89 |
90 | // As a sanity check, iterate over the tests and make sure all tests actually
91 | // reflect the package outcome.
92 |
93 | switch pkg.Summary.Action {
94 | case parse.ActionPass:
95 | // One or more tests must be marked as either pass or skip.
96 | // A single skipped test will still yield a pass package outcome.
97 | for _, test := range pkg.Tests {
98 | switch test.Status() {
99 | case parse.ActionPass, parse.ActionSkip:
100 | continue
101 | default:
102 | t.Fatalf("all tests within a passed package should have a status of pass or skip: got: %q", test.Status())
103 | }
104 | }
105 | case parse.ActionFail:
106 | // One or more tests must be marked as failed.
107 | var failed bool
108 | for _, tc := range pkg.Tests {
109 | if tc.Status() == parse.ActionFail {
110 | failed = true
111 | break
112 | }
113 | }
114 | if !failed {
115 | t.Fatalf("got no failed tests, want one or more tests to be marked as: %q", parse.ActionFail)
116 | }
117 | default:
118 | // Catch all, should never get this.
119 | t.Fatalf("got package summary action %q, want pass or fail", pkg.Summary.Action)
120 | }
121 | }
122 | })
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/tests/package_start_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "os"
5 | "testing"
6 | "time"
7 |
8 | "github.com/stretchr/testify/assert"
9 | "github.com/stretchr/testify/require"
10 |
11 | "github.com/mfridman/tparse/parse"
12 | )
13 |
14 | func TestPackageStartTime(t *testing.T) {
15 | t.Parallel()
16 |
17 | // This test depends on go120_start_action.jsonl, which contains test output from go1.20
18 |
19 | expected := map[string]string{
20 | "github.com/pressly/goose/v4": "2023-05-28T18:36:01.280967-04:00",
21 | "github.com/pressly/goose/v4/internal/check": "2023-05-28T18:36:01.281088-04:00",
22 | "github.com/pressly/goose/v4/internal/cli": "2023-05-28T18:36:01.281147-04:00",
23 | "github.com/pressly/goose/v4/internal/dialectadapter": "2023-05-28T18:36:01.281218-04:00",
24 | "github.com/pressly/goose/v4/internal/dialectadapter/dialectquery": "2023-05-28T18:36:01.281253-04:00",
25 | "github.com/pressly/goose/v4/internal/migration": "2023-05-28T18:36:01.281269-04:00",
26 | "github.com/pressly/goose/v4/internal/migrationstats": "2023-05-28T18:36:01.281381-04:00",
27 | "github.com/pressly/goose/v4/internal/migrationstats/migrationstatsos": "2023-05-28T18:36:01.281426-04:00",
28 | "github.com/pressly/goose/v4/internal/normalizedsn": "2023-05-28T18:36:01.281465-04:00",
29 | "github.com/pressly/goose/v4/internal/sqlparser": "2023-05-28T18:36:01.446915-04:00",
30 | "github.com/pressly/goose/v4/internal/testdb": "2023-05-28T18:36:01.446973-04:00",
31 | }
32 |
33 | fileName := "./testdata/go120_start_action.jsonl"
34 | f, err := os.Open(fileName)
35 | require.NoError(t, err)
36 | defer f.Close()
37 |
38 | summary, err := parse.Process(f)
39 | require.NoError(t, err)
40 | assert.Equal(t, len(summary.Packages), len(expected))
41 |
42 | for _, p := range summary.Packages {
43 | if p.StartTime.IsZero() {
44 | t.Fatalf("package %q cannot contain zero start time", p.Summary.Package)
45 | }
46 | unparsed, ok := expected[p.Summary.Package]
47 | if !ok {
48 | t.Fatalf("package %q not found in expected map", p.Summary.Package)
49 | }
50 | want, err := time.Parse(time.RFC3339, unparsed)
51 | require.NoError(t, err)
52 | if !p.StartTime.Equal(want) {
53 | t.Fatalf("package %q start time got %q want %q", p.Summary.Package, p.StartTime, want)
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/panic_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/assert"
9 | "github.com/stretchr/testify/require"
10 |
11 | "github.com/mfridman/tparse/parse"
12 | )
13 |
14 | func TestPanic(t *testing.T) {
15 | t.Parallel()
16 |
17 | // Key is the package name, bool reports whether the packages is expected to be marked as panic.
18 | type expected map[string]bool
19 |
20 | base := filepath.Join("testdata", "panic")
21 |
22 | tt := []struct {
23 | fileName string
24 | expected
25 | }{
26 | {
27 | "test_01.jsonl", expected{
28 | "github.com/pressly/goose/v3/tests/e2e": true,
29 | },
30 | },
31 | {
32 | "test_02.jsonl", expected{
33 | "github.com/mfridman/tparse/parse": true,
34 | },
35 | },
36 | {
37 | "test_03.jsonl", expected{
38 | "github.com/mfridman/tparse/tests": true,
39 | },
40 | },
41 | {
42 | "test_04.jsonl", expected{
43 | "github.com/mfridman/tparse/tests": true,
44 | "github.com/mfridman/tparse/ignore": false,
45 | "github.com/mfridman/tparse/parse": false,
46 | "github.com/mfridman/tparse": false,
47 | },
48 | },
49 | {
50 | "test_05.jsonl", expected{
51 | "github.com/mfridman/tparse/tests": true,
52 | "github.com/mfridman/tparse/parse": false,
53 | "github.com/mfridman/tparse": false,
54 | "github.com/mfridman/tparse/ignore": false,
55 | },
56 | },
57 | {
58 | "test_06.jsonl", expected{
59 | "github.com/mfridman/tparse/tests": false,
60 | "github.com/mfridman/tparse/parse": true,
61 | "github.com/mfridman/tparse": false,
62 | "github.com/mfridman/tparse/ignore": false,
63 | },
64 | },
65 | }
66 |
67 | for _, tc := range tt {
68 | t.Run(tc.fileName, func(t *testing.T) {
69 | f, err := os.Open(filepath.Join(base, tc.fileName))
70 | require.NoError(t, err)
71 |
72 | summary, err := parse.Process(f)
73 | require.NoError(t, err)
74 | assert.Equal(t, 1, summary.ExitCode())
75 |
76 | for name, pkg := range summary.Packages {
77 | want, ok := tc.expected[name]
78 | if !ok {
79 | t.Log("currently registered packages:")
80 | for k := range tc.expected {
81 | t.Log(k)
82 | }
83 | t.Fatalf("got unmapped package name %q. Check input file and record all unique package names in registry", name)
84 | }
85 | if pkg.HasPanic != want {
86 | t.Log("package: ", name)
87 | t.Logf("summary: %+v", pkg.Summary)
88 | t.Fatal("got no panic, expecting package to be marked as has panic")
89 | }
90 | }
91 | })
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/tests/prescan_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/require"
9 |
10 | "github.com/mfridman/tparse/parse"
11 | )
12 |
13 | func TestPrescan(t *testing.T) {
14 | t.Parallel()
15 |
16 | base := filepath.Join("testdata", "prescan")
17 |
18 | tt := []struct {
19 | fileName string
20 | desc string
21 | err error
22 | }{
23 | {"test_01.txt", "want err", nil},
24 | {"test_02.txt", "want failure after reading >50 lines of non-parsable events", parse.ErrNotParsable},
25 | // logic: unparsable event(s), good event(s), at least one event = fail.
26 | // Once we get a good event, we expect only good events to follow until EOF.
27 | {"test_03.txt", "want failure when stream contains a bad event(s) -> good event(s) -> bad event", parse.ErrNotParsable},
28 | {"test_04.txt", "want failure reading <50 lines of non-parsable events", parse.ErrNotParsable},
29 | }
30 |
31 | for _, tc := range tt {
32 | t.Run(tc.fileName, func(t *testing.T) {
33 | inputFile := filepath.Join(base, tc.fileName)
34 | f, err := os.Open(inputFile)
35 | require.NoError(t, err)
36 |
37 | _, err = parse.Process(f)
38 | if tc.err != nil {
39 | require.ErrorIs(t, err, tc.err)
40 | return
41 | }
42 | require.NoError(t, err)
43 | })
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/race_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/assert"
9 | "github.com/stretchr/testify/require"
10 |
11 | "github.com/mfridman/tparse/parse"
12 | )
13 |
14 | func TestRaceDetected(t *testing.T) {
15 | t.Parallel()
16 |
17 | // Key is the package name, and the value may be zero, one or more test name(s).
18 | // Not all data races may be associated with a test.
19 | type expected map[string][]string
20 |
21 | base := filepath.Join("testdata", "race")
22 |
23 | var tt = []struct {
24 | fileName string
25 | expected
26 | }{
27 | {
28 | "test_01", expected{"command-line-arguments": {"TestA"}},
29 | },
30 | {
31 | "test_02", expected{"github.com/mfridman/tparse/parse": {"TestB", "TestElapsed"}},
32 | },
33 | {
34 | "test_03", expected{"debug/tparse-24": {}},
35 | },
36 | // This is a race directly from Test only.
37 | {
38 | "test_04", expected{"github.com/mfridman/debug-go/testing": {"TestRace"}},
39 | },
40 | // This is a race directly from TestMain with other tests that have failed.
41 | {
42 | "test_05", expected{"github.com/mfridman/debug-go/testing": {}},
43 | },
44 | // This is a race directly from TestMain only.
45 | {
46 | "test_06", expected{"github.com/mfridman/debug-go/testing": {}},
47 | },
48 | // This is a race from a Test that calls into a package that has a race condition. (failed assertion)
49 | {
50 | "test_07", expected{"github.com/mfridman/debug-go/testing": {"TestRace"}},
51 | },
52 | // This is a race from a Test that calls into a package that has a race condition. (passed assertion)
53 | {
54 | "test_08", expected{"github.com/mfridman/debug-go/testing": {"TestRace"}},
55 | },
56 | }
57 |
58 | for _, tc := range tt {
59 | t.Run(tc.fileName, func(t *testing.T) {
60 | inputFile := filepath.Join(base, tc.fileName+".jsonl")
61 | f, err := os.Open(inputFile)
62 | require.NoError(t, err)
63 | defer f.Close()
64 |
65 | summary, err := parse.Process(f)
66 | require.NoError(t, err)
67 |
68 | if summary.ExitCode() == 0 {
69 | t.Fatalf("expecting non-zero exit code")
70 | }
71 | for name, pkg := range summary.Packages {
72 | wantTestName, ok := tc.expected[name]
73 | if !ok {
74 | t.Fatalf("failed to find package: %q", name)
75 | }
76 | assert.Equal(t, len(pkg.DataRaceTests), len(wantTestName))
77 | if len(pkg.DataRaceTests) > 0 {
78 | for i := range pkg.DataRaceTests {
79 | assert.Equal(t, pkg.DataRaceTests[i], wantTestName[i])
80 | }
81 | }
82 | }
83 | })
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/tests/sort_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/assert"
9 | "github.com/stretchr/testify/require"
10 |
11 | "github.com/mfridman/tparse/parse"
12 | )
13 |
14 | func TestSortName(t *testing.T) {
15 | t.Parallel()
16 |
17 | base := filepath.Join("testdata", "cover")
18 |
19 | // expected package name and corresponding cover %
20 | // Note, these numbers will vary between on go versions.
21 | type expected []string
22 |
23 | tt := []struct {
24 | fileName string
25 | expected
26 | }{
27 | {
28 | // go test -count=1 bytes log sort -json -cover | tparse -sort name
29 | "test_01",
30 | expected{
31 | "bytes",
32 | "log",
33 | "sort",
34 | },
35 | },
36 | {
37 | // go test -count=1 bufio bytes crypto fmt log mime net sort strings time -json -cover | tparse -sort name
38 | "test_02",
39 | expected{
40 | "bufio",
41 | "bytes",
42 | "crypto",
43 | "fmt",
44 | "log",
45 | "mime",
46 | "net",
47 | "sort",
48 | "strings",
49 | "time",
50 | },
51 | },
52 | }
53 | for _, tc := range tt {
54 | t.Run(tc.fileName, func(t *testing.T) {
55 | inputFile := filepath.Join(base, tc.fileName+".jsonl")
56 | f, err := os.Open(inputFile)
57 | require.NoError(t, err)
58 |
59 | summary, err := parse.Process(f)
60 | require.NoError(t, err)
61 | assert.Equal(t, len(summary.Packages), len(tc.expected))
62 | packages := summary.GetSortedPackages(parse.SortByPackageName)
63 |
64 | for i, pkg := range packages {
65 | t.Run(pkg.Summary.Package, func(t *testing.T) {
66 | wantName := tc.expected[i]
67 | if pkg.Summary.Package != wantName {
68 | t.Fatalf("got name: %s, want name: %s", pkg.Summary.Package, wantName)
69 | }
70 | })
71 | }
72 | })
73 | }
74 | }
75 |
76 | func TestSortCoverage(t *testing.T) {
77 | t.Parallel()
78 |
79 | base := filepath.Join("testdata", "cover")
80 |
81 | // expected package name and corresponding cover %
82 | // Note, these numbers will vary between on go versions.
83 | type expected []float64
84 |
85 | tt := []struct {
86 | fileName string
87 | expected
88 | }{
89 | {
90 | // go test -count=1 bytes log sort -json -cover | tparse -sort cover
91 | "test_01",
92 | expected{
93 | 86.7, // "bytes"
94 | 68.0, // "log"
95 | 60.8, // "sort"
96 | },
97 | },
98 | {
99 | // go test -count=1 bufio bytes crypto fmt log mime net sort strings time -json -cover | tparse -sort cover
100 | "test_02",
101 | expected{
102 | 98.1, // "strings"
103 | 95.6, // "bytes"
104 | 95.2, // "fmt"
105 | 93.8, // "mime"
106 | 93.3, // "bufio"
107 | 91.8, // "time"
108 | 81.2, // "net"
109 | 68.0, // "log"
110 | 60.8, // "sort"
111 | 5.9, // "crypto"
112 | },
113 | },
114 | }
115 | for _, tc := range tt {
116 | t.Run(tc.fileName, func(t *testing.T) {
117 | inputFile := filepath.Join(base, tc.fileName+".jsonl")
118 | f, err := os.Open(inputFile)
119 | require.NoError(t, err)
120 |
121 | summary, err := parse.Process(f)
122 | require.NoError(t, err)
123 | assert.Equal(t, len(summary.Packages), len(tc.expected))
124 | packages := summary.GetSortedPackages(parse.SortByCoverage)
125 |
126 | for i, pkg := range packages {
127 | t.Run(pkg.Summary.Package, func(t *testing.T) {
128 | wantCover := tc.expected[i]
129 | if pkg.Coverage != wantCover {
130 | t.Fatalf("got cover: %v(%s), want cover: %v", pkg.Coverage, pkg.Summary.Package, wantCover)
131 | }
132 | })
133 | }
134 | })
135 | }
136 | }
137 |
138 | func TestSortElapsed(t *testing.T) {
139 | t.Parallel()
140 |
141 | base := filepath.Join("testdata", "cached")
142 |
143 | // expected package name and corresponding cover %
144 | // Note, these numbers will vary between on go versions.
145 | type expected []float64
146 |
147 | tt := []struct {
148 | fileName string
149 | expected
150 | }{
151 | {
152 | // go test -count=1 fmt mime strings time -json | tparse -sort elapsed
153 | "test_01",
154 | expected{
155 | 7.168, // "time"
156 | 0.020, // "mime"
157 | 0.007, // "strings"
158 | 0.003, // "fmt"
159 | },
160 | },
161 | {
162 | // go test -count=1 bufio bytes crypto fmt log mime sort strings time -json | tparse -sort elapsed
163 | "test_02",
164 | expected{
165 | 7.641, // "time",
166 | 1.176, // "bytes",
167 | 0.220, // "fmt",
168 | 0.134, // "bufio",
169 | 0.070, // "crypto",
170 | 0.002, // "strings",
171 | 0.001, // "mime",
172 | 0.001, // "sort",
173 | 0.000, // "log",
174 | },
175 | },
176 | }
177 | for _, tc := range tt {
178 | t.Run(tc.fileName, func(t *testing.T) {
179 | inputFile := filepath.Join(base, tc.fileName+".jsonl")
180 | f, err := os.Open(inputFile)
181 | require.NoError(t, err)
182 |
183 | summary, err := parse.Process(f)
184 | require.NoError(t, err)
185 | assert.Equal(t, len(summary.Packages), len(tc.expected))
186 | packages := summary.GetSortedPackages(parse.SortByElapsed)
187 |
188 | for i, pkg := range packages {
189 | t.Run(pkg.Summary.Package, func(t *testing.T) {
190 | wantElapsed := tc.expected[i]
191 | if pkg.Summary.Elapsed != wantElapsed {
192 | t.Fatalf("got elapsed: %v (%s), want elapsed: %v", pkg.Summary.Elapsed, pkg.Summary.Package, wantElapsed)
193 | }
194 | })
195 | }
196 | })
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/tests/summary_counts_test.go:
--------------------------------------------------------------------------------
1 | package parsetest
2 |
3 | import (
4 | "os"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | "github.com/stretchr/testify/require"
9 |
10 | "github.com/mfridman/tparse/parse"
11 | )
12 |
13 | func TestSummaryCounts(t *testing.T) {
14 | t.Parallel()
15 |
16 | // This test depends on metrics_test.jsonl, which contains the output of 9 std lib packages:
17 | // go test -count=1 fmt strings bytes bufio crypto log mime sort time -json
18 |
19 | fileName := "./testdata/metrics_test.jsonl"
20 | f, err := os.Open(fileName)
21 | require.NoError(t, err)
22 |
23 | summary, err := parse.Process(f)
24 | require.NoError(t, err)
25 | assert.Len(t, summary.Packages, 9)
26 |
27 | for name, pkg := range summary.Packages {
28 | if pkg.Summary == nil {
29 | t.Fatalf("package %q cannot contain nil summary", name)
30 | }
31 | if pkg.Summary.Action != parse.ActionPass {
32 | t.Logf("failed pkg: %v", name)
33 | t.Fatalf("unexpected action %q, want %q", pkg.Summary.Action, parse.ActionPass)
34 | }
35 | }
36 |
37 | tests := []struct {
38 | name string
39 | total, passed, skipped, failed int
40 | elapsed float64
41 | }{
42 | {"fmt", 59, 58, 1, 0, 0.22},
43 | {"strings", 107, 107, 0, 0, 5.494},
44 | {"bytes", 123, 123, 0, 0, 3.5380000000000003},
45 | {"bufio", 69, 69, 0, 0, 0.07},
46 | {"crypto", 5, 5, 0, 0, 0.016},
47 | {"log", 8, 8, 0, 0, 0.085},
48 | {"mime", 20, 20, 0, 0, 0.025},
49 | {"sort", 37, 36, 1, 0, 3.117},
50 | {"time", 118, 117, 1, 0, 7.157},
51 | }
52 |
53 | for _, test := range tests {
54 | t.Run(test.name+"_test", func(t *testing.T) {
55 | pkg := summary.Packages[test.name]
56 | if len(pkg.Tests) != test.total {
57 | t.Fatalf("got %d total tests in package %q, want %d total tests", len(pkg.Tests), test.name, test.total)
58 | }
59 |
60 | pa := pkg.TestsByAction(parse.ActionPass)
61 | if len(pa) != test.passed {
62 | t.Fatalf("got %d passed tests in package %q, want %d passed tests", len(pa), test.name, test.passed)
63 | }
64 |
65 | sk := pkg.TestsByAction(parse.ActionSkip)
66 | if len(sk) != test.skipped {
67 | t.Fatalf("got %d passed tests in package %q, want %d passed tests", len(sk), test.name, test.skipped)
68 | }
69 |
70 | fa := pkg.TestsByAction(parse.ActionFail)
71 | if len(fa) != test.failed {
72 | t.Fatalf("got %d failed tests in package %q, want %d failed tests", len(fa), test.name, test.failed)
73 | }
74 |
75 | if pkg.Summary.Elapsed != test.elapsed {
76 | t.Fatalf("got elapsed time %f for package %q, want %f", pkg.Summary.Elapsed, test.name, test.elapsed)
77 | }
78 | })
79 | }
80 | }
81 |
82 | func TestElapsed(t *testing.T) {
83 | t.Parallel()
84 |
85 | // This test depends on elapsed_test.jsonl, which contains the output of 2 std lib tests
86 | // with known elapsed time.
87 | // go test -count=1 strings -run="^(TestCompareStrings|TestCaseConsistency$)" -json -cover
88 |
89 | expected := map[string]float64{
90 | "TestCompareStrings": 3.49,
91 | "TestCaseConsistency": 0.17,
92 | }
93 |
94 | fileName := "./testdata/elapsed_test.jsonl"
95 | f, err := os.Open(fileName)
96 | require.NoError(t, err)
97 | defer f.Close()
98 |
99 | summary, err := parse.Process(f)
100 | require.NoError(t, err)
101 | assert.Len(t, summary.Packages, 1)
102 |
103 | pkg, ok := summary.Packages["strings"]
104 | if !ok {
105 | t.Fatalf(`got unexpected pkg: %v\nwant "strings"`, pkg)
106 | }
107 | assert.Len(t, pkg.Tests, 2)
108 |
109 | for _, test := range pkg.Tests {
110 | wantElapsed, ok := expected[test.Name]
111 | if !ok {
112 | t.Errorf("got unknown test name %q", test.Name)
113 | }
114 | if test.Elapsed() != wantElapsed {
115 | t.Errorf("got %v elapsed time for test: %q, want %v", test.Elapsed(), test.Name, wantElapsed)
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/tests/testdata/elapsed_test.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-11-24T21:54:39.153656-05:00","Action":"run","Package":"strings","Test":"TestCompareStrings"}
2 | {"Time":"2018-11-24T21:54:39.154006-05:00","Action":"output","Package":"strings","Test":"TestCompareStrings","Output":"=== RUN TestCompareStrings\n"}
3 | {"Time":"2018-11-24T21:54:42.644837-05:00","Action":"output","Package":"strings","Test":"TestCompareStrings","Output":"--- PASS: TestCompareStrings (3.49s)\n"}
4 | {"Time":"2018-11-24T21:54:42.644875-05:00","Action":"pass","Package":"strings","Test":"TestCompareStrings","Elapsed":3.49}
5 | {"Time":"2018-11-24T21:54:42.644896-05:00","Action":"run","Package":"strings","Test":"TestCaseConsistency"}
6 | {"Time":"2018-11-24T21:54:42.644906-05:00","Action":"output","Package":"strings","Test":"TestCaseConsistency","Output":"=== RUN TestCaseConsistency\n"}
7 | {"Time":"2018-11-24T21:54:42.818776-05:00","Action":"output","Package":"strings","Test":"TestCaseConsistency","Output":"--- PASS: TestCaseConsistency (0.17s)\n"}
8 | {"Time":"2018-11-24T21:54:42.818824-05:00","Action":"pass","Package":"strings","Test":"TestCaseConsistency","Elapsed":0.17}
9 | {"Time":"2018-11-24T21:54:42.818878-05:00","Action":"output","Package":"strings","Output":"PASS\n"}
10 | {"Time":"2018-11-24T21:54:42.824353-05:00","Action":"output","Package":"strings","Output":"ok \tstrings\t3.775s\n"}
11 | {"Time":"2018-11-24T21:54:42.824402-05:00","Action":"pass","Package":"strings","Elapsed":3.775}
--------------------------------------------------------------------------------
/tests/testdata/failed/test_02.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2022-05-20T09:24:29.849233-04:00","Action":"run","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever"}
2 | {"Time":"2022-05-20T09:24:29.849381-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":"=== RUN TestWhatever\n"}
3 | {"Time":"2022-05-20T09:24:29.849391-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" main_test.go:13: assert error\n"}
4 | {"Time":"2022-05-20T09:24:29.8494-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" main_test.go:14: \n"}
5 | {"Time":"2022-05-20T09:24:29.849402-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" \tError Trace:\tmain_test.go:14\n"}
6 | {"Time":"2022-05-20T09:24:29.849404-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" \tError: \t\"does not contain\" does not contain \"ostriche\"\n"}
7 | {"Time":"2022-05-20T09:24:29.849412-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" \tTest: \tTestWhatever\n"}
8 | {"Time":"2022-05-20T09:24:29.849435-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" main_test.go:15: \n"}
9 | {"Time":"2022-05-20T09:24:29.849556-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" \tError Trace:\tmain_test.go:15\n"}
10 | {"Time":"2022-05-20T09:24:29.849562-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" \tError: \tReceived unexpected error:\n"}
11 | {"Time":"2022-05-20T09:24:29.849569-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" \t \tmake the errorss..\n"}
12 | {"Time":"2022-05-20T09:24:29.849571-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" \tTest: \tTestWhatever\n"}
13 | {"Time":"2022-05-20T09:24:30.850912-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":" main_test.go:17: skdhjfg\n"}
14 | {"Time":"2022-05-20T09:24:30.851026-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Output":"--- FAIL: TestWhatever (1.00s)\n"}
15 | {"Time":"2022-05-20T09:24:30.851526-04:00","Action":"fail","Package":"github.com/mfridman/debug-go/testing","Test":"TestWhatever","Elapsed":1}
16 | {"Time":"2022-05-20T09:24:30.851551-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"FAIL\n"}
17 | {"Time":"2022-05-20T09:24:30.854596-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"FAIL\tgithub.com/mfridman/debug-go/testing\t1.113s\n"}
18 | {"Time":"2022-05-20T09:24:30.854827-04:00","Action":"fail","Package":"github.com/mfridman/debug-go/testing","Elapsed":1.114}
--------------------------------------------------------------------------------
/tests/testdata/failed/test_04.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2022-05-21T09:04:11.074034-04:00","Action":"run","Package":"command-line-arguments","Test":"TestWhatever"}
2 | {"Time":"2022-05-21T09:04:11.074252-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":"=== RUN TestWhatever\n"}
3 | {"Time":"2022-05-21T09:04:11.074268-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" main_test.go:12: assert error\n"}
4 | {"Time":"2022-05-21T09:04:11.074275-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" main_test.go:13: \n"}
5 | {"Time":"2022-05-21T09:04:11.07428-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \tError Trace:\tmain_test.go:13\n"}
6 | {"Time":"2022-05-21T09:04:11.074284-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \tError: \t\"does not contain\" does not contain \"ostriche\"\n"}
7 | {"Time":"2022-05-21T09:04:11.07429-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \tTest: \tTestWhatever\n"}
8 | {"Time":"2022-05-21T09:04:12.075418-04:00","Action":"run","Package":"command-line-arguments","Test":"TestWhatever/foo"}
9 | {"Time":"2022-05-21T09:04:12.075597-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo","Output":"=== RUN TestWhatever/foo\n"}
10 | {"Time":"2022-05-21T09:04:12.075641-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo","Output":" main_test.go:17: some random output from foo only\n"}
11 | {"Time":"2022-05-21T09:04:12.075689-04:00","Action":"run","Package":"command-line-arguments","Test":"TestWhatever/foo/bar"}
12 | {"Time":"2022-05-21T09:04:12.075718-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar","Output":"=== RUN TestWhatever/foo/bar\n"}
13 | {"Time":"2022-05-21T09:04:12.075748-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar","Output":"=== PAUSE TestWhatever/foo/bar\n"}
14 | {"Time":"2022-05-21T09:04:12.075774-04:00","Action":"pause","Package":"command-line-arguments","Test":"TestWhatever/foo/bar"}
15 | {"Time":"2022-05-21T09:04:12.075799-04:00","Action":"run","Package":"command-line-arguments","Test":"TestWhatever/foo/baz"}
16 | {"Time":"2022-05-21T09:04:12.075823-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz","Output":"=== RUN TestWhatever/foo/baz\n"}
17 | {"Time":"2022-05-21T09:04:12.075849-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz","Output":"=== PAUSE TestWhatever/foo/baz\n"}
18 | {"Time":"2022-05-21T09:04:12.075873-04:00","Action":"pause","Package":"command-line-arguments","Test":"TestWhatever/foo/baz"}
19 | {"Time":"2022-05-21T09:04:12.075935-04:00","Action":"cont","Package":"command-line-arguments","Test":"TestWhatever/foo/bar"}
20 | {"Time":"2022-05-21T09:04:12.075964-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar","Output":"=== CONT TestWhatever/foo/bar\n"}
21 | {"Time":"2022-05-21T09:04:12.07599-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar","Output":" main_test.go:20: some random output from bar only\n"}
22 | {"Time":"2022-05-21T09:04:12.076047-04:00","Action":"run","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar"}
23 | {"Time":"2022-05-21T09:04:12.076076-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar","Output":"=== RUN TestWhatever/foo/bar/inner-bar\n"}
24 | {"Time":"2022-05-21T09:04:12.076103-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar","Output":"=== PAUSE TestWhatever/foo/bar/inner-bar\n"}
25 | {"Time":"2022-05-21T09:04:12.076127-04:00","Action":"pause","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar"}
26 | {"Time":"2022-05-21T09:04:12.076152-04:00","Action":"cont","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar"}
27 | {"Time":"2022-05-21T09:04:12.076177-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar","Output":"=== CONT TestWhatever/foo/bar/inner-bar\n"}
28 | {"Time":"2022-05-21T09:04:12.076291-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar","Output":" main_test.go:23: another inner-bar\n"}
29 | {"Time":"2022-05-21T09:04:12.076317-04:00","Action":"cont","Package":"command-line-arguments","Test":"TestWhatever/foo/baz"}
30 | {"Time":"2022-05-21T09:04:12.076336-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz","Output":"=== CONT TestWhatever/foo/baz\n"}
31 | {"Time":"2022-05-21T09:04:12.076355-04:00","Action":"run","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz"}
32 | {"Time":"2022-05-21T09:04:12.076373-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz","Output":"=== RUN TestWhatever/foo/baz/inner-baz\n"}
33 | {"Time":"2022-05-21T09:04:12.076393-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz","Output":"=== PAUSE TestWhatever/foo/baz/inner-baz\n"}
34 | {"Time":"2022-05-21T09:04:12.07641-04:00","Action":"pause","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz"}
35 | {"Time":"2022-05-21T09:04:12.076428-04:00","Action":"cont","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz"}
36 | {"Time":"2022-05-21T09:04:12.076446-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz","Output":"=== CONT TestWhatever/foo/baz/inner-baz\n"}
37 | {"Time":"2022-05-21T09:04:12.076465-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz","Output":" main_test.go:30: some inner-baz error\n"}
38 | {"Time":"2022-05-21T09:04:12.076504-04:00","Action":"cont","Package":"command-line-arguments","Test":"TestWhatever"}
39 | {"Time":"2022-05-21T09:04:12.076524-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":"=== CONT TestWhatever\n"}
40 | {"Time":"2022-05-21T09:04:12.07657-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" main_test.go:35: \n"}
41 | {"Time":"2022-05-21T09:04:12.076594-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \tError Trace:\tmain_test.go:35\n"}
42 | {"Time":"2022-05-21T09:04:12.076614-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \tError: \tNot equal: \n"}
43 | {"Time":"2022-05-21T09:04:12.076632-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \t \texpected: 7823456\n"}
44 | {"Time":"2022-05-21T09:04:12.076651-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \t \tactual : 1\n"}
45 | {"Time":"2022-05-21T09:04:12.076669-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \tTest: \tTestWhatever\n"}
46 | {"Time":"2022-05-21T09:04:12.076688-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":" \tMessages: \tnot what I was expecting\n"}
47 | {"Time":"2022-05-21T09:04:12.076716-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever","Output":"--- FAIL: TestWhatever (1.00s)\n"}
48 | {"Time":"2022-05-21T09:04:12.076737-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo","Output":" --- FAIL: TestWhatever/foo (0.00s)\n"}
49 | {"Time":"2022-05-21T09:04:12.07676-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar","Output":" --- FAIL: TestWhatever/foo/bar (0.00s)\n"}
50 | {"Time":"2022-05-21T09:04:12.076781-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar","Output":" --- FAIL: TestWhatever/foo/bar/inner-bar (0.00s)\n"}
51 | {"Time":"2022-05-21T09:04:12.076825-04:00","Action":"fail","Package":"command-line-arguments","Test":"TestWhatever/foo/bar/inner-bar","Elapsed":0}
52 | {"Time":"2022-05-21T09:04:12.076874-04:00","Action":"fail","Package":"command-line-arguments","Test":"TestWhatever/foo/bar","Elapsed":0}
53 | {"Time":"2022-05-21T09:04:12.076892-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz","Output":" --- FAIL: TestWhatever/foo/baz (0.00s)\n"}
54 | {"Time":"2022-05-21T09:04:12.076912-04:00","Action":"output","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz","Output":" --- FAIL: TestWhatever/foo/baz/inner-baz (0.00s)\n"}
55 | {"Time":"2022-05-21T09:04:12.076934-04:00","Action":"fail","Package":"command-line-arguments","Test":"TestWhatever/foo/baz/inner-baz","Elapsed":0}
56 | {"Time":"2022-05-21T09:04:12.076952-04:00","Action":"fail","Package":"command-line-arguments","Test":"TestWhatever/foo/baz","Elapsed":0}
57 | {"Time":"2022-05-21T09:04:12.07697-04:00","Action":"fail","Package":"command-line-arguments","Test":"TestWhatever/foo","Elapsed":0}
58 | {"Time":"2022-05-21T09:04:12.076986-04:00","Action":"fail","Package":"command-line-arguments","Test":"TestWhatever","Elapsed":1}
59 | {"Time":"2022-05-21T09:04:12.077054-04:00","Action":"output","Package":"command-line-arguments","Output":"FAIL\n"}
60 | {"Time":"2022-05-21T09:04:12.078217-04:00","Action":"output","Package":"command-line-arguments","Output":"FAIL\tcommand-line-arguments\t1.127s\n"}
61 | {"Time":"2022-05-21T09:04:12.078255-04:00","Action":"fail","Package":"command-line-arguments","Elapsed":1.127}
62 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_01.golden:
--------------------------------------------------------------------------------
1 | === RUN TestRace
2 | ==================
3 | WARNING: DATA RACE
4 | Write at 0x00c000090090 by goroutine 7:
5 | debug/tparse-24.TestRace.func1()
6 | /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:7 +0x38
7 |
8 | Previous write at 0x00c000090090 by goroutine 6:
9 | debug/tparse-24.TestRace()
10 | /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:8 +0x88
11 | testing.tRunner()
12 | /usr/local/go/src/testing/testing.go:827 +0x162
13 |
14 | Goroutine 7 (running) created at:
15 | debug/tparse-24.TestRace()
16 | /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:7 +0x7a
17 | testing.tRunner()
18 | /usr/local/go/src/testing/testing.go:827 +0x162
19 |
20 | Goroutine 6 (running) created at:
21 | testing.(*T).Run()
22 | /usr/local/go/src/testing/testing.go:878 +0x650
23 | testing.runTests.func1()
24 | /usr/local/go/src/testing/testing.go:1119 +0xa8
25 | testing.tRunner()
26 | /usr/local/go/src/testing/testing.go:827 +0x162
27 | testing.runTests()
28 | /usr/local/go/src/testing/testing.go:1117 +0x4ee
29 | testing.(*M).Run()
30 | /usr/local/go/src/testing/testing.go:1034 +0x2ee
31 | main.main()
32 | _testmain.go:42 +0x221
33 | ==================
34 | --- FAIL: TestRace (0.00s)
35 | some_test.go:9: 64
36 | testing.go:771: race detected during execution of test
37 | FAIL
38 | exit status 1
39 | FAIL debug/tparse-24 0.020s
40 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_01.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-12-04T22:42:29.634612-05:00","Action":"run","Package":"debug/tparse-24","Test":"TestRace"}
2 | {"Time":"2018-12-04T22:42:29.634912-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"=== RUN TestRace\n"}
3 | {"Time":"2018-12-04T22:42:29.635129-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"==================\n"}
4 | {"Time":"2018-12-04T22:42:29.635163-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"WARNING: DATA RACE\n"}
5 | {"Time":"2018-12-04T22:42:29.635172-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"Write at 0x00c000090090 by goroutine 7:\n"}
6 | {"Time":"2018-12-04T22:42:29.635205-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" debug/tparse-24.TestRace.func1()\n"}
7 | {"Time":"2018-12-04T22:42:29.635213-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:7 +0x38\n"}
8 | {"Time":"2018-12-04T22:42:29.635226-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"\n"}
9 | {"Time":"2018-12-04T22:42:29.635236-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"Previous write at 0x00c000090090 by goroutine 6:\n"}
10 | {"Time":"2018-12-04T22:42:29.635254-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" debug/tparse-24.TestRace()\n"}
11 | {"Time":"2018-12-04T22:42:29.63527-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:8 +0x88\n"}
12 | {"Time":"2018-12-04T22:42:29.635283-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.tRunner()\n"}
13 | {"Time":"2018-12-04T22:42:29.63529-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
14 | {"Time":"2018-12-04T22:42:29.635302-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"\n"}
15 | {"Time":"2018-12-04T22:42:29.635322-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"Goroutine 7 (running) created at:\n"}
16 | {"Time":"2018-12-04T22:42:29.635334-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" debug/tparse-24.TestRace()\n"}
17 | {"Time":"2018-12-04T22:42:29.635341-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:7 +0x7a\n"}
18 | {"Time":"2018-12-04T22:42:29.635348-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.tRunner()\n"}
19 | {"Time":"2018-12-04T22:42:29.635354-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
20 | {"Time":"2018-12-04T22:42:29.635361-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"\n"}
21 | {"Time":"2018-12-04T22:42:29.635367-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"Goroutine 6 (running) created at:\n"}
22 | {"Time":"2018-12-04T22:42:29.635376-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.(*T).Run()\n"}
23 | {"Time":"2018-12-04T22:42:29.635382-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /usr/local/go/src/testing/testing.go:878 +0x650\n"}
24 | {"Time":"2018-12-04T22:42:29.635388-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.runTests.func1()\n"}
25 | {"Time":"2018-12-04T22:42:29.635397-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /usr/local/go/src/testing/testing.go:1119 +0xa8\n"}
26 | {"Time":"2018-12-04T22:42:29.635403-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.tRunner()\n"}
27 | {"Time":"2018-12-04T22:42:29.635409-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
28 | {"Time":"2018-12-04T22:42:29.635415-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.runTests()\n"}
29 | {"Time":"2018-12-04T22:42:29.635422-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /usr/local/go/src/testing/testing.go:1117 +0x4ee\n"}
30 | {"Time":"2018-12-04T22:42:29.635448-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.(*M).Run()\n"}
31 | {"Time":"2018-12-04T22:42:29.635465-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" /usr/local/go/src/testing/testing.go:1034 +0x2ee\n"}
32 | {"Time":"2018-12-04T22:42:29.635472-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" main.main()\n"}
33 | {"Time":"2018-12-04T22:42:29.635478-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" _testmain.go:42 +0x221\n"}
34 | {"Time":"2018-12-04T22:42:29.635485-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"==================\n"}
35 | {"Time":"2018-12-04T22:42:29.635807-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":"--- FAIL: TestRace (0.00s)\n"}
36 | {"Time":"2018-12-04T22:42:29.635821-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" some_test.go:9: 64\n"}
37 | {"Time":"2018-12-04T22:42:29.635829-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace","Output":" testing.go:771: race detected during execution of test\n"}
38 | {"Time":"2018-12-04T22:42:29.636065-05:00","Action":"fail","Package":"debug/tparse-24","Test":"TestRace","Elapsed":0}
39 | {"Time":"2018-12-04T22:42:29.636085-05:00","Action":"output","Package":"debug/tparse-24","Output":"FAIL\n"}
40 | {"Time":"2018-12-04T22:42:29.63706-05:00","Action":"output","Package":"debug/tparse-24","Output":"exit status 1\n"}
41 | {"Time":"2018-12-04T22:42:29.637098-05:00","Action":"output","Package":"debug/tparse-24","Output":"FAIL\tdebug/tparse-24\t0.020s\n"}
42 | {"Time":"2018-12-04T22:42:29.637108-05:00","Action":"fail","Package":"debug/tparse-24","Elapsed":0.02}
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_02.golden:
--------------------------------------------------------------------------------
1 | ? github.com/mfridman/rover [no test files]
2 | ? github.com/mfridman/rover/cmd/assets [no test files]
3 | ? github.com/mfridman/rover/cmd/roverd [no test files]
4 | === RUN TestStringPager
5 | === RUN TestStringPager/PageableWithLimit
6 | === RUN TestStringPager/NonPageable
7 | --- PASS: TestStringPager (0.00s)
8 | --- PASS: TestStringPager/PageableWithLimit (0.00s)
9 | --- PASS: TestStringPager/NonPageable (0.00s)
10 | PASS
11 | coverage: 100.0% of statements
12 | ok github.com/mfridman/rover/pkg/paging/stringpager 0.033s coverage: 100.0% of statements
13 | ? github.com/mfridman/rover/pkg/web [no test files]
14 | ? github.com/mfridman/rover/rpc/mailer [no test files]
15 | ? github.com/mfridman/rover/smtp [no test files]
16 | ? github.com/mfridman/rover/smtp/smtpserver [no test files]
17 | ? github.com/mfridman/rover/storage [no test files]
18 | ? github.com/mfridman/rover/storage/badger [no test files]
19 | === RUN TestNameFormat
20 | --- PASS: TestNameFormat (0.00s)
21 | PASS
22 | coverage: 0.0% of statements
23 | ok github.com/mfridman/rover/tests/helper 0.030s coverage: 0.0% of statements
24 | 2018/12/16 20:40:12 Replaying from value pointer: {Fid:0 Len:0 Offset:0}
25 | 2018/12/16 20:40:12 Iterating file id: 0
26 | 2018/12/16 20:40:12 Iteration took: 80.538µs
27 | 2018/12/16 20:40:12 Replaying from value pointer: {Fid:0 Len:0 Offset:0}
28 | 2018/12/16 20:40:12 Iterating file id: 0
29 | 2018/12/16 20:40:12 Iteration took: 43.759µs
30 | === RUN TestAddEmail
31 | === PAUSE TestAddEmail
32 | === RUN TestDeleteEmail
33 | === PAUSE TestDeleteEmail
34 | === RUN TestMarkAsRead
35 | === PAUSE TestMarkAsRead
36 | === RUN TestMarkAsUnRead
37 | === PAUSE TestMarkAsUnRead
38 | === CONT TestAddEmail
39 | === CONT TestMarkAsUnRead
40 | === CONT TestMarkAsRead
41 | === CONT TestDeleteEmail
42 | === RUN TestAddEmail/retrieveEmail
43 | --- PASS: TestAddEmail (0.00s)
44 | --- PASS: TestAddEmail/retrieveEmail (0.00s)
45 | --- PASS: TestDeleteEmail (0.00s)
46 | --- PASS: TestMarkAsRead (0.00s)
47 | --- PASS: TestMarkAsUnRead (0.00s)
48 | PASS
49 | coverage: 0.0% of statements
50 | server closed
51 | ok github.com/mfridman/rover/tests 0.046s coverage: 0.0% of statements
52 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_04.golden:
--------------------------------------------------------------------------------
1 | === RUN TestFollow
2 | --- PASS: TestFollow (0.00s)
3 | testing: warning: no tests to run
4 | PASS
5 | ok github.com/mfridman/tparse/tests (cached) [no tests to run]
6 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_04.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2022-05-15T22:44:38.670279-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestFollow"}
2 | {"Time":"2022-05-15T22:44:38.670387-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestFollow","Output":"=== RUN TestFollow\n"}
3 | {"Time":"2022-05-15T22:44:38.670399-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestFollow","Output":"--- PASS: TestFollow (0.00s)\n"}
4 | {"Time":"2022-05-15T22:44:38.670403-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestFollow","Output":"testing: warning: no tests to run\n"}
5 | {"Time":"2022-05-15T22:44:38.670406-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Test":"TestFollow","Elapsed":0}
6 | {"Time":"2022-05-15T22:44:38.67041-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"PASS\n"}
7 | {"Time":"2022-05-15T22:44:38.670413-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"ok \tgithub.com/mfridman/tparse/tests\t(cached) [no tests to run]\n"}
8 | {"Time":"2022-05-15T22:44:38.67042-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Elapsed":0}
9 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_05.golden:
--------------------------------------------------------------------------------
1 | It
2 | is
3 | a
4 | long
5 | established
6 | fact
7 | that
8 | a
9 | reader
10 | will
11 | be
12 | distracted
13 | by
14 | the
15 | readable
16 | content
17 | of
18 | a
19 | page
20 | when
21 | looking
22 | at
23 | its
24 | layout.
25 | The
26 | point
27 | of
28 | using
29 | Lorem
30 | Ipsum
31 | is
32 | that
33 | it
34 | has
35 | a
36 | more-or-less
37 | normal
38 | distribution
39 | of
40 | letters,
41 | as
42 | opposed
43 | to
44 | using
45 | 'Content
46 | here,
47 | content
48 | here',
49 | making
50 | it
51 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_05.jsonl:
--------------------------------------------------------------------------------
1 | It
2 | is
3 | a
4 | long
5 | established
6 | fact
7 | that
8 | a
9 | reader
10 | will
11 | be
12 | distracted
13 | by
14 | the
15 | readable
16 | content
17 | of
18 | a
19 | page
20 | when
21 | looking
22 | at
23 | its
24 | layout.
25 | The
26 | point
27 | of
28 | using
29 | Lorem
30 | Ipsum
31 | is
32 | that
33 | it
34 | has
35 | a
36 | more-or-less
37 | normal
38 | distribution
39 | of
40 | letters,
41 | as
42 | opposed
43 | to
44 | using
45 | 'Content
46 | here,
47 | content
48 | here',
49 | making
50 | it
51 | look
52 | like
53 | readable
54 | English.
55 | Many
56 | desktop
57 | publishing
58 | packages
59 | and
60 | web
61 | page
62 | editors
63 | now
64 | use
65 | Lorem
66 | Ipsum
67 | as
68 | their
69 | default
70 | model
71 | text,
72 | and
73 | a
74 | search
75 | for
76 | 'lorem
77 | ipsum'
78 | will
79 | uncover
80 | many
81 | web
82 | sites
83 | still
84 | in
85 | their
86 | infancy.
87 | Various
88 | versions
89 | have
90 | evolved
91 | over
92 | the
93 | years,
94 | sometimes
95 | by
96 | accident,
97 | sometimes
98 | on
99 | purpose
100 | (injected
101 | humour
102 | and
103 | the
104 | like).
105 | It
106 | is
107 | a
108 | long
109 | established
110 | fact
111 | that
112 | a
113 | reader
114 | will
115 | be
116 | distracted
117 | by
118 | the
119 | readable
120 | content
121 | of
122 | a
123 | page
124 | when
125 | looking
126 | at
127 | its
128 | layout.
129 | The
130 | point
131 | of
132 | using
133 | Lorem
134 | Ipsum
135 | is
136 | that
137 | it
138 | has
139 | a
140 | more-or-less
141 | normal
142 | distribution
143 | of
144 | letters,
145 | as
146 | opposed
147 | to
148 | using
149 | 'Content
150 | here,
151 | content
152 | here',
153 | making
154 | it
155 | look
156 | like
157 | readable
158 | English.
159 | Many
160 | desktop
161 | publishing
162 | packages
163 | and
164 | web
165 | page
166 | editors
167 | now
168 | use
169 | Lorem
170 | Ipsum
171 | as
172 | their
173 | default
174 | model
175 | text,
176 | and
177 | a
178 | search
179 | for
180 | 'lorem
181 | ipsum'
182 | will
183 | uncover
184 | many
185 | web
186 | sites
187 | still
188 | in
189 | their
190 | infancy.
191 | Various
192 | versions
193 | have
194 | evolved
195 | over
196 | the
197 | years,
198 | sometimes
199 | by
200 | accident,
201 | sometimes
202 | on
203 | purpose
204 | (injected
205 | humour
206 | and
207 | the
208 | like).
209 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_06.golden:
--------------------------------------------------------------------------------
1 | # github.com/marco-m/tparse-bugs [github.com/marco-m/tparse-bugs.test]
2 | ./a_test.go:6:2: undefined: hello
3 | FAIL github.com/marco-m/tparse-bugs [build failed]
4 | === RUN TestB
5 | --- PASS: TestB (0.00s)
6 | PASS
7 | ok github.com/marco-m/tparse-bugs/b 0.098s
8 |
--------------------------------------------------------------------------------
/tests/testdata/follow-verbose/test_06.jsonl:
--------------------------------------------------------------------------------
1 | # github.com/marco-m/tparse-bugs [github.com/marco-m/tparse-bugs.test]
2 | ./a_test.go:6:2: undefined: hello
3 | FAIL github.com/marco-m/tparse-bugs [build failed]
4 | {"Time":"2022-05-25T23:11:20.775252-04:00","Action":"run","Package":"github.com/marco-m/tparse-bugs/b","Test":"TestB"}
5 | {"Time":"2022-05-25T23:11:20.775449-04:00","Action":"output","Package":"github.com/marco-m/tparse-bugs/b","Test":"TestB","Output":"=== RUN TestB\n"}
6 | {"Time":"2022-05-25T23:11:20.775462-04:00","Action":"output","Package":"github.com/marco-m/tparse-bugs/b","Test":"TestB","Output":"--- PASS: TestB (0.00s)\n"}
7 | {"Time":"2022-05-25T23:11:20.775464-04:00","Action":"pass","Package":"github.com/marco-m/tparse-bugs/b","Test":"TestB","Elapsed":0}
8 | {"Time":"2022-05-25T23:11:20.775467-04:00","Action":"output","Package":"github.com/marco-m/tparse-bugs/b","Output":"PASS\n"}
9 | {"Time":"2022-05-25T23:11:20.7755-04:00","Action":"output","Package":"github.com/marco-m/tparse-bugs/b","Output":"ok \tgithub.com/marco-m/tparse-bugs/b\t0.098s\n"}
10 | {"Time":"2022-05-25T23:11:20.77551-04:00","Action":"pass","Package":"github.com/marco-m/tparse-bugs/b","Elapsed":0.098}
--------------------------------------------------------------------------------
/tests/testdata/follow/test_01.golden:
--------------------------------------------------------------------------------
1 | fmt_test.go:1457: skipping; GOMAXPROCS>1
2 | ok fmt 0.143s
3 |
--------------------------------------------------------------------------------
/tests/testdata/outcome/test_01.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-17T22:05:12.535482-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus"}
2 | {"Time":"2018-10-17T22:05:12.535868-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"=== RUN TestStatus\n"}
3 | {"Time":"2018-10-17T22:05:12.535902-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"=== PAUSE TestStatus\n"}
4 | {"Time":"2018-10-17T22:05:12.535919-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus"}
5 | {"Time":"2018-10-17T22:05:12.535942-04:00","Action":"cont","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus"}
6 | {"Time":"2018-10-17T22:05:12.535959-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"=== CONT TestStatus\n"}
7 | {"Time":"2018-10-17T22:05:12.541357-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"--- FAIL: TestStatus (0.01s)\n"}
8 | {"Time":"2018-10-17T22:05:12.54139-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":" status_test.go:56: log: file: package_skip02.json\n"}
9 | {"Time":"2018-10-17T22:05:12.541412-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":" status_test.go:57: failed package summary action: got \"FAIL\", want \"PASS\"\n"}
10 | {"Time":"2018-10-17T22:05:12.541432-04:00","Action":"fail","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Elapsed":0.01}
11 | {"Time":"2018-10-17T22:05:12.541458-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"FAIL\n"}
12 | {"Time":"2018-10-17T22:05:12.541857-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"FAIL\tgithub.com/mfridman/tparse/tests\t0.017s\n"}
13 | {"Time":"2018-10-17T22:05:12.541908-04:00","Action":"fail","Package":"github.com/mfridman/tparse/tests","Elapsed":0.017}
14 |
--------------------------------------------------------------------------------
/tests/testdata/outcome/test_02.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-28T00:06:53.478265-04:00","Action":"output","Package":"github.com/astromail/rover","Output":"? \tgithub.com/astromail/rover\t[no test files]\n"}
2 | {"Time":"2018-10-28T00:06:53.478512-04:00","Action":"skip","Package":"github.com/astromail/rover","Elapsed":0}
3 | {"Time":"2018-10-28T00:06:53.511804-04:00","Action":"output","Package":"github.com/astromail/rover/cmd/roverd","Output":"? \tgithub.com/astromail/rover/cmd/roverd\t[no test files]\n"}
4 | {"Time":"2018-10-28T00:06:53.511862-04:00","Action":"skip","Package":"github.com/astromail/rover/cmd/roverd","Elapsed":0}
5 | {"Time":"2018-10-28T00:06:53.511882-04:00","Action":"output","Package":"github.com/astromail/rover/errors","Output":"? \tgithub.com/astromail/rover/errors\t[no test files]\n"}
6 | {"Time":"2018-10-28T00:06:53.511891-04:00","Action":"skip","Package":"github.com/astromail/rover/errors","Elapsed":0}
7 | {"Time":"2018-10-28T00:06:53.511907-04:00","Action":"output","Package":"github.com/astromail/rover/smtp","Output":"? \tgithub.com/astromail/rover/smtp\t[no test files]\n"}
8 | {"Time":"2018-10-28T00:06:53.511916-04:00","Action":"skip","Package":"github.com/astromail/rover/smtp","Elapsed":0}
9 | {"Time":"2018-10-28T00:06:53.511933-04:00","Action":"output","Package":"github.com/astromail/rover/storage","Output":"? \tgithub.com/astromail/rover/storage\t[no test files]\n"}
10 | {"Time":"2018-10-28T00:06:53.511942-04:00","Action":"skip","Package":"github.com/astromail/rover/storage","Elapsed":0}
11 | {"Time":"2018-10-28T00:06:53.511957-04:00","Action":"output","Package":"github.com/astromail/rover/storage/badger","Output":"? \tgithub.com/astromail/rover/storage/badger\t[no test files]\n"}
12 | {"Time":"2018-10-28T00:06:53.511969-04:00","Action":"skip","Package":"github.com/astromail/rover/storage/badger","Elapsed":0}
13 | {"Time":"2018-10-28T00:06:54.007207-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/28 00:06:54 Replaying from value pointer: {Fid:0 Len:0 Offset:0}\n"}
14 | {"Time":"2018-10-28T00:06:54.007282-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/28 00:06:54 Iterating file id: 0\n"}
15 | {"Time":"2018-10-28T00:06:54.007321-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/28 00:06:54 Replaying from value pointer: {Fid:0 Len:0 Offset:0}\n"}
16 | {"Time":"2018-10-28T00:06:54.00733-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/28 00:06:54 Iterating file id: 0\n"}
17 | {"Time":"2018-10-28T00:06:54.007389-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/28 00:06:54 Iteration took: 144.649µs\n"}
18 | {"Time":"2018-10-28T00:06:54.007462-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/28 00:06:54 Iteration took: 259.316µs\n"}
19 | {"Time":"2018-10-28T00:06:54.503762-04:00","Action":"run","Package":"github.com/astromail/rover/tests","Test":"TestCatch"}
20 | {"Time":"2018-10-28T00:06:54.503795-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch","Output":"=== RUN TestCatch\n"}
21 | {"Time":"2018-10-28T00:06:54.503829-04:00","Action":"run","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve"}
22 | {"Time":"2018-10-28T00:06:54.503838-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Output":"=== RUN TestCatch/catchAndRetrieve\n"}
23 | {"Time":"2018-10-28T00:06:54.507445-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch","Output":"--- FAIL: TestCatch (0.00s)\n"}
24 | {"Time":"2018-10-28T00:06:54.507485-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Output":" --- FAIL: TestCatch/catchAndRetrieve (0.00s)\n"}
25 | {"Time":"2018-10-28T00:06:54.507507-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Output":" catch_test.go:29: got id \"ad0892h\", want empty id\n"}
26 | {"Time":"2018-10-28T00:06:54.507517-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Output":" catch_test.go:37: email id does not match: got \"69c47b65-0ad5-47d5-9346-f4f8bd22c56e\", want \"oops@example.com\"\n"}
27 | {"Time":"2018-10-28T00:06:54.507528-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Output":" catch_test.go:41: failed to mark email as read: failed to mark email id \"123\" as read: PUT \"http://localhost:8026/api/v1/emails/123/read\": expecting valid status code, got 500 Internal Server Error\n"}
28 | {"Time":"2018-10-28T00:06:54.507536-04:00","Action":"fail","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Elapsed":0}
29 | {"Time":"2018-10-28T00:06:54.507544-04:00","Action":"fail","Package":"github.com/astromail/rover/tests","Test":"TestCatch","Elapsed":0}
30 | {"Time":"2018-10-28T00:06:54.507549-04:00","Action":"run","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat"}
31 | {"Time":"2018-10-28T00:06:54.507554-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Output":"=== RUN TestNameFormat\n"}
32 | {"Time":"2018-10-28T00:06:54.507571-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Output":"--- PASS: TestNameFormat (0.00s)\n"}
33 | {"Time":"2018-10-28T00:06:54.507578-04:00","Action":"pass","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Elapsed":0}
34 | {"Time":"2018-10-28T00:06:54.507583-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"FAIL\n"}
35 | {"Time":"2018-10-28T00:06:54.50967-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"FAIL\tgithub.com/astromail/rover/tests\t0.532s\n"}
36 | {"Time":"2018-10-28T00:06:54.509705-04:00","Action":"fail","Package":"github.com/astromail/rover/tests","Elapsed":0.532}
--------------------------------------------------------------------------------
/tests/testdata/outcome/test_04.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-15T22:57:27.729679-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 22:57:27 Replaying from value pointer: {Fid:0 Len:0 Offset:0}\n"}
2 | {"Time":"2018-10-15T22:57:27.730067-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 22:57:27 Iterating file id: 0\n"}
3 | {"Time":"2018-10-15T22:57:27.730109-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 22:57:27 Iteration took: 49.09µs\n"}
4 | {"Time":"2018-10-15T22:57:27.732221-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 22:57:27 Replaying from value pointer: {Fid:0 Len:0 Offset:0}\n"}
5 | {"Time":"2018-10-15T22:57:27.732263-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 22:57:27 Iterating file id: 0\n"}
6 | {"Time":"2018-10-15T22:57:27.732285-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 22:57:27 Iteration took: 63.3µs\n"}
7 | {"Time":"2018-10-15T22:57:28.228957-04:00","Action":"run","Package":"github.com/astromail/rover/tests","Test":"TestCatch"}
8 | {"Time":"2018-10-15T22:57:28.229033-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch","Output":"=== RUN TestCatch\n"}
9 | {"Time":"2018-10-15T22:57:28.229083-04:00","Action":"run","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve"}
10 | {"Time":"2018-10-15T22:57:28.229112-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Output":"=== RUN TestCatch/catchAndRetrieve\n"}
11 | {"Time":"2018-10-15T22:57:28.236421-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch","Output":"--- PASS: TestCatch (0.01s)\n"}
12 | {"Time":"2018-10-15T22:57:28.236461-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Output":" --- PASS: TestCatch/catchAndRetrieve (0.01s)\n"}
13 | {"Time":"2018-10-15T22:57:28.236492-04:00","Action":"pass","Package":"github.com/astromail/rover/tests","Test":"TestCatch/catchAndRetrieve","Elapsed":0.01}
14 | {"Time":"2018-10-15T22:57:28.236516-04:00","Action":"pass","Package":"github.com/astromail/rover/tests","Test":"TestCatch","Elapsed":0.01}
15 | {"Time":"2018-10-15T22:57:28.236568-04:00","Action":"run","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat"}
16 | {"Time":"2018-10-15T22:57:28.236597-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Output":"=== RUN TestNameFormat\n"}
17 | {"Time":"2018-10-15T22:57:28.236633-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Output":"--- PASS: TestNameFormat (0.00s)\n"}
18 | {"Time":"2018-10-15T22:57:28.236657-04:00","Action":"pass","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Elapsed":0}
19 | {"Time":"2018-10-15T22:57:28.236673-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"PASS\n"}
20 | {"Time":"2018-10-15T22:57:28.237956-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"ok \tgithub.com/astromail/rover/tests\t0.582s\n"}
21 | {"Time":"2018-10-15T22:57:28.23799-04:00","Action":"pass","Package":"github.com/astromail/rover/tests","Elapsed":0.582}
22 |
--------------------------------------------------------------------------------
/tests/testdata/outcome/test_05.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-15T23:00:27.929094-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 23:00:27 Replaying from value pointer: {Fid:0 Len:0 Offset:0}\n"}
2 | {"Time":"2018-10-15T23:00:27.929492-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 23:00:27 Iterating file id: 0\n"}
3 | {"Time":"2018-10-15T23:00:27.929529-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 23:00:27 Replaying from value pointer: {Fid:0 Len:0 Offset:0}\n"}
4 | {"Time":"2018-10-15T23:00:27.929543-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 23:00:27 Iterating file id: 0\n"}
5 | {"Time":"2018-10-15T23:00:27.929572-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 23:00:27 Iteration took: 73.332µs\n"}
6 | {"Time":"2018-10-15T23:00:27.929588-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"2018/10/15 23:00:27 Iteration took: 39.372µs\n"}
7 | {"Time":"2018-10-15T23:00:28.430669-04:00","Action":"run","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat"}
8 | {"Time":"2018-10-15T23:00:28.430728-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Output":"=== RUN TestNameFormat\n"}
9 | {"Time":"2018-10-15T23:00:28.43076-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Output":"--- SKIP: TestNameFormat (0.00s)\n"}
10 | {"Time":"2018-10-15T23:00:28.430781-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Output":" testutil_test.go:9: \n"}
11 | {"Time":"2018-10-15T23:00:28.430798-04:00","Action":"skip","Package":"github.com/astromail/rover/tests","Test":"TestNameFormat","Elapsed":0}
12 | {"Time":"2018-10-15T23:00:28.430825-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"PASS\n"}
13 | {"Time":"2018-10-15T23:00:28.432239-04:00","Action":"output","Package":"github.com/astromail/rover/tests","Output":"ok \tgithub.com/astromail/rover/tests\t0.530s\n"}
14 | {"Time":"2018-10-15T23:00:28.436039-04:00","Action":"pass","Package":"github.com/astromail/rover/tests","Elapsed":0.534}
15 |
--------------------------------------------------------------------------------
/tests/testdata/outcome/test_06.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-17T22:01:48.798229-04:00","Action":"output","Package":"fmt","Output":"testing: warning: no tests to run\n"}
2 | {"Time":"2018-10-17T22:01:48.798553-04:00","Action":"output","Package":"fmt","Output":"PASS\n"}
3 | {"Time":"2018-10-17T22:01:48.798617-04:00","Action":"output","Package":"fmt","Output":"ok \tfmt\t0.011s [no tests to run]\n"}
4 | {"Time":"2018-10-17T22:01:48.798647-04:00","Action":"pass","Package":"fmt","Elapsed":0.011}
5 |
--------------------------------------------------------------------------------
/tests/testdata/outcome/test_07.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-20T13:32:27.452736-04:00","Action":"output","Package":"debug/errorcause","Output":"? \tdebug/errorcause\t[no test files]\n"}
2 | {"Time":"2018-10-20T13:32:27.452995-04:00","Action":"skip","Package":"debug/errorcause","Elapsed":0}
--------------------------------------------------------------------------------
/tests/testdata/outcome/test_08.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-27T23:15:21.909874-04:00","Action":"output","Package":"github.com/awesome/pkg","Output":"PASS\n"}
2 | {"Time":"2018-10-27T23:15:21.914955-04:00","Action":"output","Package":"github.com/awesome/pkg","Output":"ok \tgithub.com/awesome/pkg\t4.543s [no tests to run]\n"}
3 | {"Time":"2018-10-27T23:15:21.914992-04:00","Action":"pass","Package":"github.com/awesome/pkg","Elapsed":4.543}
--------------------------------------------------------------------------------
/tests/testdata/panic/test_03.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-21T22:15:24.47322-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus"}
2 | {"Time":"2018-10-21T22:15:24.473515-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"=== RUN TestStatus\n"}
3 | {"Time":"2018-10-21T22:15:24.473542-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"=== PAUSE TestStatus\n"}
4 | {"Time":"2018-10-21T22:15:24.47355-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus"}
5 | {"Time":"2018-10-21T22:15:24.473565-04:00","Action":"cont","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus"}
6 | {"Time":"2018-10-21T22:15:24.473573-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"=== CONT TestStatus\n"}
7 | {"Time":"2018-10-21T22:15:24.473588-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"--- FAIL: TestStatus (0.00s)\n"}
8 | {"Time":"2018-10-21T22:15:24.47549-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"panic: runtime error: invalid memory address or nil pointer dereference [recovered]\n"}
9 | {"Time":"2018-10-21T22:15:24.475513-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\tpanic: runtime error: invalid memory address or nil pointer dereference\n"}
10 | {"Time":"2018-10-21T22:15:24.475532-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1112389]\n"}
11 | {"Time":"2018-10-21T22:15:24.47554-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\n"}
12 | {"Time":"2018-10-21T22:15:24.475549-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"goroutine 18 [running]:\n"}
13 | {"Time":"2018-10-21T22:15:24.475559-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"testing.tRunner.func1(0xc0000b6300)\n"}
14 | {"Time":"2018-10-21T22:15:24.475567-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/usr/local/go/src/testing/testing.go:792 +0x387\n"}
15 | {"Time":"2018-10-21T22:15:24.475581-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"panic(0x1137980, 0x1262100)\n"}
16 | {"Time":"2018-10-21T22:15:24.475651-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/usr/local/go/src/runtime/panic.go:513 +0x1b9\n"}
17 | {"Time":"2018-10-21T22:15:24.475682-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"github.com/mfridman/tparse/tests_test.TestStatus.func1(0x116177e, 0xe, 0x1185120, 0xc00006c820, 0x0, 0x0, 0x0, 0xc00002e6c0)\n"}
18 | {"Time":"2018-10-21T22:15:24.475695-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/Users/michael.fridman/go/src/github.com/mfridman/tparse/tests/status_test.go:26 +0x69\n"}
19 | {"Time":"2018-10-21T22:15:24.475749-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"path/filepath.walk(0x116177e, 0xe, 0x1185120, 0xc00006c820, 0xc0000666a0, 0x0, 0x10)\n"}
20 | {"Time":"2018-10-21T22:15:24.475773-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/usr/local/go/src/path/filepath/path.go:362 +0xf6\n"}
21 | {"Time":"2018-10-21T22:15:24.475781-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"path/filepath.Walk(0x116177e, 0xe, 0xc0000666a0, 0x1c338b20, 0xf815f)\n"}
22 | {"Time":"2018-10-21T22:15:24.475788-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/usr/local/go/src/path/filepath/path.go:404 +0x105\n"}
23 | {"Time":"2018-10-21T22:15:24.475798-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"github.com/mfridman/tparse/tests_test.TestStatus(0xc0000b6300)\n"}
24 | {"Time":"2018-10-21T22:15:24.475936-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/Users/michael.fridman/go/src/github.com/mfridman/tparse/tests/status_test.go:19 +0x7e\n"}
25 | {"Time":"2018-10-21T22:15:24.475945-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"testing.tRunner(0xc0000b6300, 0x116ab18)\n"}
26 | {"Time":"2018-10-21T22:15:24.475952-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/usr/local/go/src/testing/testing.go:827 +0xbf\n"}
27 | {"Time":"2018-10-21T22:15:24.475959-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"created by testing.(*T).Run\n"}
28 | {"Time":"2018-10-21T22:15:24.475975-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"\t/usr/local/go/src/testing/testing.go:878 +0x353\n"}
29 | {"Time":"2018-10-21T22:15:24.476216-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Output":"FAIL\tgithub.com/mfridman/tparse/tests\t0.014s\n"}
30 | {"Time":"2018-10-21T22:15:24.476261-04:00","Action":"fail","Package":"github.com/mfridman/tparse/tests","Test":"TestStatus","Elapsed":0.014}
--------------------------------------------------------------------------------
/tests/testdata/panic/test_04.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-21T23:42:51.496472-04:00","Action":"output","Package":"github.com/mfridman/tparse","Output":"? \tgithub.com/mfridman/tparse\t[no test files]\n"}
2 | {"Time":"2018-10-21T23:42:51.496734-04:00","Action":"skip","Package":"github.com/mfridman/tparse","Elapsed":0}
3 | {"Time":"2018-10-21T23:42:51.49677-04:00","Action":"output","Package":"github.com/mfridman/tparse/ignore","Output":"? \tgithub.com/mfridman/tparse/ignore\t[no test files]\n"}
4 | {"Time":"2018-10-21T23:42:51.496782-04:00","Action":"skip","Package":"github.com/mfridman/tparse/ignore","Elapsed":0}
5 | {"Time":"2018-10-21T23:42:51.496805-04:00","Action":"output","Package":"github.com/mfridman/tparse/parse","Output":"? \tgithub.com/mfridman/tparse/parse\t[no test files]\n"}
6 | {"Time":"2018-10-21T23:42:51.496813-04:00","Action":"skip","Package":"github.com/mfridman/tparse/parse","Elapsed":0}
7 | {"Time":"2018-10-21T23:42:51.673696-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent"}
8 | {"Time":"2018-10-21T23:42:51.673742-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent","Output":"=== RUN TestNewEvent\n"}
9 | {"Time":"2018-10-21T23:42:51.673772-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent","Output":"=== PAUSE TestNewEvent\n"}
10 | {"Time":"2018-10-21T23:42:51.673795-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent"}
11 | {"Time":"2018-10-21T23:42:51.673815-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent"}
12 | {"Time":"2018-10-21T23:42:51.673834-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent","Output":"=== RUN TestPanicEvent\n"}
13 | {"Time":"2018-10-21T23:42:51.674274-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent","Output":"--- PASS: TestPanicEvent (0.00s)\n"}
14 | {"Time":"2018-10-21T23:42:51.674295-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent","Elapsed":0}
15 | {"Time":"2018-10-21T23:42:51.674307-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
16 | {"Time":"2018-10-21T23:42:51.674314-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== RUN TestStack\n"}
17 | {"Time":"2018-10-21T23:42:51.674328-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"--- FAIL: TestStack (0.00s)\n"}
18 | {"Time":"2018-10-21T23:42:51.676397-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"panic: oops [recovered]\n"}
19 | {"Time":"2018-10-21T23:42:51.676427-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"\tpanic: oops\n"}
20 | {"Time":"2018-10-21T23:42:51.676437-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"\n"}
21 | {"Time":"2018-10-21T23:42:51.676453-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"goroutine 20 [running]:\n"}
22 | {"Time":"2018-10-21T23:42:51.676462-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"testing.tRunner.func1(0xc0000b4600)\n"}
23 | {"Time":"2018-10-21T23:42:51.676489-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"\t/usr/local/go/src/testing/testing.go:792 +0x387\n"}
24 | {"Time":"2018-10-21T23:42:51.676501-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"panic(0x112ad60, 0x1182f90)\n"}
25 | {"Time":"2018-10-21T23:42:51.67651-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"\t/usr/local/go/src/runtime/panic.go:513 +0x1b9\n"}
26 | {"Time":"2018-10-21T23:42:51.676523-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"github.com/mfridman/tparse/tests_test.TestStack(0xc0000b4600)\n"}
27 | {"Time":"2018-10-21T23:42:51.676542-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"\t/Users/michael.fridman/go/src/github.com/mfridman/tparse/tests/stack_test.go:12 +0x39\n"}
28 | {"Time":"2018-10-21T23:42:51.676555-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"testing.tRunner(0xc0000b4600, 0x116a730)\n"}
29 | {"Time":"2018-10-21T23:42:51.676585-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"\t/usr/local/go/src/testing/testing.go:827 +0xbf\n"}
30 | {"Time":"2018-10-21T23:42:51.676596-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"created by testing.(*T).Run\n"}
31 | {"Time":"2018-10-21T23:42:51.67666-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"\t/usr/local/go/src/testing/testing.go:878 +0x353\n"}
32 | {"Time":"2018-10-21T23:42:51.676943-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"FAIL\tgithub.com/mfridman/tparse/tests\t0.016s\n"}
33 | {"Time":"2018-10-21T23:42:51.676966-04:00","Action":"fail","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Elapsed":0.016}
--------------------------------------------------------------------------------
/tests/testdata/panic/test_05.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-10-24T14:32:01.310248-04:00","Action":"output","Package":"github.com/mfridman/tparse","Output":"? \tgithub.com/mfridman/tparse\t[no test files]\n"}
2 | {"Time":"2018-10-24T14:32:01.31052-04:00","Action":"skip","Package":"github.com/mfridman/tparse","Elapsed":0}
3 | {"Time":"2018-10-24T14:32:01.31056-04:00","Action":"output","Package":"github.com/mfridman/tparse/ignore","Output":"? \tgithub.com/mfridman/tparse/ignore\t[no test files]\n"}
4 | {"Time":"2018-10-24T14:32:01.310588-04:00","Action":"skip","Package":"github.com/mfridman/tparse/ignore","Elapsed":0}
5 | {"Time":"2018-10-24T14:32:01.310626-04:00","Action":"output","Package":"github.com/mfridman/tparse/parse","Output":"? \tgithub.com/mfridman/tparse/parse\t[no test files]\n"}
6 | {"Time":"2018-10-24T14:32:01.310638-04:00","Action":"skip","Package":"github.com/mfridman/tparse/parse","Elapsed":0}
7 | {"Time":"2018-10-24T14:32:01.499657-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent"}
8 | {"Time":"2018-10-24T14:32:01.499717-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent","Output":"=== RUN TestNewEvent\n"}
9 | {"Time":"2018-10-24T14:32:01.499741-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent","Output":"=== PAUSE TestNewEvent\n"}
10 | {"Time":"2018-10-24T14:32:01.499758-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestNewEvent"}
11 | {"Time":"2018-10-24T14:32:01.499771-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestCachedPackage"}
12 | {"Time":"2018-10-24T14:32:01.499781-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestCachedPackage","Output":"=== RUN TestCachedPackage\n"}
13 | {"Time":"2018-10-24T14:32:01.499789-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestCachedPackage","Output":"=== PAUSE TestCachedPackage\n"}
14 | {"Time":"2018-10-24T14:32:01.499799-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestCachedPackage"}
15 | {"Time":"2018-10-24T14:32:01.499809-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestCover"}
16 | {"Time":"2018-10-24T14:32:01.499816-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestCover","Output":"=== RUN TestCover\n"}
17 | {"Time":"2018-10-24T14:32:01.499829-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestCover","Output":"=== PAUSE TestCover\n"}
18 | {"Time":"2018-10-24T14:32:01.499836-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestCover"}
19 | {"Time":"2018-10-24T14:32:01.499843-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent"}
20 | {"Time":"2018-10-24T14:32:01.499849-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent","Output":"=== RUN TestPanicEvent\n"}
21 | {"Time":"2018-10-24T14:32:01.500824-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent","Output":"--- PASS: TestPanicEvent (0.00s)\n"}
22 | {"Time":"2018-10-24T14:32:01.500848-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Test":"TestPanicEvent","Elapsed":0}
23 | {"Time":"2018-10-24T14:32:01.500859-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan"}
24 | {"Time":"2018-10-24T14:32:01.500869-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan","Output":"=== RUN TestPrescan\n"}
25 | {"Time":"2018-10-24T14:32:01.50088-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/pass"}
26 | {"Time":"2018-10-24T14:32:01.50089-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/pass","Output":"=== RUN TestPrescan/pass\n"}
27 | {"Time":"2018-10-24T14:32:01.501224-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01"}
28 | {"Time":"2018-10-24T14:32:01.501234-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"=== RUN TestPrescan/fail01\n"}
29 | {"Time":"2018-10-24T14:32:01.503535-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"panic: runtime error: invalid memory address or nil pointer dereference [recovered]\n"}
30 | {"Time":"2018-10-24T14:32:01.503561-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\tpanic: runtime error: invalid memory address or nil pointer dereference\n"}
31 | {"Time":"2018-10-24T14:32:01.503574-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x110f2f1]\n"}
32 | {"Time":"2018-10-24T14:32:01.503582-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\n"}
33 | {"Time":"2018-10-24T14:32:01.503592-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"goroutine 11 [running]:\n"}
34 | {"Time":"2018-10-24T14:32:01.5036-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"testing.tRunner.func1(0xc0000a0b00)\n"}
35 | {"Time":"2018-10-24T14:32:01.503612-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\t/usr/local/go/src/testing/testing.go:792 +0x387\n"}
36 | {"Time":"2018-10-24T14:32:01.503664-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"panic(0x1138e80, 0x126a100)\n"}
37 | {"Time":"2018-10-24T14:32:01.50368-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\t/usr/local/go/src/runtime/panic.go:513 +0x1b9\n"}
38 | {"Time":"2018-10-24T14:32:01.503689-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"github.com/mfridman/tparse/parse.Start(0x118b220, 0xc0000fb8a0, 0x5bd0baa1, 0xc000034798, 0x106e0b6)\n"}
39 | {"Time":"2018-10-24T14:32:01.503697-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\t/Users/michael.fridman/go/src/github.com/mfridman/tparse/parse/package.go:136 +0x151\n"}
40 | {"Time":"2018-10-24T14:32:01.503707-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"github.com/mfridman/tparse/tests_test.TestPrescan.func2(0xc0000a0b00)\n"}
41 | {"Time":"2018-10-24T14:32:01.503779-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\t/Users/michael.fridman/go/src/github.com/mfridman/tparse/tests/prescan_test.go:144 +0x7a\n"}
42 | {"Time":"2018-10-24T14:32:01.503791-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"testing.tRunner(0xc0000a0b00, 0x1171820)\n"}
43 | {"Time":"2018-10-24T14:32:01.503798-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\t/usr/local/go/src/testing/testing.go:827 +0xbf\n"}
44 | {"Time":"2018-10-24T14:32:01.503805-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"created by testing.(*T).Run\n"}
45 | {"Time":"2018-10-24T14:32:01.503813-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"\t/usr/local/go/src/testing/testing.go:878 +0x353\n"}
46 | {"Time":"2018-10-24T14:32:01.504138-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Output":"FAIL\tgithub.com/mfridman/tparse/tests\t0.016s\n"}
47 | {"Time":"2018-10-24T14:32:01.50418-04:00","Action":"fail","Package":"github.com/mfridman/tparse/tests","Test":"TestPrescan/fail01","Elapsed":0.016}
--------------------------------------------------------------------------------
/tests/testdata/prescan/test_01.txt:
--------------------------------------------------------------------------------
1 | git.checkout
2 | 0.55s$ git clone --depth=50 --branch=master https://github.com/mfridman/tparse.git mfridman/tparse
3 | Cloning into 'mfridman/tparse'...
4 | remote: Enumerating objects: 485, done.
5 | remote: Counting objects: 100% (485/485), done.
6 | remote: Compressing objects: 100% (310/310), done.
7 | remote: Total 485 (delta 209), reused 442 (delta 168), pack-reused 0
8 | Receiving objects: 100% (485/485), 1.17 MiB | 20.58 MiB/s, done.
9 | Resolving deltas: 100% (209/209), done.
10 | $ cd mfridman/tparse
11 | $ git checkout -qf d7a97f658463e3abd90357d9988575e8138d9a86
12 | 4.38s$ GIMME_OUTPUT="$(gimme 1.10 | tee -a ${TRAVIS_HOME}/.bashrc)" && eval "$GIMME_OUTPUT"
13 | go version go1.10 linux/amd64
14 | $ export GOPATH=${TRAVIS_HOME}/gopath
15 | $ export PATH=${TRAVIS_HOME}/gopath/bin:$PATH
16 | $ mkdir -p ${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
17 | $ tar -Pczf ${TRAVIS_TMPDIR}/src_archive.tar.gz -C ${TRAVIS_BUILD_DIR} . && tar -Pxzf ${TRAVIS_TMPDIR}/src_archive.tar.gz -C ${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
18 | $ export TRAVIS_BUILD_DIR=${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
19 | $ cd ${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
20 | 0.00s
21 | $ gimme version
22 | v1.5.3
23 | $ go version
24 | go version go1.10 linux/amd64
25 | go.env
26 | $ go env
27 | GOARCH="amd64"
28 | GOBIN=""
29 | GOCACHE="/home/travis/.cache/go-build"
30 | GOEXE=""
31 | GOHOSTARCH="amd64"
32 | GOHOSTOS="linux"
33 | GOOS="linux"
34 | GOPATH="/home/travis/gopath"
35 | GORACE=""
36 | GOROOT="/home/travis/.gimme/versions/go1.10.linux.amd64"
37 | GOTMPDIR=""
38 | GOTOOLDIR="/home/travis/.gimme/versions/go1.10.linux.amd64/pkg/tool/linux_amd64"
39 | GCCGO="gccgo"
40 | CC="gcc"
41 | CXX="g++"
42 | CGO_ENABLED="1"
43 | CGO_CFLAGS="-g -O2"
44 | CGO_CPPFLAGS=""
45 | CGO_CXXFLAGS="-g -O2"
46 | CGO_FFLAGS="-g -O2"
47 | CGO_LDFLAGS="-g -O2"
48 | PKG_CONFIG="pkg-config"
49 | GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build714003296=/tmp/go-build -gno-record-gcc-switches"
50 | before_install
51 | {"Time":"2018-10-24T13:13:35.445816-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
52 | {"Time":"2018-10-24T13:13:35.447209-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== RUN TestStack\n"}
53 | {"Time":"2018-10-24T13:13:35.44729-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== PAUSE TestStack\n"}
54 | {"Time":"2018-10-24T13:13:35.447302-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
55 | {"Time":"2018-10-24T13:13:35.447311-04:00","Action":"cont","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
56 | {"Time":"2018-10-24T13:13:35.447317-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== CONT TestStack\n"}
57 | {"Time":"2018-10-24T13:13:35.447348-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"--- PASS: TestStack (0.00s)\n"}
58 | {"Time":"2018-10-24T13:13:35.447394-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Elapsed":0}
59 | {"Time":"2018-10-24T13:13:35.447481-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"PASS\n"}
60 | {"Time":"2018-10-24T13:13:35.447759-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"ok \tgithub.com/mfridman/tparse/tests\t0.014s\n"}
61 | {"Time":"2018-10-24T13:13:35.454438-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Elapsed":0.021}
62 |
--------------------------------------------------------------------------------
/tests/testdata/prescan/test_02.txt:
--------------------------------------------------------------------------------
1 | git.checkout
2 | 0.55s$ git clone --depth=50 --branch=master https://github.com/mfridman/tparse.git mfridman/tparse
3 | Cloning into 'mfridman/tparse'...
4 | remote: Enumerating objects: 485, done.
5 | remote: Counting objects: 100% (485/485), done.
6 | remote: Compressing objects: 100% (310/310), done.
7 | remote: Total 485 (delta 209), reused 442 (delta 168), pack-reused 0
8 | Receiving objects: 100% (485/485), 1.17 MiB | 20.58 MiB/s, done.
9 | Resolving deltas: 100% (209/209), done.
10 | $ cd mfridman/tparse
11 | $ git checkout -qf d7a97f658463e3abd90357d9988575e8138d9a86
12 | 4.38s$ GIMME_OUTPUT="$(gimme 1.10 | tee -a ${TRAVIS_HOME}/.bashrc)" && eval "$GIMME_OUTPUT"
13 | go version go1.10 linux/amd64
14 | $ export GOPATH=${TRAVIS_HOME}/gopath
15 | $ export PATH=${TRAVIS_HOME}/gopath/bin:$PATH
16 | $ mkdir -p ${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
17 | $ tar -Pczf ${TRAVIS_TMPDIR}/src_archive.tar.gz -C ${TRAVIS_BUILD_DIR} . && tar -Pxzf ${TRAVIS_TMPDIR}/src_archive.tar.gz -C ${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
18 | $ export TRAVIS_BUILD_DIR=${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
19 | $ cd ${TRAVIS_HOME}/gopath/src/github.com/mfridman/tparse
20 | 0.00s
21 | $ gimme version
22 | v1.5.3
23 | $ go version
24 | go version go1.10 linux/amd64
25 | go.env
26 | $ go env
27 | GOARCH="amd64"
28 | GOBIN=""
29 | GOCACHE="/home/travis/.cache/go-build"
30 | GOEXE=""
31 | GOHOSTARCH="amd64"
32 | GOHOSTOS="linux"
33 | GOOS="linux"
34 | GOPATH="/home/travis/gopath"
35 | GORACE=""
36 | GOROOT="/home/travis/.gimme/versions/go1.10.linux.amd64"
37 | GOTMPDIR=""
38 | GOTOOLDIR="/home/travis/.gimme/versions/go1.10.linux.amd64/pkg/tool/linux_amd64"
39 | GCCGO="gccgo"
40 | CC="gcc"
41 | CXX="g++"
42 | CGO_ENABLED="1"
43 | CGO_CFLAGS="-g -O2"
44 | CGO_CPPFLAGS=""
45 | CGO_CXXFLAGS="-g -O2"
46 | CGO_FFLAGS="-g -O2"
47 | CGO_LDFLAGS="-g -O2"
48 | PKG_CONFIG="pkg-config"
49 | GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build714003296=/tmp/go-build -gno-record-gcc-switches"
50 | before_install
51 | pirate ipsum!
52 | {"Time":"2018-10-24T13:13:35.445816-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
53 | {"Time":"2018-10-24T13:13:35.447209-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== RUN TestStack\n"}
54 | {"Time":"2018-10-24T13:13:35.44729-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== PAUSE TestStack\n"}
55 | {"Time":"2018-10-24T13:13:35.447302-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
56 | {"Time":"2018-10-24T13:13:35.447311-04:00","Action":"cont","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
57 | {"Time":"2018-10-24T13:13:35.447317-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== CONT TestStack\n"}
58 | {"Time":"2018-10-24T13:13:35.447348-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"--- PASS: TestStack (0.00s)\n"}
59 | {"Time":"2018-10-24T13:13:35.447394-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Elapsed":0}
60 | {"Time":"2018-10-24T13:13:35.447481-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"PASS\n"}
61 | {"Time":"2018-10-24T13:13:35.447759-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"ok \tgithub.com/mfridman/tparse/tests\t0.014s\n"}
62 | {"Time":"2018-10-24T13:13:35.454438-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Elapsed":0.021}
--------------------------------------------------------------------------------
/tests/testdata/prescan/test_03.txt:
--------------------------------------------------------------------------------
1 | git.checkout
2 | 0.55s$ git clone --depth=50 --branch=master https://github.com/mfridman/tparse.git mfridman/tparse
3 | Cloning into 'mfridman/tparse'...
4 | remote: Enumerating objects: 485, done.
5 | remote: Counting objects: 100% (485/485), done.
6 | {"Time":"2018-10-24T13:13:35.447209-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== RUN TestStack\n"}
7 | remote: Compressing objects: 100% (310/310), done.
8 | {"Time":"2018-10-24T13:13:35.447317-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack",
9 | remote: Total 485 (delta 209), reused 442 (delta 168), pack-reused 0
10 | Receiving objects: 100% (485/485), 1.17 MiB | 20.58 MiB/s, done.
11 | Resolving deltas: 100% (209/209), done.
12 | $ cd mfridman/tparse
13 | $ git checkout -qf d7a97f658463e3abd90357d9988575e8138d9a86
14 | {"Time":"2018-10-24T13:13:35.445816-04:00","Action":"run","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
15 | {"Time":"2018-10-24T13:13:35.447209-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== RUN TestStack\n"}
16 | {"Time":"2018-10-24T13:13:35.44729-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== PAUSE TestStack\n"}
17 | {"Time":"2018-10-24T13:13:35.447302-04:00","Action":"pause","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
18 | {"Time":"2018-10-24T13:13:35.447311-04:00","Action":"cont","Package":"github.com/mfridman/tparse/tests","Test":"TestStack"}
19 | {"Time":"2018-10-24T13:13:35.447317-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"=== CONT TestStack\n"}
20 | {"Time":"2018-10-24T13:13:35.447348-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Output":"--- PASS: TestStack (0.00s)\n"}
21 | {"Time":"2018-10-24T13:13:35.447394-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Test":"TestStack","Elapsed":0}
22 | {"Time":"2018-10-24T13:13:35.447481-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"PASS\n"}
23 | {"Time":"2018-10-24T13:13:35.447759-04:00","Action":"output","Package":"github.com/mfridman/tparse/tests","Output":"ok \tgithub.com/mfridman/tparse/tests\t0.014s\n"}
24 | {"Time":"2018-10-24T13:13:35.454438-04:00","Action":"pass","Package":"github.com/mfridman/tparse/tests","Elapsed":0.021}
--------------------------------------------------------------------------------
/tests/testdata/prescan/test_04.txt:
--------------------------------------------------------------------------------
1 | Previous write at 0x00c000090090 by goroutine 6:
2 | debug/tparse-24.TestRace()
3 | /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:8 +0x88
4 | testing.tRunner()
5 | /usr/local/go/src/testing/testing.go:827 +0x162
6 |
7 | Goroutine 7 (running) created at:
8 | debug/tparse-24.TestRace()
9 | /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:7 +0x7a
10 | testing.tRunner()
11 | /usr/local/go/src/testing/testing.go:827 +0x162
12 |
13 | Goroutine 6 (running) created at:
14 | testing.(*T).Run()
15 | /usr/local/go/src/testing/testing.go:878 +0x650
16 | testing.runTests.func1()
17 | /usr/local/go/src/testing/testing.go:1119 +0xa8
18 | testing.tRunner()
19 | /usr/local/go/src/testing/testing.go:827 +0x162
20 | testing.runTests()
21 | /usr/local/go/src/testing/testing.go:1117 +0x4ee
22 | testing.(*M).Run()
23 | /usr/local/go/src/testing/testing.go:1034 +0x2ee
24 | main.main()
25 | _testmain.go:42 +0x221
26 |
--------------------------------------------------------------------------------
/tests/testdata/race/test_01.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-12-16T20:28:29.256021-05:00","Action":"run","Package":"command-line-arguments","Test":"TestRace1"}
2 | {"Time":"2018-12-16T20:28:29.256317-05:00","Action":"output","Package":"command-line-arguments","Test":"TestRace1","Output":"=== RUN TestRace1\n"}
3 | {"Time":"2018-12-16T20:28:29.256347-05:00","Action":"output","Package":"command-line-arguments","Test":"TestRace1","Output":"=== PAUSE TestRace1\n"}
4 | {"Time":"2018-12-16T20:28:29.256355-05:00","Action":"pause","Package":"command-line-arguments","Test":"TestRace1"}
5 | {"Time":"2018-12-16T20:28:29.256363-05:00","Action":"run","Package":"command-line-arguments","Test":"TestA"}
6 | {"Time":"2018-12-16T20:28:29.25637-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"=== RUN TestA\n"}
7 | {"Time":"2018-12-16T20:28:29.256377-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"=== PAUSE TestA\n"}
8 | {"Time":"2018-12-16T20:28:29.256384-05:00","Action":"pause","Package":"command-line-arguments","Test":"TestA"}
9 | {"Time":"2018-12-16T20:28:29.256538-05:00","Action":"cont","Package":"command-line-arguments","Test":"TestA"}
10 | {"Time":"2018-12-16T20:28:29.256559-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"=== CONT TestA\n"}
11 | {"Time":"2018-12-16T20:28:29.256572-05:00","Action":"cont","Package":"command-line-arguments","Test":"TestRace1"}
12 | {"Time":"2018-12-16T20:28:29.256579-05:00","Action":"output","Package":"command-line-arguments","Test":"TestRace1","Output":"=== CONT TestRace1\n"}
13 | {"Time":"2018-12-16T20:28:29.256742-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"--- PASS: TestA (0.00s)\n"}
14 | {"Time":"2018-12-16T20:28:29.256827-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"==================\n"}
15 | {"Time":"2018-12-16T20:28:29.256859-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"WARNING: DATA RACE\n"}
16 | {"Time":"2018-12-16T20:28:29.256868-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"Write at 0x00c0000be010 by goroutine 9:\n"}
17 | {"Time":"2018-12-16T20:28:29.25689-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" command-line-arguments.TestRace1.func1()\n"}
18 | {"Time":"2018-12-16T20:28:29.256899-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:8 +0x38\n"}
19 | {"Time":"2018-12-16T20:28:29.256915-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"\n"}
20 | {"Time":"2018-12-16T20:28:29.256929-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"Previous write at 0x00c0000be010 by goroutine 6:\n"}
21 | {"Time":"2018-12-16T20:28:29.256936-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" command-line-arguments.TestRace1()\n"}
22 | {"Time":"2018-12-16T20:28:29.256942-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:9 +0x96\n"}
23 | {"Time":"2018-12-16T20:28:29.256948-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" testing.tRunner()\n"}
24 | {"Time":"2018-12-16T20:28:29.256961-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
25 | {"Time":"2018-12-16T20:28:29.256968-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"\n"}
26 | {"Time":"2018-12-16T20:28:29.256974-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"Goroutine 9 (running) created at:\n"}
27 | {"Time":"2018-12-16T20:28:29.25698-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" command-line-arguments.TestRace1()\n"}
28 | {"Time":"2018-12-16T20:28:29.256986-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:8 +0x88\n"}
29 | {"Time":"2018-12-16T20:28:29.256993-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" testing.tRunner()\n"}
30 | {"Time":"2018-12-16T20:28:29.257034-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
31 | {"Time":"2018-12-16T20:28:29.257044-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"\n"}
32 | {"Time":"2018-12-16T20:28:29.257097-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"Goroutine 6 (running) created at:\n"}
33 | {"Time":"2018-12-16T20:28:29.257111-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" testing.(*T).Run()\n"}
34 | {"Time":"2018-12-16T20:28:29.257121-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /usr/local/go/src/testing/testing.go:878 +0x650\n"}
35 | {"Time":"2018-12-16T20:28:29.257127-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" testing.runTests.func1()\n"}
36 | {"Time":"2018-12-16T20:28:29.257134-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /usr/local/go/src/testing/testing.go:1119 +0xa8\n"}
37 | {"Time":"2018-12-16T20:28:29.25714-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" testing.tRunner()\n"}
38 | {"Time":"2018-12-16T20:28:29.257146-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
39 | {"Time":"2018-12-16T20:28:29.257153-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" testing.runTests()\n"}
40 | {"Time":"2018-12-16T20:28:29.257162-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /usr/local/go/src/testing/testing.go:1117 +0x4ee\n"}
41 | {"Time":"2018-12-16T20:28:29.25717-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" testing.(*M).Run()\n"}
42 | {"Time":"2018-12-16T20:28:29.257187-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" /usr/local/go/src/testing/testing.go:1034 +0x2ee\n"}
43 | {"Time":"2018-12-16T20:28:29.257196-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" main.main()\n"}
44 | {"Time":"2018-12-16T20:28:29.257209-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":" _testmain.go:44 +0x221\n"}
45 | {"Time":"2018-12-16T20:28:29.257216-05:00","Action":"output","Package":"command-line-arguments","Test":"TestA","Output":"==================\n"}
46 | {"Time":"2018-12-16T20:28:29.257467-05:00","Action":"pass","Package":"command-line-arguments","Test":"TestA","Elapsed":0}
47 | {"Time":"2018-12-16T20:28:29.257487-05:00","Action":"output","Package":"command-line-arguments","Test":"TestRace1","Output":"--- FAIL: TestRace1 (0.00s)\n"}
48 | {"Time":"2018-12-16T20:28:29.257498-05:00","Action":"output","Package":"command-line-arguments","Test":"TestRace1","Output":" some_test.go:10: 64\n"}
49 | {"Time":"2018-12-16T20:28:29.257508-05:00","Action":"output","Package":"command-line-arguments","Test":"TestRace1","Output":" testing.go:771: race detected during execution of test\n"}
50 | {"Time":"2018-12-16T20:28:29.257518-05:00","Action":"fail","Package":"command-line-arguments","Test":"TestRace1","Elapsed":0}
51 | {"Time":"2018-12-16T20:28:29.257524-05:00","Action":"output","Package":"command-line-arguments","Output":"FAIL\n"}
52 | {"Time":"2018-12-16T20:28:29.258518-05:00","Action":"output","Package":"command-line-arguments","Output":"FAIL\tcommand-line-arguments\t0.016s\n"}
53 | {"Time":"2018-12-16T20:28:29.258549-05:00","Action":"fail","Package":"command-line-arguments","Elapsed":0.017}
--------------------------------------------------------------------------------
/tests/testdata/race/test_03.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2018-12-16T21:21:52.633949-05:00","Action":"run","Package":"debug/tparse-24","Test":"TestRace1"}
2 | {"Time":"2018-12-16T21:21:52.63424-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace1","Output":"=== RUN TestRace1\n"}
3 | {"Time":"2018-12-16T21:21:52.634262-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace1","Output":"=== PAUSE TestRace1\n"}
4 | {"Time":"2018-12-16T21:21:52.63427-05:00","Action":"pause","Package":"debug/tparse-24","Test":"TestRace1"}
5 | {"Time":"2018-12-16T21:21:52.634277-05:00","Action":"run","Package":"debug/tparse-24","Test":"TestA"}
6 | {"Time":"2018-12-16T21:21:52.634283-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestA","Output":"=== RUN TestA\n"}
7 | {"Time":"2018-12-16T21:21:52.63429-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestA","Output":"=== PAUSE TestA\n"}
8 | {"Time":"2018-12-16T21:21:52.634296-05:00","Action":"pause","Package":"debug/tparse-24","Test":"TestA"}
9 | {"Time":"2018-12-16T21:21:52.634392-05:00","Action":"cont","Package":"debug/tparse-24","Test":"TestA"}
10 | {"Time":"2018-12-16T21:21:52.634498-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestA","Output":"=== CONT TestA\n"}
11 | {"Time":"2018-12-16T21:21:52.634516-05:00","Action":"cont","Package":"debug/tparse-24","Test":"TestRace1"}
12 | {"Time":"2018-12-16T21:21:52.634522-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace1","Output":"=== CONT TestRace1\n"}
13 | {"Time":"2018-12-16T21:21:52.634537-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestA","Output":"--- PASS: TestA (0.00s)\n"}
14 | {"Time":"2018-12-16T21:21:52.635006-05:00","Action":"pass","Package":"debug/tparse-24","Test":"TestA","Elapsed":0}
15 | {"Time":"2018-12-16T21:21:52.635058-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace1","Output":"--- PASS: TestRace1 (0.00s)\n"}
16 | {"Time":"2018-12-16T21:21:52.635074-05:00","Action":"output","Package":"debug/tparse-24","Test":"TestRace1","Output":" some_test.go:10: 64\n"}
17 | {"Time":"2018-12-16T21:21:52.635111-05:00","Action":"pass","Package":"debug/tparse-24","Test":"TestRace1","Elapsed":0}
18 | {"Time":"2018-12-16T21:21:52.63512-05:00","Action":"output","Package":"debug/tparse-24","Output":"PASS\n"}
19 | {"Time":"2018-12-16T21:21:52.635128-05:00","Action":"output","Package":"debug/tparse-24","Output":"==================\n"}
20 | {"Time":"2018-12-16T21:21:52.635145-05:00","Action":"output","Package":"debug/tparse-24","Output":"WARNING: DATA RACE\n"}
21 | {"Time":"2018-12-16T21:21:52.635154-05:00","Action":"output","Package":"debug/tparse-24","Output":"Write at 0x00c0000c8010 by goroutine 9:\n"}
22 | {"Time":"2018-12-16T21:21:52.635161-05:00","Action":"output","Package":"debug/tparse-24","Output":" debug/tparse-24.TestRace1.func1()\n"}
23 | {"Time":"2018-12-16T21:21:52.635168-05:00","Action":"output","Package":"debug/tparse-24","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:8 +0x38\n"}
24 | {"Time":"2018-12-16T21:21:52.635178-05:00","Action":"output","Package":"debug/tparse-24","Output":"\n"}
25 | {"Time":"2018-12-16T21:21:52.635184-05:00","Action":"output","Package":"debug/tparse-24","Output":"Previous write at 0x00c0000c8010 by goroutine 6:\n"}
26 | {"Time":"2018-12-16T21:21:52.63519-05:00","Action":"output","Package":"debug/tparse-24","Output":" debug/tparse-24.TestRace1()\n"}
27 | {"Time":"2018-12-16T21:21:52.635201-05:00","Action":"output","Package":"debug/tparse-24","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:9 +0x96\n"}
28 | {"Time":"2018-12-16T21:21:52.635208-05:00","Action":"output","Package":"debug/tparse-24","Output":" testing.tRunner()\n"}
29 | {"Time":"2018-12-16T21:21:52.635217-05:00","Action":"output","Package":"debug/tparse-24","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
30 | {"Time":"2018-12-16T21:21:52.635223-05:00","Action":"output","Package":"debug/tparse-24","Output":"\n"}
31 | {"Time":"2018-12-16T21:21:52.635228-05:00","Action":"output","Package":"debug/tparse-24","Output":"Goroutine 9 (running) created at:\n"}
32 | {"Time":"2018-12-16T21:21:52.635237-05:00","Action":"output","Package":"debug/tparse-24","Output":" debug/tparse-24.TestRace1()\n"}
33 | {"Time":"2018-12-16T21:21:52.635242-05:00","Action":"output","Package":"debug/tparse-24","Output":" /Users/michael.fridman/go/src/debug/tparse-24/some_test.go:8 +0x88\n"}
34 | {"Time":"2018-12-16T21:21:52.635248-05:00","Action":"output","Package":"debug/tparse-24","Output":" testing.tRunner()\n"}
35 | {"Time":"2018-12-16T21:21:52.635253-05:00","Action":"output","Package":"debug/tparse-24","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
36 | {"Time":"2018-12-16T21:21:52.635276-05:00","Action":"output","Package":"debug/tparse-24","Output":"\n"}
37 | {"Time":"2018-12-16T21:21:52.635285-05:00","Action":"output","Package":"debug/tparse-24","Output":"Goroutine 6 (finished) created at:\n"}
38 | {"Time":"2018-12-16T21:21:52.635291-05:00","Action":"output","Package":"debug/tparse-24","Output":" testing.(*T).Run()\n"}
39 | {"Time":"2018-12-16T21:21:52.635298-05:00","Action":"output","Package":"debug/tparse-24","Output":" /usr/local/go/src/testing/testing.go:878 +0x650\n"}
40 | {"Time":"2018-12-16T21:21:52.635304-05:00","Action":"output","Package":"debug/tparse-24","Output":" testing.runTests.func1()\n"}
41 | {"Time":"2018-12-16T21:21:52.635309-05:00","Action":"output","Package":"debug/tparse-24","Output":" /usr/local/go/src/testing/testing.go:1119 +0xa8\n"}
42 | {"Time":"2018-12-16T21:21:52.635319-05:00","Action":"output","Package":"debug/tparse-24","Output":" testing.tRunner()\n"}
43 | {"Time":"2018-12-16T21:21:52.635329-05:00","Action":"output","Package":"debug/tparse-24","Output":" /usr/local/go/src/testing/testing.go:827 +0x162\n"}
44 | {"Time":"2018-12-16T21:21:52.635335-05:00","Action":"output","Package":"debug/tparse-24","Output":" testing.runTests()\n"}
45 | {"Time":"2018-12-16T21:21:52.635344-05:00","Action":"output","Package":"debug/tparse-24","Output":" /usr/local/go/src/testing/testing.go:1117 +0x4ee\n"}
46 | {"Time":"2018-12-16T21:21:52.63535-05:00","Action":"output","Package":"debug/tparse-24","Output":" testing.(*M).Run()\n"}
47 | {"Time":"2018-12-16T21:21:52.635356-05:00","Action":"output","Package":"debug/tparse-24","Output":" /usr/local/go/src/testing/testing.go:1034 +0x2ee\n"}
48 | {"Time":"2018-12-16T21:21:52.635362-05:00","Action":"output","Package":"debug/tparse-24","Output":" main.main()\n"}
49 | {"Time":"2018-12-16T21:21:52.63537-05:00","Action":"output","Package":"debug/tparse-24","Output":" _testmain.go:44 +0x221\n"}
50 | {"Time":"2018-12-16T21:21:52.635378-05:00","Action":"output","Package":"debug/tparse-24","Output":"==================\n"}
51 | {"Time":"2018-12-16T21:21:53.635797-05:00","Action":"output","Package":"debug/tparse-24","Output":"Found 1 data race(s)\n"}
52 | {"Time":"2018-12-16T21:21:53.637742-05:00","Action":"output","Package":"debug/tparse-24","Output":"FAIL\tdebug/tparse-24\t1.017s\n"}
53 | {"Time":"2018-12-16T21:21:53.637783-05:00","Action":"pass","Package":"debug/tparse-24","Elapsed":1.018}
--------------------------------------------------------------------------------
/tests/testdata/race/test_04.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2022-05-23T10:24:01.895489-04:00","Action":"run","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace"}
2 | {"Time":"2022-05-23T10:24:01.895869-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"=== RUN TestRace\n"}
3 | {"Time":"2022-05-23T10:24:01.895882-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"2\n"}
4 | {"Time":"2022-05-23T10:24:01.895886-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"3\n"}
5 | {"Time":"2022-05-23T10:24:01.896021-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"==================\n"}
6 | {"Time":"2022-05-23T10:24:01.896034-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"WARNING: DATA RACE\n"}
7 | {"Time":"2022-05-23T10:24:01.896039-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Read at 0x00c0000b0188 by goroutine 8:\n"}
8 | {"Time":"2022-05-23T10:24:01.896047-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/testing_test.TestRace.func1()\n"}
9 | {"Time":"2022-05-23T10:24:01.896053-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:55 +0x3c\n"}
10 | {"Time":"2022-05-23T10:24:01.896059-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"\n"}
11 | {"Time":"2022-05-23T10:24:01.896063-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Previous write at 0x00c0000b0188 by goroutine 7:\n"}
12 | {"Time":"2022-05-23T10:24:01.896066-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/testing_test.TestRace()\n"}
13 | {"Time":"2022-05-23T10:24:01.896069-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:53 +0x88\n"}
14 | {"Time":"2022-05-23T10:24:01.896075-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.tRunner()\n"}
15 | {"Time":"2022-05-23T10:24:01.896082-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x18c\n"}
16 | {"Time":"2022-05-23T10:24:01.896088-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*T).Run.func1()\n"}
17 | {"Time":"2022-05-23T10:24:01.896091-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x44\n"}
18 | {"Time":"2022-05-23T10:24:01.896094-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"\n"}
19 | {"Time":"2022-05-23T10:24:01.896097-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Goroutine 8 (running) created at:\n"}
20 | {"Time":"2022-05-23T10:24:01.8961-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/testing_test.TestRace()\n"}
21 | {"Time":"2022-05-23T10:24:01.896103-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:54 +0x70\n"}
22 | {"Time":"2022-05-23T10:24:01.896106-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.tRunner()\n"}
23 | {"Time":"2022-05-23T10:24:01.896109-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x18c\n"}
24 | {"Time":"2022-05-23T10:24:01.89612-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*T).Run.func1()\n"}
25 | {"Time":"2022-05-23T10:24:01.896123-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x44\n"}
26 | {"Time":"2022-05-23T10:24:01.896125-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"\n"}
27 | {"Time":"2022-05-23T10:24:01.896128-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Goroutine 7 (running) created at:\n"}
28 | {"Time":"2022-05-23T10:24:01.896131-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*T).Run()\n"}
29 | {"Time":"2022-05-23T10:24:01.896134-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x560\n"}
30 | {"Time":"2022-05-23T10:24:01.896137-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.runTests.func1()\n"}
31 | {"Time":"2022-05-23T10:24:01.89614-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1839 +0x94\n"}
32 | {"Time":"2022-05-23T10:24:01.896143-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.tRunner()\n"}
33 | {"Time":"2022-05-23T10:24:01.896146-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x18c\n"}
34 | {"Time":"2022-05-23T10:24:01.896149-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.runTests()\n"}
35 | {"Time":"2022-05-23T10:24:01.896152-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1837 +0x6c8\n"}
36 | {"Time":"2022-05-23T10:24:01.896155-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*M).Run()\n"}
37 | {"Time":"2022-05-23T10:24:01.896157-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1719 +0x878\n"}
38 | {"Time":"2022-05-23T10:24:01.89616-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" main.main()\n"}
39 | {"Time":"2022-05-23T10:24:01.896163-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" _testmain.go:47 +0x2fc\n"}
40 | {"Time":"2022-05-23T10:24:01.896166-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"==================\n"}
41 | {"Time":"2022-05-23T10:24:01.896169-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"3\n"}
42 | {"Time":"2022-05-23T10:24:01.896171-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"3\n"}
43 | {"Time":"2022-05-23T10:24:01.896178-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"5\n"}
44 | {"Time":"2022-05-23T10:24:01.896181-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.go:1312: race detected during execution of test\n"}
45 | {"Time":"2022-05-23T10:24:01.896199-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"--- FAIL: TestRace (0.00s)\n"}
46 | {"Time":"2022-05-23T10:24:01.896221-04:00","Action":"fail","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Elapsed":0}
47 | {"Time":"2022-05-23T10:24:01.896233-04:00","Action":"cont","Package":"github.com/mfridman/debug-go/testing"}
48 | {"Time":"2022-05-23T10:24:01.896243-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"=== CONT \n"}
49 | {"Time":"2022-05-23T10:24:01.896248-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" testing.go:1312: race detected during execution of test\n"}
50 | {"Time":"2022-05-23T10:24:01.896251-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"FAIL\n"}
51 | {"Time":"2022-05-23T10:24:01.896704-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"FAIL\tgithub.com/mfridman/debug-go/testing\t0.158s\n"}
52 | {"Time":"2022-05-23T10:24:01.896714-04:00","Action":"fail","Package":"github.com/mfridman/debug-go/testing","Elapsed":0.158}
53 |
--------------------------------------------------------------------------------
/tests/testdata/race/test_06.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2022-05-23T10:22:27.087816-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"testing: warning: no tests to run\n"}
2 | {"Time":"2022-05-23T10:22:27.088274-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"PASS\n"}
3 | {"Time":"2022-05-23T10:22:27.088645-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"2\n"}
4 | {"Time":"2022-05-23T10:22:27.089042-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"==================\n"}
5 | {"Time":"2022-05-23T10:22:27.089065-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"WARNING: DATA RACE\n"}
6 | {"Time":"2022-05-23T10:22:27.08908-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"Read at 0x00c0000b0178 by goroutine 7:\n"}
7 | {"Time":"2022-05-23T10:22:27.089102-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" github.com/mfridman/debug-go/testing_test.TestMain.func1()\n"}
8 | {"Time":"2022-05-23T10:22:27.089119-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:16 +0x3c\n"}
9 | {"Time":"2022-05-23T10:22:27.089138-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"\n"}
10 | {"Time":"2022-05-23T10:22:27.089151-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"Previous write at 0x00c0000b0178 by main goroutine:\n"}
11 | {"Time":"2022-05-23T10:22:27.089163-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" github.com/mfridman/debug-go/testing_test.TestMain()\n"}
12 | {"Time":"2022-05-23T10:22:27.089175-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:14 +0x98\n"}
13 | {"Time":"2022-05-23T10:22:27.089187-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" main.main()\n"}
14 | {"Time":"2022-05-23T10:22:27.089198-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" _testmain.go:47 +0x304\n"}
15 | {"Time":"2022-05-23T10:22:27.08921-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"\n"}
16 | {"Time":"2022-05-23T10:22:27.089222-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"Goroutine 7 (running) created at:\n"}
17 | {"Time":"2022-05-23T10:22:27.089256-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" github.com/mfridman/debug-go/testing_test.TestMain()\n"}
18 | {"Time":"2022-05-23T10:22:27.089269-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:15 +0x80\n"}
19 | {"Time":"2022-05-23T10:22:27.089282-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" main.main()\n"}
20 | {"Time":"2022-05-23T10:22:27.089293-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" _testmain.go:47 +0x304\n"}
21 | {"Time":"2022-05-23T10:22:27.089305-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"==================\n"}
22 | {"Time":"2022-05-23T10:22:27.089317-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"2\n"}
23 | {"Time":"2022-05-23T10:22:27.089338-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"2\n"}
24 | {"Time":"2022-05-23T10:22:27.08935-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"4\n"}
25 | {"Time":"2022-05-23T10:22:27.089364-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"5\n"}
26 | {"Time":"2022-05-23T10:22:27.10081-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"Found 1 data race(s)\n"}
27 | {"Time":"2022-05-23T10:22:27.101697-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"FAIL\tgithub.com/mfridman/debug-go/testing\t0.173s\n"}
28 | {"Time":"2022-05-23T10:22:27.101757-04:00","Action":"fail","Package":"github.com/mfridman/debug-go/testing","Elapsed":0.173}
29 |
--------------------------------------------------------------------------------
/tests/testdata/race/test_08.jsonl:
--------------------------------------------------------------------------------
1 | {"Time":"2022-05-23T10:28:13.239864-04:00","Action":"run","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace"}
2 | {"Time":"2022-05-23T10:28:13.24012-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"=== RUN TestRace\n"}
3 | {"Time":"2022-05-23T10:28:13.240135-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"2\n"}
4 | {"Time":"2022-05-23T10:28:13.240149-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"3\n"}
5 | {"Time":"2022-05-23T10:28:13.240422-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"==================\n"}
6 | {"Time":"2022-05-23T10:28:13.240433-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"WARNING: DATA RACE\n"}
7 | {"Time":"2022-05-23T10:28:13.240438-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Read at 0x00c0000a8f68 by goroutine 8:\n"}
8 | {"Time":"2022-05-23T10:28:13.240446-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/pkg/racey.RaceFunc.func1()\n"}
9 | {"Time":"2022-05-23T10:28:13.240453-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/pkg/racey/racey.go:13 +0x3c\n"}
10 | {"Time":"2022-05-23T10:28:13.240468-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"\n"}
11 | {"Time":"2022-05-23T10:28:13.240473-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Previous write at 0x00c0000a8f68 by goroutine 7:\n"}
12 | {"Time":"2022-05-23T10:28:13.240478-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/pkg/racey.RaceFunc()\n"}
13 | {"Time":"2022-05-23T10:28:13.240484-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/pkg/racey/racey.go:11 +0x88\n"}
14 | {"Time":"2022-05-23T10:28:13.240489-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/testing_test.TestRace()\n"}
15 | {"Time":"2022-05-23T10:28:13.240495-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:52 +0x28\n"}
16 | {"Time":"2022-05-23T10:28:13.240502-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.tRunner()\n"}
17 | {"Time":"2022-05-23T10:28:13.240509-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x18c\n"}
18 | {"Time":"2022-05-23T10:28:13.240515-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*T).Run.func1()\n"}
19 | {"Time":"2022-05-23T10:28:13.240519-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x44\n"}
20 | {"Time":"2022-05-23T10:28:13.240524-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"\n"}
21 | {"Time":"2022-05-23T10:28:13.240528-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Goroutine 8 (running) created at:\n"}
22 | {"Time":"2022-05-23T10:28:13.240533-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/pkg/racey.RaceFunc()\n"}
23 | {"Time":"2022-05-23T10:28:13.240537-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/pkg/racey/racey.go:12 +0x70\n"}
24 | {"Time":"2022-05-23T10:28:13.240562-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" github.com/mfridman/debug-go/testing_test.TestRace()\n"}
25 | {"Time":"2022-05-23T10:28:13.240566-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /Users/mfridman/src/github.com/mfridman/debug-go/testing/main_test.go:52 +0x28\n"}
26 | {"Time":"2022-05-23T10:28:13.240569-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.tRunner()\n"}
27 | {"Time":"2022-05-23T10:28:13.240572-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x18c\n"}
28 | {"Time":"2022-05-23T10:28:13.240575-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*T).Run.func1()\n"}
29 | {"Time":"2022-05-23T10:28:13.240578-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x44\n"}
30 | {"Time":"2022-05-23T10:28:13.240581-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"\n"}
31 | {"Time":"2022-05-23T10:28:13.240584-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"Goroutine 7 (running) created at:\n"}
32 | {"Time":"2022-05-23T10:28:13.240587-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*T).Run()\n"}
33 | {"Time":"2022-05-23T10:28:13.24059-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x560\n"}
34 | {"Time":"2022-05-23T10:28:13.240593-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.runTests.func1()\n"}
35 | {"Time":"2022-05-23T10:28:13.240596-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1839 +0x94\n"}
36 | {"Time":"2022-05-23T10:28:13.240599-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.tRunner()\n"}
37 | {"Time":"2022-05-23T10:28:13.240602-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x18c\n"}
38 | {"Time":"2022-05-23T10:28:13.240605-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.runTests()\n"}
39 | {"Time":"2022-05-23T10:28:13.240608-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1837 +0x6c8\n"}
40 | {"Time":"2022-05-23T10:28:13.240611-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.(*M).Run()\n"}
41 | {"Time":"2022-05-23T10:28:13.240614-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" /opt/homebrew/Cellar/go/1.18.1/libexec/src/testing/testing.go:1719 +0x878\n"}
42 | {"Time":"2022-05-23T10:28:13.240617-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" main.main()\n"}
43 | {"Time":"2022-05-23T10:28:13.24062-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" _testmain.go:47 +0x2fc\n"}
44 | {"Time":"2022-05-23T10:28:13.240623-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"==================\n"}
45 | {"Time":"2022-05-23T10:28:13.240626-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"3\n"}
46 | {"Time":"2022-05-23T10:28:13.240635-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"4\n"}
47 | {"Time":"2022-05-23T10:28:13.240638-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"5\n"}
48 | {"Time":"2022-05-23T10:28:13.240641-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":" testing.go:1312: race detected during execution of test\n"}
49 | {"Time":"2022-05-23T10:28:13.240651-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Output":"--- FAIL: TestRace (0.00s)\n"}
50 | {"Time":"2022-05-23T10:28:13.240655-04:00","Action":"fail","Package":"github.com/mfridman/debug-go/testing","Test":"TestRace","Elapsed":0}
51 | {"Time":"2022-05-23T10:28:13.240659-04:00","Action":"cont","Package":"github.com/mfridman/debug-go/testing"}
52 | {"Time":"2022-05-23T10:28:13.240664-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"=== CONT \n"}
53 | {"Time":"2022-05-23T10:28:13.240667-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":" testing.go:1312: race detected during execution of test\n"}
54 | {"Time":"2022-05-23T10:28:13.24067-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"FAIL\n"}
55 | {"Time":"2022-05-23T10:28:13.241273-04:00","Action":"output","Package":"github.com/mfridman/debug-go/testing","Output":"FAIL\tgithub.com/mfridman/debug-go/testing\t0.116s\n"}
56 | {"Time":"2022-05-23T10:28:13.241305-04:00","Action":"fail","Package":"github.com/mfridman/debug-go/testing","Elapsed":0.116}
57 |
--------------------------------------------------------------------------------