[#321] platformtest: generate test case list + coverage tooling
This commit is contained in:
@@ -24,33 +24,36 @@ var bold = color.New(color.Bold)
|
||||
var boldRed = color.New(color.Bold, color.FgHiRed)
|
||||
var boldGreen = color.New(color.Bold, color.FgHiGreen)
|
||||
|
||||
var args struct {
|
||||
createArgs platformtest.ZpoolCreateArgs
|
||||
stopAndKeepPoolOnFail bool
|
||||
|
||||
run string
|
||||
runRE *regexp.Regexp
|
||||
}
|
||||
const DefaultPoolImageSize = 200 * (1 << 20)
|
||||
|
||||
func main() {
|
||||
if err := doMain(); err != nil {
|
||||
|
||||
var args HarnessArgs
|
||||
|
||||
flag.StringVar(&args.CreateArgs.PoolName, "poolname", "", "")
|
||||
flag.StringVar(&args.CreateArgs.ImagePath, "imagepath", "", "")
|
||||
flag.Int64Var(&args.CreateArgs.ImageSize, "imagesize", DefaultPoolImageSize, "")
|
||||
flag.StringVar(&args.CreateArgs.Mountpoint, "mountpoint", "", "")
|
||||
flag.BoolVar(&args.StopAndKeepPoolOnFail, "failure.stop-and-keep-pool", false, "if a test case fails, stop test execution and keep pool as it was when the test failed")
|
||||
flag.StringVar(&args.Run, "run", "", "")
|
||||
flag.Parse()
|
||||
|
||||
if err := HarnessRun(args); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var exitWithErr = fmt.Errorf("exit with error")
|
||||
|
||||
func doMain() error {
|
||||
type HarnessArgs struct {
|
||||
CreateArgs platformtest.ZpoolCreateArgs
|
||||
StopAndKeepPoolOnFail bool
|
||||
Run string
|
||||
}
|
||||
|
||||
flag.StringVar(&args.createArgs.PoolName, "poolname", "", "")
|
||||
flag.StringVar(&args.createArgs.ImagePath, "imagepath", "", "")
|
||||
flag.Int64Var(&args.createArgs.ImageSize, "imagesize", 200*(1<<20), "")
|
||||
flag.StringVar(&args.createArgs.Mountpoint, "mountpoint", "", "")
|
||||
flag.BoolVar(&args.stopAndKeepPoolOnFail, "failure.stop-and-keep-pool", false, "if a test case fails, stop test execution and keep pool as it was when the test failed")
|
||||
flag.StringVar(&args.run, "run", "", "")
|
||||
flag.Parse()
|
||||
func HarnessRun(args HarnessArgs) error {
|
||||
|
||||
args.runRE = regexp.MustCompile(args.run)
|
||||
runRE := regexp.MustCompile(args.Run)
|
||||
|
||||
outlets := logger.NewOutlets()
|
||||
outlet, level, err := logging.ParseOutlet(config.LoggingOutletEnum{Ret: &config.StdoutLoggingOutlet{
|
||||
@@ -65,7 +68,7 @@ func doMain() error {
|
||||
outlets.Add(outlet, level)
|
||||
logger := logger.NewLogger(outlets, 1*time.Second)
|
||||
|
||||
if err := args.createArgs.Validate(); err != nil {
|
||||
if err := args.CreateArgs.Validate(); err != nil {
|
||||
logger.Error(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
@@ -81,7 +84,7 @@ func doMain() error {
|
||||
|
||||
invocations := make([]*invocation, 0, len(tests.Cases))
|
||||
for _, c := range tests.Cases {
|
||||
if args.runRE.MatchString(c.String()) {
|
||||
if runRE.MatchString(c.String()) {
|
||||
invocations = append(invocations, &invocation{runFunc: c})
|
||||
}
|
||||
}
|
||||
@@ -90,7 +93,7 @@ func doMain() error {
|
||||
|
||||
bold.Printf("BEGIN TEST CASE %s\n", inv.runFunc.String())
|
||||
|
||||
pool, err := platformtest.CreateOrReplaceZpool(ctx, ex, args.createArgs)
|
||||
pool, err := platformtest.CreateOrReplaceZpool(ctx, ex, args.CreateArgs)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "create test pool"))
|
||||
}
|
||||
@@ -106,7 +109,7 @@ func doMain() error {
|
||||
fmt.Printf("%+v\n", res.failedStack) // print with stack trace
|
||||
}
|
||||
|
||||
if res.failed && args.stopAndKeepPoolOnFail {
|
||||
if res.failed && args.StopAndKeepPoolOnFail {
|
||||
boldRed.Printf("STOPPING TEST RUN AT FAILING TEST PER USER REQUEST\n")
|
||||
return exitWithErr
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -21,22 +22,29 @@ import (
|
||||
// https://github.com/wadey/gocovmerge
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
fmt.Println("incoming args: ", os.Args)
|
||||
|
||||
var (
|
||||
args []string
|
||||
run bool
|
||||
args []string
|
||||
run bool
|
||||
startCaptureArgs bool
|
||||
)
|
||||
|
||||
for _, arg := range os.Args {
|
||||
for i, arg := range os.Args {
|
||||
switch {
|
||||
case arg == "__DEVEL--i-heard-you-like-tests":
|
||||
run = true
|
||||
startCaptureArgs = true
|
||||
case strings.HasPrefix(arg, "-test"):
|
||||
case strings.HasPrefix(arg, "__DEVEL"):
|
||||
default:
|
||||
case i == 0:
|
||||
args = append(args, arg)
|
||||
case startCaptureArgs:
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
os.Args = args
|
||||
fmt.Println("using args: ", os.Args)
|
||||
|
||||
if run {
|
||||
main()
|
||||
|
||||
@@ -58,6 +58,14 @@ func CreateOrReplaceZpool(ctx context.Context, e Execer, args ZpoolCreateArgs) (
|
||||
}
|
||||
}
|
||||
|
||||
// clear the mountpoint dir
|
||||
if err := os.RemoveAll(args.Mountpoint); err != nil {
|
||||
return nil, errors.Wrapf(err, "remove mountpoint dir %q", args.Mountpoint)
|
||||
}
|
||||
if err := os.Mkdir(args.Mountpoint, 0700); err != nil {
|
||||
return nil, errors.Wrapf(err, "create mountpoint dir %q", args.Mountpoint)
|
||||
}
|
||||
|
||||
// idempotently (re)create the pool image
|
||||
image, err := os.OpenFile(args.ImagePath, os.O_CREATE|os.O_RDWR, 0600)
|
||||
if err != nil {
|
||||
@@ -93,5 +101,9 @@ func (p *Zpool) Destroy(ctx context.Context, e Execer) error {
|
||||
return errors.Wrapf(err, "remove pool image")
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(p.args.Mountpoint); err != nil {
|
||||
return errors.Wrapf(err, "remove mountpoint dir %q", p.args.Mountpoint)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type platformtestFuncDeclFinder struct {
|
||||
pkg *packages.Package
|
||||
testFuncs []*ast.FuncDecl
|
||||
}
|
||||
|
||||
func isPlatformtestFunc(n *ast.FuncDecl) bool {
|
||||
if !n.Name.IsExported() {
|
||||
return false
|
||||
}
|
||||
if n.Recv != nil {
|
||||
return false
|
||||
}
|
||||
if n.Type.Results.NumFields() != 0 {
|
||||
return false
|
||||
}
|
||||
if n.Type.Params.NumFields() != 1 {
|
||||
return false
|
||||
}
|
||||
se, ok := n.Type.Params.List[0].Type.(*ast.StarExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
sel, ok := se.X.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
x, ok := sel.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if x.Name != "platformtest" || sel.Sel.Name != "Context" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *platformtestFuncDeclFinder) Visit(n2 ast.Node) ast.Visitor {
|
||||
switch n := n2.(type) {
|
||||
case *ast.File:
|
||||
return e
|
||||
case *ast.FuncDecl:
|
||||
if isPlatformtestFunc(n) {
|
||||
e.testFuncs = append(e.testFuncs, n)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// TODO safeguards that prevent us from deleting non-generated generated_cases.go
|
||||
os.Remove("generated_cases.go")
|
||||
// (no error handling to easily cover the case where the file doesn't exist)
|
||||
|
||||
pkgs, err := packages.Load(
|
||||
&packages.Config{
|
||||
Mode: packages.LoadFiles,
|
||||
Tests: false,
|
||||
},
|
||||
os.Args[1],
|
||||
)
|
||||
check(err)
|
||||
|
||||
if len(pkgs) != 1 {
|
||||
panic(pkgs)
|
||||
}
|
||||
|
||||
p := pkgs[0]
|
||||
|
||||
var tests []*ast.FuncDecl
|
||||
|
||||
for _, f := range p.GoFiles {
|
||||
s := token.NewFileSet()
|
||||
a, err := parser.ParseFile(s, f, nil, parser.AllErrors)
|
||||
check(err)
|
||||
finder := &platformtestFuncDeclFinder{
|
||||
pkg: p,
|
||||
}
|
||||
ast.Walk(finder, a)
|
||||
tests = append(tests, finder.testFuncs...)
|
||||
}
|
||||
|
||||
sort.Slice(tests, func(i, j int) bool {
|
||||
return strings.Compare(tests[i].Name.Name, tests[j].Name.Name) < 0
|
||||
})
|
||||
|
||||
{
|
||||
casesTemplate := `
|
||||
// Code generated by zrepl tooling; DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
var Cases = []Case {
|
||||
{{- range . -}}
|
||||
{{ .Name }},
|
||||
{{ end -}}
|
||||
}
|
||||
|
||||
`
|
||||
t, err := template.New("CaseFunc").Parse(casesTemplate)
|
||||
check(err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = t.Execute(&buf, tests)
|
||||
check(err)
|
||||
|
||||
formatted, err := format.Source(buf.Bytes())
|
||||
check(err)
|
||||
|
||||
err = ioutil.WriteFile("generated_cases.go", formatted, 0664)
|
||||
check(err)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Code generated by zrepl tooling; DO NOT EDIT.
|
||||
|
||||
package tests
|
||||
|
||||
var Cases = []Case{BatchDestroy,
|
||||
CreateReplicationCursor,
|
||||
GetNonexistent,
|
||||
IdempotentBookmark,
|
||||
IdempotentDestroy,
|
||||
IdempotentHold,
|
||||
ListFilesystemVersionsFilesystemNotExist,
|
||||
ListFilesystemVersionsTypeFilteringAndPrefix,
|
||||
ListFilesystemVersionsUserrefs,
|
||||
ListFilesystemVersionsZeroExistIsNotAnError,
|
||||
ListFilesystemsNoFilter,
|
||||
ResumableRecvAndTokenHandling,
|
||||
ResumeTokenParsing,
|
||||
SendArgsValidationEncryptedSendOfUnencryptedDatasetForbidden,
|
||||
SendArgsValidationResumeTokenDifferentFilesystemForbidden,
|
||||
SendArgsValidationResumeTokenEncryptionMismatchForbidden,
|
||||
UndestroyableSnapshotParsing,
|
||||
}
|
||||
@@ -13,22 +13,4 @@ func (c Case) String() string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(c).Pointer()).Name()
|
||||
}
|
||||
|
||||
var Cases = []Case{
|
||||
BatchDestroy,
|
||||
UndestroyableSnapshotParsing,
|
||||
GetNonexistent,
|
||||
CreateReplicationCursor,
|
||||
IdempotentHold,
|
||||
IdempotentBookmark,
|
||||
IdempotentDestroy,
|
||||
ResumeTokenParsing,
|
||||
ResumableRecvAndTokenHandling,
|
||||
SendArgsValidationEncryptedSendOfUnencryptedDatasetForbidden,
|
||||
SendArgsValidationResumeTokenEncryptionMismatchForbidden,
|
||||
SendArgsValidationResumeTokenDifferentFilesystemForbidden,
|
||||
ListFilesystemVersionsTypeFilteringAndPrefix,
|
||||
ListFilesystemVersionsFilesystemNotExist,
|
||||
ListFilesystemVersionsFilesystemNotExist,
|
||||
ListFilesystemVersionsUserrefs,
|
||||
ListFilesystemsNoFilter,
|
||||
}
|
||||
//go:generate ../../artifacts/generate-platform-test-list github.com/zrepl/zrepl/platformtest/tests
|
||||
|
||||
Reference in New Issue
Block a user