github.com/lixvbnet/courtney@v0.0.0-20221025031132-0dcb02231211/tester/merge/merge.go (about) 1 package merge 2 3 import ( 4 "fmt" 5 "io" 6 "sort" 7 8 "github.com/pkg/errors" 9 "golang.org/x/tools/cover" 10 ) 11 12 // notest 13 14 // AddProfile adds and merges a profile to a slice of profiles 15 func AddProfile(profiles []*cover.Profile, p *cover.Profile) ([]*cover.Profile, error) { 16 i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName }) 17 if i < len(profiles) && profiles[i].FileName == p.FileName { 18 if err := mergeProfiles(profiles[i], p); err != nil { 19 return nil, err 20 } 21 } else { 22 profiles = append(profiles, nil) 23 copy(profiles[i+1:], profiles[i:]) 24 profiles[i] = p 25 } 26 return profiles, nil 27 } 28 29 // DumpProfiles writes a slice of profiles to a writer in the standard format. 30 func DumpProfiles(profiles []*cover.Profile, out io.Writer) { 31 if len(profiles) == 0 { 32 return 33 } 34 fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode) 35 for _, p := range profiles { 36 for _, b := range p.Blocks { 37 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) 38 } 39 } 40 } 41 42 func mergeProfiles(p *cover.Profile, merge *cover.Profile) error { 43 if p.Mode != merge.Mode { 44 return errors.New("cannot merge profiles with different modes") 45 } 46 // Since the blocks are sorted, we can keep track of where the last block 47 // was inserted and only look at the blocks after that as targets for merge 48 startIndex := 0 49 var err error 50 for _, b := range merge.Blocks { 51 if startIndex, err = mergeProfileBlock(p, b, startIndex); err != nil { 52 return err 53 } 54 } 55 return nil 56 } 57 58 func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) (int, error) { 59 sortFunc := func(i int) bool { 60 pi := p.Blocks[i+startIndex] 61 return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol) 62 } 63 64 i := 0 65 if sortFunc(i) != true { 66 i = sort.Search(len(p.Blocks)-startIndex, sortFunc) 67 } 68 i += startIndex 69 if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol { 70 if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol { 71 return 0, errors.Errorf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb) 72 } 73 switch p.Mode { 74 case "set": 75 p.Blocks[i].Count |= pb.Count 76 case "count", "atomic": 77 p.Blocks[i].Count += pb.Count 78 default: 79 return 0, errors.Errorf("unsupported covermode: '%s'", p.Mode) 80 } 81 } else { 82 if i > 0 { 83 pa := p.Blocks[i-1] 84 if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) { 85 return 0, errors.Errorf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb) 86 } 87 } 88 if i < len(p.Blocks)-1 { 89 pa := p.Blocks[i+1] 90 if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) { 91 return 0, errors.Errorf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb) 92 } 93 } 94 p.Blocks = append(p.Blocks, cover.ProfileBlock{}) 95 copy(p.Blocks[i+1:], p.Blocks[i:]) 96 p.Blocks[i] = pb 97 } 98 return i + 1, nil 99 }