-
Notifications
You must be signed in to change notification settings - Fork 52
Add code coverage #961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add code coverage #961
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -270,6 +270,7 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -eu | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docker buildx bake frontend | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "${TEST_SUITE}" = "other" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -283,19 +284,64 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docker buildx bake worker | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_SUITE: ${{ matrix.suite }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Run integration tests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Run integration tests (with coverage tracking) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -ex | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -n "${TEST_SUITE}" ] && [ ! "${TEST_SUITE}" = "other" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mkdir -p coverage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # The frontend covdata files (covmeta/covcounters) are written by the test harness | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # (writeFrontendCovdata) on the RUNNER filesystem. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export DALEC_FRONTEND_GOCOVERDIR="${GITHUB_WORKSPACE}/coverage/frontend-${TEST_SUITE}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rm -rf "${DALEC_FRONTEND_GOCOVERDIR}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mkdir -p "${DALEC_FRONTEND_GOCOVERDIR}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| skip="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -n "${TEST_SUITE}" ] && [ "${TEST_SUITE}" != "other" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run="-run=${TEST_SUITE}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -n "${TEST_SKIP}" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| skip="-skip=${TEST_SKIP}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| go test -timeout=59m -v -json ${run} ${skip} ./test | go run ./cmd/test2json2gha --slow 120s --logdir /tmp/testlogs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| go test -timeout=59m -v -json \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -covermode=atomic -coverpkg=./... \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -coverprofile="coverage/integration-${TEST_SUITE}.out" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ${run} ${skip} ./test \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | go run ./cmd/test2json2gha --slow 120s --logdir /tmp/testlogs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Convert frontend covdata -> legacy coverprofile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! ls "${DALEC_FRONTEND_GOCOVERDIR}"/covmeta.* >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::group::frontend coverage debug" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "DALEC_FRONTEND_GOCOVERDIR=${DALEC_FRONTEND_GOCOVERDIR}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Contents:" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ls -la "${DALEC_FRONTEND_GOCOVERDIR}" || true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Searching workspace for covmeta/covcounters..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| find "${GITHUB_WORKSPACE}" \( -name 'covmeta.*' -o -name 'covcounters.*' \) 2>/dev/null | head -n 200 || true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::endgroup::" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::error::No frontend coverage covmeta.* found in ${DALEC_FRONTEND_GOCOVERDIR} (frontend coverage not collected)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| go tool covdata textfmt \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -i="${DALEC_FRONTEND_GOCOVERDIR}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -o="coverage/frontend-${TEST_SUITE}.out" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_SUITE: ${{ matrix.suite }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_SKIP: ${{ matrix.skip }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Upload integration coverage profile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: coverage-integration-${{ matrix.suite }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| coverage/integration-${{ matrix.suite }}.out | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| coverage/frontend-${{ matrix.suite }}.out | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if-no-files-found: ignore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retention-days: 7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Get traces | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -354,8 +400,27 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cache: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: download deps | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: go mod download | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Run unit tests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: go test -v --test.short --json ./... | go run ./cmd/test2json2gha | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Run unit tests (with coverage tracking) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -eux | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mkdir -p coverage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pkgs="$(go list ./... | grep -v '/test$' | grep -v '/test/' )" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| go test -v --test.short --json \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -covermode=atomic \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -coverprofile="coverage/unit.out" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ${pkgs} \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | go run ./cmd/test2json2gha | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Upload unit coverage profile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: coverage-unit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: coverage/unit.out | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if-no-files-found: ignore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retention-days: 7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e2e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-22.04 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -443,3 +508,82 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: ${{ steps.dump-logs.outputs.DOCKERD_LOG_PATH }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retention-days: 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| coverage-report: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-22.04 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| needs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - unit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - integration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - integration | |
| - integration | |
| if: ${{ always() }} |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing version comment for the download-artifact action. All other action uses in this workflow include version comments (e.g., # v6.0.0). Please add a version comment to maintain consistency with the established convention in this file.
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| with: | |
| name: coverage-unit | |
| path: coverage | |
| - name: Download integration coverage artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # download-artifact pinned SHA | |
| with: | |
| name: coverage-unit | |
| path: coverage | |
| - name: Download integration coverage artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # download-artifact pinned SHA |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing version comment for the download-artifact action. All other action uses in this workflow include version comments (e.g., # v6.0.0). Please add a version comment to maintain consistency with the established convention in this file.
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| with: | |
| name: coverage-unit | |
| path: coverage | |
| - name: Download integration coverage artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.1.8 | |
| with: | |
| name: coverage-unit | |
| path: coverage | |
| - name: Download integration coverage artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.1.8 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "compress/gzip" | ||
| "context" | ||
| "errors" | ||
| "strings" | ||
|
|
||
| "runtime/coverage" | ||
|
|
||
| gwclient "github.com/moby/buildkit/frontend/gateway/client" | ||
| "github.com/project-dalec/dalec/internal/frontendcoverage" | ||
| ) | ||
|
|
||
| func isNoMetaErr(err error) bool { | ||
| if err == nil { | ||
| return false | ||
| } | ||
| // runtime/coverage: "no meta-data available (binary not built with -cover?)" | ||
| return strings.Contains(strings.ToLower(err.Error()), "no meta-data available") | ||
| } | ||
|
|
||
| // Enabled per solve via SolveRequest.FrontendOpt[frontendcoverage.OptKey]="1" | ||
| func wantFrontendCoverage(c gwclient.Client) bool { | ||
| return frontendcoverage.Want(c.BuildOpts().Opts) | ||
| } | ||
|
|
||
| func gzipBytes(in []byte) ([]byte, error) { | ||
| var buf bytes.Buffer | ||
| zw := gzip.NewWriter(&buf) | ||
| if _, err := zw.Write(in); err != nil { | ||
| _ = zw.Close() | ||
| return nil, err | ||
| } | ||
| if err := zw.Close(); err != nil { | ||
| return nil, err | ||
| } | ||
| return buf.Bytes(), nil | ||
| } | ||
|
|
||
| var frontendCoverageCollector = collectFrontendCoveragePayload | ||
|
|
||
| func collectFrontendCoveragePayload() (*frontendcoverage.Payload, error) { | ||
| var metaBuf, ctrBuf bytes.Buffer | ||
|
|
||
| if err := coverage.WriteMeta(&metaBuf); err != nil { | ||
| if isNoMetaErr(err) { | ||
| return nil, nil | ||
| } | ||
| return nil, err | ||
| } | ||
| if err := coverage.WriteCounters(&ctrBuf); err != nil { | ||
| if isNoMetaErr(err) { | ||
| return nil, nil | ||
| } | ||
| return nil, err | ||
| } | ||
|
|
||
| metaGz, err := gzipBytes(metaBuf.Bytes()) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| ctrGz, err := gzipBytes(ctrBuf.Bytes()) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Avoid cross-solve accumulation if the frontend process is reused. | ||
| // Only works for binaries built with -cover (and typically atomic counters). | ||
| _ = coverage.ClearCounters() | ||
|
|
||
| return &frontendcoverage.Payload{ | ||
| MetaGz: metaGz, | ||
| CountersGz: ctrGz, | ||
| }, nil | ||
| } | ||
|
|
||
| func wrapWithCoverage(next gwclient.BuildFunc) gwclient.BuildFunc { | ||
| return func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) { | ||
| res, err := next(ctx, c) | ||
| if !wantFrontendCoverage(c) { | ||
| return res, err | ||
| } | ||
|
|
||
| payload, covErr := frontendCoverageCollector() | ||
| if covErr != nil { | ||
| if err != nil { | ||
| return res, errors.Join(err, covErr) | ||
| } | ||
| return res, covErr | ||
| } | ||
| if payload == nil { | ||
| return res, err | ||
| } | ||
|
|
||
| if err != nil { | ||
| errWithCoverage, attachErr := payload.AttachToError(err) | ||
| if attachErr != nil { | ||
| return res, errors.Join(err, attachErr) | ||
| } | ||
| return res, errWithCoverage | ||
| } | ||
|
|
||
| if res == nil { | ||
| res = gwclient.NewResult() | ||
| } | ||
| payload.AttachToResult(res) | ||
|
|
||
| return res, nil | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.