-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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 unit test coverage requirements for some packages #22401
Changes from 12 commits
85ea22e
3ac48ce
b2db338
56802c0
164d427
9803449
495eb4a
3901862
6e70b13
cbad492
68b8fab
d9b221e
07565b5
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 |
---|---|---|
|
@@ -10,12 +10,15 @@ | |
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"io" | ||
"log" | ||
"math" | ||
"os" | ||
"path/filepath" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
|
||
"golang.org/x/tools/cover" | ||
) | ||
|
@@ -99,12 +102,52 @@ func dumpProfiles(profiles []*cover.Profile, out io.Writer) { | |
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
func parseArgs() (mainOptions map[string]string, subCmd string, subArgs []string) { | ||
mainOptions = map[string]string{} | ||
for i := 1; i < len(os.Args); i++ { | ||
arg := os.Args[i] | ||
if arg == "" { | ||
break | ||
} | ||
if arg[0] == '-' { | ||
arg = strings.TrimPrefix(arg, "-") | ||
arg = strings.TrimPrefix(arg, "-") | ||
fields := strings.SplitN(arg, "=", 2) | ||
if len(fields) == 1 { | ||
mainOptions[fields[0]] = "1" | ||
} else { | ||
mainOptions[fields[0]] = fields[1] | ||
} | ||
} else { | ||
subCmd = arg | ||
subArgs = os.Args[i+1:] | ||
break | ||
} | ||
} | ||
return | ||
} | ||
|
||
func showUsage() { | ||
fmt.Printf(`Usage: %[1]s {command} [arguments] | ||
|
||
Commands: | ||
%[1]s merge ... | ||
%[1]s check coverage_file requirement_file.txt | ||
|
||
Arguments: | ||
{file-list} the file list | ||
|
||
Example: | ||
%[1]s merge {file-list} | ||
%[1]s check coverage.out requirement_file.txt | ||
|
||
`, "gocoverage") | ||
} | ||
|
||
func merge(args []string) { | ||
var merged []*cover.Profile | ||
|
||
for _, file := range flag.Args() { | ||
for _, file := range args { | ||
profiles, err := cover.ParseProfiles(file) | ||
if err != nil { | ||
log.Fatalf("failed to parse profile '%s': %v", file, err) | ||
|
@@ -116,3 +159,88 @@ func main() { | |
|
||
dumpProfiles(merged, os.Stdout) | ||
} | ||
|
||
func percentToInt64(percent string) int64 { | ||
value := strings.ReplaceAll(percent, "%", "") | ||
i, err := strconv.ParseFloat(value, 10) | ||
if err != nil { | ||
log.Fatalf("invalid percent: %s", percent) | ||
} | ||
return int64(i * 10) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some output will be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say this trick is difficult to understand, and I do not understand why it must be casted to IMO keep the original float "12.3" is clear enough, or unify the unit to natural unit (1.0 mean 100%, 0.02 mean 2%) |
||
} | ||
|
||
func profileCount(p *cover.Profile) (int64, int64) { | ||
blocks := p.Blocks | ||
var active, total int64 | ||
for i := range blocks { | ||
stmts := int64(blocks[i].NumStmt) | ||
total += stmts | ||
if blocks[i].Count > 0 { | ||
active += stmts | ||
} | ||
} | ||
return total, active | ||
} | ||
|
||
func checkPackages(args []string) { | ||
if len(args) != 2 { | ||
log.Fatalf("invalid arguments: %v", args) | ||
return | ||
} | ||
coverageFile, packagesFile := args[0], args[1] | ||
profiles, err := cover.ParseProfiles(coverageFile) | ||
if err != nil { | ||
log.Fatalf("failed to parse profile '%s': %v", coverageFile, err) | ||
} | ||
packagesRequirements := make(map[string]int64) | ||
packages, err := os.ReadFile(packagesFile) | ||
if err != nil { | ||
log.Fatalf("failed to read packages file '%s': %v", packagesFile, err) | ||
} | ||
lines := strings.Split(string(packages), "\n") | ||
for _, p := range lines { | ||
parts := strings.Split(p, "=") | ||
if len(parts) != 2 { | ||
continue | ||
} | ||
packagesRequirements[parts[0]] = percentToInt64(parts[1]) | ||
} | ||
packagesTotals := make(map[string]int64) | ||
packagesActives := make(map[string]int64) | ||
for _, p := range profiles { | ||
pkg := filepath.Dir(p.FileName) | ||
_, ok := packagesRequirements[pkg] | ||
if !ok { | ||
continue | ||
} | ||
total, active := profileCount(p) | ||
packagesTotals[pkg] += total | ||
packagesActives[pkg] += active | ||
} | ||
var failed bool | ||
for k, v := range packagesRequirements { | ||
actual := 100 * float64(packagesActives[k]) / float64(packagesTotals[k]) | ||
if v > int64(math.Floor(actual*10+0.5)) { | ||
log.Printf("package %s coverage is %.1f%%, required %.1f%%", k, actual, float64(v)/10.0) | ||
failed = true | ||
} | ||
} | ||
if failed { | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func main() { | ||
_, subCmd, subArgs := parseArgs() | ||
if subCmd == "" { | ||
showUsage() | ||
os.Exit(1) | ||
} | ||
|
||
switch subCmd { | ||
case "merge": | ||
merge(subArgs) | ||
case "check": | ||
checkPackages(subArgs) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
code.gitea.io/gitea/modules/activitypub=60.66% | ||
code.gitea.io/gitea/modules/analyze=73.91% | ||
code.gitea.io/gitea/modules/auth=60.29% | ||
code.gitea.io/gitea/modules/avatar=55.20% | ||
code.gitea.io/gitea/modules/base=92.96% | ||
code.gitea.io/gitea/modules/cache=14.5% | ||
code.gitea.io/gitea/modules/charset=56.67% | ||
code.gitea.io/gitea/modules/container=100.00% | ||
code.gitea.io/gitea/modules/context=58.91% | ||
code.gitea.io/gitea/modules/csv=100.00% | ||
code.gitea.io/gitea/modules/doctor=15.1% | ||
code.gitea.io/gitea/modules/emoji=83.3% | ||
code.gitea.io/gitea/modules/eventsource=53.88% | ||
code.gitea.io/gitea/modules/generate=0.00% | ||
code.gitea.io/gitea/modules/git=61.93% | ||
code.gitea.io/gitea/modules/gitgraph=75.77% | ||
code.gitea.io/gitea/modules/graceful=29.0% | ||
code.gitea.io/gitea/modules/hcaptcha=0.00% | ||
code.gitea.io/gitea/modules/highlight=67.14% | ||
code.gitea.io/gitea/modules/hostmatcher=79.34% | ||
code.gitea.io/gitea/modules/html=100.00% | ||
code.gitea.io/gitea/modules/httpcache=78.95% | ||
code.gitea.io/gitea/modules/httplib=67.86% | ||
code.gitea.io/gitea/modules/indexer=39.21% | ||
code.gitea.io/gitea/modules/issue=75.47% | ||
code.gitea.io/gitea/modules/json=43.75% | ||
code.gitea.io/gitea/modules/lfs=70.98% | ||
code.gitea.io/gitea/modules/log=69.41% | ||
code.gitea.io/gitea/modules/markup=53.32% | ||
code.gitea.io/gitea/modules/mcaptcha=0.00% | ||
code.gitea.io/gitea/modules/metrics=0.00% | ||
code.gitea.io/gitea/modules/migration=70.45% | ||
code.gitea.io/gitea/modules/mirror=16.67% | ||
code.gitea.io/gitea/modules/nosql=29.21% | ||
code.gitea.io/gitea/modules/notification=64.94% | ||
code.gitea.io/gitea/modules/options=48.74% | ||
code.gitea.io/gitea/modules/packages=70.63% | ||
code.gitea.io/gitea/modules/paginator=100.00% | ||
code.gitea.io/gitea/modules/password=66.18% | ||
code.gitea.io/gitea/modules/pprof=0.00% | ||
code.gitea.io/gitea/modules/private=13.40% | ||
code.gitea.io/gitea/modules/process=38.5% | ||
code.gitea.io/gitea/modules/proxy=17.2% | ||
code.gitea.io/gitea/modules/proxyprotocol=0.00% | ||
code.gitea.io/gitea/modules/public=53.26% | ||
code.gitea.io/gitea/modules/queue=39.17% | ||
code.gitea.io/gitea/modules/recaptcha=0.00% | ||
code.gitea.io/gitea/modules/references=80.50% | ||
code.gitea.io/gitea/modules/regexplru=65.22% | ||
code.gitea.io/gitea/modules/repository=49.68% | ||
code.gitea.io/gitea/modules/secret=64.00% | ||
code.gitea.io/gitea/modules/session=18.63% | ||
code.gitea.io/gitea/modules/setting=54.19% | ||
code.gitea.io/gitea/modules/sitemap=82.35% | ||
code.gitea.io/gitea/modules/ssh=45.98% | ||
code.gitea.io/gitea/modules/storage=57.14% | ||
code.gitea.io/gitea/modules/structs=54.22% | ||
code.gitea.io/gitea/modules/svg=94.4% | ||
code.gitea.io/gitea/modules/sync=100.00% | ||
code.gitea.io/gitea/modules/system=70.00% | ||
code.gitea.io/gitea/modules/templates=43.01% | ||
code.gitea.io/gitea/modules/test=68.83% | ||
code.gitea.io/gitea/modules/timeutil=80.80% | ||
code.gitea.io/gitea/modules/translation=60.70% | ||
code.gitea.io/gitea/modules/typesniffer=92.86% | ||
code.gitea.io/gitea/modules/updatechecker=0.00% | ||
code.gitea.io/gitea/modules/upload=73.85% | ||
code.gitea.io/gitea/modules/uri=27.78% | ||
code.gitea.io/gitea/modules/user=23.53% | ||
code.gitea.io/gitea/modules/util=72.93% | ||
code.gitea.io/gitea/modules/validation=79.4% | ||
code.gitea.io/gitea/modules/watcher=0.00% | ||
code.gitea.io/gitea/modules/web=76.76% | ||
code.gitea.io/gitea/modules/webhook=13.3% | ||
code.gitea.io/gitea/models/activities=58.48% | ||
code.gitea.io/gitea/models/admin=33.55% | ||
code.gitea.io/gitea/models/asymkey=41.40% | ||
code.gitea.io/gitea/models/auth=46.36% | ||
code.gitea.io/gitea/models/avatars=31.82% | ||
code.gitea.io/gitea/models/db=41.53% | ||
code.gitea.io/gitea/models/git=44.19% | ||
code.gitea.io/gitea/models/issues=57.31% | ||
code.gitea.io/gitea/models/organization=71.00% | ||
code.gitea.io/gitea/models/packages=71.66% | ||
code.gitea.io/gitea/models/perm=57.05% | ||
code.gitea.io/gitea/models/project=31.85% | ||
code.gitea.io/gitea/models/pull=36.13% | ||
code.gitea.io/gitea/models/repo=58.62% | ||
code.gitea.io/gitea/models/secret=24.56% | ||
code.gitea.io/gitea/models/system=56.16% | ||
code.gitea.io/gitea/models/unit=48.72% | ||
code.gitea.io/gitea/models/unittest=31.91% | ||
code.gitea.io/gitea/models/user=55.82% | ||
code.gitea.io/gitea/models/webhook=66.02% | ||
code.gitea.io/gitea/routers/api=60.66% | ||
code.gitea.io/gitea/routers/common=61.15% | ||
code.gitea.io/gitea/routers/install=5.7% | ||
code.gitea.io/gitea/routers/private=34.74% | ||
code.gitea.io/gitea/routers/utils=100.00% | ||
code.gitea.io/gitea/routers/web=28.95% | ||
code.gitea.io/gitea/services/agit=56.59% | ||
code.gitea.io/gitea/services/asymkey=36.73% | ||
code.gitea.io/gitea/services/attachment=60.87% | ||
code.gitea.io/gitea/services/auth=38.99% | ||
code.gitea.io/gitea/services/automerge=44.83% | ||
code.gitea.io/gitea/services/context=47.22% | ||
code.gitea.io/gitea/services/convert=73.5% | ||
code.gitea.io/gitea/services/cron=56.2% | ||
code.gitea.io/gitea/services/externalaccount=0.00% | ||
code.gitea.io/gitea/services/forms=36.5% | ||
code.gitea.io/gitea/services/gitdiff=70.52% | ||
code.gitea.io/gitea/services/issue=40.50% | ||
code.gitea.io/gitea/services/lfs=57.06% | ||
code.gitea.io/gitea/services/mailer=54.55% | ||
code.gitea.io/gitea/services/markup=100.00% | ||
code.gitea.io/gitea/services/migrations=35.97% | ||
code.gitea.io/gitea/services/mirror=29.83% | ||
code.gitea.io/gitea/services/org=49.02% | ||
code.gitea.io/gitea/services/packages=51.94% | ||
code.gitea.io/gitea/services/pull=43.34% | ||
code.gitea.io/gitea/services/release=45.09% | ||
code.gitea.io/gitea/services/repository=41.19% | ||
code.gitea.io/gitea/services/task=43.75% | ||
code.gitea.io/gitea/services/user=46.24% | ||
code.gitea.io/gitea/services/webhook=66.63% | ||
code.gitea.io/gitea/services/wiki=56.41% |
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.
Calls like this should be in the Makefile so that coverage can be produced locally as well. Please don't lock us in into actions with commands like this.