github.com/thediveo/gons@v0.9.9/reexec/testing/coverage.go (about) 1 // Copyright 2020 Harald Albrecht. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package testing 16 17 import ( 18 "fmt" 19 "os" 20 "sort" 21 "strings" 22 ) 23 24 // Testing (coverage) related CLI arguments picked up from os.Args, which are 25 // of relevance to coverage profile data handling. 26 var ( 27 outputDir string // "-test.outputdir" 28 coverProfile string // "-test.coverprofile" 29 ) 30 31 // mergeAndReportCoverages picks up the coverage profile data files created by 32 // re-executed copies and merges them into this (parent) process' coverage 33 // profile data. 34 func mergeAndReportCoverages(maincovprof string, childcovprofs []string) { 35 sumcp := coverageProfile{ 36 Sources: make(map[string]*coverageProfileSource), 37 } 38 // Prime summary coverage profile data from this parent's coverage profile 39 // data... 40 mergeCoverageFile(toOutputDir(maincovprof), &sumcp) 41 // ...then merge in the re-executed children's coverage profile data, and 42 // write the results into a file. 43 mergeWithCoverProfileAndReport(&sumcp, childcovprofs, maincovprof) 44 } 45 46 // mergeWithCoverProfileAndReport takes a coverage profile, merges in other 47 // coverage profile data files, and then writes the summary coverage profile 48 // data to the specified file. 49 func mergeWithCoverProfileAndReport(sumcp *coverageProfile, childcovprofs []string, mergedname string) { 50 // Merge in other coverage profile data files (typically created by 51 // re-executed child processes). 52 for _, coverprofilename := range childcovprofs { 53 mergeCoverageFile(toOutputDir(coverprofilename), sumcp) 54 } 55 // Finally dump the summary coverage profile data onto the parent's 56 // coverage profile data, overwriting it. 57 f, err := os.Create(toOutputDir(mergedname)) 58 if err != nil { 59 panic("cannot report summary coverage profile data: " + err.Error()) 60 } 61 defer f.Close() 62 fmt.Fprintf(f, "mode: %s\n", sumcp.Mode) 63 // To make testing deterministic, we need to deterministically sort the 64 // source filename keys, as otherwise the map may iterate in arbitrary 65 // order over the sources. 66 sourcenames := make([]string, len(sumcp.Sources)) 67 idx := 0 68 for sourcename := range sumcp.Sources { 69 sourcenames[idx] = sourcename 70 idx++ 71 } 72 sort.Strings(sourcenames) 73 for _, sourcename := range sourcenames { 74 for _, block := range sumcp.Sources[sourcename].Blocks { 75 fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", 76 sourcename, 77 block.StartLine, block.StartCol, 78 block.EndLine, block.EndCol, 79 block.NumStmts, 80 block.Counts) 81 } 82 } 83 } 84 85 // parseCoverageArgs gathers the output directory and cover profile file from 86 // the CLI arguments. 87 func parseCoverageArgs(args []string) { 88 for idx := 0; idx < len(args); idx++ { 89 arg := args[idx] 90 if strings.HasPrefix(arg, "-test.outputdir=") { 91 outputDir = strings.SplitN(arg, "=", 2)[1] 92 } else if strings.HasPrefix(arg, "-test.coverprofile=") { 93 coverProfile = strings.SplitN(arg, "=", 2)[1] 94 } else if arg == "-args" || arg == "--args" { 95 break 96 } 97 } 98 } 99 100 // toOutputDir is a Linux-only variant of testing's toOutputDir: it returns 101 // the specified filename relocated, if required, to outputDir. 102 func toOutputDir(path string) string { 103 if outputDir == "" || path == "" { 104 return path 105 } 106 // If the name of the coverage profile data file is already an absolute 107 // path, then simply return it. 108 if os.IsPathSeparator(path[0]) { 109 return path 110 } 111 // Otherwise return the coverage profile data filename relative to the 112 // specified output directory path ... the latter might be relative or 113 // absolute, but we don't care here. 114 return fmt.Sprintf("%s%c%s", outputDir, os.PathSeparator, path) 115 }