diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f96d4c1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + time: "08:00" + labels: + - "dependencies" + commit-message: + prefix: "feat" + include: "scope" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + time: "08:00" + labels: + - "dependencies" + commit-message: + prefix: "chore" + include: "scope" + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + time: "08:00" + labels: + - "dependencies" + commit-message: + prefix: "feat" + include: "scope" \ No newline at end of file diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml deleted file mode 100644 index 275e965..0000000 --- a/.github/workflows/goreleaser.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: goreleaser - -on: - push: - # run only against tags - tags: - - "*" - -permissions: - contents: write - # packages: write - # issues: write - -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - run: git fetch --force --tags - - uses: actions/setup-go@v3 - with: - go-version: ">=1.19.1" - cache: true - # More assembly might be required: Docker logins, GPG, etc. It all depends - # on your needs. - - uses: goreleaser/goreleaser-action@v2 - with: - # either 'goreleaser' (default) or 'goreleaser-pro' - distribution: goreleaser - version: latest - args: release --rm-dist - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution - # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..e90c8aa --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,25 @@ +name: golangci-lint +on: + push: + paths: + - '**.go' + branches: + - main + pull_request: + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v4 + with: + go-version: '1.19' + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.55.1 + args: -v --timeout=5m \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bddf285 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +junit2html diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index 511ad74..0000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# This is an example .goreleaser.yml file with some sensible defaults. -# Make sure to check the documentation at https://goreleaser.com -before: - hooks: - # You may remove this if you don't use go modules. - - go mod tidy - # you may remove this if you don't need go generate - - go generate ./... -builds: - - env: - - CGO_ENABLED=0 - goos: - - linux - - darwin -archives: - - replacements: - darwin: Darwin - linux: Linux - 386: i386 - amd64: x86_64 -checksum: - name_template: "checksums.txt" -snapshot: - name_template: "{{ incpatch .Version }}-next" -changelog: - sort: asc - filters: - exclude: - - "^docs:" - - "^test:" -# modelines, feel free to remove those if you don't want/use them: -# yaml-language-server: $schema=https://goreleaser.com/static/schema.json -# vim: set ts=2 sw=2 tw=0 fo=cnqoj -brews: - - homepage: "https://github.com/kitproj/junit2html" - description: "Crazy fast local dev loop." - folder: Formula - commit_author: - name: Alex Collins - email: alex_collins@intuit.com - tap: - owner: kitproj - name: junit2html diff --git a/Formula/junit2html.rb b/Formula/junit2html.rb deleted file mode 100644 index 53dc5be..0000000 --- a/Formula/junit2html.rb +++ /dev/null @@ -1,47 +0,0 @@ -# typed: false -# frozen_string_literal: true - -# This file was generated by GoReleaser. DO NOT EDIT. -class Junit2html < Formula - desc "Crazy fast local dev loop." - homepage "https://github.com/kitproj/junit2html" - version "0.1.0" - - on_macos do - if Hardware::CPU.intel? - url "https://github.com/kitproj/junit2html/releases/download/v0.1.0/junit2html_0.1.0_Darwin_x86_64.tar.gz" - sha256 "c8338b3f32e4069ae54051d88f2e6f90cece6a4820df49227b5d78cc37e92670" - - def install - bin.install "junit2html" - end - end - if Hardware::CPU.arm? - url "https://github.com/kitproj/junit2html/releases/download/v0.1.0/junit2html_0.1.0_Darwin_arm64.tar.gz" - sha256 "da3d8d58dbbdb057d0033f23c5a40bdeef41a7c48766df914fb0598fbd8f702d" - - def install - bin.install "junit2html" - end - end - end - - on_linux do - if Hardware::CPU.intel? - url "https://github.com/kitproj/junit2html/releases/download/v0.1.0/junit2html_0.1.0_Linux_x86_64.tar.gz" - sha256 "f6ed57332baa27d089cf70ca5e0ae17c81109a317e983ceac3750f5938df7dd3" - - def install - bin.install "junit2html" - end - end - if Hardware::CPU.arm? && Hardware::CPU.is_64_bit? - url "https://github.com/kitproj/junit2html/releases/download/v0.1.0/junit2html_0.1.0_Linux_arm64.tar.gz" - sha256 "7a120ebf0a8907bc12efe9790715d8bf77cc5e10fdab9179a92b7daffdabd8e1" - - def install - bin.install "junit2html" - end - end - end -end diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4109ebb --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +projectname?=junit2html + +export GOFLAGS := -mod=mod + +default: help + +.PHONY: help +help: ## list makefile targets + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: build +build: ## build golang binary + @go build -ldflags "-X main.version=$(shell git describe --abbrev=0 --tags --always)" -o $(projectname) + +.PHONY: install +install: ## install golang binary + @go install -ldflags "-X main.version=$(shell git describe --abbrev=0 --tags --always)" \ No newline at end of file diff --git a/README.md b/README.md index 81c609c..70d08fe 100644 --- a/README.md +++ b/README.md @@ -11,43 +11,15 @@ Convert Junit XML reports (`junit.xml`) into HTML reports using a single standal ![screenshot](screenshot.png) -## Install - -Like `jq`, `junit2html` is a tiny (8Mb) standalone binary. You can download it from the [releases page](https://github.com/kitproj/junit2html/releases/latest). - -If you're on MacOS, you can use `brew`: - -```bash -brew tap kitproj/junit2html --custom-remote https://github.com/kitproj/junit2html -brew install junit2html -``` - -Otherwise, you can use `curl`: - -```bash -curl -q https://raw.githubusercontent.com/kitproj/junit2html/main/install.sh | sh -``` - ## Usage -Here is an example that uses trap to always created the test report: +### Build ```bash -go install github.com/alexec/junit2html@latest - -trap 'go-junit-report < test.out > junit.xml && junit2html < junit.xml > test-report.html' EXIT - -go test -v -cover ./... 2>&1 > test.out +make build ``` -💡 Don't use pipes (i.e. `|`) in shell, pipes swallow exit codes. Use `<` and `>` which is POSIX compliant. - -## Test - -How to test this locally: - +### Run ```bash -go test -v -cover ./... 2>&1 > test.out -go-junit-report < test.out > junit.xml -go run . < junit.xml > test-report.html -``` +./junit2html < > +``` \ No newline at end of file diff --git a/example/coverage/coverage.go b/example/coverage/coverage.go deleted file mode 100644 index 378faa9..0000000 --- a/example/coverage/coverage.go +++ /dev/null @@ -1,11 +0,0 @@ -package example - -import "log" - -func foo() { - log.Println("foo") -} - -func bar() { - log.Println("bar") -} diff --git a/example/coverage/coverage_test.go b/example/coverage/coverage_test.go deleted file mode 100644 index ee26c1b..0000000 --- a/example/coverage/coverage_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package example - -import "testing" - -func TestCoverage(t *testing.T) { - foo() -} diff --git a/example/example.go b/example/example.go deleted file mode 100644 index ad13a4c..0000000 --- a/example/example.go +++ /dev/null @@ -1,11 +0,0 @@ -package example - -import "log" - -func ok() { - log.Println("ok") -} - -func kaboom() { - panic("kaboom!") -} diff --git a/example/example_test.go b/example/example_test.go deleted file mode 100644 index c412159..0000000 --- a/example/example_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package example - -import "testing" - -func TestPassing(t *testing.T) { - ok() -} - -func TestFailing(t *testing.T) { - t.Errorf("this test failed") -} - -func TestSkipped(t *testing.T) { - t.Skip("this test is skipped") -} - -func TestPanic(t *testing.T) { - kaboom() -} diff --git a/go.mod b/go.mod index c94d9ad..dbca6f4 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ -module github.com/alexec/junit2html +module github.com/redhat-appstudio-qe/junit2html -go 1.18 +go 1.19 -require github.com/jstemmer/go-junit-report v1.0.0 +require github.com/jstemmer/go-junit-report/v2 v2.0.0 diff --git a/go.sum b/go.sum index 58237d5..18a993a 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ -github.com/jstemmer/go-junit-report v1.0.0 h1:8X1gzZpR+nVQLAht+L/foqOeX2l9DTZoaIPbEQHxsds= -github.com/jstemmer/go-junit-report v1.0.0/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/jstemmer/go-junit-report/v2 v2.0.0 h1:bMZNO9B16VFn07tKyi4YJFIbZtVmJaa5Xakv9dcwK58= +github.com/jstemmer/go-junit-report/v2 v2.0.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ= diff --git a/install.sh b/install.sh deleted file mode 100755 index 3098d5f..0000000 --- a/install.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env sh -set -eux - -tag=$(curl --retry 3 -s "https://api.github.com/repos/kitproj/junit2html/releases/latest" | jq -r '.tag_name') -version=$(echo $tag | cut -c 2-) -url="https://github.com/kitproj/junit2html/releases/download/${tag}/kit_${version}_$(uname)_$(uname -m | sed 's/aarch64/arm64/').tar.gz" -curl --retry 3 -L $url | tar -zxvf - junit2html -chmod +x junit2html -sudo mv junit2html /usr/local/bin/junit2html diff --git a/main.go b/main.go index 31b7f7d..6671e91 100644 --- a/main.go +++ b/main.go @@ -1,97 +1,25 @@ package main import ( - _ "embed" "encoding/xml" "fmt" "os" - "strconv" - "strings" - "time" - "github.com/jstemmer/go-junit-report/formatter" + "github.com/jstemmer/go-junit-report/v2/junit" + "github.com/redhat-appstudio-qe/junit2html/pkg/convert" ) -//go:embed style.css -var styles string - -func printTest(s formatter.JUnitTestSuite, c formatter.JUnitTestCase) { - id := fmt.Sprintf("%s.%s.%s", s.Name, c.Classname, c.Name) - class, text := "passed", "Pass" - f := c.Failure - if f != nil { - class, text = "failed", "Fail" - } - k := c.SkipMessage - if k != nil { - class, text = "skipped", "Skip" - } - fmt.Printf("
\n", class, id) - fmt.Printf("%s %s\n", id, c.Name, text) - fmt.Printf("
\n") - if f != nil { - fmt.Printf("
%s
\n", f.Contents) - } else if k != nil { - fmt.Printf("
%s
\n", k.Message) - } - d, _ := time.ParseDuration(c.Time) - fmt.Printf("

%v

\n", d) - fmt.Printf("
\n") - fmt.Printf("
\n") -} - func main() { - suites := &formatter.JUnitTestSuites{} + suites := &junit.Testsuites{} err := xml.NewDecoder(os.Stdin).Decode(suites) if err != nil { panic(err) } - - fmt.Println("") - fmt.Println("") - fmt.Println("") - fmt.Println("") - fmt.Println("") - fmt.Println("") - failures, total := 0, 0 - for _, s := range suites.Suites { - failures += s.Failures - total += len(s.TestCases) - } - fmt.Printf("

%d of %d tests failed

\n", failures, total) - for _, s := range suites.Suites { - if s.Failures > 0 { - printSuiteHeader(s) - for _, c := range s.TestCases { - if f := c.Failure; f != nil { - printTest(s, c) - } - } - } - } - for _, s := range suites.Suites { - printSuiteHeader(s) - for _, c := range s.TestCases { - if c.Failure == nil { - printTest(s, c) - } - } + html, err := convert.Convert(suites) + if err != nil { + panic(err) } - fmt.Println("") - fmt.Println("") -} + fmt.Println(html) -func printSuiteHeader(s formatter.JUnitTestSuite) { - fmt.Println("

") - fmt.Println(s.Name) - for _, p := range s.Properties { - if strings.HasPrefix(p.Name, "coverage.") { - v, _ := strconv.ParseFloat(p.Value, 10) - fmt.Printf("%.0f%%\n", p.Name, v) - } - } - fmt.Println("

") } diff --git a/pkg/convert/convert.go b/pkg/convert/convert.go new file mode 100644 index 0000000..6c46dc7 --- /dev/null +++ b/pkg/convert/convert.go @@ -0,0 +1,128 @@ +package convert + +import ( + _ "embed" + "fmt" + "strconv" + "strings" + "time" + + "github.com/jstemmer/go-junit-report/v2/junit" +) + +//go:embed style.css +var styles string + +var output string + +func Convert(suites *junit.Testsuites) (string, error) { + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + failures, total := 0, 0 + for _, s := range suites.Suites { + failures += s.Failures + total += len(s.Testcases) + } + output += fmt.Sprintf("

%d of %d tests failed

\n", failures, total) + printLinkToReport(suites.Suites) + for _, s := range suites.Suites { + if s.Failures > 0 { + printSuiteHeader(s) + for _, c := range s.Testcases { + if f := c.Failure; f != nil { + printTest(s, c) + } + } + } + printGatherLinks(s) + } + + for _, s := range suites.Suites { + printSuiteHeader(s) + for _, c := range s.Testcases { + if c.Failure == nil { + printTest(s, c) + } + } + } + output += "" + output += "" + return output, nil +} + +func printTest(testSuite junit.Testsuite, testCase junit.Testcase) { + id := fmt.Sprintf("%s.%s.%s", testSuite.Name, testCase.Classname, testCase.Name) + class, text := "passed", "Pass" + failure := testCase.Failure + if failure != nil { + class, text = "failed", "Fail" + } + skipped := testCase.Skipped + if skipped != nil { + class, text = "skipped", "Skip" + } + + output += fmt.Sprintf("
\n", class, "div-"+id) + + output += fmt.Sprintf("\n", id, testCase.Name, text) + output += fmt.Sprintf("", id) + output += "
\n" + if failure != nil { + failure.Data = strings.ReplaceAll(failure.Data, ``, `"bool"`) + output += fmt.Sprintf("
Failure message: \n\n%s
\n", failure.Message) + output += fmt.Sprintf("
Failure data: \n\n%s
\n", failure.Data) + if testCase.SystemErr != nil { + testCase.SystemErr.Data = strings.ReplaceAll(testCase.SystemErr.Data, ``, `"bool"`) + output += fmt.Sprintf("
Log: \n\n%s
\n", testCase.SystemErr.Data) + } + } else if skipped != nil { + output += fmt.Sprintf("
%s
\n", skipped.Message) + } + d, _ := time.ParseDuration(testCase.Time) + output += fmt.Sprintf("

%v

\n", d) + output += "
\n" + output += "
\n" + +} + +func printSuiteHeader(s junit.Testsuite) { + output += "

" + output += s.Name + if s.Properties != nil { + for _, p := range *s.Properties { + if strings.HasPrefix(p.Name, "coverage.") { + v, _ := strconv.ParseFloat(p.Value, 32) + output += fmt.Sprintf("%.0f%%\n", p.Name, v) + } + } + + } + output += "

" +} + +func printGatherLinks(s junit.Testsuite) { + if s.Properties != nil { + for _, p := range *s.Properties { + if strings.Contains(p.Name, "gather") { + output += fmt.Sprintf("Link to %s artifacts\n", p.Value, p.Name) + } + } + } +} + +func printLinkToReport(suites []junit.Testsuite) { + for _, suite := range suites { + if suite.Properties != nil { + for _, p := range *suite.Properties { + if strings.Contains(p.Name, "html-report-link") { + output += fmt.Sprintf("Having trouble viewing this report? Click here to open it in another tab\n", p.Value) + } + } + } + } +} diff --git a/pkg/convert/style.css b/pkg/convert/style.css new file mode 100644 index 0000000..c8db71c --- /dev/null +++ b/pkg/convert/style.css @@ -0,0 +1,111 @@ +body { + font-family: "Helvetica", sans-serif; + margin: 16px 64px; + background-color: white; +} + +h4 { + padding: 4px 8px; +} + +a { + display: block; +} + +.coverage { + font-size: small; + font-weight: normal; +} + +label { + padding: 8px 8px; + color: black; + text-decoration: none; + display: block; + border: thin #eeeeee solid; +} + + +.passed label { + background-color: #dfd; +} + +.skipped label { + background-color: #eee; +} + +.failed label { + background-color: #fdd; +} + +.badge { + background-color: #888; + color: white; + border-radius: 4px; + padding: 2px 8px; + float: right; +} + +.passed .badge { + background-color: #5c5; +} + +.failed .badge { + background-color: #d66; +} + +.expando { + padding: 8px; + display: none; +} + +.content { + padding: 8px; + border-bottom: thin #eeeeee solid; + color: #444; + white-space: pre; + background-color: #EEE; + font-family: 'Courier New'; + text-wrap: wrap; +} + +.duration { + font-size: x-small; +} + +div:target .expando { + display: block +} + +.toggle-el { + /* padding: 2rem; */ + /* height: 240px; */ + background: white; + transition: all 0.2s ease; + opacity: 1; + margin-top: 1rem; + overflow: hidden; +} + +input[type=checkbox].hide-input:not(:checked) + .toggle-el { + height: 0; + opacity: 0; + padding-top: 0; + padding-bottom: 0; +} + +input.hide-input { + position: absolute; + left: -999em; +} + +label.toggle { + display: block; + cursor: pointer; + padding: 0.5em 1em; + font-size: 1rem; + color: #242424; + /* background: #b5b5b5; */ + border-radius: 3px; + user-select: none; +} \ No newline at end of file diff --git a/release b/release deleted file mode 100755 index 7a37325..0000000 --- a/release +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -go install github.com/ffurrer2/semver/v2/cmd/semver@latest - -git tag v$(semver next patch $(git tag|sort -V|tail -n1|cut -c 2-)) - -git push --tags \ No newline at end of file diff --git a/style.css b/style.css deleted file mode 100644 index ccf472e..0000000 --- a/style.css +++ /dev/null @@ -1,74 +0,0 @@ -body { - font-family: "Helvetica", sans-serif; - margin: 16px 64px -} - -h4 { - padding: 4px 8px; -} - - -.coverage { - font-size: small; - font-weight: normal; -} - -a { - padding: 8px 8px; - color: black; - text-decoration: none; - display: block; - border: thin #eeeeee solid; -} - - -.passed a { - background-color: #dfd; -} - -.skipped a { - background-color: #eee; -} - -.failed a { - background-color: #fdd; -} - -.badge { - background-color: #888; - color: white; - border-radius: 4px; - padding: 2px 8px; - float: right; -} - -.passed .badge { - background-color: #5c5; -} - -.skipped .badge { -} - -.failed .badge { - background-color: #d66; -} - -.expando { - padding: 8px; - display: none; -} - -.content { - padding: 16px 0; - border-bottom: thin #eeeeee solid; - color: #444; - white-space: pre; -} - -.duration { - font-size: x-small; -} - -div:target .expando { - display: block -} \ No newline at end of file diff --git a/test-report.html b/test-report.html deleted file mode 100644 index c2fc07b..0000000 --- a/test-report.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - -

2 of 5 tests failed

-

-github.com/alexec/junit2html/example -

-
-TestFailing Fail -
-
example_test.go:10: this test failed
-

0s

-
-
-
-TestPanic Fail -
-
panic: kaboom! -/usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1389 +0x24e -/usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1392 +0x39f -/usr/local/Cellar/go/1.18.1/libexec/src/runtime/panic.go:838 +0x207 -/Users/acollins8/go/src/github.com/alexec/junit2html/example/example.go:10 -/Users/acollins8/go/src/github.com/alexec/junit2html/example/example_test.go:18 +0x31 -/usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x102 -/usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x35f
-

0s

-
-
-

-github.com/alexec/junit2html/example -

-
-TestPassing Pass -
-

0s

-
-
-
-TestSkipped Skip -
-
example_test.go:14: this test is skipped
-

0s

-
-
-

-github.com/alexec/junit2html/example/coverage -50% -

-
-TestCoverage Pass -
-

0s

-
-
- - diff --git a/test.out b/test.out deleted file mode 100644 index 916af3f..0000000 --- a/test.out +++ /dev/null @@ -1,38 +0,0 @@ -? github.com/alexec/junit2html [no test files] -=== RUN TestPassing -2022/05/06 13:40:57 ok ---- PASS: TestPassing (0.00s) -=== RUN TestFailing - example_test.go:10: this test failed ---- FAIL: TestFailing (0.00s) -=== RUN TestSkipped - example_test.go:14: this test is skipped ---- SKIP: TestSkipped (0.00s) -=== RUN TestPanic ---- FAIL: TestPanic (0.00s) -panic: kaboom! [recovered] - panic: kaboom! - -goroutine 7 [running]: -testing.tRunner.func1.2({0x10fe520, 0x114a750}) - /usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1389 +0x24e -testing.tRunner.func1() - /usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1392 +0x39f -panic({0x10fe520, 0x114a750}) - /usr/local/Cellar/go/1.18.1/libexec/src/runtime/panic.go:838 +0x207 -github.com/alexec/junit2html/example.kaboom(...) - /Users/acollins8/go/src/github.com/alexec/junit2html/example/example.go:10 -github.com/alexec/junit2html/example.TestPanic(0x0?) - /Users/acollins8/go/src/github.com/alexec/junit2html/example/example_test.go:18 +0x31 -testing.tRunner(0xc000120820, 0x1129578) - /usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1439 +0x102 -created by testing.(*T).Run - /usr/local/Cellar/go/1.18.1/libexec/src/testing/testing.go:1486 +0x35f -FAIL github.com/alexec/junit2html/example 0.129s -=== RUN TestCoverage -2022/05/06 13:31:17 foo ---- PASS: TestCoverage (0.00s) -PASS -coverage: 50.0% of statements -ok github.com/alexec/junit2html/example/coverage (cached) coverage: 50.0% of statements -FAIL