From f7f6457f010322caa56fe4686d0d8f8ee19e10be Mon Sep 17 00:00:00 2001 From: Christoph Deppisch Date: Mon, 20 Apr 2020 09:08:21 +0200 Subject: [PATCH] fix(#60): Add possibility to run scripts before/after a test group You can run scripts before/after a test group. Just add your commands to the yaks-config.yaml configuration for the test group --- README.md | 26 +++++++++++ examples/run-scripts/finish.sh | 3 ++ examples/run-scripts/prepare.sh | 3 ++ examples/run-scripts/test.feature | 4 ++ examples/run-scripts/yaks-config.yaml | 10 ++++ pkg/cmd/config/config.go | 19 +++++--- pkg/cmd/test.go | 67 ++++++++++++++++++++++++++- 7 files changed, 124 insertions(+), 8 deletions(-) create mode 100755 examples/run-scripts/finish.sh create mode 100755 examples/run-scripts/prepare.sh create mode 100644 examples/run-scripts/test.feature create mode 100644 examples/run-scripts/yaks-config.yaml diff --git a/README.md b/README.md index a7080f84..8c73f011 100644 --- a/README.md +++ b/README.md @@ -433,6 +433,32 @@ Also we can make use of command line options when using the `yaks` binary. yaks test hello-world.feature --tag @regression --glue org.citrusframework.yaks ``` +## Pre/Post scripts + +You can run scripts before/after a test group. Just add your commands to the `yaks-config.yaml` configuration for the test group. + +```yaml +config: + namespace: + temporary: false + autoRemove: true +pre: + - script: prepare.sh + - run: echo Start! +post: + - script: finish.sh + - run: echo Bye! +``` + +The section `pre` runs before a test group and `post` is added after the test group has finished. The post steps are run even if the tests or pre steps fail +for some reason. This ensures that cleanup tasks are performed also in case of errors. + +The `script` option provides a file path to bash script to execute. The user has to make sure that the script is executable. If no absolute file path is +given it is assumed to be a file path relative to the current test group directory. + +With `run` you can add any shell command. At the moment only single line commands are supported here. You can add multiple `run` commands in a `pre` +or `post` section. + ## For YAKS Developers Requirements: diff --git a/examples/run-scripts/finish.sh b/examples/run-scripts/finish.sh new file mode 100755 index 00000000..c228c1b2 --- /dev/null +++ b/examples/run-scripts/finish.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo Test finshed! \ No newline at end of file diff --git a/examples/run-scripts/prepare.sh b/examples/run-scripts/prepare.sh new file mode 100755 index 00000000..db0b926e --- /dev/null +++ b/examples/run-scripts/prepare.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo Now preparing the test! \ No newline at end of file diff --git a/examples/run-scripts/test.feature b/examples/run-scripts/test.feature new file mode 100644 index 00000000..c88eafef --- /dev/null +++ b/examples/run-scripts/test.feature @@ -0,0 +1,4 @@ +Feature: test + + Scenario: print message + Given print 'Hello from YAKS!' diff --git a/examples/run-scripts/yaks-config.yaml b/examples/run-scripts/yaks-config.yaml new file mode 100644 index 00000000..0ba06b0e --- /dev/null +++ b/examples/run-scripts/yaks-config.yaml @@ -0,0 +1,10 @@ +config: + namespace: + temporary: false + autoRemove: true +pre: + - script: prepare.sh + - run: echo Start! +post: + - script: finish.sh + - run: echo Bye! diff --git a/pkg/cmd/config/config.go b/pkg/cmd/config/config.go index 55d632c0..83b571b0 100644 --- a/pkg/cmd/config/config.go +++ b/pkg/cmd/config/config.go @@ -26,16 +26,23 @@ import ( type RunConfig struct { Config Config `yaml:"config"` + Pre []StepConfig `yaml:"pre"` + Post []StepConfig `yaml:"post"` } type Config struct { - Recursive bool `yaml:"recursive"` - Namespace NamespaceConfig - Runtime RuntimeConfig + Recursive bool `yaml:"recursive"` + Namespace NamespaceConfig + Runtime RuntimeConfig +} + +type StepConfig struct { + Run string `yaml:"run"` + Script string `yaml:"script"` } type RuntimeConfig struct { - Cucumber CucumberConfig + Cucumber CucumberConfig } type CucumberConfig struct { @@ -50,7 +57,7 @@ type NamespaceConfig struct { AutoRemove bool `yaml:"autoremove"` } -func newWithDefaults() *RunConfig { +func NewWithDefaults() *RunConfig { ns := NamespaceConfig{ AutoRemove: true, Temporary: false, @@ -61,7 +68,7 @@ func newWithDefaults() *RunConfig { } func LoadConfig(file string) (*RunConfig, error) { - config := newWithDefaults() + config := NewWithDefaults() data, err := ioutil.ReadFile(file) if err != nil && os.IsNotExist(err) { return config, nil diff --git a/pkg/cmd/test.go b/pkg/cmd/test.go index 501f93d0..e3a6235e 100644 --- a/pkg/cmd/test.go +++ b/pkg/cmd/test.go @@ -23,6 +23,7 @@ import ( "io/ioutil" "net/http" "os" + "os/exec" "path" "regexp" "strings" @@ -147,6 +148,12 @@ func (o *testCmdOptions) runTest(source string) error { return err } + baseDir := getBaseDir(source) + defer runSteps(runConfig.Post, baseDir) + if err = runSteps(runConfig.Pre, baseDir); err != nil { + return err + } + _, err = o.createAndRunTest(c, source, runConfig) return err } @@ -180,6 +187,12 @@ func (o *testCmdOptions) runTestGroup(source string, results *map[string]error) return err } + baseDir := getBaseDir(source) + defer runSteps(runConfig.Post, baseDir) + if err = runSteps(runConfig.Pre, baseDir); err != nil { + return err + } + for _, f := range files { name := path.Join(source, f.Name()) if f.IsDir() && runConfig.Config.Recursive { @@ -200,6 +213,19 @@ func (o *testCmdOptions) runTestGroup(source string, results *map[string]error) return err } +func getBaseDir(source string) string { + if isRemoteFile(source) { + return "" + } + + if isDir(source) { + return source + } else { + dir, _ := path.Split(source); + return dir + } +} + func printSummary(results map[string]error) { summary := "\n\nTest suite results:\n" for k, v := range results { @@ -214,6 +240,12 @@ func printSummary(results map[string]error) { func (o *testCmdOptions) getRunConfig(source string) (*config.RunConfig, error) { var configFile string + var runConfig *config.RunConfig + + if isRemoteFile(source) { + return config.NewWithDefaults(), nil + } + if isDir(source) { // search for config file in given directory configFile = path.Join(source, ConfigFile) @@ -223,8 +255,6 @@ func (o *testCmdOptions) getRunConfig(source string) (*config.RunConfig, error) configFile = path.Join(dir, ConfigFile); } - var runConfig *config.RunConfig - runConfig, err := config.LoadConfig(configFile) if err != nil { return nil, err @@ -492,6 +522,39 @@ func isDir(fileName string) bool { return false } +func runSteps(steps []config.StepConfig, baseDir string) error { + for _, step := range steps { + if len(step.Script) > 0 { + var scriptFile string + + if len(baseDir) > 0 && !path.IsAbs(step.Script) { + scriptFile = path.Join(baseDir, step.Script) + } else { + scriptFile = step.Script + } + + if out, err := exec.Command(scriptFile).Output(); err == nil { + fmt.Printf("Running script %s: \n%s\n", step.Script, out) + } else { + fmt.Printf("Failed to run script %s: \n%s\n", step.Script, err) + return err + } + } + + if len(step.Run) > 0 { + tokens := strings.Split(step.Run, " ") + if out, err := exec.Command(tokens[0], tokens[1:]...).Output(); err == nil { + fmt.Printf("Running command %s: \n%s\n", step.Run, out) + } else { + fmt.Printf("Failed to run command %s: \n%s\n", step.Run, err) + return err + } + } + } + + return nil +} + func initializeTempNamespace(name string, c client.Client, context context.Context) (metav1.Object, error) { var obj runtime.Object