From 09ff0cd9185def7ff8b1b897d343eaf7b785034e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E8=81=AA?= Date: Mon, 27 Dec 2021 22:03:16 +0800 Subject: [PATCH] feat: data-driven. --- boomer.go | 9 +++++++-- models.go | 15 ++++++++------- parser.go | 32 ++++++++++++++++++++++++++++++-- runner.go | 13 ++++--------- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/boomer.go b/boomer.go index fb8b5d71..c70bb005 100644 --- a/boomer.go +++ b/boomer.go @@ -45,8 +45,13 @@ func (b *hrpBoomer) Run(testcases ...ITestCase) { if err != nil { panic(err) } - task := b.convertBoomerTask(testcase) - taskSlice = append(taskSlice, task) + cfg := testcase.Config.ToStruct() + parameters := getParameters(testcase.Config) + for _, parameter := range parameters { + cfg.Variables = mergeVariables(parameter, cfg.Variables) + task := b.convertBoomerTask(testcase) + taskSlice = append(taskSlice, task) + } } b.Boomer.Run(taskSlice...) } diff --git a/models.go b/models.go index cbaf4b4e..703623fc 100644 --- a/models.go +++ b/models.go @@ -13,13 +13,14 @@ const ( // TConfig represents config data structure for testcase. // Each testcase should contain one config part. type TConfig struct { - Name string `json:"name" yaml:"name"` // required - Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` - BaseURL string `json:"base_url,omitempty" yaml:"base_url,omitempty"` - Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` - Parameters map[string]interface{} `json:"parameters,omitempty" yaml:"parameters,omitempty"` - Export []string `json:"export,omitempty" yaml:"export,omitempty"` - Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` + Name string `json:"name" yaml:"name"` // required + Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` + BaseURL string `json:"base_url,omitempty" yaml:"base_url,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty" yaml:"parameters,omitempty"` + ParametersSetting map[string]interface{} `json:"parameters_setting,omitempty" yaml:"parameters_setting,omitempty"` + Export []string `json:"export,omitempty" yaml:"export,omitempty"` + Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` } // Request represents HTTP request data structure. diff --git a/parser.go b/parser.go index 10ec4a59..7fe8e410 100644 --- a/parser.go +++ b/parser.go @@ -3,10 +3,12 @@ package hrp import ( "encoding/json" "fmt" + "math/rand" "net/url" "reflect" "regexp" "strings" + "time" "github.com/maja42/goval" "github.com/pkg/errors" @@ -515,18 +517,44 @@ func findallVariables(raw string) variableSet { return varSet } +func shuffleCartesianProduct(slice []map[string]interface{}) { + r := rand.New(rand.NewSource(time.Now().Unix())) + for len(slice) > 0 { + n := len(slice) + randIndex := r.Intn(n) + slice[n-1], slice[randIndex] = slice[randIndex], slice[n-1] + slice = slice[:n-1] + } +} + func genCartesianProduct(params [][]map[string]interface{}) []map[string]interface{} { var cartesianProduct []map[string]interface{} + cartesianProduct = params[0] for i := 0; i < len(params)-1; i++ { - for _, param1 := range params[i] { + var tempProduct []map[string]interface{} + for _, param1 := range cartesianProduct { for _, param2 := range params[i+1] { - cartesianProduct = append(cartesianProduct, mergeVariables(param1, param2)) + tempProduct = append(tempProduct, mergeVariables(param1, param2)) } } + cartesianProduct = tempProduct } return cartesianProduct } +func getParameters(config IConfig) []map[string]interface{} { + cfg := config.ToStruct() + // parse config parameters + parsedParams, err := parseParameters(cfg.Parameters, cfg.Variables) + if err != nil { + log.Error().Interface("params", cfg.Parameters).Err(err).Msg("parse config parameters failed") + } + if cfg.ParametersSetting["strategy"] != nil && strings.ToLower(cfg.ParametersSetting["strategy"].(string)) == "random" { + shuffleCartesianProduct(parsedParams) + } + return parsedParams +} + func parseParameters(parameters map[string]interface{}, variablesMapping map[string]interface{}) ([]map[string]interface{}, error) { var parsedParametersList [][]map[string]interface{} for k, v := range parameters { diff --git a/runner.go b/runner.go index 24d77ef6..18dff670 100644 --- a/runner.go +++ b/runner.go @@ -128,16 +128,11 @@ func (r *hrpRunner) runCase(testcase *TestCase) error { if err := r.parseConfig(config); err != nil { return err } - + cfg := config.ToStruct() log.Info().Str("testcase", config.Name()).Msg("run testcase start") - // parse config parameters - parsedParams, err := parseParameters(config.ToStruct().Parameters, config.ToStruct().Variables) - if err != nil { - log.Error().Interface("params", config.ToStruct().Parameters).Err(err).Msg("parse config parameters failed") - return err - } - for _, parameter := range parsedParams { - config.ToStruct().Variables = mergeVariables(parameter, config.ToStruct().Variables) + parameters := getParameters(config) + for _, parameter := range parameters { + cfg.Variables = mergeVariables(parameter, cfg.Variables) r.startTime = time.Now() for _, step := range testcase.TestSteps { _, err := r.runStep(step, config)