Merge pull request #12579 from hashicorp/ci-missing-packages-oss

ci: ensure package coverage of test-core
This commit is contained in:
Seth Hoenig
2022-04-15 08:11:41 -05:00
committed by GitHub
6 changed files with 260 additions and 29 deletions

View File

@@ -42,6 +42,7 @@ jobs:
cache-key-suffix: -checks
- name: Run make check
run: |
make missing
make bootstrap
make check
compile:
@@ -87,42 +88,51 @@ jobs:
fail-fast: false
matrix:
pkg:
- acl
- acl/...
- client
- client/allocdir
- client/allochealth
- client/allocrunner
- client/allocwatcher
- client/config
- client/consul
- client/devicemanager
- client/dynamicplugins
- client/fingerprint
- client/allocdir/...
- client/allochealth/...
- client/allocrunner/...
- client/allocwatcher/...
- client/config/...
- client/consul/...
- client/devicemanager/...
- client/dynamicplugins/...
- client/fingerprint/...
- client/interfaces/...
- client/lib/...
- client/logmon
- client/pluginmanager
- client/state
- client/stats
- client/structs
- client/taskenv
- client/logmon/...
- client/pluginmanager/...
- client/servers/...
- client/serviceregistration/...
- client/state/...
- client/stats/...
- client/structs/...
- client/taskenv/...
- command
- command/agent
- drivers/docker
- drivers/exec
- drivers/java
- drivers/rawexec
- command/agent/...
- command/raft_tools/...
- drivers/docker/...
- drivers/exec/...
- drivers/java/...
- drivers/mock/...
- drivers/rawexec/...
- drivers/shared/...
- drivers/qemu/...
- helper/...
- internal/...
- jobspec/...
- lib/...
- nomad
- nomad/deploymentwatcher
- nomad/stream
- nomad/structs
- nomad/volumewatcher
- nomad/deploymentwatcher/...
- nomad/drainer/...
- nomad/state/...
- nomad/stream/...
- nomad/structs/...
- nomad/volumewatcher/...
- plugins/...
- scheduler/...
- testutil
- testutil/...
steps:
- uses: actions/checkout@v2
- uses: magnetikonline/action-golang-cache@v1

View File

@@ -410,6 +410,7 @@ ui-screenshots-local:
@echo "==> Collecting UI screenshots (local)..."
@cd scripts/screenshots/src && SCREENSHOTS_DIR="../screenshots" node index.js
.PHONY: version
version:
ifneq (,$(wildcard version/version_ent.go))
@$(CURDIR)/scripts/version.sh version/version.go version/version_ent.go
@@ -417,4 +418,8 @@ else
@$(CURDIR)/scripts/version.sh version/version.go version/version.go
endif
.PHONY: version
.PHONY: missing
missing:
@echo "==> Checking for packages not being tested ..."
@go run -modfile tools/go.mod tools/missing/main.go \
.github/workflows/test-core.yaml

View File

@@ -2,6 +2,16 @@ module github.com/hashicorp/nomad/tools
go 1.17
require github.com/aws/aws-sdk-go v1.37.26
require (
github.com/aws/aws-sdk-go v1.37.26
github.com/stretchr/testify v1.7.1
gophers.dev/pkgs/ignore v0.3.1
gopkg.in/yaml.v2 v2.2.8
)
require github.com/jmespath/go-jmespath v0.4.0 // indirect
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

View File

@@ -10,6 +10,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -20,6 +22,11 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gophers.dev/pkgs/ignore v0.3.1 h1:MmNywpk5VAxWQ7/Yz9E1NmEEHLTh0hRWLiHENdVy4Ls=
gophers.dev/pkgs/ignore v0.3.1/go.mod h1:HwTn4Fc9oicp2TxFV/8P0CswwfsVQczZvG6ZTy2TRXA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

178
tools/missing/main.go Normal file
View File

@@ -0,0 +1,178 @@
package main
import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"gophers.dev/pkgs/ignore"
"gopkg.in/yaml.v2"
)
func main() {
if err := run(os.Args[1:]); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "FAIL: %s\n", err)
os.Exit(1)
}
}
type YamlFile struct {
Jobs struct {
TestPackages struct {
Strategy struct {
Matrix struct {
Packages []string `yaml:"pkg"`
} `yaml:"matrix"`
} `yaml:"strategy"`
} `yaml:"tests-pkgs"`
} `yaml:"jobs"`
}
func run(args []string) error {
if len(args) != 1 {
return errors.New("requires filename")
}
f, err := os.Open(args[0])
if err != nil {
return err
}
defer ignore.Close(f)
coverage, err := inMatrix(f)
if err != nil {
return err
}
packages, err := inCode(".")
if err != nil {
return err
}
var isMissing []string
for _, pkg := range packages {
if !isCovered(coverage, pkg) {
isMissing = append(isMissing, pkg)
}
}
sort.Strings(isMissing)
for _, pkg := range isMissing {
_, _ = fmt.Fprintf(os.Stderr, "missing: %s\n", pkg)
}
if len(isMissing) > 0 {
return fmt.Errorf("detected %d packages not tested", len(isMissing))
}
return nil
}
// isCovered returns true if pkg is covered by a package in coverage.
func isCovered(coverage []string, pkg string) bool {
for _, p := range coverage {
if isCoveredOne(p, pkg) {
return true
}
}
return false
}
// isCoveredOne returns true if p covers pkg.
//
// p may be a complete path, or a prefix ending with recursive '...'
func isCoveredOne(p string, pkg string) bool {
if p == pkg {
return true
}
if strings.HasSuffix(p, "/...") {
prefix := strings.TrimSuffix(p, "/...")
if strings.HasPrefix(pkg, prefix) {
return true
}
}
return false
}
func inMatrix(r io.Reader) ([]string, error) {
var yFile YamlFile
if err := yaml.NewDecoder(r).Decode(&yFile); err != nil {
return nil, err
}
p := yFile.Jobs.TestPackages.Strategy.Matrix.Packages
return p, nil
}
type nothing struct{}
var null = nothing{}
// uninteresting lists remaining packages that contain Go code but still
// do not need to be covered by test cases.
var uninteresting = []string{
// module
"api",
// main
".",
// testing helpers
"ci",
"client/testutil",
"client/vaultclient",
"e2e",
"nomad/mock",
"plugins/csi/fake",
// not core code
"demo",
"tools",
"version",
}
func skip(p string) bool {
for _, prefix := range uninteresting {
if strings.HasPrefix(p, prefix) {
return true
}
}
return false
}
func inCode(root string) ([]string, error) {
m := map[string]nothing{}
err := filepath.Walk(root, func(path string, info fs.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if skip(path) {
return nil
}
if ext := filepath.Ext(path); ext == ".go" {
m[filepath.Dir(path)] = null
}
return nil
})
if err != nil {
return nil, err
}
delete(m, ".") // package main
var packages []string
for p := range m {
packages = append(packages, p)
}
sort.Strings(packages)
return packages, nil
}

View File

@@ -0,0 +1,21 @@
package main
import (
"testing"
"github.com/stretchr/testify/require"
)
func Test_isCoveredOne(t *testing.T) {
try := func(p string, exp bool) {
result := isCoveredOne(p, "foo/bar")
require.Equal(t, exp, result)
}
try("baz", false)
try("foo", false)
try("foo/bar/baz", false)
try("foo/bar", true)
try("foo/bar/...", true)
try("foo/...", true)
try("abc/...", false)
}