code.gitea.io/gitea@v1.21.7/build/gocovmerge.go (about)

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