You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gocovmerge.go 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Copyright (c) 2015, Wade Simmons
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. // gocovmerge takes the results from multiple `go test -coverprofile` runs and
  6. // merges them into one profile
  7. //go:build ignore
  8. // +build ignore
  9. package main
  10. import (
  11. "flag"
  12. "fmt"
  13. "io"
  14. "log"
  15. "os"
  16. "sort"
  17. "golang.org/x/tools/cover"
  18. )
  19. func mergeProfiles(p *cover.Profile, merge *cover.Profile) {
  20. if p.Mode != merge.Mode {
  21. log.Fatalf("cannot merge profiles with different modes")
  22. }
  23. // Since the blocks are sorted, we can keep track of where the last block
  24. // was inserted and only look at the blocks after that as targets for merge
  25. startIndex := 0
  26. for _, b := range merge.Blocks {
  27. startIndex = mergeProfileBlock(p, b, startIndex)
  28. }
  29. }
  30. func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int {
  31. sortFunc := func(i int) bool {
  32. pi := p.Blocks[i+startIndex]
  33. return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
  34. }
  35. i := 0
  36. if sortFunc(i) != true {
  37. i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
  38. }
  39. i += startIndex
  40. if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
  41. if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
  42. log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb)
  43. }
  44. switch p.Mode {
  45. case "set":
  46. p.Blocks[i].Count |= pb.Count
  47. case "count", "atomic":
  48. p.Blocks[i].Count += pb.Count
  49. default:
  50. log.Fatalf("unsupported covermode: '%s'", p.Mode)
  51. }
  52. } else {
  53. if i > 0 {
  54. pa := p.Blocks[i-1]
  55. if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) {
  56. log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb)
  57. }
  58. }
  59. if i < len(p.Blocks)-1 {
  60. pa := p.Blocks[i+1]
  61. if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
  62. log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb)
  63. }
  64. }
  65. p.Blocks = append(p.Blocks, cover.ProfileBlock{})
  66. copy(p.Blocks[i+1:], p.Blocks[i:])
  67. p.Blocks[i] = pb
  68. }
  69. return i + 1
  70. }
  71. func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
  72. i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
  73. if i < len(profiles) && profiles[i].FileName == p.FileName {
  74. mergeProfiles(profiles[i], p)
  75. } else {
  76. profiles = append(profiles, nil)
  77. copy(profiles[i+1:], profiles[i:])
  78. profiles[i] = p
  79. }
  80. return profiles
  81. }
  82. func dumpProfiles(profiles []*cover.Profile, out io.Writer) {
  83. if len(profiles) == 0 {
  84. return
  85. }
  86. fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode)
  87. for _, p := range profiles {
  88. for _, b := range p.Blocks {
  89. fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count)
  90. }
  91. }
  92. }
  93. func main() {
  94. flag.Parse()
  95. var merged []*cover.Profile
  96. for _, file := range flag.Args() {
  97. profiles, err := cover.ParseProfiles(file)
  98. if err != nil {
  99. log.Fatalf("failed to parse profile '%s': %v", file, err)
  100. }
  101. for _, p := range profiles {
  102. merged = addProfile(merged, p)
  103. }
  104. }
  105. dumpProfiles(merged, os.Stdout)
  106. }