mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-07 23:41:22 +08:00
feat: eval literal
This commit is contained in:
1
go.mod
1
go.mod
@@ -8,6 +8,7 @@ require (
|
|||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/imroc/req v0.3.0 // indirect
|
github.com/imroc/req v0.3.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/maja42/goval v1.2.1 // indirect
|
||||||
github.com/myzhan/boomer v1.6.0
|
github.com/myzhan/boomer v1.6.0
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/shirou/gopsutil v3.21.8+incompatible // indirect
|
github.com/shirou/gopsutil v3.21.8+incompatible // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -13,6 +13,8 @@ github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Ca
|
|||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
github.com/maja42/goval v1.2.1 h1:fyEgzddqPgCZsKcFLk4C6SdCHyEaAHYvtZG4mGzQOHU=
|
||||||
|
github.com/maja42/goval v1.2.1/go.mod h1:42LU+BQXL/veE9jnTTUOSj38GRmOTSThYSXRVodI5J4=
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0=
|
github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0=
|
||||||
|
|||||||
28
parser.go
28
parser.go
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/httprunner/httpboomer/builtin"
|
"github.com/httprunner/httpboomer/builtin"
|
||||||
|
"github.com/maja42/goval"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseStep(step IStep, config *TConfig) *TStep {
|
func parseStep(step IStep, config *TConfig) *TStep {
|
||||||
@@ -77,13 +78,15 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore
|
regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore
|
||||||
regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore
|
regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore
|
||||||
|
regexNumber = `-?\d+(\.\d+)?` // match number, e.g. 123, -123, 1.23, -1.23
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var
|
regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var
|
||||||
regexCompileFunction = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\(([\$\w\.\-/\s=,]*)\)\}`, regexFunctionName)) // parse ${func1($a, $b)}
|
regexCompileFunction = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\(([\$\w\.\-/\s=,]*)\)\}`, regexFunctionName)) // parse ${func1($a, $b)}
|
||||||
|
regexCompileNumber = regexp.MustCompile(regexNumber) // parse number
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseString parse string with variables
|
// parseString parse string with variables
|
||||||
@@ -226,3 +229,24 @@ func callFunc(funcName string, arguments ...interface{}) (interface{}, error) {
|
|||||||
log.Printf("[callFunction] output result: %+v(%T)", result, result)
|
log.Printf("[callFunction] output result: %+v(%T)", result, result)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var eval = goval.NewEvaluator()
|
||||||
|
|
||||||
|
// literalEval parse string to number if possible
|
||||||
|
// e.g. "123" => 123
|
||||||
|
// "1.23" => 1.23
|
||||||
|
// "abc" => "abc"
|
||||||
|
// "$var" => "$var"
|
||||||
|
func literalEval(raw string) (interface{}, error) {
|
||||||
|
// check if raw is a number
|
||||||
|
if !regexCompileNumber.Match([]byte(raw)) {
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// eval string to number
|
||||||
|
result, err := eval.Evaluate(raw, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return raw, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -315,3 +315,28 @@ func TestCallFunction(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLiteralEval(t *testing.T) {
|
||||||
|
testData := []struct {
|
||||||
|
expr string
|
||||||
|
expect interface{}
|
||||||
|
}{
|
||||||
|
{"123", 123},
|
||||||
|
{"1.23", 1.23},
|
||||||
|
{"-123", -123},
|
||||||
|
{"-1.23", -1.23},
|
||||||
|
{"abc", "abc"},
|
||||||
|
{"$var", "$var"},
|
||||||
|
{"", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, data := range testData {
|
||||||
|
value, err := literalEval(data.expr)
|
||||||
|
if !assert.Nil(t, err) {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t, data.expect, value) {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user