mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-06 22:18:28 +00:00
Capture coverage of integration tests
To capture the coverage also for the integration tests, a test only executing the cmd.Run function is used. The test always exits with code 0 and prints the real exit code to stdout. Otherwise no coverage report is generated. Those changes enable a more accurate coverage report for future contributions.
This commit is contained in:
@@ -51,10 +51,15 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
|
|
||||||
|
- name: Get aptly version
|
||||||
|
run: |
|
||||||
|
make version
|
||||||
|
go generate
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -82,9 +87,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
directory: ${{ runner.temp }}
|
directory: ${{ runner.temp }}
|
||||||
|
|
||||||
- name: Get aptly version
|
- name: Create temp dir
|
||||||
run: |
|
run: |
|
||||||
make version
|
echo "COVERAGE_DIR=$(mktemp -d)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Make
|
- name: Make
|
||||||
env:
|
env:
|
||||||
@@ -95,8 +100,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
make
|
make
|
||||||
|
|
||||||
|
- name: Merge code coverage
|
||||||
|
if: matrix.run_long_tests == 'yes'
|
||||||
|
run: |
|
||||||
|
go install github.com/wadey/gocovmerge@latest
|
||||||
|
~/go/bin/gocovmerge unit.out $COVERAGE_DIR/*.out > coverage.txt
|
||||||
|
|
||||||
- name: Upload code coverage
|
- name: Upload code coverage
|
||||||
if: matrix.run_long_tests
|
if: matrix.run_long_tests == 'yes'
|
||||||
uses: codecov/codecov-action@v2
|
uses: codecov/codecov-action@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ man/aptly.1.ronn
|
|||||||
system/env/
|
system/env/
|
||||||
|
|
||||||
# created by make build for release artifacts
|
# created by make build for release artifacts
|
||||||
|
VERSION
|
||||||
|
aptly.test
|
||||||
|
|
||||||
build/
|
build/
|
||||||
|
|
||||||
pgp/keyrings/aptly2*.gpg
|
pgp/keyrings/aptly2*.gpg
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
GOVERSION=$(shell go version | awk '{print $$3;}')
|
GOVERSION=$(shell go version | awk '{print $$3;}')
|
||||||
ifdef TRAVIS_TAG
|
|
||||||
TAG=$(TRAVIS_TAG)
|
|
||||||
else
|
|
||||||
TAG="$(shell git describe --tags --always)"
|
TAG="$(shell git describe --tags --always)"
|
||||||
endif
|
|
||||||
VERSION=$(shell echo $(TAG) | sed 's@^v@@' | sed 's@-@+@g')
|
VERSION=$(shell echo $(TAG) | sed 's@^v@@' | sed 's@-@+@g')
|
||||||
PACKAGES=context database deb files gpg http query swift s3 utils
|
PACKAGES=context database deb files gpg http query swift s3 utils
|
||||||
PYTHON?=python3
|
PYTHON?=python3
|
||||||
TESTS?=
|
TESTS?=
|
||||||
BINPATH?=$(GOPATH)/bin
|
BINPATH?=$(GOPATH)/bin
|
||||||
RUN_LONG_TESTS?=yes
|
RUN_LONG_TESTS?=yes
|
||||||
|
COVERAGE_DIR?=$(shell mktemp -d)
|
||||||
|
|
||||||
all: modules test bench check system-test
|
all: modules test bench check system-test
|
||||||
|
|
||||||
prepare:
|
prepare:
|
||||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.43.0
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.45.0
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
go mod download
|
go mod download
|
||||||
@@ -31,7 +28,8 @@ ifeq ($(RUN_LONG_TESTS), yes)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
install:
|
install:
|
||||||
go install -v -ldflags "-X main.Version=$(VERSION)"
|
go generate
|
||||||
|
go install -v
|
||||||
|
|
||||||
system/env: system/requirements.txt
|
system/env: system/requirements.txt
|
||||||
ifeq ($(RUN_LONG_TESTS), yes)
|
ifeq ($(RUN_LONG_TESTS), yes)
|
||||||
@@ -42,13 +40,15 @@ endif
|
|||||||
|
|
||||||
system-test: install system/env
|
system-test: install system/env
|
||||||
ifeq ($(RUN_LONG_TESTS), yes)
|
ifeq ($(RUN_LONG_TESTS), yes)
|
||||||
|
go generate
|
||||||
|
go test -v -coverpkg="./..." -c -tags testruncli
|
||||||
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
|
if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
|
||||||
if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
|
if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
|
||||||
PATH=$(BINPATH)/:$(PATH) && . system/env/bin/activate && APTLY_VERSION=$(VERSION) $(PYTHON) system/run.py --long $(TESTS)
|
PATH=$(BINPATH)/:$(PATH) && . system/env/bin/activate && APTLY_VERSION=$(VERSION) $(PYTHON) system/run.py --long $(TESTS) --coverage-dir $(COVERAGE_DIR)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test -v ./... -gocheck.v=true -race -coverprofile=coverage.txt -covermode=atomic
|
go test -v ./... -gocheck.v=true -coverprofile=unit.out
|
||||||
|
|
||||||
bench:
|
bench:
|
||||||
go test -v ./deb -run=nothing -bench=. -benchmem
|
go test -v ./deb -run=nothing -bench=. -benchmem
|
||||||
|
|||||||
+3
-2
@@ -2,8 +2,6 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
ctx "github.com/aptly-dev/aptly/context"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -11,6 +9,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
ctx "github.com/aptly-dev/aptly/context"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
|
|
||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/aptly-dev/aptly
|
module github.com/aptly-dev/aptly
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlekSi/pointer v1.0.0
|
github.com/AlekSi/pointer v1.0.0
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ import (
|
|||||||
|
|
||||||
"github.com/aptly-dev/aptly/aptly"
|
"github.com/aptly-dev/aptly/aptly"
|
||||||
"github.com/aptly-dev/aptly/cmd"
|
"github.com/aptly-dev/aptly/cmd"
|
||||||
|
|
||||||
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version variable, filled in at link time
|
//go:generate sh -c "make -s version | tr -d '\n' > VERSION"
|
||||||
|
//go:embed VERSION
|
||||||
var Version string
|
var Version string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
//go:build testruncli
|
||||||
|
// +build testruncli
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aptly-dev/aptly/aptly"
|
||||||
|
"github.com/aptly-dev/aptly/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func filterOutTestArgs(args []string) (out []string) {
|
||||||
|
for _, arg := range args {
|
||||||
|
if !strings.Contains(arg, "-test.coverprofile") {
|
||||||
|
out = append(out, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// redefine all the flags otherwise the go testing tool
|
||||||
|
// is not able to parse them ...
|
||||||
|
var _ = flag.Int("db-open-attempts", 10, "number of attempts to open DB if it's locked by other instance")
|
||||||
|
var _ = flag.Bool("dep-follow-suggests", false, "when processing dependencies, follow Suggests")
|
||||||
|
var _ = flag.Bool("dep-follow-source", false, "when processing dependencies, follow from binary to Source packages")
|
||||||
|
var _ = flag.Bool("dep-follow-recommends", false, "when processing dependencies, follow Recommends")
|
||||||
|
var _ = flag.Bool("dep-follow-all-variants", false, "when processing dependencies, follow a & b if dependency is 'a|b'")
|
||||||
|
var _ = flag.Bool("dep-verbose-resolve", false, "when processing dependencies, print detailed logs")
|
||||||
|
var _ = flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
|
||||||
|
var _ = flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
|
||||||
|
var _ = flag.String("gpg-provider", "", "PGP implementation (\"gpg\", \"gpg1\", \"gpg2\" for external gpg or \"internal\" for Go internal implementation)")
|
||||||
|
|
||||||
|
var _ = flag.String("cpuprofile", "", "write cpu profile to file")
|
||||||
|
var _ = flag.String("memprofile", "", "write memory profile to this file")
|
||||||
|
var _ = flag.String("memstats", "", "write memory stats periodically to this file")
|
||||||
|
var _ = flag.Duration("meminterval", 100*time.Millisecond, "memory stats dump interval")
|
||||||
|
|
||||||
|
var _ = flag.Bool("raw", false, "raw")
|
||||||
|
var _ = flag.String("sort", "false", "sort")
|
||||||
|
var _ = flag.Bool("json", false, "json")
|
||||||
|
|
||||||
|
func TestRunMain(t *testing.T) {
|
||||||
|
if Version == "" {
|
||||||
|
Version = "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
aptly.Version = Version
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
args := filterOutTestArgs(os.Args[1:])
|
||||||
|
root := cmd.RootCommand()
|
||||||
|
root.UsageLine = "aptly"
|
||||||
|
|
||||||
|
fmt.Printf("EXIT: %d\n", cmd.Run(root, args, true))
|
||||||
|
}
|
||||||
+30
-4
@@ -19,6 +19,8 @@ import urllib.request
|
|||||||
import pprint
|
import pprint
|
||||||
import socketserver
|
import socketserver
|
||||||
import http.server
|
import http.server
|
||||||
|
from uuid import uuid4
|
||||||
|
from pathlib import Path
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
|
|
||||||
@@ -264,6 +266,10 @@ class BaseTest(object):
|
|||||||
command = string.Template(command).substitute(params)
|
command = string.Template(command).substitute(params)
|
||||||
command = shlex.split(command)
|
command = shlex.split(command)
|
||||||
|
|
||||||
|
if command[0] == "aptly":
|
||||||
|
aptly_testing_bin = Path(__file__).parent / ".." / "aptly.test"
|
||||||
|
command = [str(aptly_testing_bin), f"-test.coverprofile={Path(self.coverage_dir) / self.__class__.__name__}-{uuid4()}.out", *command[1:]]
|
||||||
|
|
||||||
environ = os.environ.copy()
|
environ = os.environ.copy()
|
||||||
environ["LC_ALL"] = "C"
|
environ["LC_ALL"] = "C"
|
||||||
environ.update(self.environmentOverride)
|
environ.update(self.environmentOverride)
|
||||||
@@ -272,14 +278,34 @@ class BaseTest(object):
|
|||||||
def run_cmd(self, command, expected_code=0):
|
def run_cmd(self, command, expected_code=0):
|
||||||
try:
|
try:
|
||||||
proc = self._start_process(command, stdout=subprocess.PIPE)
|
proc = self._start_process(command, stdout=subprocess.PIPE)
|
||||||
output, _ = proc.communicate()
|
raw_output, _ = proc.communicate()
|
||||||
|
|
||||||
|
returncodes = [proc.returncode]
|
||||||
|
is_aptly_command = False
|
||||||
|
if isinstance(command, str):
|
||||||
|
is_aptly_command = command.startswith("aptly")
|
||||||
|
|
||||||
|
if isinstance(command, list):
|
||||||
|
is_aptly_command = command[0] == "aptly"
|
||||||
|
|
||||||
|
if is_aptly_command:
|
||||||
|
# remove the last two rows as go tests always print PASS/FAIL and coverage in those
|
||||||
|
# two lines. This would otherwise fail the tests as they would not match gold
|
||||||
|
output, _, returncode = re.findall(r"((.|\n)*)EXIT: (\d)\n.*\ncoverage: .*", raw_output.decode("utf-8"))[0]
|
||||||
|
|
||||||
|
output = output.encode()
|
||||||
|
returncodes.append(int(returncode))
|
||||||
|
|
||||||
|
else:
|
||||||
|
output = raw_output
|
||||||
|
|
||||||
if expected_code is not None:
|
if expected_code is not None:
|
||||||
if proc.returncode != expected_code:
|
if expected_code not in returncodes:
|
||||||
raise Exception("exit code %d != %d (output: %s)" % (
|
raise Exception("exit code %d != %d (output: %s)" % (
|
||||||
proc.returncode, expected_code, output))
|
proc.returncode, expected_code, raw_output))
|
||||||
return output
|
return output
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception("Running command %s failed: %s" %
|
raise Exception("Running command '%s' failed: %s" %
|
||||||
(command, str(e)))
|
(command, str(e)))
|
||||||
|
|
||||||
def gold_processor(self, gold):
|
def gold_processor(self, gold):
|
||||||
|
|||||||
+12
-2
@@ -7,6 +7,7 @@ import inspect
|
|||||||
import fnmatch
|
import fnmatch
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
from tempfile import mkdtemp
|
||||||
import traceback
|
import traceback
|
||||||
import random
|
import random
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -42,7 +43,7 @@ def walk_modules(package):
|
|||||||
yield importlib.import_module(package + "." + name)
|
yield importlib.import_module(package + "." + name)
|
||||||
|
|
||||||
|
|
||||||
def run(include_long_tests=False, capture_results=False, tests=None, filters=None):
|
def run(include_long_tests=False, capture_results=False, tests=None, filters=None, coverage_dir=None):
|
||||||
"""
|
"""
|
||||||
Run system test.
|
Run system test.
|
||||||
"""
|
"""
|
||||||
@@ -51,6 +52,8 @@ def run(include_long_tests=False, capture_results=False, tests=None, filters=Non
|
|||||||
fails = []
|
fails = []
|
||||||
numTests = numFailed = numSkipped = 0
|
numTests = numFailed = numSkipped = 0
|
||||||
lastBase = None
|
lastBase = None
|
||||||
|
if not coverage_dir:
|
||||||
|
coverage_dir = mkdtemp(suffix="aptly-coverage")
|
||||||
|
|
||||||
for test in tests:
|
for test in tests:
|
||||||
for testModule in walk_modules(test):
|
for testModule in walk_modules(test):
|
||||||
@@ -95,6 +98,7 @@ def run(include_long_tests=False, capture_results=False, tests=None, filters=Non
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
t.captureResults = capture_results
|
t.captureResults = capture_results
|
||||||
|
t.coverage_dir = coverage_dir
|
||||||
t.test()
|
t.test()
|
||||||
except Exception:
|
except Exception:
|
||||||
numFailed += 1
|
numFailed += 1
|
||||||
@@ -110,6 +114,8 @@ def run(include_long_tests=False, capture_results=False, tests=None, filters=Non
|
|||||||
if lastBase is not None:
|
if lastBase is not None:
|
||||||
lastBase.shutdown_class()
|
lastBase.shutdown_class()
|
||||||
|
|
||||||
|
print("COVERAGE_RESULTS: %s" % coverage_dir)
|
||||||
|
|
||||||
print("TESTS: %d SUCCESS: %d FAIL: %d SKIP: %d" % (
|
print("TESTS: %d SUCCESS: %d FAIL: %d SKIP: %d" % (
|
||||||
numTests, numTests - numFailed, numFailed, numSkipped))
|
numTests, numTests - numFailed, numFailed, numSkipped))
|
||||||
|
|
||||||
@@ -149,6 +155,7 @@ if __name__ == "__main__":
|
|||||||
random.seed()
|
random.seed()
|
||||||
include_long_tests = False
|
include_long_tests = False
|
||||||
capture_results = False
|
capture_results = False
|
||||||
|
coverage_dir = None
|
||||||
tests = None
|
tests = None
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
|
|
||||||
@@ -157,6 +164,9 @@ if __name__ == "__main__":
|
|||||||
include_long_tests = True
|
include_long_tests = True
|
||||||
elif args[0] == "--capture":
|
elif args[0] == "--capture":
|
||||||
capture_results = True
|
capture_results = True
|
||||||
|
elif args[0] == "--coverage-dir":
|
||||||
|
coverage_dir = args[1]
|
||||||
|
args = args[1:]
|
||||||
|
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
|
|
||||||
@@ -169,4 +179,4 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
filters.append(arg)
|
filters.append(arg)
|
||||||
|
|
||||||
run(include_long_tests, capture_results, tests, filters)
|
run(include_long_tests, capture_results, tests, filters, coverage_dir)
|
||||||
|
|||||||
Reference in New Issue
Block a user