registry.k8s.io/hack/tools/require-coverage/main.go

107 lines
3.3 KiB
Go

/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// A small utility to enforce code coverage levels
// hack/make-rules/test.sh && (cd ./hack/tools && go run ./require-coverage)
package main
import (
"fmt"
"os"
"golang.org/x/tools/cover"
"k8s.io/apimachinery/pkg/util/sets"
)
// TODO: instead of fully excluding files, maybe we should have a more
// flexible pattern of minimum coverage?
//
// For now the goal is to require 100% coverage for production serving code.
// See also: cmd/archeio/docs/testing.md
//
// Reviewers should be wary of approving additions to this list.
var knownFailingFiles = sets.NewString(
// this code is used only at development time and integration testing it
// is probably excessive
"k8s.io/registry.k8s.io/pkg/net/cloudcidrs/internal/ranges2go/main.go",
// TODO: this is reasonable to test but shy of 100% coverage, mostly error handling ...
"k8s.io/registry.k8s.io/pkg/net/cloudcidrs/internal/ranges2go/gen.go",
// geranos is not easily tested and is not in the blocking path in production
// we should still test it better
"k8s.io/registry.k8s.io/cmd/geranos/main.go",
"k8s.io/registry.k8s.io/cmd/geranos/ratelimitroundtrip.go",
"k8s.io/registry.k8s.io/cmd/geranos/s3uploader.go",
"k8s.io/registry.k8s.io/cmd/geranos/schemav1.go",
"k8s.io/registry.k8s.io/cmd/geranos/walkimages.go",
// We cover this with integration tests and including integration coverage
// here would mask a lack of unit test coverage.
"k8s.io/registry.k8s.io/cmd/archeio/main.go",
)
func main() {
fmt.Println("Checking coverage ...")
profiles, err := cover.ParseProfiles("./../../bin/all.cov")
if err != nil {
panic(err)
}
failedAny := false
needToRemove := []string{}
for _, profile := range profiles {
coverage := coverPercent(profile)
file := profile.FileName
if coverage < 100.0 {
if !knownFailingFiles.Has(file) {
failedAny = true
fmt.Printf("FAILED: %s %v%%\n", file, coverage)
} else {
fmt.Printf("IGNORE: %s %v%%\n", file, coverage)
}
} else {
if knownFailingFiles.Has(file) {
needToRemove = append(needToRemove, file)
}
fmt.Printf("PASSED: %s %v%%\n", file, coverage)
}
}
if failedAny {
fmt.Println("Failed required coverage levels for one or more go files")
os.Exit(-1)
} else {
fmt.Println("All code coverage either acceptable or ignored")
}
if len(needToRemove) > 0 {
fmt.Println("FAILED: The following files are now passing and must be removed frmo the ignored list:")
for _, file := range needToRemove {
fmt.Println(file)
}
os.Exit(-1)
}
}
func coverPercent(profile *cover.Profile) float64 {
totalStatements := 0
coveredStatements := 0
for _, block := range profile.Blocks {
totalStatements += block.NumStmt
if block.Count > 0 {
coveredStatements += block.NumStmt
}
}
return float64(coveredStatements) / float64(totalStatements) * 100
}