diff --git a/platformtest/harness/harness.go b/platformtest/harness/harness.go index eb6ac97..368f181 100644 --- a/platformtest/harness/harness.go +++ b/platformtest/harness/harness.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "os" "path/filepath" "time" @@ -17,12 +18,25 @@ import ( "github.com/zrepl/zrepl/platformtest/tests" ) -func main() { +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 +var args struct { + createArgs platformtest.ZpoolCreateArgs + stopAndKeepPoolOnFail bool +} + +func main() { + if err := doMain(); err != nil { + os.Exit(1) } +} + +var exitWithErr = fmt.Errorf("exit with error") + +func doMain() error { + flag.StringVar(&args.createArgs.PoolName, "poolname", "", "") flag.StringVar(&args.createArgs.ImagePath, "imagepath", "", "") flag.Int64Var(&args.createArgs.ImageSize, "imagesize", 100*(1<<20), "") @@ -50,48 +64,83 @@ func main() { ctx := platformtest.WithLogger(context.Background(), logger) ex := platformtest.NewEx(logger) - bold := color.New(color.Bold) - boldRed := color.New(color.Bold, color.FgHiRed) - boldGreen := color.New(color.Bold, color.FgHiGreen) for _, c := range tests.Cases { - // ATTENTION future parallelism must pass c by value into closure! - err := func() (err error) { - var oErr = &err - bold.Printf("BEGIN TEST CASE %s\n", c) - pool, err := platformtest.CreateOrReplaceZpool(ctx, ex, args.createArgs) - if err != nil { - return errors.Wrap(err, "create test pool") - } - defer func() { - if err := recover(); err != nil { - *oErr = errors.Errorf("panic while running test: %v", err) // shadow - if args.stopAndKeepPoolOnFail { - return - } - // fallthrough - } - if err := pool.Destroy(ctx, ex); err != nil { - fmt.Printf("error destroying test pool: %s", err) - } - }() - ctx := &platformtest.Context{ - Context: ctx, - RootDataset: filepath.Join(pool.Name(), "rootds"), - } - c(ctx) - return nil - }() + + bold.Printf("BEGIN TEST CASE %s\n", c) + + pool, err := platformtest.CreateOrReplaceZpool(ctx, ex, args.createArgs) if err != nil { - boldRed.Printf("TEST CASE FAILED WITH ERROR:\n") - fmt.Printf("%+v\n", err) // print with stack trace - if args.stopAndKeepPoolOnFail { - bold.Printf("STOPPING TEST RUN AT FAILING TEST PER USER REQUEST\n") - return - } - } else { - boldGreen.Printf("DONE TEST CASE %s\n", c) + panic(errors.Wrap(err, "create test pool")) } + + ctx := &platformtest.Context{ + Context: ctx, + RootDataset: filepath.Join(pool.Name(), "rootds"), + } + + res := runTestCase(ctx, ex, c) + if res.failed { + fmt.Printf("%+v\n", res.failedStack) // print with stack trace + } + + if res.failed && args.stopAndKeepPoolOnFail { + boldRed.Printf("STOPPING TEST RUN AT FAILING TEST PER USER REQUEST\n") + return exitWithErr + } + + if err := pool.Destroy(ctx, ex); err != nil { + panic(fmt.Sprintf("error destroying test pool: %s", err)) + } + + if res.failed { + boldRed.Printf("TEST FAILED\n") + } else if res.skipped { + bold.Printf("TEST SKIPPED\n") + } else if res.succeeded { + boldGreen.Printf("TEST PASSED\n") + } + fmt.Println() } + return nil +} + +type testCaseResult struct { + // oneof + failed, skipped, succeeded bool + + failedStack error // has stack inside, valid if failed=true +} + +func runTestCase(ctx *platformtest.Context, ex platformtest.Execer, c tests.Case) testCaseResult { + + // run case + var paniced = false + var panicValue interface{} = nil + var panicStack error + func() { + defer func() { + if item := recover(); item != nil { + panicValue = item + paniced = true + panicStack = errors.Errorf("panic while running test: %v", panicValue) + } + }() + c(ctx) + }() + + if paniced { + switch panicValue { + case platformtest.SkipNowSentinel: + return testCaseResult{skipped: true} + case platformtest.FailNowSentinel: + return testCaseResult{failed: true, failedStack: panicStack} + default: + return testCaseResult{failed: true, failedStack: panicStack} + } + } else { + return testCaseResult{succeeded: true} + } + } diff --git a/platformtest/platformtest.go b/platformtest/platformtest.go index e506ba7..513c0cc 100644 --- a/platformtest/platformtest.go +++ b/platformtest/platformtest.go @@ -15,6 +15,8 @@ type Context struct { var FailNowSentinel = fmt.Errorf("platformtest: FailNow called on context") +var SkipNowSentinel = fmt.Errorf("platformtest: SkipNow called on context") + var _ assert.TestingT = (*Context)(nil) var _ require.TestingT = (*Context)(nil) @@ -25,3 +27,7 @@ func (c *Context) Errorf(format string, args ...interface{}) { func (c *Context) FailNow() { panic(FailNowSentinel) } + +func (c *Context) SkipNow() { + panic(SkipNowSentinel) +}