mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-28 19:31:34 +00:00
Merge pull request #3616 from nspcc-dev/coverage-enh
neotest: coverage collection polishing
This commit is contained in:
commit
86ed214e8a
5 changed files with 49 additions and 21 deletions
|
@ -3,7 +3,7 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd-nns
|
|||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/nspcc-dev/neo-go v0.106.4-0.20241007161539-0968c3a81fb0
|
||||
github.com/nspcc-dev/neo-go v0.106.4-0.20241016130346-d8e945978af6
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
|
||||
github.com/stretchr/testify v1.9.0
|
||||
)
|
||||
|
|
|
@ -74,8 +74,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b h1:DRG4c
|
|||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b/go.mod h1:d3cUseu4Asxfo9/QA/w4TtGjM0AbC9ynyab+PfH+Bso=
|
||||
github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUOU=
|
||||
github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA=
|
||||
github.com/nspcc-dev/neo-go v0.106.4-0.20241007161539-0968c3a81fb0 h1:8IYGFnZQ+wyH5Qdtq4oxBEyiWSNMdi6AXSHQunyZ9VU=
|
||||
github.com/nspcc-dev/neo-go v0.106.4-0.20241007161539-0968c3a81fb0/go.mod h1:ds91T4WJwtk7eWUo0fuVC36HpTQKkkdj5AjNxbjXAR0=
|
||||
github.com/nspcc-dev/neo-go v0.106.4-0.20241016130346-d8e945978af6 h1:3/V1U2ZgbFZQ5xGjTD9NMsz4NHzPy/nYJzcaHDVDu88=
|
||||
github.com/nspcc-dev/neo-go v0.106.4-0.20241016130346-d8e945978af6/go.mod h1:ds91T4WJwtk7eWUo0fuVC36HpTQKkkdj5AjNxbjXAR0=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec h1:vDrbVXF2+2uP0RlkZmem3QYATcXCu9BzzGGCNsNcK7Q=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no=
|
||||
|
|
|
@ -381,7 +381,7 @@ func TestTransfer(t *testing.T) {
|
|||
func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data any) {}`),
|
||||
&compiler.Options{Name: "foo"})
|
||||
e.DeployContract(t, ctr, nil)
|
||||
e.EnableCoverage() // contracts above have no source files which leads to unprocessable coverage data
|
||||
e.EnableCoverage(t) // contracts above have no source files which leads to unprocessable coverage data
|
||||
cTo.Invoke(t, true, "transfer", ctr.Hash, []byte("neo.com"), nil)
|
||||
cFrom.Invoke(t, 1, "totalSupply")
|
||||
cFrom.Invoke(t, ctr.Hash.BytesBE(), "ownerOf", []byte("neo.com"))
|
||||
|
|
|
@ -48,7 +48,7 @@ func NewExecutor(t testing.TB, bc *core.Blockchain, validator, committee Signer)
|
|||
Validator: validator,
|
||||
Committee: committee,
|
||||
CommitteeHash: committee.ScriptHash(),
|
||||
collectCoverage: isCoverageEnabled(),
|
||||
collectCoverage: isCoverageEnabled(t),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,8 +452,8 @@ func (e *Executor) GetTxExecResult(t testing.TB, h util.Uint256) *state.AppExecR
|
|||
}
|
||||
|
||||
// EnableCoverage enables coverage collection for this executor, but only when `go test` is running with coverage enabled.
|
||||
func (e *Executor) EnableCoverage() {
|
||||
e.collectCoverage = isCoverageEnabled()
|
||||
func (e *Executor) EnableCoverage(t testing.TB) {
|
||||
e.collectCoverage = isCoverageEnabled(t)
|
||||
}
|
||||
|
||||
// DisableCoverage disables coverage collection for this executor until enabled explicitly through EnableCoverage.
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package neotest
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
|
@ -16,13 +18,22 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// goCoverProfileFlag specifies the name of `go test` flag that tells it where to save coverage data.
|
||||
// Neotest coverage can be enabled only when this flag is set.
|
||||
// goCoverProfileFlag specifies the name of `go test` command flag `coverprofile`
|
||||
// that tells it where to save coverage data. Neotest coverage can be enabled
|
||||
// only when this flag is set.
|
||||
goCoverProfileFlag = "test.coverprofile"
|
||||
// goCoverModeFlag specifies the name of `go test` command flag `covermode` that
|
||||
// specifies the coverage calculation mode.
|
||||
goCoverModeFlag = "test.covermode"
|
||||
// disableNeotestCover is name of the environmental variable used to explicitly disable neotest coverage.
|
||||
disableNeotestCover = "DISABLE_NEOTEST_COVER"
|
||||
)
|
||||
|
||||
const (
|
||||
// goCoverModeSet is the name of "set" go test coverage mode.
|
||||
goCoverModeSet = "set"
|
||||
)
|
||||
|
||||
var (
|
||||
// coverageLock protects all vars below from concurrent modification when tests are run in parallel.
|
||||
coverageLock sync.Mutex
|
||||
|
@ -34,6 +45,8 @@ var (
|
|||
coverageEnabled bool
|
||||
// coverProfile specifies the file all coverage data is written to, unless empty.
|
||||
coverProfile = ""
|
||||
// coverMode is the mode of go coverage collection.
|
||||
coverMode = goCoverModeSet
|
||||
)
|
||||
|
||||
type scriptRawCoverage struct {
|
||||
|
@ -59,7 +72,7 @@ type coverBlock struct {
|
|||
// documentName makes it clear when a `string` maps path to the script file.
|
||||
type documentName = string
|
||||
|
||||
func isCoverageEnabled() bool {
|
||||
func isCoverageEnabled(t testing.TB) bool {
|
||||
coverageLock.Lock()
|
||||
defer coverageLock.Unlock()
|
||||
|
||||
|
@ -72,7 +85,7 @@ func isCoverageEnabled() bool {
|
|||
if v, ok := os.LookupEnv(disableNeotestCover); ok {
|
||||
disabled, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("coverage: error when parsing environment variable '%s', expected bool, but got '%s'", disableNeotestCover, v))
|
||||
t.Fatalf("coverage: error when parsing environment variable '%s', expected bool, but got '%s'", disableNeotestCover, v)
|
||||
}
|
||||
disabledByEnvironment = disabled
|
||||
}
|
||||
|
@ -83,16 +96,22 @@ func isCoverageEnabled() bool {
|
|||
goToolCoverageEnabled = true
|
||||
coverProfile = f.Value.String()
|
||||
}
|
||||
if f.Name == goCoverModeFlag && f.Value != nil && f.Value.String() != "" {
|
||||
coverMode = f.Value.String()
|
||||
}
|
||||
})
|
||||
|
||||
coverageEnabled = !disabledByEnvironment && goToolCoverageEnabled
|
||||
|
||||
if coverageEnabled {
|
||||
if coverMode != goCoverModeSet {
|
||||
t.Fatalf("coverage: only '%s' cover mode is currently supported (#3587), got '%s'", goCoverModeSet, coverMode)
|
||||
}
|
||||
// This is needed so go cover tool doesn't overwrite
|
||||
// the file with our coverage when all tests are done.
|
||||
err := flag.Set(goCoverProfileFlag, "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
t.Fatalf("coverage: failed to overwrite coverprofile flag: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,25 +138,25 @@ func reportCoverage(t testing.TB) {
|
|||
}
|
||||
|
||||
func writeCoverageReport(w io.Writer) {
|
||||
fmt.Fprintf(w, "mode: set\n")
|
||||
fmt.Fprintf(w, "mode: %s\n", coverMode)
|
||||
cover := processCover()
|
||||
for name, blocks := range cover {
|
||||
for _, b := range blocks {
|
||||
c := 0
|
||||
if b.counts > 0 {
|
||||
c = 1
|
||||
var counts = b.counts
|
||||
if coverMode == goCoverModeSet && counts > 0 {
|
||||
counts = 1
|
||||
}
|
||||
fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n", name,
|
||||
b.startLine, b.startCol,
|
||||
b.endLine, b.endCol,
|
||||
b.stmts,
|
||||
c,
|
||||
counts,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processCover() map[documentName][]coverBlock {
|
||||
func processCover() map[documentName][]*coverBlock {
|
||||
documents := make(map[documentName]struct{})
|
||||
for _, scriptRawCoverage := range rawCoverage {
|
||||
for _, documentName := range scriptRawCoverage.debugInfo.Documents {
|
||||
|
@ -145,7 +164,7 @@ func processCover() map[documentName][]coverBlock {
|
|||
}
|
||||
}
|
||||
|
||||
cover := make(map[documentName][]coverBlock)
|
||||
cover := make(map[documentName][]*coverBlock)
|
||||
|
||||
for documentName := range documents {
|
||||
mappedBlocks := make(map[int]*coverBlock)
|
||||
|
@ -180,10 +199,19 @@ func processCover() map[documentName][]coverBlock {
|
|||
}
|
||||
}
|
||||
|
||||
var blocks []coverBlock
|
||||
var blocks = make([]*coverBlock, 0, len(mappedBlocks))
|
||||
for _, b := range mappedBlocks {
|
||||
blocks = append(blocks, *b)
|
||||
blocks = append(blocks, b)
|
||||
}
|
||||
slices.SortFunc(blocks, func(a, b *coverBlock) int {
|
||||
return cmp.Or(
|
||||
cmp.Compare(a.startLine, b.startLine),
|
||||
cmp.Compare(a.endLine, b.endLine),
|
||||
cmp.Compare(a.startCol, b.startCol),
|
||||
cmp.Compare(a.endCol, b.endCol),
|
||||
cmp.Compare(a.stmts, b.stmts),
|
||||
cmp.Compare(a.counts, b.counts))
|
||||
})
|
||||
cover[documentName] = blocks
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue