From 019b1b53e57336935eb49cf51e802fe71625b47f Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Fri, 5 Mar 2021 17:06:14 +0100 Subject: [PATCH] fix: user_executor inside user_executor (#380) close #371 fix display info from user executor Signed-off-by: Yvonnick Esnault --- dump.go | 2 ++ process_testcase.go | 1 + process_teststep.go | 13 ++++++++++++- process_testsuite.go | 2 -- read_partial.go | 2 +- tests/lib/user_executor_A.yml | 10 ++++++++++ tests/lib/user_executor_B.yml | 12 ++++++++++++ tests/user_executor_many.yml | 12 ++++++++++++ types.go | 2 ++ types_executor.go | 19 ++++++++++++++----- venom.go | 32 ++++++++++++++++++-------------- 11 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 tests/lib/user_executor_A.yml create mode 100644 tests/lib/user_executor_B.yml create mode 100644 tests/user_executor_many.yml diff --git a/dump.go b/dump.go index eb7060ed..bd948f2a 100644 --- a/dump.go +++ b/dump.go @@ -15,6 +15,7 @@ func Dump(v interface{}) (map[string]interface{}, error) { return e.ToMap(v) } +// DumpString dumps v as a map[string]string{}, key in lowercase func DumpString(v interface{}) (map[string]string, error) { e := dump.NewDefaultEncoder() e.ExtraFields.Len = true @@ -26,6 +27,7 @@ func DumpString(v interface{}) (map[string]string, error) { return e.ToStringMap(v) } +// DumpStringPreserveCase dumps v as a map[string]string{} func DumpStringPreserveCase(v interface{}) (map[string]string, error) { e := dump.NewDefaultEncoder() e.ExtraFields.Len = true diff --git a/process_testcase.go b/process_testcase.go index 2a4876eb..4f812379 100644 --- a/process_testcase.go +++ b/process_testcase.go @@ -121,6 +121,7 @@ func (v *Venom) parseTestCase(ts *TestSuite, tc *TestCase) ([]string, []string, func (v *Venom) runTestCase(ctx context.Context, ts *TestSuite, tc *TestCase) { ctx = context.WithValue(ctx, ContextKey("testcase"), tc.Name) + tc.TestSuiteVars = ts.Vars.Clone() tc.Vars = ts.Vars.Clone() tc.Vars.Add("venom.testcase", tc.Name) tc.Vars.AddAll(ts.ComputedVars) diff --git a/process_teststep.go b/process_teststep.go index de889702..8437cd6e 100644 --- a/process_teststep.go +++ b/process_teststep.go @@ -80,7 +80,18 @@ func (v *Venom) RunTestStep(ctx context.Context, e ExecutorRunner, tc *TestCase, continue } filename := StringVarFromCtx(ctx, "venom.testsuite.filename") - info += fmt.Sprintf(" (%s:%d)", filename, findLineNumber(filename, tc.originalName, stepNumber, i, ninfo+1)) + originalName := tc.originalName + lineNumber := findLineNumber(filename, tc.originalName, stepNumber, i, ninfo+1) + if lineNumber > 0 { + info += fmt.Sprintf(" (%s:%d)", filename, lineNumber) + } else if tc.IsExecutor { + filename = StringVarFromCtx(ctx, "venom.executor.filename") + originalName = StringVarFromCtx(ctx, "venom.executor.name") + lineNumber = findLineNumber(filename, originalName, stepNumber, i, ninfo+1) + } + if lineNumber > 0 { + info += fmt.Sprintf(" (%s:%d)", filename, lineNumber) + } Info(ctx, info) tc.computedInfo = append(tc.computedInfo, info) } diff --git a/process_testsuite.go b/process_testsuite.go index f6a06ed9..390a0c91 100644 --- a/process_testsuite.go +++ b/process_testsuite.go @@ -12,8 +12,6 @@ import ( log "github.com/sirupsen/logrus" ) -type ContextKey string - func (v *Venom) runTestSuite(ctx context.Context, ts *TestSuite) { if v.Verbose == 3 { var filename, filenameCPU, filenameMem string diff --git a/read_partial.go b/read_partial.go index 981a2adf..ab26d70e 100644 --- a/read_partial.go +++ b/read_partial.go @@ -11,7 +11,7 @@ import ( "github.com/pkg/errors" ) -func getUserExecutorPartialYML(ctx context.Context, btesIn []byte) (H, error) { +func getUserExecutorInputYML(ctx context.Context, btesIn []byte) (H, error) { btes := readPartialYML(btesIn, "input") var result = map[string]interface{}{} diff --git a/tests/lib/user_executor_A.yml b/tests/lib/user_executor_A.yml new file mode 100644 index 00000000..e2f32f14 --- /dev/null +++ b/tests/lib/user_executor_A.yml @@ -0,0 +1,10 @@ +executor: userExecutorA +input: + foo: defaultValueA +steps: +- script: echo '{{.input.foo}}' + info: 'value on A: {{.input.foo}}' + assertions: + - result.code ShouldEqual 0 +- type: userExecutorB + foo: '{{.input.foo}}_on_b' \ No newline at end of file diff --git a/tests/lib/user_executor_B.yml b/tests/lib/user_executor_B.yml new file mode 100644 index 00000000..903eb4e0 --- /dev/null +++ b/tests/lib/user_executor_B.yml @@ -0,0 +1,12 @@ +executor: userExecutorB +input: + foo: defaultValueB +steps: + +- script: echo '{{.input.foo}}' + info: 'value on B: {{.input.foo}}' +- type: exec + script: echo 'input.foo {{.input.foo}}; fooFromEnv {{.foo}}' + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldEqual input.foo foo_from_vars_on_b; fooFromEnv foo_from_vars \ No newline at end of file diff --git a/tests/user_executor_many.yml b/tests/user_executor_many.yml new file mode 100644 index 00000000..18214d41 --- /dev/null +++ b/tests/user_executor_many.yml @@ -0,0 +1,12 @@ +name: Many user executor +vars: + foo: "foo_from_vars" + +testcases: +- name: info + steps: + - script: echo "foo" + info: 'value foo vars {{.foo}}' +- name: mytc + steps: + - type: userExecutorA \ No newline at end of file diff --git a/types.go b/types.go index 0c9b6300..54e5d6da 100644 --- a/types.go +++ b/types.go @@ -111,11 +111,13 @@ type TestCase struct { Time string `xml:"time,attr,omitempty" json:"time" yaml:"time,omitempty"` RawTestSteps []json.RawMessage `xml:"-" json:"steps" yaml:"steps"` testSteps []TestStep + TestSuiteVars H `xml:"-" json:"-" yaml:"-"` Vars H `xml:"-" json:"-" yaml:"vars"` computedVars H computedInfo []string computedVerbose []string Skip []string `xml:"-" json:"skip" yaml:"skip"` + IsExecutor bool `xml:"-" json:"-" yaml:"-"` } // TestStep represents a testStep diff --git a/types_executor.go b/types_executor.go index a810881d..3f8af20a 100644 --- a/types_executor.go +++ b/types_executor.go @@ -145,8 +145,10 @@ type UserExecutor struct { Input H `json:"input" yaml:"input"` RawTestSteps []json.RawMessage `json:"steps" yaml:"steps"` Output json.RawMessage `json:"output" yaml:"output"` + Filename string `json:"-" yaml:"-"` } +// Run is not implemented on user executor func (ux UserExecutor) Run(ctx context.Context, step TestStep) (interface{}, error) { return nil, errors.New("Run not implemented for user interface, use RunUserExecutor instead") } @@ -172,11 +174,14 @@ func (ux UserExecutor) ZeroValueResult() interface{} { } func (v *Venom) RunUserExecutor(ctx context.Context, runner ExecutorRunner, tcIn *TestCase, step TestStep) (interface{}, error) { - vrs := H{} + vrs := tcIn.TestSuiteVars.Clone() uxIn := runner.GetExecutor().(UserExecutor) for k, va := range uxIn.Input { - if !strings.HasPrefix(k, "venom") { + if strings.HasPrefix(k, "input.") { + // do not reinject input.vars from parent user executor if exists + continue + } else if !strings.HasPrefix(k, "venom") { if vl, ok := step[k]; ok && vl != "" { // value from step vrs.AddWithPrefix("input", k, vl) } else { // default value from executor @@ -191,14 +196,18 @@ func (v *Venom) RunUserExecutor(ctx context.Context, runner ExecutorRunner, tcIn ux := exe.GetExecutor().(UserExecutor) tc := &TestCase{ - Name: ux.Executor, - RawTestSteps: ux.RawTestSteps, - Vars: vrs, + Name: ux.Executor, + RawTestSteps: ux.RawTestSteps, + Vars: vrs, + TestSuiteVars: tcIn.TestSuiteVars, + IsExecutor: true, } tc.originalName = tc.Name tc.Name = slug.Make(tc.Name) tc.Vars.Add("venom.testcase", tc.Name) + tc.Vars.Add("venom.executor.filename", ux.Filename) + tc.Vars.Add("venom.executor.name", ux.Executor) tc.computedVars = H{} Debug(ctx, "running user executor %v", tc.Name) diff --git a/venom.go b/venom.go index 0bad2b03..e7253875 100644 --- a/venom.go +++ b/venom.go @@ -25,6 +25,10 @@ var ( Version = "snapshot" ) +// ContextKey can be added in context to store contextual infos. Also used by logger. +type ContextKey string + +// New instanciates a new venom on venom run cmd func New() *Venom { v := &Venom{ LogOutput: os.Stdout, @@ -93,25 +97,25 @@ func (v *Venom) RegisterExecutorUser(name string, e Executor) { // GetExecutorRunner initializes a test by name // no type -> exec is default -func (v *Venom) GetExecutorRunner(ctx context.Context, t TestStep, h H) (context.Context, ExecutorRunner, error) { - name, _ := t.StringValue("type") +func (v *Venom) GetExecutorRunner(ctx context.Context, ts TestStep, h H) (context.Context, ExecutorRunner, error) { + name, _ := ts.StringValue("type") if name == "" { name = "exec" } - retry, err := t.IntValue("retry") + retry, err := ts.IntValue("retry") if err != nil { return nil, nil, err } - delay, err := t.IntValue("delay") + delay, err := ts.IntValue("delay") if err != nil { return nil, nil, err } - timeout, err := t.IntValue("timeout") + timeout, err := ts.IntValue("timeout") if err != nil { return nil, nil, err } - info, _ := t.StringSliceValue("info") + info, _ := ts.StringSliceValue("info") vars, err := DumpStringPreserveCase(h) if err != nil { return ctx, nil, err @@ -128,7 +132,7 @@ func (v *Venom) GetExecutorRunner(ctx context.Context, t TestStep, h H) (context return ctx, newExecutorRunner(ex, name, "builtin", retry, delay, timeout, info), nil } - if err := v.registerUserExecutors(ctx, name, vars); err != nil { + if err := v.registerUserExecutors(ctx, name, ts, vars); err != nil { Debug(ctx, "executor %q is not implemented as user executor - err:%v", name, err) } @@ -175,7 +179,7 @@ func (v *Venom) getUserExecutorFilesPath(vars map[string]string) (filePaths []st return filePaths, nil } -func (v *Venom) registerUserExecutors(ctx context.Context, name string, vars map[string]string) error { +func (v *Venom) registerUserExecutors(ctx context.Context, name string, ts TestStep, vars map[string]string) error { executorsPath, err := v.getUserExecutorFilesPath(vars) if err != nil { return err @@ -188,13 +192,13 @@ func (v *Venom) registerUserExecutors(ctx context.Context, name string, vars map return errors.Wrapf(err, "unable to read file %q", f) } - varsFromPartial, err := getUserExecutorPartialYML(ctx, btes) + varsFromInput, err := getUserExecutorInputYML(ctx, btes) if err != nil { return err } - // varsFromPartial contains the default vars from the executor - varsFromPartialMap, err := DumpStringPreserveCase(varsFromPartial) + // varsFromInput contains the default vars from the executor + varsFromInputMap, err := DumpStringPreserveCase(varsFromInput) if err != nil { return errors.Wrapf(err, "unable to parse variables") } @@ -203,8 +207,8 @@ func (v *Venom) registerUserExecutors(ctx context.Context, name string, vars map for k, v := range vars { varsComputed[k] = v } - for k, v := range varsFromPartialMap { - // we only take vars from varsFromPartialMap if it's not already exist in vars from arg + for k, v := range varsFromInputMap { + // we only take vars from varsFromInputMap if it's not already exist in vars from teststep vars if _, ok := vars[k]; !ok { varsComputed[k] = v } @@ -215,7 +219,7 @@ func (v *Venom) registerUserExecutors(ctx context.Context, name string, vars map return err } - ux := UserExecutor{} + ux := UserExecutor{Filename: f} if err := yaml.Unmarshal([]byte(content), &ux); err != nil { return errors.Wrapf(err, "unable to parse file %q", f) }