mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-25 13:47:19 +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
|
go 1.22
|
||||||
|
|
||||||
require (
|
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/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
|
||||||
github.com/stretchr/testify v1.9.0
|
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/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 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUOU=
|
||||||
github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA=
|
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.20241016130346-d8e945978af6 h1:3/V1U2ZgbFZQ5xGjTD9NMsz4NHzPy/nYJzcaHDVDu88=
|
||||||
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/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 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/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=
|
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) {}`),
|
func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data any) {}`),
|
||||||
&compiler.Options{Name: "foo"})
|
&compiler.Options{Name: "foo"})
|
||||||
e.DeployContract(t, ctr, nil)
|
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)
|
cTo.Invoke(t, true, "transfer", ctr.Hash, []byte("neo.com"), nil)
|
||||||
cFrom.Invoke(t, 1, "totalSupply")
|
cFrom.Invoke(t, 1, "totalSupply")
|
||||||
cFrom.Invoke(t, ctr.Hash.BytesBE(), "ownerOf", []byte("neo.com"))
|
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,
|
Validator: validator,
|
||||||
Committee: committee,
|
Committee: committee,
|
||||||
CommitteeHash: committee.ScriptHash(),
|
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.
|
// EnableCoverage enables coverage collection for this executor, but only when `go test` is running with coverage enabled.
|
||||||
func (e *Executor) EnableCoverage() {
|
func (e *Executor) EnableCoverage(t testing.TB) {
|
||||||
e.collectCoverage = isCoverageEnabled()
|
e.collectCoverage = isCoverageEnabled(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableCoverage disables coverage collection for this executor until enabled explicitly through EnableCoverage.
|
// DisableCoverage disables coverage collection for this executor until enabled explicitly through EnableCoverage.
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package neotest
|
package neotest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -16,13 +18,22 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// goCoverProfileFlag specifies the name of `go test` flag that tells it where to save coverage data.
|
// goCoverProfileFlag specifies the name of `go test` command flag `coverprofile`
|
||||||
// Neotest coverage can be enabled only when this flag is set.
|
// that tells it where to save coverage data. Neotest coverage can be enabled
|
||||||
|
// only when this flag is set.
|
||||||
goCoverProfileFlag = "test.coverprofile"
|
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 is name of the environmental variable used to explicitly disable neotest coverage.
|
||||||
disableNeotestCover = "DISABLE_NEOTEST_COVER"
|
disableNeotestCover = "DISABLE_NEOTEST_COVER"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// goCoverModeSet is the name of "set" go test coverage mode.
|
||||||
|
goCoverModeSet = "set"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// coverageLock protects all vars below from concurrent modification when tests are run in parallel.
|
// coverageLock protects all vars below from concurrent modification when tests are run in parallel.
|
||||||
coverageLock sync.Mutex
|
coverageLock sync.Mutex
|
||||||
|
@ -34,6 +45,8 @@ var (
|
||||||
coverageEnabled bool
|
coverageEnabled bool
|
||||||
// coverProfile specifies the file all coverage data is written to, unless empty.
|
// coverProfile specifies the file all coverage data is written to, unless empty.
|
||||||
coverProfile = ""
|
coverProfile = ""
|
||||||
|
// coverMode is the mode of go coverage collection.
|
||||||
|
coverMode = goCoverModeSet
|
||||||
)
|
)
|
||||||
|
|
||||||
type scriptRawCoverage struct {
|
type scriptRawCoverage struct {
|
||||||
|
@ -59,7 +72,7 @@ type coverBlock struct {
|
||||||
// documentName makes it clear when a `string` maps path to the script file.
|
// documentName makes it clear when a `string` maps path to the script file.
|
||||||
type documentName = string
|
type documentName = string
|
||||||
|
|
||||||
func isCoverageEnabled() bool {
|
func isCoverageEnabled(t testing.TB) bool {
|
||||||
coverageLock.Lock()
|
coverageLock.Lock()
|
||||||
defer coverageLock.Unlock()
|
defer coverageLock.Unlock()
|
||||||
|
|
||||||
|
@ -72,7 +85,7 @@ func isCoverageEnabled() bool {
|
||||||
if v, ok := os.LookupEnv(disableNeotestCover); ok {
|
if v, ok := os.LookupEnv(disableNeotestCover); ok {
|
||||||
disabled, err := strconv.ParseBool(v)
|
disabled, err := strconv.ParseBool(v)
|
||||||
if err != nil {
|
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
|
disabledByEnvironment = disabled
|
||||||
}
|
}
|
||||||
|
@ -83,16 +96,22 @@ func isCoverageEnabled() bool {
|
||||||
goToolCoverageEnabled = true
|
goToolCoverageEnabled = true
|
||||||
coverProfile = f.Value.String()
|
coverProfile = f.Value.String()
|
||||||
}
|
}
|
||||||
|
if f.Name == goCoverModeFlag && f.Value != nil && f.Value.String() != "" {
|
||||||
|
coverMode = f.Value.String()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
coverageEnabled = !disabledByEnvironment && goToolCoverageEnabled
|
coverageEnabled = !disabledByEnvironment && goToolCoverageEnabled
|
||||||
|
|
||||||
if coverageEnabled {
|
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
|
// This is needed so go cover tool doesn't overwrite
|
||||||
// the file with our coverage when all tests are done.
|
// the file with our coverage when all tests are done.
|
||||||
err := flag.Set(goCoverProfileFlag, "")
|
err := flag.Set(goCoverProfileFlag, "")
|
||||||
if err != nil {
|
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) {
|
func writeCoverageReport(w io.Writer) {
|
||||||
fmt.Fprintf(w, "mode: set\n")
|
fmt.Fprintf(w, "mode: %s\n", coverMode)
|
||||||
cover := processCover()
|
cover := processCover()
|
||||||
for name, blocks := range cover {
|
for name, blocks := range cover {
|
||||||
for _, b := range blocks {
|
for _, b := range blocks {
|
||||||
c := 0
|
var counts = b.counts
|
||||||
if b.counts > 0 {
|
if coverMode == goCoverModeSet && counts > 0 {
|
||||||
c = 1
|
counts = 1
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n", name,
|
fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n", name,
|
||||||
b.startLine, b.startCol,
|
b.startLine, b.startCol,
|
||||||
b.endLine, b.endCol,
|
b.endLine, b.endCol,
|
||||||
b.stmts,
|
b.stmts,
|
||||||
c,
|
counts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func processCover() map[documentName][]coverBlock {
|
func processCover() map[documentName][]*coverBlock {
|
||||||
documents := make(map[documentName]struct{})
|
documents := make(map[documentName]struct{})
|
||||||
for _, scriptRawCoverage := range rawCoverage {
|
for _, scriptRawCoverage := range rawCoverage {
|
||||||
for _, documentName := range scriptRawCoverage.debugInfo.Documents {
|
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 {
|
for documentName := range documents {
|
||||||
mappedBlocks := make(map[int]*coverBlock)
|
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 {
|
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
|
cover[documentName] = blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue