Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(decl): fix syscalls argument parsing and cleanup logic #247

Merged
merged 5 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions pkg/test/field/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ const (
TypeOpenMode Type = "open_mode"
// TypeOpenHow specifies that the field contains the openat2 system call open_how parameter.
TypeOpenHow Type = "open_how"
// TypeOpenHowFlags specifies that the field contains the openat2 system call open_how flags.
TypeOpenHowFlags Type = "open_how_flags"
// TypeOpenHowMode specifies that the field contains the openat2 system call open_how mode.
TypeOpenHowMode Type = "open_how_mode"
// TypeOpenHowResolve specifies that the field contains the openat2 system call open_how resolve value.
TypeOpenHowResolve Type = "open_how_resolve"
// TypeLinkAtFlags specifies that the field contains the linkat system call flags.
TypeLinkAtFlags Type = "linkat_flags"
// TypeModuleParams specifies that the field contains the init_module system call params.
Expand Down Expand Up @@ -121,7 +127,7 @@ func Paths(fieldContainer reflect.Type) map[string]struct{} {
subFieldPaths := Paths(fieldTy)
// Generate the complete field path by prefixing, to each subfield, the current field path.
for subFieldPath := range subFieldPaths {
fieldPaths[fieldPath+fieldPathSegmentsSeparator+subFieldPath] = struct{}{}
fieldPaths[JoinFieldPathSegments(fieldPath, subFieldPath)] = struct{}{}
}
}
return fieldPaths
Expand All @@ -132,16 +138,21 @@ func Path(s string) string {
return strings.ToLower(s)
}

// splitFieldPath splits the provided field path into multiple segments.
func splitFieldPath(fieldPath string) []string {
// splitFieldPathSegments splits the provided field path into multiple segments.
func splitFieldPathSegments(fieldPath string) []string {
return strings.Split(fieldPath, fieldPathSegmentsSeparator)
}

// JoinFieldPathSegments joins the provided field path segments into a single field path.
func JoinFieldPathSegments(pathSegments ...string) string {
return strings.Join(pathSegments, fieldPathSegmentsSeparator)
}

// ByName returns information for the field identified by name. The field is searched in the provided fieldContainers,
// and the first match is returned.
func ByName(name string, fieldContainers ...reflect.Value) (*Field, error) {
fieldPath := Path(name)
fieldPathSegments := splitFieldPath(fieldPath)
fieldPathSegments := splitFieldPathSegments(fieldPath)

for _, fieldContainer := range fieldContainers {
if field := byName(fieldContainer, fieldPath, fieldPathSegments); field != nil {
Expand Down
54 changes: 33 additions & 21 deletions pkg/test/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func (r *TestResource) UnmarshalYAML(node *yaml.Node) error {
// marshal the content.
// TODO: this method should be implemented with a pointer receiver but unfortunately, the yaml.v3 library is only able
// to call it if it is implemented with a value receiver. Uniform the receivers once the library is replaced.
func (r TestResource) MarshalYAML() (interface{}, error) {
func (r TestResource) MarshalYAML() (any, error) {
switch resourceType := r.Type; resourceType {
case TestResourceTypeClientServer:
return struct {
Expand All @@ -271,7 +271,7 @@ func (r TestResource) MarshalYAML() (interface{}, error) {
// provide an addition MarshalYAML method for TestResourceFDSpec, as it will not be called by the library if the Spec
// field specify "inline" (as it should be in our case). Take care of replace this with a more elegant solution once
// yaml.v3 is replaced.
func (r *TestResource) marshalFD() (interface{}, error) {
func (r *TestResource) marshalFD() (any, error) {
spec := r.Spec.(*TestResourceFDSpec)
subSpec := spec.Spec
switch subtype := spec.Subtype; subtype {
Expand Down Expand Up @@ -576,7 +576,7 @@ func (s *TestStep) UnmarshalYAML(node *yaml.Node) error {
return fmt.Errorf("error decoding syscall parameters: %w", err)
}
spec = &syscallSpec
fieldBindings = syscallSpec.fieldBindings()
fieldBindings = getFieldBindings("", syscallSpec.Args)
default:
panic(fmt.Sprintf("unknown test step type %q", decodedType))
}
Expand All @@ -592,11 +592,11 @@ func (s *TestStep) UnmarshalYAML(node *yaml.Node) error {
// marshal the content.
// TODO: this method should be implemented with a pointer receiver but unfortunately, the yaml.v3 library is only able
// to call it if it is implemented with a value receiver. Uniform the receivers once the library is replaced.
func (s TestStep) MarshalYAML() (interface{}, error) {
func (s TestStep) MarshalYAML() (any, error) {
switch stepType := s.Type; stepType {
case TestStepTypeSyscall:
spec := s.Spec.(*TestStepSyscallSpec)
args := make(map[string]string, len(spec.Args)+len(s.FieldBindings))
args := make(map[string]any, len(spec.Args)+len(s.FieldBindings))
for arg, argValue := range spec.Args {
args[arg] = argValue
}
Expand Down Expand Up @@ -640,8 +640,8 @@ func (t *TestStepType) UnmarshalYAML(node *yaml.Node) error {

// TestStepSyscallSpec describes a system call test step.
type TestStepSyscallSpec struct {
Syscall SyscallName `yaml:"syscall" validate:"-"`
Args map[string]string `yaml:"args" validate:"required"`
Syscall SyscallName `yaml:"syscall" validate:"-"`
Args map[string]any `yaml:"args" validate:"required"`
}

// TestStepFieldBinding contains the information to perform the binding of a field belonging to a source step.
Expand All @@ -653,22 +653,34 @@ type TestStepFieldBinding struct {

var fieldBindingRegex = regexp.MustCompile(`^\${(.+?)\.(.+)}$`)

func (s *TestStepSyscallSpec) fieldBindings() []*TestStepFieldBinding {
func getFieldBindings(containingArgName string, args map[string]any) []*TestStepFieldBinding {
// The prefix of each contained argument is composed by the containing argument name.
var argsPrefix string
if containingArgName != "" {
argsPrefix = containingArgName + "."
}

var bindings []*TestStepFieldBinding
for arg, argValue := range s.Args {
// Check if the user specified a field binding as value.
match := fieldBindingRegex.FindStringSubmatch(argValue)
if match == nil {
continue
}
for arg, argValue := range args {
switch argValue := argValue.(type) {
case string:
// Check if the user specified a field binding as value.
match := fieldBindingRegex.FindStringSubmatch(argValue)
if match == nil {
continue
}

bindings = append(bindings, &TestStepFieldBinding{
SrcStep: match[1],
SrcField: match[2],
LocalField: argsPrefix + arg,
})

bindings = append(bindings, &TestStepFieldBinding{
SrcStep: match[1],
SrcField: match[2],
LocalField: arg,
})
// If an argument value is a field binding, remove it from arguments.
delete(s.Args, arg)
// If an argument value is a field binding, remove it from arguments.
delete(args, arg)
case map[string]any:
bindings = append(bindings, getFieldBindings(arg, argValue)...)
}
}
return bindings
}
Expand Down
Loading
Loading