1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cov 18 19 import ( 20 "errors" 21 "fmt" 22 "golang.org/x/tools/cover" 23 "sort" 24 ) 25 26 // MergeProfiles merges two coverage profiles. 27 // The profiles are expected to be similar - that is, from multiple invocations of a 28 // single binary, or multiple binaries using the same codebase. 29 // In particular, any source files with the same path must have had identical content 30 // when building the binaries. 31 // MergeProfiles expects its arguments to be sorted: Profiles in alphabetical order, 32 // and lines in files in the order those lines appear. These are standard constraints for 33 // Go coverage profiles. The resulting profile will also obey these constraints. 34 func MergeProfiles(a []*cover.Profile, b []*cover.Profile) ([]*cover.Profile, error) { 35 var result []*cover.Profile 36 files := make(map[string]*cover.Profile, len(a)) 37 for _, profile := range a { 38 np := deepCopyProfile(*profile) 39 result = append(result, &np) 40 files[np.FileName] = &np 41 } 42 43 needsSort := false 44 // Now merge b into the result 45 for _, profile := range b { 46 dest, ok := files[profile.FileName] 47 if ok { 48 if err := ensureProfilesMatch(profile, dest); err != nil { 49 return nil, fmt.Errorf("error merging %s: %v", profile.FileName, err) 50 } 51 for i, block := range profile.Blocks { 52 db := &dest.Blocks[i] 53 db.Count += block.Count 54 } 55 } else { 56 // If we get some file we haven't seen before, we just append it. 57 // We need to sort this later to ensure the resulting profile is still correctly sorted. 58 np := deepCopyProfile(*profile) 59 files[np.FileName] = &np 60 result = append(result, &np) 61 needsSort = true 62 } 63 } 64 if needsSort { 65 sort.Slice(result, func(i, j int) bool { return result[i].FileName < result[j].FileName }) 66 } 67 return result, nil 68 } 69 70 // MergeMultipleProfiles merges more than two profiles together. 71 // MergeMultipleProfiles is equivalent to calling MergeProfiles on pairs of profiles 72 // until only one profile remains. 73 func MergeMultipleProfiles(profiles [][]*cover.Profile) ([]*cover.Profile, error) { 74 if len(profiles) < 1 { 75 return nil, errors.New("can't merge zero profiles") 76 } 77 result := profiles[0] 78 for _, profile := range profiles[1:] { 79 var err error 80 if result, err = MergeProfiles(result, profile); err != nil { 81 return nil, err 82 } 83 } 84 return result, nil 85 } 86