Skip to content

Commit

Permalink
Enforce testutils package is only imported by test files outside plug…
Browse files Browse the repository at this point in the history
…in/evm
  • Loading branch information
qdm12 committed Jan 15, 2025
1 parent c6a75ea commit 7c08815
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 0 deletions.
1 change: 1 addition & 0 deletions plugin/evm/testutils/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
// If the [metrics.Enabled] is already true, nothing is done.
// Otherwise, it is set to true and is reverted to false when the test finishes.
func WithMetrics(t *testing.T) {
panicIfCallsFromNonTest()
if metrics.Enabled {
return
}
Expand Down
27 changes: 27 additions & 0 deletions plugin/evm/testutils/testutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Package testutils contains test utilities ONLY to be used outside plugin/evm.
// The aim is to reduce changes in geth tests by using the utilities defined here.
// This package MUST NOT be imported by non-test packages.

package testutils

import (
"runtime"
"strings"
)

// panicIfCallsFromNonTest should be added at the top of every function defined in this package
// to enforce this package to be used only by tests.
func panicIfCallsFromNonTest() {
pc := make([]uintptr, 64)
runtime.Callers(0, pc)
frames := runtime.CallersFrames(pc)
for {
f, more := frames.Next()
if strings.HasPrefix(f.File, "/testing/") || strings.HasSuffix(f.File, "_test.go") {
return
}
if !more {
panic("no test file in call stack")
}
}
}
81 changes: 81 additions & 0 deletions plugin/evm/testutils/testutils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package testutils

import (
"fmt"
"go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestMustNotImport fails if a package in plugin/evm imports the testutils package.
func TestMustNotImport(t *testing.T) {
var evmPackagePaths []string
filepath.WalkDir("../", func(path string, d fs.DirEntry, err error) error {
if !d.IsDir() {
return nil
} else if path == "../testutils" {
return nil
}
evmPackagePaths = append(evmPackagePaths, path)
return nil
})

for _, packagePath := range evmPackagePaths {
packageName := "github.com/ava-labs/subnet-evm/plugin/evm/" + strings.TrimPrefix(packagePath, "../")
imports, err := getPackageImports(packagePath)
require.NoError(t, err)
_, ok := imports["github.com/ava-labs/subnet-evm/plugin/evm/testutils"]
assert.Falsef(t, ok, "package %s imports testutils: testutils should only be used outside plugin/evm.", packageName)
}
}

func getPackageImports(packagePath string) (imports map[string]struct{}, err error) {
imports = make(map[string]struct{})

err = filepath.Walk(packagePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
} else if path != packagePath && info.IsDir() {
return fs.SkipDir
} else if !strings.HasSuffix(info.Name(), ".go") {
return nil
}
err = parseImportsFromFile(path, imports)
if err != nil {
return fmt.Errorf("failed to parse imports: %s", err)
}
return nil
})

if err != nil {
return nil, fmt.Errorf("failed to walk through package files: %v", err)
}

return imports, nil
}

func parseImportsFromFile(filePath string, imports map[string]struct{}) error {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("failed to open file %s: %v", filePath, err)
}
defer file.Close()

node, err := parser.ParseFile(token.NewFileSet(), filePath, file, parser.ImportsOnly)
if err != nil {
return fmt.Errorf("failed to parse file %s: %v", filePath, err)
}

for _, nodeImport := range node.Imports {
imports[strings.Trim(nodeImport.Path.Value, `"`)] = struct{}{}
}

return nil
}

0 comments on commit 7c08815

Please sign in to comment.