How to use UnknownDecorator method of types Package

Best Ginkgo code snippet using types.UnknownDecorator

Run Ginkgo automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

unknown.go

Source: unknown.go Github

copy
1package expressions
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/google/cel-go/common/operators"
8	"github.com/google/cel-go/common/overloads"
9	"github.com/google/cel-go/common/types"
10	"github.com/google/cel-go/common/types/ref"
11	"github.com/google/cel-go/interpreter"
12	"github.com/google/cel-go/interpreter/functions"
13)
14
15// unknownDecorator returns a decorator for inspecting and handling unknowns at runtime.  This
16// decorator is called before _any_ attribute or function is evaluated in CEL, allowing us to
17// intercept and return our own values.
18//
19// For example, natively in CEL `size(null)` returns a "no such overload" error.  We intercept
20// the evalutation of `size(null)` and return a new type (0) instead of the error.
21func unknownDecorator(act interpreter.PartialActivation) interpreter.InterpretableDecorator {
22	// Create a new dispatcher with all functions added
23	dispatcher := interpreter.NewDispatcher()
24	overloads := append(functions.StandardOverloads(), celOverloads()...)
25	_ = dispatcher.Add(overloads...)
26
27	return func(i interpreter.Interpretable) (interpreter.Interpretable, error) {
28		// If this is a fold call, this is a macro (exists, has, etc), and is not an InterpretableCall
29		call, ok := i.(interpreter.InterpretableCall)
30		if !ok {
31			return i, nil
32		}
33
34		argTypes := &argColl{}
35
36		args := call.Args()
37		for _, arg := range args {
38			// We want both attributes (variables) & consts to check for coercion.
39			argTypes.Add(arg.Eval(act))
40		}
41
42		if argTypes.TypeLen() == 1 && !argTypes.Exists(types.ErrType) && !argTypes.Exists(types.UnknownType) {
43			// A single type used within the function with no error and unknown is
44			// safe to call as usual.
45			return i, nil
46		}
47
48		if argTypes.Exists(types.ErrType) || argTypes.Exists(types.UnknownType) {
49			// We work with unknown and error types, handling both as non-existent
50			// types.
51			//
52			// Errors can appear when calling macros on unknowns (eg:
53			// event.data.nonexistent.subkey.contains("something")).
54			//
55			// This could be because:
56			//
57			// 1. we're calling a macro on an unknown value. This happens before we can intercept
58			// the InterpretableCall and will always happen.  That's fine, and this produces the
59			// error "no such key".  These are the errors we want to intercept.
60			//
61			// 2. we're inside a macro and we're using the __result__ or lambda
62			//    variable.  This error contains "no such attribute", and this is a usual
63			//    part of runing macros.  XXX: Figure out why Eval() on macro variables fails:
64			//    this is actually _not_ an error.
65			for _, val := range argTypes.OfType(types.ErrType) {
66				if strings.HasPrefix(val.(error).Error(), "no such attribute") {
67					// This must be a macro call;  handle as usual
68					return i, nil
69				}
70			}
71			// This is an unknown type.  Dependent on the function being called return
72			// a concrete true or false value by default.
73			return handleUnknownCall(call, argTypes)
74		}
75
76		// Here we have multiple types called together.  If these are coercible, we'll
77		// attempt to coerce them (eg. ints and floats).
78		//
79		// We can't create a custom null type, because Compare may run on String, Int, Double,
80		// etc:  we'd have to wrap every type and add null checking.  This is a maintenance
81		// en and could be bug-prone.
82		//
83		// Therefore, we have to evaluate this here within a decorator.
84		if argTypes.Exists(types.NullType) && argTypes.ArgLen() == 2 {
85			switch call.Function() {
86			case operators.Equals:
87				return staticCall{result: types.False, InterpretableCall: call}, nil
88			case operators.NotEquals:
89				return staticCall{result: types.True, InterpretableCall: call}, nil
90			}
91
92			// Other operators, such as >, <=, depend on the argument order to evaluate
93			// correctly.
94			//
95			// We must create a new zero type in place of the null argument,
96			// then fetch the overload from the standard dispatcher and run the function.
97			args, err := argTypes.ZeroValArgs()
98			if err != nil {
99				return i, nil
100			}
101
102			fn, ok := dispatcher.FindOverload(call.Function())
103			if !ok {
104				return i, nil
105			}
106			return staticCall{result: fn.Binary(args[0], args[1]), InterpretableCall: call}, nil
107		}
108
109		return i, nil
110	}
111}
112
113// By default, CEL tracks unknowns as a separate value.  This is fantastic, but when
114// we're evaluating expressions we want to treat unknowns as nulls.
115//
116// This functionality adds custom logic for each overload to return a static ref.Val
117// which is used in place of unknown.
118func handleUnknownCall(i interpreter.InterpretableCall, args *argColl) (interpreter.Interpretable, error) {
119	switch i.Function() {
120	case operators.Equals:
121		// Comparing an unknown to null is true, else return false.
122		result := types.False
123		if args.Exists(types.NullType) {
124			result = types.True
125		}
126		return staticCall{result: result, InterpretableCall: i}, nil
127
128	case operators.NotEquals:
129		if args.Exists(types.NullType) {
130			// Unknowns are null, so this is false.
131			return staticCall{result: types.False, InterpretableCall: i}, nil
132		}
133		// Are we comparing against a zero type (eg. empty string, 0).
134		// The only item that should return true is not equals, as nil is always not equals
135		return staticCall{result: types.True, InterpretableCall: i}, nil
136
137	case operators.Less, operators.LessEquals:
138		// Unknown is less than anything.
139		if args.arguments[0].Type() == types.UnknownType || args.arguments[0].Type() == types.ErrType {
140			return staticCall{result: types.True, InterpretableCall: i}, nil
141		}
142		return staticCall{result: types.False, InterpretableCall: i}, nil
143
144	case operators.Greater, operators.GreaterEquals:
145		// If the first arg is unkown, return false:  unknown is not greater.
146		if args.arguments[0].Type() == types.UnknownType || args.arguments[0].Type() == types.ErrType {
147			return staticCall{result: types.False, InterpretableCall: i}, nil
148		}
149		return staticCall{result: types.True, InterpretableCall: i}, nil
150
151	case overloads.Size,
152		overloads.SizeString,
153		overloads.SizeBytes,
154		overloads.SizeList,
155		overloads.SizeStringInst,
156		overloads.SizeBytesInst,
157		overloads.SizeListInst,
158		overloads.SizeMapInst:
159		// Size on unknowns should always return zero to avoid type errors.
160		return staticCall{result: types.IntZero, InterpretableCall: i}, nil
161	default:
162
163		// By default, return false, for eaxmple: "_<_", "@in", "@not_strictly_false"
164		// return staticCall{result: types.False, InterpretableCall: call}, nil
165		return staticCall{result: types.False, InterpretableCall: i}, nil
166	}
167}
168
169// staticCall represents a wrapped interpreter.InterpretableCall function within
170// an expression that always returns a static value.
171type staticCall struct {
172	interpreter.InterpretableCall
173	result ref.Val
174}
175
176func (u staticCall) Eval(ctx interpreter.Activation) ref.Val {
177	return u.result
178}
179
180// argColl inspects all of the types available within a function call in
181// CEL, storing their type information.
182type argColl struct {
183	// types represents a map of types to their values used within the
184	// function.
185	types map[ref.Type][]ref.Val
186
187	// arguments represents the function arguments, in order.
188	arguments []ref.Val
189}
190
191// Add adds a new value to the type collection, storing its type in the map.
192func (t *argColl) Add(vals ...ref.Val) {
193	if t.types == nil {
194		t.types = map[ref.Type][]ref.Val{}
195	}
196
197	for _, val := range vals {
198		// Store the arguments in order (left and right hand side of operators)
199		t.arguments = append(t.arguments, val)
200
201		typ := val.Type()
202		coll, ok := t.types[typ]
203		if !ok {
204			t.types[typ] = []ref.Val{val}
205			return
206		}
207		t.types[typ] = append(coll, val)
208	}
209}
210
211func (t *argColl) TypeLen() int {
212	return len(t.types)
213}
214
215func (t *argColl) ArgLen() int {
216	return len(t.arguments)
217}
218
219func (t *argColl) Exists(typ ref.Type) bool {
220	_, ok := t.types[typ]
221	return ok
222}
223
224// OfType returns all arguments of the given type.
225func (t *argColl) OfType(typ ref.Type) []ref.Val {
226	coll, ok := t.types[typ]
227	if !ok {
228		return nil
229	}
230	return coll
231}
232
233// NonNull returns all non-null types as a slice.
234func (t *argColl) NonNull() []ref.Type {
235	coll := []ref.Type{}
236	for typ := range t.types {
237		if typ == types.NullType {
238			continue
239		}
240		coll = append(coll, typ)
241	}
242	return coll
243}
244
245// ZeroValArgs returns all args with null types replaced as zero values
246func (t *argColl) ZeroValArgs() ([]ref.Val, error) {
247	typ := t.NonNull()
248	if len(typ) != 1 {
249		return t.arguments, fmt.Errorf("not exactly one other non-null type present")
250	}
251
252	coll := make([]ref.Val, len(t.arguments))
253	for n, arg := range t.arguments {
254		coll[n] = arg
255		if arg.Type() == types.NullType {
256			coll[n] = zeroVal(typ[0])
257		}
258	}
259
260	return coll, nil
261}
262
263// zeroVal returns a zero value for common cel datatypes.  This helps us
264// convert null values to a zero value of a specific type.
265func zeroVal(t ref.Type) ref.Val {
266	switch t.TypeName() {
267	case "int":
268		return types.IntZero
269	case "uint":
270		return types.Uint(0)
271	case "double":
272		return types.Double(0)
273	case "string":
274		return types.String("")
275	}
276
277	return types.NullValue
278}
279
Full Screen

expressions.go

Source: expressions.go Github

copy
1// Package expressions provides the ability to inspect and evaluate arbitrary
2// user-defined expressions.  We use the Cel-Go package as a runtime to implement
3// computationally bounded, non-turing complete expressions with a familiar c-like
4// syntax.
5//
6// Unlike cel-go's defaults, this package handles unknowns similarly to null values,
7// and allows arbitrary attributes within expressions without errors.  We also
8// provide basic type coercion allowing eg. int <> float comparisons, which errors
9// within cel by default.
10//
11// Expressions can be inspected to determine the variables that they reference,
12// partially evaluated with missing data, and report timestamps used within the
13// expression for future reference (eg. recomputing state at that time).
14package expressions
15
16import (
17	"context"
18	"fmt"
19	"strings"
20	"time"
21
22	"github.com/google/cel-go/cel"
23	"github.com/google/cel-go/common/types"
24	"github.com/pkg/errors"
25	expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
26)
27
28var (
29	ErrNoResult      = errors.New("expression did not return true or false")
30	ErrInvalidResult = errors.New("expression errored")
31)
32
33// Evaluable represents a cacheable, goroutine safe manager for evaluating a single
34// precompiled expression with arbitrary data.
35type Evaluable interface {
36	// Evaluate tests the incoming Data against the expression that is
37	// stored within the Evaluable implementation.
38	//
39	// Attributes that are present within the expression but missing from the
40	// data should be treated as null values;  the expression must not error.
41	Evaluate(ctx context.Context, data *Data) (bool, *time.Time, error)
42
43	// UsedAttributes returns the attributes that are referenced within the
44	// expression.
45	UsedAttributes(ctx context.Context) *UsedAttributes
46
47	// FilteredAttributes filters the given data to contain only attributes
48	// referenced from the expression.
49	FilteredAttributes(ctx context.Context, data *Data) *Data
50}
51
52// Evaluate is a helper function to create a new, cached expression evaluator to evaluate
53// the given data immediately.
54func Evaluate(ctx context.Context, expression string, input map[string]interface{}) (bool, *time.Time, error) {
55	eval, err := NewExpressionEvaluator(ctx, expression)
56	if err != nil {
57		return false, nil, err
58	}
59	data := NewData(input)
60	return eval.Evaluate(ctx, data)
61}
62
63// NewExpressionEvaluator returns a new Evaluable instance for a given expression. The
64// instance can be used across many goroutines to evaluate the expression against any
65// data. The Evaluable instance is loaded from the cache, or is cached if not found.
66func NewExpressionEvaluator(ctx context.Context, expression string) (Evaluable, error) {
67	e, err := env()
68	if err != nil {
69		return nil, err
70	}
71
72	ast, issues := e.Compile(expression)
73	if issues != nil {
74		return nil, fmt.Errorf("error compiling expression: %w", issues.Err())
75	}
76
77	eval := &expressionEvaluator{
78		ast:        ast,
79		env:        e,
80		expression: expression,
81	}
82
83	if err := eval.parseAttributes(ctx); err != nil {
84		return nil, err
85	}
86
87	return eval, nil
88}
89
90type expressionEvaluator struct {
91	// TODO: Refactor unknownEval to remove the need tor attr.Eval(activation),
92	// and make dateRefs thread safe.  We can then place a cel.Program on the
93	// evaluator as it's thread safe.  Without these changes, Programs are bound
94	// to specific expression & data combinations.
95	ast *cel.Ast
96	env *cel.Env
97
98	// expression is the raw expression
99	expression string
100
101	// attrs is allows us to determine which attributes are used within an expression.
102	// This is needed to create partial activations, and is also used to optimistically
103	// load only necessary attributes.
104	attrs *UsedAttributes
105}
106
107// Evaluate compiles an expression string against a set of variables, returning whether the
108// expression evaluates to true, the next earliest time to re-test the evaluation (if dates are
109// compared), and any errors.
110func (e *expressionEvaluator) Evaluate(ctx context.Context, data *Data) (bool, *time.Time, error) {
111	if data == nil {
112		return false, nil, nil
113	}
114
115	act, err := data.Partial(ctx, *e.attrs)
116	if err != nil {
117		return false, nil, err
118	}
119
120	// We want to perform an exhaustive search and track the state of the search
121	// to see if dates are compared, then return the minimum date compared.
122	tr, td := timeDecorator(act)
123
124	// Create the program, refusing to short circuit if a match is found.
125	//
126	// This will add all functions from functions.StandardOverloads as we
127	// created the environment with our custom library.
128	program, err := e.env.Program(
129		e.ast,
130		cel.EvalOptions(cel.OptExhaustiveEval, cel.OptTrackState, cel.OptPartialEval), // Exhaustive, always, right now.
131		cel.CustomDecorator(unknownDecorator(act)),
132		cel.CustomDecorator(td),
133	)
134	if err != nil {
135		return false, nil, err
136	}
137
138	result, _, err := program.Eval(act)
139	if result == nil {
140		return false, nil, ErrNoResult
141	}
142	if types.IsUnknown(result) {
143		// When evaluating to a strict result this should never happen.  We inject a decorator
144		// to handle unknowns as values similar to null, and should always get a value.
145		return false, nil, nil
146	}
147	if types.IsError(result) {
148		return false, nil, errors.Wrapf(ErrInvalidResult, "invalid type comparison: %s", err.Error())
149	}
150	if err != nil {
151		// This shouldn't be handled, as we should get an Error type in result above.
152		return false, nil, fmt.Errorf("error evaluating expression '%s': %w", e.expression, err)
153	}
154
155	b, ok := result.Value().(bool)
156	if !ok {
157		return false, nil, errors.Wrapf(ErrInvalidResult, "returned type %T (%s)", result, result)
158	}
159
160	// Find earliest date that we need to test against.
161	earliest := tr.Next()
162	return b, earliest, nil
163}
164
165// UsedAttributes returns the attributes used within the expression.
166func (e *expressionEvaluator) UsedAttributes(ctx context.Context) *UsedAttributes {
167	return e.attrs
168}
169
170// FilteredAttributes returns a new Data pointer with only the attributes
171// used within the expression.
172func (e *expressionEvaluator) FilteredAttributes(ctx context.Context, d *Data) *Data {
173	if d == nil {
174		return nil
175	}
176
177	filtered := map[string]interface{}{}
178
179	current := filtered
180	stack := e.attrs.FullPaths()
181	for len(stack) > 0 {
182		path := stack[0]
183		stack = stack[1:]
184
185		val, ok := d.Get(ctx, path)
186		if !ok {
187			continue
188		}
189
190		for n, part := range path {
191			if n == len(path)-1 {
192				// This is the value.
193				current[part] = val
194				continue
195			}
196
197			if _, ok := current[part]; !ok {
198				current[part] = map[string]interface{}{}
199			}
200			current = current[part].(map[string]interface{})
201		}
202
203		current = filtered
204	}
205
206	// It is safe to set data directly here, as we've manually
207	// created a map containing raw values from a previously set
208	// Data field.  This prevents us from needlesly mapifying
209	// data from a constructor.
210	return &Data{data: filtered}
211}
212
213// ParseAttributes returns the attributes used within the expression.
214func (e *expressionEvaluator) parseAttributes(ctx context.Context) error {
215	if e.attrs != nil {
216		return nil
217	}
218
219	attrs := &UsedAttributes{
220		Root:   []string{},
221		Fields: map[string][][]string{},
222	}
223
224	// Walk through the AST, looking for all instances of "select_expr" expression
225	// kinds.  These elements are specifically selecting fields from parents, which
226	// is exactly what we need to figure out the variables used within an expression.
227	stack := []*expr.Expr{e.ast.Expr()}
228	for len(stack) > 0 {
229		ast := stack[0]
230		stack = stack[1:]
231
232		// Depending on the item, add the following
233		switch ast.ExprKind.(type) {
234		case *expr.Expr_ComprehensionExpr:
235			// eg. "event.data.tags.exists(x, x == 'Open'), so put what we're iterating over
236			// onto the stack to parse, ignoring this function call but adding the data.
237			c := ast.GetComprehensionExpr()
238			stack = append(stack, c.IterRange)
239		case *expr.Expr_CallExpr:
240			// Everything is a function call:
241			// - > evaluates to _>_ with two arguments, etc.
242			// This means pop all args onto the stack so that we can find
243			// all select expressions.
244			stack = append(stack, ast.GetCallExpr().GetArgs()...)
245
246		case *expr.Expr_IdentExpr:
247			name := ast.GetIdentExpr().Name
248			attrs.add(name, nil)
249
250		case *expr.Expr_SelectExpr:
251			// Note that the select expression unravels from the deepest key first:
252			// given "event.data.foo.bar", the current ast node will be for "foo"
253			// and the field name will be for "bar".
254			//
255			// Iterate through all object selects until there are no more, adding
256			// to the path.
257
258			path := []string{}
259			for ast.GetSelectExpr() != nil {
260				path = append([]string{ast.GetSelectExpr().Field}, path...)
261				ast = ast.GetSelectExpr().Operand
262			}
263
264			ident := ast.GetIdentExpr()
265			caller := ast.GetCallExpr()
266
267			if ident == nil && caller != nil && caller.Function == "_[_]" {
268				// This might be square notation: "actions[1]".  This should
269				// have two args:  the object (eg. actions), which is an
270				// IdentExpr, and a ConstExpr containing the number.
271				args := caller.GetArgs()
272				if len(args) != 2 {
273					return fmt.Errorf("unknown number of callers for bracket notation: %d", len(args))
274				}
275
276				// Functions have been rewritten to move "actions.1" into a string:
277				// actions["1"]
278				id := args[1].GetConstExpr().GetStringValue()
279				path = append([]string{args[0].GetIdentExpr().GetName(), id}, path...)
280			}
281
282			if ident != nil {
283				path = append([]string{ident.Name}, path...)
284			}
285
286			root := path[0]
287			fields := path[1:]
288
289			attrs.add(root, fields)
290		}
291	}
292
293	e.attrs = attrs
294	return nil
295}
296
297// UsedAttributes represents the evaluated expression's root and top-level fields used.
298type UsedAttributes struct {
299	// Root represents root-level variables used within the expression
300	Root []string
301
302	// Fields represent fields within each root-level variable accessed.
303	//
304	// For example, given an attribute of "event.data.index", this map holds
305	// a key of "event" with a slice of [][]string{{"data", "index"}}
306	Fields map[string][][]string
307
308	// exists
309	exists map[string]struct{}
310}
311
312// FullPaths returns a slice of path slices with the roots appended.
313func (u UsedAttributes) FullPaths() [][]string {
314	paths := [][]string{}
315	for root, items := range u.Fields {
316		for _, path := range items {
317			path = append([]string{root}, path...)
318			paths = append(paths, path)
319		}
320	}
321	return paths
322}
323
324func (u *UsedAttributes) add(root string, path []string) {
325	if u.exists == nil {
326		u.exists = map[string]struct{}{}
327	}
328
329	if _, ok := u.Fields[root]; !ok {
330		u.Root = append(u.Root, root)
331		u.Fields[root] = [][]string{}
332	}
333
334	// Add this once.
335	key := fmt.Sprintf("%s.%s", root, strings.Join(path, "."))
336	if _, ok := u.exists[key]; !ok && len(path) > 0 {
337		u.Fields[root] = append(u.Fields[root], path)
338		// store this key so it's not duplicated.
339		u.exists[key] = struct{}{}
340	}
341}
342
Full Screen

node.go

Source: node.go Github

copy
1package internal
2
3import (
4	"fmt"
5	"reflect"
6	"sort"
7
8	"sync"
9
10	"github.com/onsi/ginkgo/v2/types"
11)
12
13var _global_node_id_counter = uint(0)
14var _global_id_mutex = &sync.Mutex{}
15
16func UniqueNodeID() uint {
17	//There's a reace in the internal integration tests if we don't make
18	//accessing _global_node_id_counter safe across goroutines.
19	_global_id_mutex.Lock()
20	defer _global_id_mutex.Unlock()
21	_global_node_id_counter += 1
22	return _global_node_id_counter
23}
24
25type Node struct {
26	ID       uint
27	NodeType types.NodeType
28
29	Text         string
30	Body         func()
31	CodeLocation types.CodeLocation
32	NestingLevel int
33
34	SynchronizedBeforeSuiteProc1Body    func() []byte
35	SynchronizedBeforeSuiteAllProcsBody func([]byte)
36
37	SynchronizedAfterSuiteAllProcsBody func()
38	SynchronizedAfterSuiteProc1Body    func()
39
40	ReportEachBody       func(types.SpecReport)
41	ReportAfterSuiteBody func(types.Report)
42
43	MarkedFocus          bool
44	MarkedPending        bool
45	MarkedSerial         bool
46	MarkedOrdered        bool
47	MarkedOncePerOrdered bool
48	FlakeAttempts        int
49	Labels               Labels
50
51	NodeIDWhereCleanupWasGenerated uint
52}
53
54// Decoration Types
55type focusType bool
56type pendingType bool
57type serialType bool
58type orderedType bool
59type honorsOrderedType bool
60
61const Focus = focusType(true)
62const Pending = pendingType(true)
63const Serial = serialType(true)
64const Ordered = orderedType(true)
65const OncePerOrdered = honorsOrderedType(true)
66
67type FlakeAttempts uint
68type Offset uint
69type Done chan<- interface{} // Deprecated Done Channel for asynchronous testing
70type Labels []string
71
72func UnionOfLabels(labels ...Labels) Labels {
73	out := Labels{}
74	seen := map[string]bool{}
75	for _, labelSet := range labels {
76		for _, label := range labelSet {
77			if !seen[label] {
78				seen[label] = true
79				out = append(out, label)
80			}
81		}
82	}
83	return out
84}
85
86func PartitionDecorations(args ...interface{}) ([]interface{}, []interface{}) {
87	decorations := []interface{}{}
88	remainingArgs := []interface{}{}
89	for _, arg := range args {
90		if isDecoration(arg) {
91			decorations = append(decorations, arg)
92		} else {
93			remainingArgs = append(remainingArgs, arg)
94		}
95	}
96	return decorations, remainingArgs
97}
98
99func isDecoration(arg interface{}) bool {
100	switch t := reflect.TypeOf(arg); {
101	case t == nil:
102		return false
103	case t == reflect.TypeOf(Offset(0)):
104		return true
105	case t == reflect.TypeOf(types.CodeLocation{}):
106		return true
107	case t == reflect.TypeOf(Focus):
108		return true
109	case t == reflect.TypeOf(Pending):
110		return true
111	case t == reflect.TypeOf(Serial):
112		return true
113	case t == reflect.TypeOf(Ordered):
114		return true
115	case t == reflect.TypeOf(OncePerOrdered):
116		return true
117	case t == reflect.TypeOf(FlakeAttempts(0)):
118		return true
119	case t == reflect.TypeOf(Labels{}):
120		return true
121	case t.Kind() == reflect.Slice && isSliceOfDecorations(arg):
122		return true
123	default:
124		return false
125	}
126}
127
128func isSliceOfDecorations(slice interface{}) bool {
129	vSlice := reflect.ValueOf(slice)
130	if vSlice.Len() == 0 {
131		return false
132	}
133	for i := 0; i < vSlice.Len(); i++ {
134		if !isDecoration(vSlice.Index(i).Interface()) {
135			return false
136		}
137	}
138	return true
139}
140
141func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeType, text string, args ...interface{}) (Node, []error) {
142	baseOffset := 2
143	node := Node{
144		ID:           UniqueNodeID(),
145		NodeType:     nodeType,
146		Text:         text,
147		Labels:       Labels{},
148		CodeLocation: types.NewCodeLocation(baseOffset),
149		NestingLevel: -1,
150	}
151	errors := []error{}
152	appendError := func(err error) {
153		if err != nil {
154			errors = append(errors, err)
155		}
156	}
157
158	args = unrollInterfaceSlice(args)
159
160	remainingArgs := []interface{}{}
161	//First get the CodeLocation up-to-date
162	for _, arg := range args {
163		switch v := arg.(type) {
164		case Offset:
165			node.CodeLocation = types.NewCodeLocation(baseOffset + int(v))
166		case types.CodeLocation:
167			node.CodeLocation = v
168		default:
169			remainingArgs = append(remainingArgs, arg)
170		}
171	}
172
173	labelsSeen := map[string]bool{}
174	trackedFunctionError := false
175	args = remainingArgs
176	remainingArgs = []interface{}{}
177	//now process the rest of the args
178	for _, arg := range args {
179
180		switch t := reflect.TypeOf(arg); {
181		case t == reflect.TypeOf(float64(0)):
182			break //ignore deprecated timeouts
183		case t == reflect.TypeOf(Focus):
184			node.MarkedFocus = bool(arg.(focusType))
185			if !nodeType.Is(types.NodeTypesForContainerAndIt) {
186				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "Focus"))
187			}
188		case t == reflect.TypeOf(Pending):
189			node.MarkedPending = bool(arg.(pendingType))
190			if !nodeType.Is(types.NodeTypesForContainerAndIt) {
191				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "Pending"))
192			}
193		case t == reflect.TypeOf(Serial):
194			node.MarkedSerial = bool(arg.(serialType))
195			if !nodeType.Is(types.NodeTypesForContainerAndIt) {
196				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "Serial"))
197			}
198		case t == reflect.TypeOf(Ordered):
199			node.MarkedOrdered = bool(arg.(orderedType))
200			if !nodeType.Is(types.NodeTypeContainer) {
201				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "Ordered"))
202			}
203		case t == reflect.TypeOf(OncePerOrdered):
204			node.MarkedOncePerOrdered = bool(arg.(honorsOrderedType))
205			if !nodeType.Is(types.NodeTypeBeforeEach | types.NodeTypeJustBeforeEach | types.NodeTypeAfterEach | types.NodeTypeJustAfterEach) {
206				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "OncePerOrdered"))
207			}
208		case t == reflect.TypeOf(FlakeAttempts(0)):
209			node.FlakeAttempts = int(arg.(FlakeAttempts))
210			if !nodeType.Is(types.NodeTypesForContainerAndIt) {
211				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "FlakeAttempts"))
212			}
213		case t == reflect.TypeOf(Labels{}):
214			if !nodeType.Is(types.NodeTypesForContainerAndIt) {
215				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "Label"))
216			}
217			for _, label := range arg.(Labels) {
218				if !labelsSeen[label] {
219					labelsSeen[label] = true
220					label, err := types.ValidateAndCleanupLabel(label, node.CodeLocation)
221					node.Labels = append(node.Labels, label)
222					appendError(err)
223				}
224			}
225		case t.Kind() == reflect.Func:
226			if node.Body != nil {
227				appendError(types.GinkgoErrors.MultipleBodyFunctions(node.CodeLocation, nodeType))
228				trackedFunctionError = true
229				break
230			}
231			isValid := (t.NumOut() == 0) && (t.NumIn() <= 1) && (t.NumIn() == 0 || t.In(0) == reflect.TypeOf(make(Done)))
232			if !isValid {
233				appendError(types.GinkgoErrors.InvalidBodyType(t, node.CodeLocation, nodeType))
234				trackedFunctionError = true
235				break
236			}
237			if t.NumIn() == 0 {
238				node.Body = arg.(func())
239			} else {
240				deprecationTracker.TrackDeprecation(types.Deprecations.Async(), node.CodeLocation)
241				deprecatedAsyncBody := arg.(func(Done))
242				node.Body = func() { deprecatedAsyncBody(make(Done)) }
243			}
244		default:
245			remainingArgs = append(remainingArgs, arg)
246		}
247	}
248
249	//validations
250	if node.MarkedPending && node.MarkedFocus {
251		appendError(types.GinkgoErrors.InvalidDeclarationOfFocusedAndPending(node.CodeLocation, nodeType))
252	}
253
254	if node.Body == nil && !node.MarkedPending && !trackedFunctionError {
255		appendError(types.GinkgoErrors.MissingBodyFunction(node.CodeLocation, nodeType))
256	}
257	for _, arg := range remainingArgs {
258		appendError(types.GinkgoErrors.UnknownDecorator(node.CodeLocation, nodeType, arg))
259	}
260
261	if len(errors) > 0 {
262		return Node{}, errors
263	}
264
265	return node, errors
266}
267
268func NewSynchronizedBeforeSuiteNode(proc1Body func() []byte, allProcsBody func([]byte), codeLocation types.CodeLocation) (Node, []error) {
269	return Node{
270		ID:                                  UniqueNodeID(),
271		NodeType:                            types.NodeTypeSynchronizedBeforeSuite,
272		SynchronizedBeforeSuiteProc1Body:    proc1Body,
273		SynchronizedBeforeSuiteAllProcsBody: allProcsBody,
274		CodeLocation:                        codeLocation,
275	}, nil
276}
277
278func NewSynchronizedAfterSuiteNode(allProcsBody func(), proc1Body func(), codeLocation types.CodeLocation) (Node, []error) {
279	return Node{
280		ID:                                 UniqueNodeID(),
281		NodeType:                           types.NodeTypeSynchronizedAfterSuite,
282		SynchronizedAfterSuiteAllProcsBody: allProcsBody,
283		SynchronizedAfterSuiteProc1Body:    proc1Body,
284		CodeLocation:                       codeLocation,
285	}, nil
286}
287
288func NewReportBeforeEachNode(body func(types.SpecReport), codeLocation types.CodeLocation) (Node, []error) {
289	return Node{
290		ID:             UniqueNodeID(),
291		NodeType:       types.NodeTypeReportBeforeEach,
292		ReportEachBody: body,
293		CodeLocation:   codeLocation,
294		NestingLevel:   -1,
295	}, nil
296}
297
298func NewReportAfterEachNode(body func(types.SpecReport), codeLocation types.CodeLocation) (Node, []error) {
299	return Node{
300		ID:             UniqueNodeID(),
301		NodeType:       types.NodeTypeReportAfterEach,
302		ReportEachBody: body,
303		CodeLocation:   codeLocation,
304		NestingLevel:   -1,
305	}, nil
306}
307
308func NewReportAfterSuiteNode(text string, body func(types.Report), codeLocation types.CodeLocation) (Node, []error) {
309	return Node{
310		ID:                   UniqueNodeID(),
311		Text:                 text,
312		NodeType:             types.NodeTypeReportAfterSuite,
313		ReportAfterSuiteBody: body,
314		CodeLocation:         codeLocation,
315	}, nil
316}
317
318func NewCleanupNode(fail func(string, types.CodeLocation), args ...interface{}) (Node, []error) {
319	baseOffset := 2
320	node := Node{
321		ID:           UniqueNodeID(),
322		NodeType:     types.NodeTypeCleanupInvalid,
323		CodeLocation: types.NewCodeLocation(baseOffset),
324		NestingLevel: -1,
325	}
326	remainingArgs := []interface{}{}
327	for _, arg := range args {
328		switch t := reflect.TypeOf(arg); {
329		case t == reflect.TypeOf(Offset(0)):
330			node.CodeLocation = types.NewCodeLocation(baseOffset + int(arg.(Offset)))
331		case t == reflect.TypeOf(types.CodeLocation{}):
332			node.CodeLocation = arg.(types.CodeLocation)
333		default:
334			remainingArgs = append(remainingArgs, arg)
335		}
336	}
337
338	if len(remainingArgs) == 0 {
339		return Node{}, []error{types.GinkgoErrors.DeferCleanupInvalidFunction(node.CodeLocation)}
340	}
341	callback := reflect.ValueOf(remainingArgs[0])
342	if !(callback.Kind() == reflect.Func && callback.Type().NumOut() <= 1) {
343		return Node{}, []error{types.GinkgoErrors.DeferCleanupInvalidFunction(node.CodeLocation)}
344	}
345	callArgs := []reflect.Value{}
346	for _, arg := range remainingArgs[1:] {
347		callArgs = append(callArgs, reflect.ValueOf(arg))
348	}
349	cl := node.CodeLocation
350	node.Body = func() {
351		out := callback.Call(callArgs)
352		if len(out) == 1 && !out[0].IsNil() {
353			fail(fmt.Sprintf("DeferCleanup callback returned error: %v", out[0]), cl)
354		}
355	}
356
357	return node, nil
358}
359
360func (n Node) IsZero() bool {
361	return n.ID == 0
362}
363
364/* Nodes */
365type Nodes []Node
366
367func (n Nodes) CopyAppend(nodes ...Node) Nodes {
368	numN := len(n)
369	out := make(Nodes, numN+len(nodes))
370	for i, node := range n {
371		out[i] = node
372	}
373	for j, node := range nodes {
374		out[numN+j] = node
375	}
376	return out
377}
378
379func (n Nodes) SplitAround(pivot Node) (Nodes, Nodes) {
380	pivotIdx := len(n)
381	for i := range n {
382		if n[i].ID == pivot.ID {
383			pivotIdx = i
384			break
385		}
386	}
387	left := n[:pivotIdx]
388	right := Nodes{}
389	if pivotIdx+1 < len(n) {
390		right = n[pivotIdx+1:]
391	}
392
393	return left, right
394}
395
396func (n Nodes) FirstNodeWithType(nodeTypes types.NodeType) Node {
397	for i := range n {
398		if n[i].NodeType.Is(nodeTypes) {
399			return n[i]
400		}
401	}
402	return Node{}
403}
404
405func (n Nodes) WithType(nodeTypes types.NodeType) Nodes {
406	count := 0
407	for i := range n {
408		if n[i].NodeType.Is(nodeTypes) {
409			count++
410		}
411	}
412
413	out, j := make(Nodes, count), 0
414	for i := range n {
415		if n[i].NodeType.Is(nodeTypes) {
416			out[j] = n[i]
417			j++
418		}
419	}
420	return out
421}
422
423func (n Nodes) WithoutType(nodeTypes types.NodeType) Nodes {
424	count := 0
425	for i := range n {
426		if !n[i].NodeType.Is(nodeTypes) {
427			count++
428		}
429	}
430
431	out, j := make(Nodes, count), 0
432	for i := range n {
433		if !n[i].NodeType.Is(nodeTypes) {
434			out[j] = n[i]
435			j++
436		}
437	}
438	return out
439}
440
441func (n Nodes) WithoutNode(nodeToExclude Node) Nodes {
442	idxToExclude := len(n)
443	for i := range n {
444		if n[i].ID == nodeToExclude.ID {
445			idxToExclude = i
446			break
447		}
448	}
449	if idxToExclude == len(n) {
450		return n
451	}
452	out, j := make(Nodes, len(n)-1), 0
453	for i := range n {
454		if i == idxToExclude {
455			continue
456		}
457		out[j] = n[i]
458		j++
459	}
460	return out
461}
462
463func (n Nodes) Filter(filter func(Node) bool) Nodes {
464	trufa, count := make([]bool, len(n)), 0
465	for i := range n {
466		if filter(n[i]) {
467			trufa[i] = true
468			count += 1
469		}
470	}
471	out, j := make(Nodes, count), 0
472	for i := range n {
473		if trufa[i] {
474			out[j] = n[i]
475			j++
476		}
477	}
478	return out
479}
480
481func (n Nodes) FirstSatisfying(filter func(Node) bool) Node {
482	for i := range n {
483		if filter(n[i]) {
484			return n[i]
485		}
486	}
487	return Node{}
488}
489
490func (n Nodes) WithinNestingLevel(deepestNestingLevel int) Nodes {
491	count := 0
492	for i := range n {
493		if n[i].NestingLevel <= deepestNestingLevel {
494			count++
495		}
496	}
497	out, j := make(Nodes, count), 0
498	for i := range n {
499		if n[i].NestingLevel <= deepestNestingLevel {
500			out[j] = n[i]
501			j++
502		}
503	}
504	return out
505}
506
507func (n Nodes) SortedByDescendingNestingLevel() Nodes {
508	out := make(Nodes, len(n))
509	copy(out, n)
510	sort.SliceStable(out, func(i int, j int) bool {
511		return out[i].NestingLevel > out[j].NestingLevel
512	})
513
514	return out
515}
516
517func (n Nodes) SortedByAscendingNestingLevel() Nodes {
518	out := make(Nodes, len(n))
519	copy(out, n)
520	sort.SliceStable(out, func(i int, j int) bool {
521		return out[i].NestingLevel < out[j].NestingLevel
522	})
523
524	return out
525}
526
527func (n Nodes) FirstWithNestingLevel(level int) Node {
528	for i := range n {
529		if n[i].NestingLevel == level {
530			return n[i]
531		}
532	}
533	return Node{}
534}
535
536func (n Nodes) Reverse() Nodes {
537	out := make(Nodes, len(n))
538	for i := range n {
539		out[len(n)-1-i] = n[i]
540	}
541	return out
542}
543
544func (n Nodes) Texts() []string {
545	out := make([]string, len(n))
546	for i := range n {
547		out[i] = n[i].Text
548	}
549	return out
550}
551
552func (n Nodes) Labels() [][]string {
553	out := make([][]string, len(n))
554	for i := range n {
555		if n[i].Labels == nil {
556			out[i] = []string{}
557		} else {
558			out[i] = []string(n[i].Labels)
559		}
560	}
561	return out
562}
563
564func (n Nodes) UnionOfLabels() []string {
565	out := []string{}
566	seen := map[string]bool{}
567	for i := range n {
568		for _, label := range n[i].Labels {
569			if !seen[label] {
570				seen[label] = true
571				out = append(out, label)
572			}
573		}
574	}
575	return out
576}
577
578func (n Nodes) CodeLocations() []types.CodeLocation {
579	out := make([]types.CodeLocation, len(n))
580	for i := range n {
581		out[i] = n[i].CodeLocation
582	}
583	return out
584}
585
586func (n Nodes) BestTextFor(node Node) string {
587	if node.Text != "" {
588		return node.Text
589	}
590	parentNestingLevel := node.NestingLevel - 1
591	for i := range n {
592		if n[i].Text != "" && n[i].NestingLevel == parentNestingLevel {
593			return n[i].Text
594		}
595	}
596
597	return ""
598}
599
600func (n Nodes) ContainsNodeID(id uint) bool {
601	for i := range n {
602		if n[i].ID == id {
603			return true
604		}
605	}
606	return false
607}
608
609func (n Nodes) HasNodeMarkedPending() bool {
610	for i := range n {
611		if n[i].MarkedPending {
612			return true
613		}
614	}
615	return false
616}
617
618func (n Nodes) HasNodeMarkedFocus() bool {
619	for i := range n {
620		if n[i].MarkedFocus {
621			return true
622		}
623	}
624	return false
625}
626
627func (n Nodes) HasNodeMarkedSerial() bool {
628	for i := range n {
629		if n[i].MarkedSerial {
630			return true
631		}
632	}
633	return false
634}
635
636func (n Nodes) FirstNodeMarkedOrdered() Node {
637	for i := range n {
638		if n[i].MarkedOrdered {
639			return n[i]
640		}
641	}
642	return Node{}
643}
644
645func unrollInterfaceSlice(args interface{}) []interface{} {
646	v := reflect.ValueOf(args)
647	if v.Kind() != reflect.Slice {
648		return []interface{}{args}
649	}
650	out := []interface{}{}
651	for i := 0; i < v.Len(); i++ {
652		el := reflect.ValueOf(v.Index(i).Interface())
653		if el.Kind() == reflect.Slice && el.Type() != reflect.TypeOf(Labels{}) {
654			out = append(out, unrollInterfaceSlice(el.Interface())...)
655		} else {
656			out = append(out, v.Index(i).Interface())
657		}
658	}
659	return out
660}
661
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Most used method in

Trigger UnknownDecorator code on LambdaTest Cloud Grid

Execute automation tests with UnknownDecorator on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)