forked from gitea/gitea
		
	Move gocovmerge as vendor (#10947)
* Move gocovmerge as vendor * Update Makefile Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									14c97c0501
								
							
						
					
					
						commit
						b264c30394
					
				
							
								
								
									
										5
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
									
									
									
									
								
							| @ -293,10 +293,7 @@ test\#%: | ||||
| 
 | ||||
| .PHONY: coverage | ||||
| coverage: | ||||
| 	@hash gocovmerge > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | ||||
| 		$(GO) get -u github.com/wadey/gocovmerge; \
 | ||||
| 	fi | ||||
| 	gocovmerge integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all;\
 | ||||
| 	GO111MODULE=on $(GO) run -mod=vendor build/gocovmerge.go integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all | ||||
| 
 | ||||
| .PHONY: unit-test-coverage | ||||
| unit-test-coverage: | ||||
|  | ||||
							
								
								
									
										119
									
								
								build/gocovmerge.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								build/gocovmerge.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | ||||
| // Copyright 2020 The Gitea Authors. All rights reserved. | ||||
| // Copyright (c) 2015, Wade Simmons | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| // gocovmerge takes the results from multiple `go test -coverprofile` runs and | ||||
| // merges them into one profile | ||||
| 
 | ||||
| // +build ignore | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"sort" | ||||
| 
 | ||||
| 	"golang.org/x/tools/cover" | ||||
| ) | ||||
| 
 | ||||
| func mergeProfiles(p *cover.Profile, merge *cover.Profile) { | ||||
| 	if p.Mode != merge.Mode { | ||||
| 		log.Fatalf("cannot merge profiles with different modes") | ||||
| 	} | ||||
| 	// Since the blocks are sorted, we can keep track of where the last block | ||||
| 	// was inserted and only look at the blocks after that as targets for merge | ||||
| 	startIndex := 0 | ||||
| 	for _, b := range merge.Blocks { | ||||
| 		startIndex = mergeProfileBlock(p, b, startIndex) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int { | ||||
| 	sortFunc := func(i int) bool { | ||||
| 		pi := p.Blocks[i+startIndex] | ||||
| 		return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol) | ||||
| 	} | ||||
| 
 | ||||
| 	i := 0 | ||||
| 	if sortFunc(i) != true { | ||||
| 		i = sort.Search(len(p.Blocks)-startIndex, sortFunc) | ||||
| 	} | ||||
| 	i += startIndex | ||||
| 	if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol { | ||||
| 		if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol { | ||||
| 			log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb) | ||||
| 		} | ||||
| 		switch p.Mode { | ||||
| 		case "set": | ||||
| 			p.Blocks[i].Count |= pb.Count | ||||
| 		case "count", "atomic": | ||||
| 			p.Blocks[i].Count += pb.Count | ||||
| 		default: | ||||
| 			log.Fatalf("unsupported covermode: '%s'", p.Mode) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if i > 0 { | ||||
| 			pa := p.Blocks[i-1] | ||||
| 			if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) { | ||||
| 				log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb) | ||||
| 			} | ||||
| 		} | ||||
| 		if i < len(p.Blocks)-1 { | ||||
| 			pa := p.Blocks[i+1] | ||||
| 			if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) { | ||||
| 				log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb) | ||||
| 			} | ||||
| 		} | ||||
| 		p.Blocks = append(p.Blocks, cover.ProfileBlock{}) | ||||
| 		copy(p.Blocks[i+1:], p.Blocks[i:]) | ||||
| 		p.Blocks[i] = pb | ||||
| 	} | ||||
| 	return i + 1 | ||||
| } | ||||
| 
 | ||||
| func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile { | ||||
| 	i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName }) | ||||
| 	if i < len(profiles) && profiles[i].FileName == p.FileName { | ||||
| 		mergeProfiles(profiles[i], p) | ||||
| 	} else { | ||||
| 		profiles = append(profiles, nil) | ||||
| 		copy(profiles[i+1:], profiles[i:]) | ||||
| 		profiles[i] = p | ||||
| 	} | ||||
| 	return profiles | ||||
| } | ||||
| 
 | ||||
| func dumpProfiles(profiles []*cover.Profile, out io.Writer) { | ||||
| 	if len(profiles) == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode) | ||||
| 	for _, p := range profiles { | ||||
| 		for _, b := range p.Blocks { | ||||
| 			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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 
 | ||||
| 	var merged []*cover.Profile | ||||
| 
 | ||||
| 	for _, file := range flag.Args() { | ||||
| 		profiles, err := cover.ParseProfiles(file) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("failed to parse profiles: %v", err) | ||||
| 		} | ||||
| 		for _, p := range profiles { | ||||
| 			merged = addProfile(merged, p) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	dumpProfiles(merged, os.Stdout) | ||||
| } | ||||
							
								
								
									
										3
									
								
								build/vendor.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								build/vendor.go
									
									
									
									
										vendored
									
									
								
							| @ -15,4 +15,7 @@ import ( | ||||
| 
 | ||||
| 	// for embed | ||||
| 	_ "github.com/shurcooL/vfsgen" | ||||
| 
 | ||||
| 	// for cover merge | ||||
| 	_ "golang.org/x/tools/cover" | ||||
| ) | ||||
|  | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @ -111,6 +111,7 @@ require ( | ||||
| 	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | ||||
| 	golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 | ||||
| 	golang.org/x/text v0.3.2 | ||||
| 	golang.org/x/tools v0.0.0-20200225230052-807dcd883420 | ||||
| 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | ||||
| 	gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect | ||||
| 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df | ||||
|  | ||||
							
								
								
									
										256
									
								
								vendor/golang.org/x/tools/cover/profile.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								vendor/golang.org/x/tools/cover/profile.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,256 @@ | ||||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| // Package cover provides support for parsing coverage profiles | ||||
| // generated by "go test -coverprofile=cover.out". | ||||
| package cover // import "golang.org/x/tools/cover" | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"os" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Profile represents the profiling data for a specific file. | ||||
| type Profile struct { | ||||
| 	FileName string | ||||
| 	Mode     string | ||||
| 	Blocks   []ProfileBlock | ||||
| } | ||||
| 
 | ||||
| // ProfileBlock represents a single block of profiling data. | ||||
| type ProfileBlock struct { | ||||
| 	StartLine, StartCol int | ||||
| 	EndLine, EndCol     int | ||||
| 	NumStmt, Count      int | ||||
| } | ||||
| 
 | ||||
| type byFileName []*Profile | ||||
| 
 | ||||
| func (p byFileName) Len() int           { return len(p) } | ||||
| func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName } | ||||
| func (p byFileName) Swap(i, j int)      { p[i], p[j] = p[j], p[i] } | ||||
| 
 | ||||
| // ParseProfiles parses profile data in the specified file and returns a | ||||
| // Profile for each source file described therein. | ||||
| func ParseProfiles(fileName string) ([]*Profile, error) { | ||||
| 	pf, err := os.Open(fileName) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer pf.Close() | ||||
| 
 | ||||
| 	files := make(map[string]*Profile) | ||||
| 	buf := bufio.NewReader(pf) | ||||
| 	// First line is "mode: foo", where foo is "set", "count", or "atomic". | ||||
| 	// Rest of file is in the format | ||||
| 	//	encoding/base64/base64.go:34.44,37.40 3 1 | ||||
| 	// where the fields are: name.go:line.column,line.column numberOfStatements count | ||||
| 	s := bufio.NewScanner(buf) | ||||
| 	mode := "" | ||||
| 	for s.Scan() { | ||||
| 		line := s.Text() | ||||
| 		if mode == "" { | ||||
| 			const p = "mode: " | ||||
| 			if !strings.HasPrefix(line, p) || line == p { | ||||
| 				return nil, fmt.Errorf("bad mode line: %v", line) | ||||
| 			} | ||||
| 			mode = line[len(p):] | ||||
| 			continue | ||||
| 		} | ||||
| 		fn, b, err := parseLine(line) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("line %q doesn't match expected format: %v", line, err) | ||||
| 		} | ||||
| 		p := files[fn] | ||||
| 		if p == nil { | ||||
| 			p = &Profile{ | ||||
| 				FileName: fn, | ||||
| 				Mode:     mode, | ||||
| 			} | ||||
| 			files[fn] = p | ||||
| 		} | ||||
| 		p.Blocks = append(p.Blocks, b) | ||||
| 	} | ||||
| 	if err := s.Err(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, p := range files { | ||||
| 		sort.Sort(blocksByStart(p.Blocks)) | ||||
| 		// Merge samples from the same location. | ||||
| 		j := 1 | ||||
| 		for i := 1; i < len(p.Blocks); i++ { | ||||
| 			b := p.Blocks[i] | ||||
| 			last := p.Blocks[j-1] | ||||
| 			if b.StartLine == last.StartLine && | ||||
| 				b.StartCol == last.StartCol && | ||||
| 				b.EndLine == last.EndLine && | ||||
| 				b.EndCol == last.EndCol { | ||||
| 				if b.NumStmt != last.NumStmt { | ||||
| 					return nil, fmt.Errorf("inconsistent NumStmt: changed from %d to %d", last.NumStmt, b.NumStmt) | ||||
| 				} | ||||
| 				if mode == "set" { | ||||
| 					p.Blocks[j-1].Count |= b.Count | ||||
| 				} else { | ||||
| 					p.Blocks[j-1].Count += b.Count | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			p.Blocks[j] = b | ||||
| 			j++ | ||||
| 		} | ||||
| 		p.Blocks = p.Blocks[:j] | ||||
| 	} | ||||
| 	// Generate a sorted slice. | ||||
| 	profiles := make([]*Profile, 0, len(files)) | ||||
| 	for _, profile := range files { | ||||
| 		profiles = append(profiles, profile) | ||||
| 	} | ||||
| 	sort.Sort(byFileName(profiles)) | ||||
| 	return profiles, nil | ||||
| } | ||||
| 
 | ||||
| // parseLine parses a line from a coverage file. | ||||
| // It is equivalent to the regex | ||||
| // ^(.+):([0-9]+)\.([0-9]+),([0-9]+)\.([0-9]+) ([0-9]+) ([0-9]+)$ | ||||
| // | ||||
| // However, it is much faster: https://golang.org/cl/179377 | ||||
| func parseLine(l string) (fileName string, block ProfileBlock, err error) { | ||||
| 	end := len(l) | ||||
| 
 | ||||
| 	b := ProfileBlock{} | ||||
| 	b.Count, end, err = seekBack(l, ' ', end, "Count") | ||||
| 	if err != nil { | ||||
| 		return "", b, err | ||||
| 	} | ||||
| 	b.NumStmt, end, err = seekBack(l, ' ', end, "NumStmt") | ||||
| 	if err != nil { | ||||
| 		return "", b, err | ||||
| 	} | ||||
| 	b.EndCol, end, err = seekBack(l, '.', end, "EndCol") | ||||
| 	if err != nil { | ||||
| 		return "", b, err | ||||
| 	} | ||||
| 	b.EndLine, end, err = seekBack(l, ',', end, "EndLine") | ||||
| 	if err != nil { | ||||
| 		return "", b, err | ||||
| 	} | ||||
| 	b.StartCol, end, err = seekBack(l, '.', end, "StartCol") | ||||
| 	if err != nil { | ||||
| 		return "", b, err | ||||
| 	} | ||||
| 	b.StartLine, end, err = seekBack(l, ':', end, "StartLine") | ||||
| 	if err != nil { | ||||
| 		return "", b, err | ||||
| 	} | ||||
| 	fn := l[0:end] | ||||
| 	if fn == "" { | ||||
| 		return "", b, errors.New("a FileName cannot be blank") | ||||
| 	} | ||||
| 	return fn, b, nil | ||||
| } | ||||
| 
 | ||||
| // seekBack searches backwards from end to find sep in l, then returns the | ||||
| // value between sep and end as an integer. | ||||
| // If seekBack fails, the returned error will reference what. | ||||
| func seekBack(l string, sep byte, end int, what string) (value int, nextSep int, err error) { | ||||
| 	// Since we're seeking backwards and we know only ASCII is legal for these values, | ||||
| 	// we can ignore the possibility of non-ASCII characters. | ||||
| 	for start := end - 1; start >= 0; start-- { | ||||
| 		if l[start] == sep { | ||||
| 			i, err := strconv.Atoi(l[start+1 : end]) | ||||
| 			if err != nil { | ||||
| 				return 0, 0, fmt.Errorf("couldn't parse %q: %v", what, err) | ||||
| 			} | ||||
| 			if i < 0 { | ||||
| 				return 0, 0, fmt.Errorf("negative values are not allowed for %s, found %d", what, i) | ||||
| 			} | ||||
| 			return i, start, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return 0, 0, fmt.Errorf("couldn't find a %s before %s", string(sep), what) | ||||
| } | ||||
| 
 | ||||
| type blocksByStart []ProfileBlock | ||||
| 
 | ||||
| func (b blocksByStart) Len() int      { return len(b) } | ||||
| func (b blocksByStart) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | ||||
| func (b blocksByStart) Less(i, j int) bool { | ||||
| 	bi, bj := b[i], b[j] | ||||
| 	return bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol | ||||
| } | ||||
| 
 | ||||
| // Boundary represents the position in a source file of the beginning or end of a | ||||
| // block as reported by the coverage profile. In HTML mode, it will correspond to | ||||
| // the opening or closing of a <span> tag and will be used to colorize the source | ||||
| type Boundary struct { | ||||
| 	Offset int     // Location as a byte offset in the source file. | ||||
| 	Start  bool    // Is this the start of a block? | ||||
| 	Count  int     // Event count from the cover profile. | ||||
| 	Norm   float64 // Count normalized to [0..1]. | ||||
| } | ||||
| 
 | ||||
| // Boundaries returns a Profile as a set of Boundary objects within the provided src. | ||||
| func (p *Profile) Boundaries(src []byte) (boundaries []Boundary) { | ||||
| 	// Find maximum count. | ||||
| 	max := 0 | ||||
| 	for _, b := range p.Blocks { | ||||
| 		if b.Count > max { | ||||
| 			max = b.Count | ||||
| 		} | ||||
| 	} | ||||
| 	// Divisor for normalization. | ||||
| 	divisor := math.Log(float64(max)) | ||||
| 
 | ||||
| 	// boundary returns a Boundary, populating the Norm field with a normalized Count. | ||||
| 	boundary := func(offset int, start bool, count int) Boundary { | ||||
| 		b := Boundary{Offset: offset, Start: start, Count: count} | ||||
| 		if !start || count == 0 { | ||||
| 			return b | ||||
| 		} | ||||
| 		if max <= 1 { | ||||
| 			b.Norm = 0.8 // Profile is in"set" mode; we want a heat map. Use cov8 in the CSS. | ||||
| 		} else if count > 0 { | ||||
| 			b.Norm = math.Log(float64(count)) / divisor | ||||
| 		} | ||||
| 		return b | ||||
| 	} | ||||
| 
 | ||||
| 	line, col := 1, 2 // TODO: Why is this 2? | ||||
| 	for si, bi := 0, 0; si < len(src) && bi < len(p.Blocks); { | ||||
| 		b := p.Blocks[bi] | ||||
| 		if b.StartLine == line && b.StartCol == col { | ||||
| 			boundaries = append(boundaries, boundary(si, true, b.Count)) | ||||
| 		} | ||||
| 		if b.EndLine == line && b.EndCol == col || line > b.EndLine { | ||||
| 			boundaries = append(boundaries, boundary(si, false, 0)) | ||||
| 			bi++ | ||||
| 			continue // Don't advance through src; maybe the next block starts here. | ||||
| 		} | ||||
| 		if src[si] == '\n' { | ||||
| 			line++ | ||||
| 			col = 0 | ||||
| 		} | ||||
| 		col++ | ||||
| 		si++ | ||||
| 	} | ||||
| 	sort.Sort(boundariesByPos(boundaries)) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type boundariesByPos []Boundary | ||||
| 
 | ||||
| func (b boundariesByPos) Len() int      { return len(b) } | ||||
| func (b boundariesByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | ||||
| func (b boundariesByPos) Less(i, j int) bool { | ||||
| 	if b[i].Offset == b[j].Offset { | ||||
| 		return !b[i].Start && b[j].Start | ||||
| 	} | ||||
| 	return b[i].Offset < b[j].Offset | ||||
| } | ||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @ -732,6 +732,8 @@ golang.org/x/text/unicode/bidi | ||||
| golang.org/x/text/unicode/norm | ||||
| golang.org/x/text/width | ||||
| # golang.org/x/tools v0.0.0-20200225230052-807dcd883420 | ||||
| ## explicit | ||||
| golang.org/x/tools/cover | ||||
| golang.org/x/tools/go/ast/astutil | ||||
| golang.org/x/tools/go/buildutil | ||||
| golang.org/x/tools/go/gcexportdata | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 GitHub
							GitHub