test
API
test
packageAPI reference for the test
package.
Imports
(10)
S
struct
plugin
cmd/rfw/plugins/test/test.go:19-19
type plugin struct
Methods
Build
Method
Parameters
raw
json.RawMessage
Returns
error
func (*plugin) Build(raw json.RawMessage) error
{
cfg := struct {
Packages []string `json:"packages"`
}{Packages: []string{"./..."}}
if len(raw) > 0 {
if err := json.Unmarshal(raw, &cfg); err != nil {
return fmt.Errorf("decode test plugin config: %w", err)
}
}
for _, pattern := range configuredPackages(cfg.Packages) {
cmd, err := packageTestCommand(pattern)
if err != nil {
return err
}
output, runErr := cmd.CombinedOutput()
out := strings.TrimSpace(string(output))
if runErr != nil {
logging.Log.Error("go test failed", logging.F("plugin", "test"), logging.F("package", pattern), logging.F("output", out), logging.F("error", runErr.Error()))
return runErr
}
logging.Log.Info("go test ok", logging.F("plugin", "test"), logging.F("package", pattern), logging.F("output", out))
}
return nil
}
ShouldRebuild
Method
Parameters
path
string
Returns
bool
func (*plugin) ShouldRebuild(path string) bool
{
return strings.HasSuffix(path, "_test.go")
}
F
function
init
cmd/rfw/plugins/test/test.go:21-21
func init()
{ plugins.Register(&plugin{}) }
F
function
configuredPackages
Parameters
packages
[]string
Returns
[]string
cmd/rfw/plugins/test/test.go:52-57
func configuredPackages(packages []string) []string
{
if len(packages) == 0 {
return []string{"."}
}
return packages
}
F
function
validatePackagePattern
Parameters
pkg
string
Returns
error
cmd/rfw/plugins/test/test.go:59-88
func validatePackagePattern(pkg string) error
{
if pkg == "." || pkg == "./..." {
return nil
}
if pkg == "" || strings.HasPrefix(pkg, "-") || strings.HasPrefix(pkg, "/") {
return fmt.Errorf("test package %q is not a package pattern", pkg)
}
for _, char := range pkg {
if unicode.IsSpace(char) || unicode.IsControl(char) {
return fmt.Errorf("test package %q contains whitespace or control characters", pkg)
}
if char >= 'a' && char <= 'z' ||
char >= 'A' && char <= 'Z' ||
char >= '0' && char <= '9' ||
strings.ContainsRune("-_./+", char) {
continue
}
return fmt.Errorf("test package %q contains invalid character %q", pkg, char)
}
segments := strings.Split(strings.TrimPrefix(pkg, "./"), "/")
for index, segment := range segments {
if segment == "" || segment == "." || segment == ".." {
return fmt.Errorf("test package %q escapes the project", pkg)
}
if segment == "..." && index != len(segments)-1 {
return fmt.Errorf("test package %q has a non-terminal recursive wildcard", pkg)
}
}
return nil
}
F
function
packageTestCommand
Parameters
pattern
string
Returns
error
cmd/rfw/plugins/test/test.go:90-133
func packageTestCommand(pattern string) (*exec.Cmd, error)
{
if err := validatePackagePattern(pattern); err != nil {
return nil, err
}
localPattern := pattern
if pattern != "." && !strings.HasPrefix(pattern, "./") {
moduleOutput, err := exec.Command("go", "list", "-m").Output()
if err != nil {
return nil, fmt.Errorf("resolve project module: %w", err)
}
modulePath := strings.TrimSpace(string(moduleOutput))
switch {
case pattern == modulePath:
localPattern = "."
case strings.HasPrefix(pattern, modulePath+"/"):
localPattern = "./" + strings.TrimPrefix(pattern, modulePath+"/")
default:
return nil, fmt.Errorf("test package %q is outside module %q", pattern, modulePath)
}
}
switch localPattern {
case ".":
return exec.Command("go", "test", "."), nil
case "./...":
return exec.Command("go", "test", "./..."), nil
}
relative := strings.TrimPrefix(localPattern, "./")
recursive := strings.HasSuffix(relative, "/...")
relative = strings.TrimSuffix(relative, "/...")
directory, err := resolveProjectDirectory(relative)
if err != nil {
return nil, fmt.Errorf("test package %q: %w", pattern, err)
}
var cmd *exec.Cmd
if recursive {
cmd = exec.Command("go", "test", "./...")
} else {
cmd = exec.Command("go", "test", ".")
}
cmd.Dir = directory
return cmd, nil
}
F
function
resolveProjectDirectory
Parameters
relative
string
Returns
string
error
cmd/rfw/plugins/test/test.go:135-160
func resolveProjectDirectory(relative string) (string, error)
{
projectRoot, err := filepath.Abs(".")
if err != nil {
return "", err
}
resolvedRoot, err := filepath.EvalSymlinks(projectRoot)
if err != nil {
return "", err
}
resolvedDirectory, err := filepath.EvalSymlinks(filepath.Join(resolvedRoot, filepath.Clean(relative)))
if err != nil {
return "", err
}
info, err := os.Stat(resolvedDirectory)
if err != nil {
return "", err
}
if !info.IsDir() {
return "", fmt.Errorf("%q is not a directory", relative)
}
pathWithinRoot, err := filepath.Rel(resolvedRoot, resolvedDirectory)
if err != nil || pathWithinRoot == ".." || strings.HasPrefix(pathWithinRoot, ".."+string(filepath.Separator)) {
return "", fmt.Errorf("%q resolves outside the project", relative)
}
return resolvedDirectory, nil
}
F
function
TestShouldRebuild
TestShouldRebuild verifies that the test plugin triggers rebuilds for Go test
files only.
Parameters
t
cmd/rfw/plugins/test/test_test.go:13-21
func TestShouldRebuild(t *testing.T)
{
p := &plugin{}
if !p.ShouldRebuild("foo_test.go") {
t.Fatalf("expected rebuild for _test.go files")
}
if p.ShouldRebuild("main.go") {
t.Fatalf("non-test files should not trigger rebuild")
}
}
F
function
TestValidatePackagePattern
Parameters
t
cmd/rfw/plugins/test/test_test.go:23-34
func TestValidatePackagePattern(t *testing.T)
{
for _, pattern := range []string{".", "./...", "./pkg/...", "./foo-bar", "example.com/team/module/pkg"} {
if err := validatePackagePattern(pattern); err != nil {
t.Errorf("expected %q to be valid: %v", pattern, err)
}
}
for _, pattern := range []string{"", "-exec=sh", "../...", "/tmp/pkg", "./pkg name", "./pkg=./other", "./pkg/.../nested"} {
if err := validatePackagePattern(pattern); err == nil {
t.Errorf("expected %q to be rejected", pattern)
}
}
}
F
function
TestConfiguredPackagesDefaultsToCurrentPackage
Parameters
t
cmd/rfw/plugins/test/test_test.go:36-48
func TestConfiguredPackagesDefaultsToCurrentPackage(t *testing.T)
{
for _, packages := range [][]string{nil, {}} {
configured := configuredPackages(packages)
if len(configured) != 1 || configured[0] != "." {
t.Fatalf("configured packages %v, want current package", configured)
}
}
configured := configuredPackages([]string{"./..."})
if len(configured) != 1 || configured[0] != "./..." {
t.Fatalf("configured packages %v, want recursive project pattern", configured)
}
}
F
function
TestPackageTestCommand
Parameters
t
cmd/rfw/plugins/test/test_test.go:50-105
func TestPackageTestCommand(t *testing.T)
{
root := t.TempDir()
packageDirectory := filepath.Join(root, "pkg")
if err := os.Mkdir(packageDirectory, 0o750); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example.com/team/module\n\ngo 1.25\n"), 0o600); err != nil {
t.Fatal(err)
}
oldWorkingDirectory, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(root); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := os.Chdir(oldWorkingDirectory); err != nil {
t.Errorf("restore working directory: %v", err)
}
})
cmd, err := packageTestCommand("./pkg/...")
if err != nil {
t.Fatalf("create package test command: %v", err)
}
if cmd.Path == "" || len(cmd.Args) != 3 || cmd.Args[1] != "test" || cmd.Args[2] != "./..." {
t.Fatalf("unexpected command: path=%q args=%v", cmd.Path, cmd.Args)
}
resolvedPackageDirectory, err := filepath.EvalSymlinks(packageDirectory)
if err != nil {
t.Fatal(err)
}
if cmd.Dir != resolvedPackageDirectory {
t.Fatalf("command directory %q, want %q", cmd.Dir, resolvedPackageDirectory)
}
moduleCommand, err := packageTestCommand("example.com/team/module/pkg")
if err != nil {
t.Fatalf("create module package test command: %v", err)
}
if moduleCommand.Dir != resolvedPackageDirectory || moduleCommand.Args[2] != "." {
t.Fatalf("unexpected module command: dir=%q args=%v", moduleCommand.Dir, moduleCommand.Args)
}
if _, err := packageTestCommand("example.com/other/module/pkg"); err == nil {
t.Fatal("expected external module package to be rejected")
}
outside := t.TempDir()
if err := os.Symlink(outside, filepath.Join(root, "outside")); err != nil {
t.Fatal(err)
}
if _, err := packageTestCommand("./outside"); err == nil {
t.Fatal("expected symlink escape to be rejected")
}
}