From 5b5b4f7b2c09c073e067562ed66b8b94341eda35 Mon Sep 17 00:00:00 2001 From: Sean Kane <68240067+seankane-msft@users.noreply.github.com> Date: Mon, 26 Jul 2021 11:32:30 -0400 Subject: [PATCH] [EngSys] adding coverage tools (#15076) * adding coverage tools * forgot to include 'go run' * need to find where the script is running from * fixing script * fixing passing of coverage goal value * removing flag from build-test * fixing tools script for multiple files * updating ci.yml files, polishing script * removing coverage goals * undoing changes * dropping core down, fixing to.go lint * creating a config file for the SDK and using that instead of putting values in ci.yml * putting coverage in a script * fixing location of script * fixing scope * fixing powershell portions, float instead of &float64 * forcing pipelines * Update tools/internal/coverage/main.go Co-authored-by: Ben Broderick Phillips Co-authored-by: Ben Broderick Phillips --- eng/config.json | 12 ++ .../templates/jobs/archetype-sdk-client.yml | 2 + eng/pipelines/templates/steps/build-test.yml | 13 +- eng/scripts/create_coverage.ps1 | 19 +++ eng/scripts/get_module_dirs.ps1 | 2 +- eng/scripts/get_test_dirs.ps1 | 2 +- sdk/to/ci.yml | 2 +- sdk/to/to.go | 14 +- tools/internal/coverage/go.mod | 3 + tools/internal/coverage/main.go | 137 ++++++++++++++++++ 10 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 eng/config.json create mode 100644 eng/scripts/create_coverage.ps1 create mode 100644 tools/internal/coverage/go.mod create mode 100644 tools/internal/coverage/main.go diff --git a/eng/config.json b/eng/config.json new file mode 100644 index 000000000000..d3a9dbdc69cd --- /dev/null +++ b/eng/config.json @@ -0,0 +1,12 @@ +{ + "Packages": [ + { + "Name": "azidentity", + "CoverageGoal": 0.68 + }, + { + "Name": "azcore", + "CoverageGoal": 0.68 + } + ] +} \ No newline at end of file diff --git a/eng/pipelines/templates/jobs/archetype-sdk-client.yml b/eng/pipelines/templates/jobs/archetype-sdk-client.yml index 609be1d1463d..64c2c88cc33a 100644 --- a/eng/pipelines/templates/jobs/archetype-sdk-client.yml +++ b/eng/pipelines/templates/jobs/archetype-sdk-client.yml @@ -1,5 +1,6 @@ parameters: ServiceDirectory: '' + CoverageGoal: 0.95 RunTests: false stages: @@ -54,6 +55,7 @@ stages: Scope: $(SCOPE) Image: $(vm.image) GoVersion: $(go.version) + CoverageGoal: ${{ parameters.CoverageGoal }} RunTests: ${{ parameters.RunTests }} - job: Analyze diff --git a/eng/pipelines/templates/steps/build-test.yml b/eng/pipelines/templates/steps/build-test.yml index 3182b3ca10b0..a743df0da820 100644 --- a/eng/pipelines/templates/steps/build-test.yml +++ b/eng/pipelines/templates/steps/build-test.yml @@ -47,18 +47,7 @@ steps: env: GO111MODULE: 'on' - - pwsh: | - $coverageFiles = [Collections.Generic.List[String]]@() - Get-Childitem -recurse -path $(SCOPE) -filter coverage.txt | foreach-object { - $covFile = $_.FullName - Write-Host "Adding $covFile to the list of code coverage files" - $coverageFiles.Add($covFile) - } - gocovmerge $coverageFiles > mergedCoverage.txt - gocov convert ./mergedCoverage.txt > ./coverage.json - # gocov converts rely on standard input - Get-Content ./coverage.json | gocov-xml > ./coverage.xml - Get-Content ./coverage.json | gocov-html > ./coverage.html + - pwsh: ../eng/scripts/create_coverage.ps1 displayName: 'Generate Coverage XML' workingDirectory: '${{parameters.GoWorkspace}}sdk' diff --git a/eng/scripts/create_coverage.ps1 b/eng/scripts/create_coverage.ps1 new file mode 100644 index 000000000000..56b1a6b3f8a4 --- /dev/null +++ b/eng/scripts/create_coverage.ps1 @@ -0,0 +1,19 @@ +#Requires -Version 7.0 + +$coverageFiles = [Collections.Generic.List[String]]@() +Get-ChildItem -recurse -path . -filter coverage.txt | ForEach-Object { + $covFile = $_.FullName + Write-Host "Adding $covFile to the list of code coverage files" + $coverageFiles.Add($covFile) +} + +# merge coverage files +gocovmerge $coverageFiles > mergedCoverage.txt +gocov convert ./mergedCoverage.txt > ./coverage.json + +# gocov converts rely on standard input +Get-Content ./coverage.json | gocov-xml > ./coverage.xml +Get-Content ./coverage.json | gocov-html > ./coverage.html + +# use internal tool to fail if coverage is too low +go run ../tools/internal/coverage/main.go \ No newline at end of file diff --git a/eng/scripts/get_module_dirs.ps1 b/eng/scripts/get_module_dirs.ps1 index 5404ea45a7a7..f8cddb955163 100644 --- a/eng/scripts/get_module_dirs.ps1 +++ b/eng/scripts/get_module_dirs.ps1 @@ -5,7 +5,7 @@ Param( $modDirs = [Collections.Generic.List[String]]@() # find each module directory under $serviceDir -Get-Childitem -recurse -path $serviceDir -filter go.mod | foreach-object { +Get-ChildItem -recurse -path $serviceDir -filter go.mod | ForEach-Object { $cdir = $_.Directory Write-Host "Adding $cdir to list of module paths" $modDirs.Add($cdir) diff --git a/eng/scripts/get_test_dirs.ps1 b/eng/scripts/get_test_dirs.ps1 index 0ad6b8136616..c599382af411 100644 --- a/eng/scripts/get_test_dirs.ps1 +++ b/eng/scripts/get_test_dirs.ps1 @@ -5,7 +5,7 @@ Param( $testDirs = [Collections.Generic.List[String]]@() # find each directory under $serviceDir that contains Go test files -Get-Childitem -recurse -path $serviceDir -filter *_test.go | foreach-object { +Get-ChildItem -recurse -path $serviceDir -filter *_test.go | ForEach-Object { $cdir = $_.Directory $tests = Select-String -Path $_ 'Test' -AllMatches diff --git a/sdk/to/ci.yml b/sdk/to/ci.yml index e685d1b05e7d..8fa2e11155a9 100644 --- a/sdk/to/ci.yml +++ b/sdk/to/ci.yml @@ -8,7 +8,7 @@ pr: paths: include: - sdk/to/ - + stages: - template: ../../eng/pipelines/templates/jobs/archetype-sdk-client.yml parameters: diff --git a/sdk/to/to.go b/sdk/to/to.go index bbc5af802db2..b9ccec2e5cac 100644 --- a/sdk/to/to.go +++ b/sdk/to/to.go @@ -44,7 +44,7 @@ func TimePtr(t time.Time) *time.Time { // Int32PtrArray returns an array of *int32 from the specified values. func Int32PtrArray(vals ...int32) []*int32 { - arr := make([]*int32, len(vals), len(vals)) + arr := make([]*int32, len(vals)) for i := range vals { arr[i] = Int32Ptr(vals[i]) } @@ -53,7 +53,7 @@ func Int32PtrArray(vals ...int32) []*int32 { // Int64PtrArray returns an array of *int64 from the specified values. func Int64PtrArray(vals ...int64) []*int64 { - arr := make([]*int64, len(vals), len(vals)) + arr := make([]*int64, len(vals)) for i := range vals { arr[i] = Int64Ptr(vals[i]) } @@ -62,7 +62,7 @@ func Int64PtrArray(vals ...int64) []*int64 { // Float32PtrArray returns an array of *float32 from the specified values. func Float32PtrArray(vals ...float32) []*float32 { - arr := make([]*float32, len(vals), len(vals)) + arr := make([]*float32, len(vals)) for i := range vals { arr[i] = Float32Ptr(vals[i]) } @@ -71,7 +71,7 @@ func Float32PtrArray(vals ...float32) []*float32 { // Float64PtrArray returns an array of *float64 from the specified values. func Float64PtrArray(vals ...float64) []*float64 { - arr := make([]*float64, len(vals), len(vals)) + arr := make([]*float64, len(vals)) for i := range vals { arr[i] = Float64Ptr(vals[i]) } @@ -80,7 +80,7 @@ func Float64PtrArray(vals ...float64) []*float64 { // BoolPtrArray returns an array of *bool from the specified values. func BoolPtrArray(vals ...bool) []*bool { - arr := make([]*bool, len(vals), len(vals)) + arr := make([]*bool, len(vals)) for i := range vals { arr[i] = BoolPtr(vals[i]) } @@ -89,7 +89,7 @@ func BoolPtrArray(vals ...bool) []*bool { // StringPtrArray returns an array of *string from the specified values. func StringPtrArray(vals ...string) []*string { - arr := make([]*string, len(vals), len(vals)) + arr := make([]*string, len(vals)) for i := range vals { arr[i] = StringPtr(vals[i]) } @@ -98,7 +98,7 @@ func StringPtrArray(vals ...string) []*string { // TimePtrArray returns an array of *time.Time from the specified values. func TimePtrArray(vals ...time.Time) []*time.Time { - arr := make([]*time.Time, len(vals), len(vals)) + arr := make([]*time.Time, len(vals)) for i := range vals { arr[i] = TimePtr(vals[i]) } diff --git a/tools/internal/coverage/go.mod b/tools/internal/coverage/go.mod new file mode 100644 index 000000000000..35c6bac8061d --- /dev/null +++ b/tools/internal/coverage/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure/azure-sdk-for-go/tools/internal/coverage + +go 1.16 diff --git a/tools/internal/coverage/main.go b/tools/internal/coverage/main.go new file mode 100644 index 000000000000..9ac4d26e39c3 --- /dev/null +++ b/tools/internal/coverage/main.go @@ -0,0 +1,137 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +type CodeCoverage struct { + Packages []Package `json:"Packages"` +} + +type Package struct { + Name string `json:"name"` + CoverageGoal float64 `json:"CoverageGoal"` +} + +const ( + coverageXmlFile = "coverage.xml" +) + +var configFile, _ = filepath.Abs(filepath.Join("..", "eng", "config.json")) + +func check(e error) { + if e != nil { + log.Fatal(e) + } +} + +var coverageFiles []string + +func filterFiles(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if strings.Contains(path, "coverage.xml") { + coverageFiles = append(coverageFiles, path) + } + return nil +} + +func FindCoverageFiles(p string) { + err := filepath.Walk(".", filterFiles) + check(err) +} + +func ReadConfigData() *CodeCoverage { + jsonFile, err := os.Open(configFile) + check(err) + defer jsonFile.Close() + + byteValue, err := ioutil.ReadAll(jsonFile) + check(err) + + var cov CodeCoverage + err = json.Unmarshal([]byte(byteValue), &cov) + check(err) + return &cov +} + +// This supports doing a single package at a time. If this needs to be expanded in the future +// this method will have to return a []*float64 for each packages goal +func findCoverageGoal(covFiles []string, configData *CodeCoverage) float64 { + for _, covFile := range covFiles { + for _, p := range configData.Packages { + if strings.Contains(covFile, p.Name) { + return p.CoverageGoal + } + } + } + fmt.Println("WARNING: Could not find a coverage goal, defaulting to 95%.") + return 0.95 +} + +func main() { + coverageFiles = make([]string, 0) + rootPath, err := filepath.Abs(".") + check(err) + + FindCoverageFiles(rootPath) + + configData := ReadConfigData() + coverageGoal := findCoverageGoal(coverageFiles, configData) + + fmt.Printf("Failing if the coverage is below %.2f\n", coverageGoal) + + coverageValues := make([]float64, 0) + for _, coverageFile := range coverageFiles { + xmlFile, err := os.Open(coverageFile) + check(err) + defer xmlFile.Close() + + byteValue, err := ioutil.ReadAll(xmlFile) + check(err) + + re := regexp.MustCompile(`