github.com/thediveo/gons@v0.9.9/reexec/testing/testmain.go (about) 1 package testing 2 3 import ( 4 "os" 5 "sync/atomic" 6 gotesting "testing" 7 _ "unsafe" // needed in order to use "go:linkname". 8 9 "github.com/thediveo/gons/reexec/internal/testsupport" 10 ) 11 12 // In order to get complete coverage of our M.Run() during our own tests, we 13 // have to resort to dirty tricks by accessing the package private 14 // testing.cover variable which contains the complete coverage profile data 15 // gathered. 16 17 //go:linkname cover testing.cover 18 var cover gotesting.Cover 19 20 // coverageProfileFromCover returns the profile data from testing.cover, but 21 // in our own coverage profile format. 22 func coverageProfileFromTestingCover() *coverageProfile { 23 cp := newCoverageProfile() 24 cp.Mode = cover.Mode 25 var count uint32 26 for sourcename, counts := range cover.Counters { 27 source := &coverageProfileSource{ 28 Blocks: make([]coverageProfileBlock, len(counts)), 29 } 30 cp.Sources[sourcename] = source 31 blocks := cover.Blocks[sourcename] 32 for idx := range counts { 33 count = atomic.LoadUint32(&counts[idx]) 34 source.Blocks[idx] = coverageProfileBlock{ 35 StartLine: blocks[idx].Line0, 36 StartCol: blocks[idx].Col0, 37 EndLine: blocks[idx].Line1, 38 EndCol: blocks[idx].Col1, 39 NumStmts: blocks[idx].Stmts, 40 Counts: count, 41 } 42 } 43 } 44 return cp 45 } 46 47 // TestMainWithCoverage is only for our own testing, in order to gather "more 48 // complete" coverage profile data including our M.Run()/M.run() methods. 49 // 50 // We achieve this with an unfortunate hack: we update the already written 51 // coverage data after the fact, that is, after mm.run() (or its public 52 // mm.Run() facade) has called gotesting.M.Run() which in turns writes the 53 // coverage profile data. This way, we can also get coverage of the code parts 54 // of ours M.run() which run after gotesting.M.Run(). 55 func TestMainWithCoverage(m *gotesting.M) { 56 mm := &M{M: m, skipCleanup: true} 57 exitcode, reexeced := mm.run() 58 if coverProfile != "" { 59 // Take the final coverage profile data as our starting point, ignoring 60 // whatever mm.run() wrote to the final coverage file. We need to write a 61 // new version of it with the most recent coverage profile data. 62 cp := coverageProfileFromTestingCover() 63 var merges []string 64 if !reexeced { 65 merges = testsupport.CoverageProfiles 66 } 67 mergeWithCoverProfileAndReport(cp, merges, coverProfile) 68 for _, coverprof := range merges { 69 _ = os.Remove(toOutputDir(coverprof)) 70 } 71 } 72 os.Exit(exitcode) 73 }