From 01cc71b87a5bbf9fce0d146fa5e6b0e588daf410 Mon Sep 17 00:00:00 2001 From: Dimitar Georgievski Date: Thu, 2 May 2024 20:48:13 +0000 Subject: [PATCH 1/4] Added: - Initial implementation of a user defined function. - TCK tests for user defined functions. - Instructions on using user defined functions in README file. Signed-off-by: Dimitar Georgievski --- sql/v2/README.md | 48 ++++++++++++++ sql/v2/function/function.go | 16 ++++- sql/v2/runtime/functions_resolver.go | 5 ++ sql/v2/runtime/functions_resolver_test.go | 70 +++++++++++++++++++++ sql/v2/test/tck/user_defined_functions.yaml | 27 ++++++++ sql/v2/test/tck_test.go | 38 +++++++++++ 6 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 sql/v2/runtime/functions_resolver_test.go create mode 100644 sql/v2/test/tck/user_defined_functions.yaml diff --git a/sql/v2/README.md b/sql/v2/README.md index f45641d97..948f48f41 100644 --- a/sql/v2/README.md +++ b/sql/v2/README.md @@ -18,6 +18,54 @@ expression, err := cesqlparser.Parse("subject = 'Hello world'") res, err := expression.Evaluate(event) ``` +Add a user defined function +```go +import ( + cesql "github.com/cloudevents/sdk-go/sql/v2" + cefn "github.com/cloudevents/sdk-go/sql/v2/function" + cesqlparser "github.com/cloudevents/sdk-go/sql/v2/parser" + ceruntime "github.com/cloudevents/sdk-go/sql/v2/runtime" + cloudevents "github.com/cloudevents/sdk-go/v2" +) + +// Create a test event +event := cloudevents.NewEvent() +event.SetID("aaaa-bbbb-dddd") +event.SetSource("https://my-source") +event.SetType("dev.tekton.event") + +// Create and add a new user defined function +var HasPrefixFunction cesql.Function = cefn.NewFunction( + "HASPREFIX", + []cesql.Type{cesql.StringType, cesql.StringType}, + nil, + func(event cloudevents.Event, i []interface{}) (interface{}, error) { + str := i[0].(string) + prefix := i[1].(string) + + return strings.HasPrefix(str, prefix), nil + }, +) + +err := ceruntime.AddFunction(HasPrefixFunction) + +// parse the expression +expression, err := cesqlparser.Parse("HASPREFIX(type, 'dev.tekton.event')") + if err != nil { + fmt.Println("parser err: ", err) + os.Exit(1) + } + +// Evalute the expression with the test event +res, err := expression.Evaluate(event) + +if res.(bool) { + fmt.Println("Event type has the prefix") +} else { + fmt.Println("Event type doesn't have the prefix") +} +``` + ## Development guide To regenerate the parser, make sure you have [ANTLR4 installed](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md) and then run: diff --git a/sql/v2/function/function.go b/sql/v2/function/function.go index 4ad61faed..f43db3e9d 100644 --- a/sql/v2/function/function.go +++ b/sql/v2/function/function.go @@ -10,11 +10,13 @@ import ( cloudevents "github.com/cloudevents/sdk-go/v2" ) +type FuncType func(cloudevents.Event, []interface{}) (interface{}, error) + type function struct { name string fixedArgs []cesql.Type variadicArgs *cesql.Type - fn func(cloudevents.Event, []interface{}) (interface{}, error) + fn FuncType } func (f function) Name() string { @@ -39,3 +41,15 @@ func (f function) ArgType(index int) *cesql.Type { func (f function) Run(event cloudevents.Event, arguments []interface{}) (interface{}, error) { return f.fn(event, arguments) } + +func NewFunction(name string, + fixedargs []cesql.Type, + variadicArgs *cesql.Type, + fn FuncType) cesql.Function { + return function{ + name: name, + fixedArgs: fixedargs, + variadicArgs: variadicArgs, + fn: fn, + } +} diff --git a/sql/v2/runtime/functions_resolver.go b/sql/v2/runtime/functions_resolver.go index b80136842..5ab964fb7 100644 --- a/sql/v2/runtime/functions_resolver.go +++ b/sql/v2/runtime/functions_resolver.go @@ -58,6 +58,11 @@ func (table functionTable) AddFunction(function cesql.Function) error { } } +// Adds user defined function +func AddFunction(fn cesql.Function) error { + return globalFunctionTable.AddFunction(fn) +} + func (table functionTable) ResolveFunction(name string, args int) cesql.Function { item := table[strings.ToUpper(name)] if item == nil { diff --git a/sql/v2/runtime/functions_resolver_test.go b/sql/v2/runtime/functions_resolver_test.go new file mode 100644 index 000000000..afe23ddf8 --- /dev/null +++ b/sql/v2/runtime/functions_resolver_test.go @@ -0,0 +1,70 @@ +/* + Copyright 2021 The CloudEvents Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package runtime + +import ( + "os" + "strings" + "testing" + + cesql "github.com/cloudevents/sdk-go/sql/v2" + "github.com/cloudevents/sdk-go/sql/v2/function" + cloudevents "github.com/cloudevents/sdk-go/v2" +) + +var HasPrefixCustomFunction cesql.Function + +func TestMain(m *testing.M) { + HasPrefixCustomFunction = function.NewFunction( + "HASPREFIX", + []cesql.Type{cesql.StringType, cesql.StringType}, + nil, + func(event cloudevents.Event, i []interface{}) (interface{}, error) { + str := i[0].(string) + prefix := i[1].(string) + + return strings.HasPrefix(str, prefix), nil + }, + ) + os.Exit(m.Run()) +} + +func Test_functionTable_AddFunction(t *testing.T) { + + type args struct { + function cesql.Function + } + tests := []struct { + name string + table functionTable + args args + wantErr bool + }{ + { + name: "Add custom fixedArgs func", + table: globalFunctionTable, + args: args{ + function: HasPrefixCustomFunction, + }, + wantErr: false, + }, + { + name: "Fail add custom fixedArgs func if it exists", + table: globalFunctionTable, + args: args{ + function: HasPrefixCustomFunction, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.table.AddFunction(tt.args.function); (err != nil) != tt.wantErr { + t.Errorf("functionTable.AddFunction() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/sql/v2/test/tck/user_defined_functions.yaml b/sql/v2/test/tck/user_defined_functions.yaml new file mode 100644 index 000000000..d07012f3f --- /dev/null +++ b/sql/v2/test/tck/user_defined_functions.yaml @@ -0,0 +1,27 @@ +name: User defined functions +tests: + - name: HASPREFIX (1) + expression: "HASPREFIX('abcdef', 'ab')" + result: true + - name: HASPREFIX (2) + expression: "HASPREFIX('abcdef', 'abcdef')" + result: true + - name: HASPREFIX (3) + expression: "HASPREFIX('abcdef', '')" + result: true + - name: HASPREFIX (4) + expression: "HASPREFIX('abcdef', 'gh')" + result: false + - name: HASPREFIX (5) + expression: "HASPREFIX('abcdef', 'abcdefg')" + result: false + + - name: KONKAT (1) + expression: "KONKAT('a', 'b', 'c')" + result: abc + - name: KONKAT (2) + expression: "KONKAT()" + result: "" + - name: KONKAT (3) + expression: "KONKAT('a')" + result: "a" diff --git a/sql/v2/test/tck_test.go b/sql/v2/test/tck_test.go index f215c8db4..6d20d39e9 100644 --- a/sql/v2/test/tck_test.go +++ b/sql/v2/test/tck_test.go @@ -10,12 +10,16 @@ import ( "os" "path" "runtime" + "strings" "testing" "github.com/stretchr/testify/require" "sigs.k8s.io/yaml" + cesql "github.com/cloudevents/sdk-go/sql/v2" + "github.com/cloudevents/sdk-go/sql/v2/function" "github.com/cloudevents/sdk-go/sql/v2/parser" + ceruntime "github.com/cloudevents/sdk-go/sql/v2/runtime" cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/cloudevents/sdk-go/v2/binding/spec" "github.com/cloudevents/sdk-go/v2/event" @@ -41,6 +45,33 @@ var TCKFileNames = []string{ "string_builtin_functions", "sub_expression", "subscriptions_api_recreations", + "user_defined_functions", +} + +var TCKUserDefinedFunctions = []cesql.Function{ + function.NewFunction( + "HASPREFIX", + []cesql.Type{cesql.StringType, cesql.StringType}, + nil, + func(event cloudevents.Event, i []interface{}) (interface{}, error) { + str := i[0].(string) + prefix := i[1].(string) + + return strings.HasPrefix(str, prefix), nil + }, + ), + function.NewFunction( + "KONKAT", + []cesql.Type{}, + cesql.TypePtr(cesql.StringType), + func(event cloudevents.Event, i []interface{}) (interface{}, error) { + var sb strings.Builder + for _, v := range i { + sb.WriteString(v.(string)) + } + return sb.String(), nil + }, + ), } type ErrorType string @@ -70,6 +101,13 @@ type TckTestCase struct { EventOverrides map[string]interface{} `json:"eventOverrides"` } +func TestMain(m *testing.M) { + for _, f := range TCKUserDefinedFunctions { + ceruntime.AddFunction(f) + } + os.Exit(m.Run()) +} + func (tc TckTestCase) InputEvent(t *testing.T) cloudevents.Event { var inputEvent cloudevents.Event if tc.Event != nil { From 31e1907ccd8bc41b8411624339a0ce6033286f56 Mon Sep 17 00:00:00 2001 From: Dimitar Georgievski Date: Thu, 9 May 2024 18:14:11 +0000 Subject: [PATCH 2/4] Moved tests assets for user defined fuctions from sql/v2/test to sql/v2/runtime Signed-off-by: Dimitar Georgievski --- sql/v2/go.mod | 2 +- sql/v2/runtime/functions_resolver_test.go | 70 ------ .../tck/user_defined_functions.yaml | 0 sql/v2/runtime/user_defined_functions_test.go | 209 ++++++++++++++++++ sql/v2/test/tck_test.go | 38 ---- 5 files changed, 210 insertions(+), 109 deletions(-) delete mode 100644 sql/v2/runtime/functions_resolver_test.go rename sql/v2/{test => runtime}/tck/user_defined_functions.yaml (100%) create mode 100644 sql/v2/runtime/user_defined_functions_test.go diff --git a/sql/v2/go.mod b/sql/v2/go.mod index 86d00e196..631a5b536 100644 --- a/sql/v2/go.mod +++ b/sql/v2/go.mod @@ -6,6 +6,7 @@ require ( github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 github.com/cloudevents/sdk-go/v2 v2.5.0 github.com/stretchr/testify v1.8.0 + gopkg.in/yaml.v2 v2.4.0 sigs.k8s.io/yaml v1.3.0 ) @@ -20,7 +21,6 @@ require ( go.uber.org/atomic v1.4.0 // indirect go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.10.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sql/v2/runtime/functions_resolver_test.go b/sql/v2/runtime/functions_resolver_test.go deleted file mode 100644 index afe23ddf8..000000000 --- a/sql/v2/runtime/functions_resolver_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright 2021 The CloudEvents Authors - SPDX-License-Identifier: Apache-2.0 -*/ - -package runtime - -import ( - "os" - "strings" - "testing" - - cesql "github.com/cloudevents/sdk-go/sql/v2" - "github.com/cloudevents/sdk-go/sql/v2/function" - cloudevents "github.com/cloudevents/sdk-go/v2" -) - -var HasPrefixCustomFunction cesql.Function - -func TestMain(m *testing.M) { - HasPrefixCustomFunction = function.NewFunction( - "HASPREFIX", - []cesql.Type{cesql.StringType, cesql.StringType}, - nil, - func(event cloudevents.Event, i []interface{}) (interface{}, error) { - str := i[0].(string) - prefix := i[1].(string) - - return strings.HasPrefix(str, prefix), nil - }, - ) - os.Exit(m.Run()) -} - -func Test_functionTable_AddFunction(t *testing.T) { - - type args struct { - function cesql.Function - } - tests := []struct { - name string - table functionTable - args args - wantErr bool - }{ - { - name: "Add custom fixedArgs func", - table: globalFunctionTable, - args: args{ - function: HasPrefixCustomFunction, - }, - wantErr: false, - }, - { - name: "Fail add custom fixedArgs func if it exists", - table: globalFunctionTable, - args: args{ - function: HasPrefixCustomFunction, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.table.AddFunction(tt.args.function); (err != nil) != tt.wantErr { - t.Errorf("functionTable.AddFunction() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/sql/v2/test/tck/user_defined_functions.yaml b/sql/v2/runtime/tck/user_defined_functions.yaml similarity index 100% rename from sql/v2/test/tck/user_defined_functions.yaml rename to sql/v2/runtime/tck/user_defined_functions.yaml diff --git a/sql/v2/runtime/user_defined_functions_test.go b/sql/v2/runtime/user_defined_functions_test.go new file mode 100644 index 000000000..a9ddfb303 --- /dev/null +++ b/sql/v2/runtime/user_defined_functions_test.go @@ -0,0 +1,209 @@ +/* + Copyright 2021 The CloudEvents Authors + SPDX-License-Identifier: Apache-2.0 +*/ + +package runtime_test + +import ( + "io" + "os" + "path" + "runtime" + "strings" + "testing" + + cesql "github.com/cloudevents/sdk-go/sql/v2" + "github.com/cloudevents/sdk-go/sql/v2/function" + "github.com/cloudevents/sdk-go/sql/v2/parser" + ceruntime "github.com/cloudevents/sdk-go/sql/v2/runtime" + cloudevents "github.com/cloudevents/sdk-go/v2" + "github.com/cloudevents/sdk-go/v2/binding/spec" + "github.com/cloudevents/sdk-go/v2/event" + "github.com/cloudevents/sdk-go/v2/test" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +var TCKFileNames = []string{ + "user_defined_functions", +} + +var TCKUserDefinedFunctions = []cesql.Function{ + function.NewFunction( + "HASPREFIX", + []cesql.Type{cesql.StringType, cesql.StringType}, + nil, + func(event cloudevents.Event, i []interface{}) (interface{}, error) { + str := i[0].(string) + prefix := i[1].(string) + + return strings.HasPrefix(str, prefix), nil + }, + ), + function.NewFunction( + "KONKAT", + []cesql.Type{}, + cesql.TypePtr(cesql.StringType), + func(event cloudevents.Event, i []interface{}) (interface{}, error) { + var sb strings.Builder + for _, v := range i { + sb.WriteString(v.(string)) + } + return sb.String(), nil + }, + ), +} + +type ErrorType string + +const ( + ParseError ErrorType = "parse" + MathError ErrorType = "math" + CastError ErrorType = "cast" + MissingAttributeError ErrorType = "missingAttribute" + MissingFunctionError ErrorType = "missingFunction" + FunctionEvaluationError ErrorType = "functionEvaluation" +) + +type TckFile struct { + Name string `json:"name"` + Tests []TckTestCase `json:"tests"` +} + +type TckTestCase struct { + Name string `json:"name"` + Expression string `json:"expression"` + + Result interface{} `json:"result"` + Error ErrorType `json:"error"` + + Event *cloudevents.Event `json:"event"` + EventOverrides map[string]interface{} `json:"eventOverrides"` +} + +func (tc TckTestCase) InputEvent(t *testing.T) cloudevents.Event { + var inputEvent cloudevents.Event + if tc.Event != nil { + inputEvent = *tc.Event + } else { + inputEvent = test.FullEvent() + } + + // Make sure the event is v1 + inputEvent.SetSpecVersion(event.CloudEventsVersionV1) + + for k, v := range tc.EventOverrides { + require.NoError(t, spec.V1.SetAttribute(inputEvent.Context, k, v)) + } + + return inputEvent +} + +func (tc TckTestCase) ExpectedResult() interface{} { + switch tc.Result.(type) { + case int: + return int32(tc.Result.(int)) + case float64: + return int32(tc.Result.(float64)) + case bool: + return tc.Result.(bool) + } + return tc.Result +} + +func Test_functionTable_AddFunction(t *testing.T) { + + type args struct { + functions []cesql.Function + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Add user functions to global table", + + args: args{ + functions: TCKUserDefinedFunctions, + }, + wantErr: false, + }, + { + name: "Fail add user functions to global table", + args: args{ + functions: TCKUserDefinedFunctions, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for _, fn := range tt.args.functions { + if err := ceruntime.AddFunction(fn); (err != nil) != tt.wantErr { + t.Errorf("functionTable.AddFunction() error = %v, wantErr %v", err, tt.wantErr) + } + } + }) + } +} + +func Test_UserFunctions(t *testing.T) { + tckFiles := make([]TckFile, 0, len(TCKFileNames)) + + _, basePath, _, _ := runtime.Caller(0) + basePath, _ = path.Split(basePath) + + for _, testFile := range TCKFileNames { + testFilePath := path.Join(basePath, "tck", testFile+".yaml") + + t.Logf("Loading file %s", testFilePath) + file, err := os.Open(testFilePath) + require.NoError(t, err) + + fileBytes, err := io.ReadAll(file) + require.NoError(t, err) + + tckFileModel := TckFile{} + require.NoError(t, yaml.Unmarshal(fileBytes, &tckFileModel)) + + tckFiles = append(tckFiles, tckFileModel) + } + + for i, file := range tckFiles { + i := i + t.Run(file.Name, func(t *testing.T) { + for j, testCase := range tckFiles[i].Tests { + j := j + testCase := testCase + t.Run(testCase.Name, func(t *testing.T) { + t.Parallel() + testCase := tckFiles[i].Tests[j] + + t.Logf("Test expression: '%s'", testCase.Expression) + + if testCase.Error == ParseError { + _, err := parser.Parse(testCase.Expression) + require.NotNil(t, err) + return + } + + expr, err := parser.Parse(testCase.Expression) + require.NoError(t, err) + require.NotNil(t, expr) + + inputEvent := testCase.InputEvent(t) + result, err := expr.Evaluate(inputEvent) + + if testCase.Error != "" { + require.NotNil(t, err) + } else { + require.NoError(t, err) + require.Equal(t, testCase.ExpectedResult(), result) + } + }) + } + }) + } +} diff --git a/sql/v2/test/tck_test.go b/sql/v2/test/tck_test.go index 6d20d39e9..f215c8db4 100644 --- a/sql/v2/test/tck_test.go +++ b/sql/v2/test/tck_test.go @@ -10,16 +10,12 @@ import ( "os" "path" "runtime" - "strings" "testing" "github.com/stretchr/testify/require" "sigs.k8s.io/yaml" - cesql "github.com/cloudevents/sdk-go/sql/v2" - "github.com/cloudevents/sdk-go/sql/v2/function" "github.com/cloudevents/sdk-go/sql/v2/parser" - ceruntime "github.com/cloudevents/sdk-go/sql/v2/runtime" cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/cloudevents/sdk-go/v2/binding/spec" "github.com/cloudevents/sdk-go/v2/event" @@ -45,33 +41,6 @@ var TCKFileNames = []string{ "string_builtin_functions", "sub_expression", "subscriptions_api_recreations", - "user_defined_functions", -} - -var TCKUserDefinedFunctions = []cesql.Function{ - function.NewFunction( - "HASPREFIX", - []cesql.Type{cesql.StringType, cesql.StringType}, - nil, - func(event cloudevents.Event, i []interface{}) (interface{}, error) { - str := i[0].(string) - prefix := i[1].(string) - - return strings.HasPrefix(str, prefix), nil - }, - ), - function.NewFunction( - "KONKAT", - []cesql.Type{}, - cesql.TypePtr(cesql.StringType), - func(event cloudevents.Event, i []interface{}) (interface{}, error) { - var sb strings.Builder - for _, v := range i { - sb.WriteString(v.(string)) - } - return sb.String(), nil - }, - ), } type ErrorType string @@ -101,13 +70,6 @@ type TckTestCase struct { EventOverrides map[string]interface{} `json:"eventOverrides"` } -func TestMain(m *testing.M) { - for _, f := range TCKUserDefinedFunctions { - ceruntime.AddFunction(f) - } - os.Exit(m.Run()) -} - func (tc TckTestCase) InputEvent(t *testing.T) cloudevents.Event { var inputEvent cloudevents.Event if tc.Event != nil { From f811b6639aaa239bcd4736d1e4b0d0e3d7b25f65 Mon Sep 17 00:00:00 2001 From: Dimitar Georgievski Date: Thu, 9 May 2024 21:04:18 +0000 Subject: [PATCH 3/4] moved runtime test to runtime/test dir Signed-off-by: Dimitar Georgievski --- sql/v2/runtime/{ => test}/tck/user_defined_functions.yaml | 0 sql/v2/runtime/{ => test}/user_defined_functions_test.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename sql/v2/runtime/{ => test}/tck/user_defined_functions.yaml (100%) rename sql/v2/runtime/{ => test}/user_defined_functions_test.go (100%) diff --git a/sql/v2/runtime/tck/user_defined_functions.yaml b/sql/v2/runtime/test/tck/user_defined_functions.yaml similarity index 100% rename from sql/v2/runtime/tck/user_defined_functions.yaml rename to sql/v2/runtime/test/tck/user_defined_functions.yaml diff --git a/sql/v2/runtime/user_defined_functions_test.go b/sql/v2/runtime/test/user_defined_functions_test.go similarity index 100% rename from sql/v2/runtime/user_defined_functions_test.go rename to sql/v2/runtime/test/user_defined_functions_test.go From 22596a9eadcf376e13bc90274c6ad007e88874e9 Mon Sep 17 00:00:00 2001 From: Dimitar Georgievski Date: Wed, 5 Jun 2024 15:05:17 +0000 Subject: [PATCH 4/4] Corrected test functions naming format and copyright year Signed-off-by: Dimitar Georgievski --- sql/v2/runtime/test/tck/user_defined_functions.yaml | 2 +- sql/v2/runtime/test/user_defined_functions_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sql/v2/runtime/test/tck/user_defined_functions.yaml b/sql/v2/runtime/test/tck/user_defined_functions.yaml index d07012f3f..c2a3a922e 100644 --- a/sql/v2/runtime/test/tck/user_defined_functions.yaml +++ b/sql/v2/runtime/test/tck/user_defined_functions.yaml @@ -24,4 +24,4 @@ tests: result: "" - name: KONKAT (3) expression: "KONKAT('a')" - result: "a" + result: "a" \ No newline at end of file diff --git a/sql/v2/runtime/test/user_defined_functions_test.go b/sql/v2/runtime/test/user_defined_functions_test.go index a9ddfb303..944ba98dd 100644 --- a/sql/v2/runtime/test/user_defined_functions_test.go +++ b/sql/v2/runtime/test/user_defined_functions_test.go @@ -1,5 +1,5 @@ /* - Copyright 2021 The CloudEvents Authors + Copyright 2024 The CloudEvents Authors SPDX-License-Identifier: Apache-2.0 */ @@ -112,7 +112,7 @@ func (tc TckTestCase) ExpectedResult() interface{} { return tc.Result } -func Test_functionTable_AddFunction(t *testing.T) { +func TestFunctionTableAddFunction(t *testing.T) { type args struct { functions []cesql.Function @@ -142,14 +142,14 @@ func Test_functionTable_AddFunction(t *testing.T) { t.Run(tt.name, func(t *testing.T) { for _, fn := range tt.args.functions { if err := ceruntime.AddFunction(fn); (err != nil) != tt.wantErr { - t.Errorf("functionTable.AddFunction() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("AddFunction() error = %v, wantErr %v", err, tt.wantErr) } } }) } } -func Test_UserFunctions(t *testing.T) { +func TestUserFunctions(t *testing.T) { tckFiles := make([]TckFile, 0, len(TCKFileNames)) _, basePath, _, _ := runtime.Caller(0)