test pkg/exprhelpers: explicit message if the tag "expr_debug" is missing (#3400)

* test pkg/exprhelpers: explicit message if the tag "expr_debug" is missing

* typo

* lint: use build tag expr_debug while linting

* lint
This commit is contained in:
mmetc 2025-01-15 12:13:54 +01:00 committed by GitHub
parent cc1196c3ad
commit 9ef5f58f88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 95 additions and 36 deletions

View file

@ -143,11 +143,11 @@ jobs:
go generate ./...
protoc --version
if [[ $(git status --porcelain) ]]; then
echo "Error: Uncommitted changes found after running 'make generate'. Please commit all generated code."
echo "Error: Uncommitted changes found after running 'go generate'. Please commit all generated code."
git diff
exit 1
else
echo "No changes detected after running 'make generate'."
echo "No changes detected after running 'go generate'."
fi
- name: Create localstack streams

View file

@ -1,5 +1,9 @@
# https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
run:
build-tags:
- expr_debug
linters-settings:
gci:
sections:

View file

@ -57,6 +57,7 @@ func (o *OpOutput) String() string {
if o.Code != "" {
ret += fmt.Sprintf("[%s]", o.Code)
}
ret += " "
switch {
@ -68,10 +69,13 @@ func (o *OpOutput) String() string {
if indent < 0 {
indent = 0
}
ret = fmt.Sprintf("%*cBLOCK_END [%s]", indent, ' ', o.Code)
if o.StrConditionResult != "" {
ret += fmt.Sprintf(" -> %s", o.StrConditionResult)
}
return ret
// A block end can carry a value, for example if it's a count, any, all etc. XXX
case o.Func:
@ -80,7 +84,9 @@ func (o *OpOutput) String() string {
if o.Negated {
ret += "NOT "
}
ret += fmt.Sprintf("%s == %s -> %s", o.Left, o.Right, o.StrConditionResult)
return ret
case o.ConditionIn:
return ret + fmt.Sprintf("%s in %s -> %s", o.Args[0], o.Args[1], o.StrConditionResult)
@ -91,18 +97,23 @@ func (o *OpOutput) String() string {
if *o.ConditionResult {
return ret + "OR -> false"
}
return ret + "OR -> true"
}
return ret + "OR(?)"
case o.JumpIf && o.IfFalse:
if o.ConditionResult != nil {
if *o.ConditionResult {
return ret + "AND -> true"
}
return ret + "AND -> false"
}
return ret + "AND(?)"
}
return ret + ""
}
@ -147,7 +158,6 @@ func autoQuote(v any) string {
}
func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, parts []string, outputs []OpOutput) ([]OpOutput, error) {
IdxOut := len(outputs)
prevIdxOut := 0
currentDepth := 0
@ -156,26 +166,32 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
if IdxOut > 0 {
prevIdxOut = IdxOut - 1
currentDepth = outputs[prevIdxOut].CodeDepth
if outputs[prevIdxOut].Func && !outputs[prevIdxOut].Finalized {
stack := vm.Stack
num_items := 1
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
outputs[prevIdxOut].FuncResults = append(outputs[prevIdxOut].FuncResults, autoQuote(stack[i]))
num_items--
}
outputs[prevIdxOut].Finalized = true
} else if (outputs[prevIdxOut].Comparison || outputs[prevIdxOut].Condition) && !outputs[prevIdxOut].Finalized {
stack := vm.Stack
outputs[prevIdxOut].StrConditionResult = fmt.Sprintf("%+v", stack)
if val, ok := stack[0].(bool); ok {
outputs[prevIdxOut].ConditionResult = new(bool)
*outputs[prevIdxOut].ConditionResult = val
}
outputs[prevIdxOut].Finalized = true
}
}
erp.Logger.Tracef("[STEP %d:%s] (stack:%+v) (parts:%+v) {depth:%d}", ip, parts[1], vm.Stack, parts, currentDepth)
out := OpOutput{}
out.CodeDepth = currentDepth
out.Code = erp.extractCode(ip, program)
@ -192,6 +208,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
if len(vm.Stack) > 0 {
out.StrConditionResult = fmt.Sprintf("%v", vm.Stack)
}
outputs = append(outputs, out)
case "OpNot":
// negate the previous condition
@ -218,46 +235,55 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
out.ConditionResult = new(bool)
*out.ConditionResult = val
}
outputs = append(outputs, out)
case "OpJumpIfFalse": // AND
stack := vm.Stack
out.JumpIf = true
out.IfFalse = true
out.StrConditionResult = fmt.Sprintf("%v", stack[0])
if val, ok := stack[0].(bool); ok {
out.ConditionResult = new(bool)
*out.ConditionResult = val
}
outputs = append(outputs, out)
case "OpCall1": // Op for function calls
out.Func = true
out.FuncName = parts[3]
stack := vm.Stack
num_items := 1
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
out.Args = append(out.Args, autoQuote(stack[i]))
num_items--
}
outputs = append(outputs, out)
case "OpCall2": // Op for function calls
out.Func = true
out.FuncName = parts[3]
stack := vm.Stack
num_items := 2
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
out.Args = append(out.Args, autoQuote(stack[i]))
num_items--
}
outputs = append(outputs, out)
case "OpCall3": // Op for function calls
out.Func = true
out.FuncName = parts[3]
stack := vm.Stack
num_items := 3
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
out.Args = append(out.Args, autoQuote(stack[i]))
num_items--
}
outputs = append(outputs, out)
// double check OpCallFast and OpCallTyped
case "OpCallFast", "OpCallTyped":
@ -282,6 +308,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
out.Args = append(out.Args, autoQuote(val))
}
}
outputs = append(outputs, out)
case "OpEqualString", "OpEqual", "OpEqualInt": // comparisons
stack := vm.Stack
@ -310,6 +337,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
out.Args = append(out.Args, autoQuote(stack[1]))
outputs = append(outputs, out)
}
return outputs, nil
}
@ -319,10 +347,12 @@ func (erp ExprRuntimeDebug) ipSeek(ip int) []string {
if len(parts) == 0 {
continue
}
if parts[0] == strconv.Itoa(ip) {
return parts
}
}
return nil
}
@ -330,19 +360,23 @@ func Run(program *vm.Program, env interface{}, logger *log.Entry, debug bool) (a
if debug {
dbgInfo, ret, err := RunWithDebug(program, env, logger)
DisplayExprDebug(program, dbgInfo, logger, ret)
return ret, err
}
return expr.Run(program, env)
}
func cleanTextForDebug(text string) string {
text = strings.Join(strings.Fields(text), " ")
text = strings.Trim(text, " \t\n")
return text
}
func DisplayExprDebug(program *vm.Program, outputs []OpOutput, logger *log.Entry, ret any) {
logger.Debugf("dbg(result=%v): %s", ret, cleanTextForDebug(string(program.Source())))
for _, output := range outputs {
logger.Debugf("%s", output.String())
}
@ -362,29 +396,37 @@ func RunWithDebug(program *vm.Program, env interface{}, logger *log.Entry) ([]Op
go func() {
// We must never return until the execution of the program is done
var err error
erp.Logger.Tracef("[START] ip 0")
ops := erp.ipSeek(0)
if ops == nil {
log.Warningf("error while debugging expr: failed getting ops for ip 0")
}
if outputs, err = erp.ipDebug(0, vm, program, ops, outputs); err != nil {
log.Warningf("error while debugging expr: error while debugging at ip 0")
}
vm.Step()
for ip := range vm.Position() {
ops := erp.ipSeek(ip)
if ops == nil {
erp.Logger.Tracef("[DONE] ip %d", ip)
break
}
if outputs, err = erp.ipDebug(ip, vm, program, ops, outputs); err != nil {
log.Warningf("error while debugging expr: error while debugging at ip %d", ip)
}
vm.Step()
}
}()
var return_error error
ret, err := vm.Run(program, env)
// if the expr runtime failed, we don't need to wait for the debug to finish
if err != nil {
@ -396,6 +438,7 @@ func RunWithDebug(program *vm.Program, env interface{}, logger *log.Entry) ([]Op
if lastOutIdx > 0 {
lastOutIdx -= 1
}
switch val := ret.(type) {
case bool:
log.Tracef("completing with bool %t", ret)
@ -412,5 +455,6 @@ func RunWithDebug(program *vm.Program, env interface{}, logger *log.Entry) ([]Op
} else {
log.Tracef("no output from expr runtime")
}
return outputs, ret, return_error
}

View file

@ -1,3 +1,4 @@
//go:build expr_debug
package exprhelpers
import (

View file

@ -0,0 +1,10 @@
//go:build !expr_debug
package exprhelpers
import (
"testing"
)
func TestFailWithoutExprDebug(t *testing.T) {
t.Fatal("To test pkg/exprhelpers, you need the expr_debug build tag")
}