Skip to content

Commit

Permalink
[BEAM-13904] Increase unit testing in the reflectx package (#17024)
Browse files Browse the repository at this point in the history
* [BEAM-13904] Increase unit testing in the reflectx package

* Error at end of string
  • Loading branch information
damccorm authored Mar 9, 2022
1 parent 2d1b023 commit eeccbb0
Show file tree
Hide file tree
Showing 4 changed files with 609 additions and 0 deletions.
125 changes: 125 additions & 0 deletions sdks/go/pkg/beam/core/util/reflectx/call_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package reflectx

import (
"reflect"
"strings"
"testing"
)

type mapperString struct {
fn func(string) string
}

func mapperStringMaker(fn interface{}) Func {
f := fn.(func(string) string)
return &mapperString{fn: f}
}

func (c *mapperString) Name() string {
return "testMapperString"
}

func (c *mapperString) Type() reflect.Type {
return reflect.TypeOf(c.fn)
}

func (c *mapperString) Call(args []interface{}) []interface{} {
out := c.fn(args[0].(string))
return []interface{}{out}
}

func (c *mapperString) Call1x1(v interface{}) interface{} {
return c.fn(v.(string))
}

func TestMakeFunc(t *testing.T) {
RegisterFunc(reflect.TypeOf((*func(string) string)(nil)).Elem(), mapperStringMaker)
fn := func(str string) string {
return string(str)
}
madeFn := MakeFunc(fn)

if got, want := madeFn.Name(), "testMapperString"; got != want {
t.Fatalf("MakeFunc(fn).Name()=%v, want %v", got, want)
}
}

func TestCallNoPanic(t *testing.T) {
RegisterFunc(reflect.TypeOf((*func(string) string)(nil)).Elem(), mapperStringMaker)
fn := func(str string) string {
return string(str)
}
madeFn := MakeFunc(fn)

ret, err := CallNoPanic(madeFn, []interface{}{"tester"})
if err != nil {
t.Fatalf("CallNoPanic(madeFn, [\"tester\"]) - unexpected error %v", err)
}
if got, want := ret[0].(string), string("tester"); got != want {
t.Fatalf("CallNoPanic(madeFn, [\"tester\"]) got %v, want %v", got, want)
}
}

func TestCallNoPanic_Panic(t *testing.T) {
RegisterFunc(reflect.TypeOf((*func(string) string)(nil)).Elem(), mapperStringMaker)
fn := func(str string) string {
if str == "tester" {
panic("OH NO!")
}
return string(str)
}
madeFn := MakeFunc(fn)

_, err := CallNoPanic(madeFn, []interface{}{"tester"})
if err == nil {
t.Fatalf("CallNoPanic(madeFn, [\"tester\"]) didn't error when it should have")
}
if !strings.Contains(err.Error(), "OH NO!") {
t.Fatalf("CallNoPanic(madeFn, [\"tester\"]) error should have contained OH NO! instead returned error %v", err)
}
}

func TestValue(t *testing.T) {
interfaces := []interface{}{"hi", 42, func() {}}
want := []reflect.Kind{reflect.String, reflect.Int, reflect.Func}

got := ValueOf(interfaces)
if len(got) != len(want) {
t.Fatalf("ValueOf(interfaces) got slice %v, expected slice of length %v", got, len(want))
}
for idx, _ := range got {
if got[idx].Kind() != want[idx] {
t.Errorf("ValueOf(interfaces)[%v], got %v of kind %v, want %v", idx, got[idx], got[idx].Kind(), want[idx])
}
}
}

func TestInterface(t *testing.T) {
interfaces := []interface{}{"hi", 42}
values := ValueOf(interfaces)
got := Interface(values)

if len(got) != len(interfaces) {
t.Fatalf("Interface(values) got slice %v, expected slice of length %v", got, len(interfaces))
}
for idx, _ := range got {
if got[idx] != interfaces[idx] {
t.Errorf("Interface(values)[%v]=%v, want %v", idx, got[idx], interfaces[idx])
}
}
}
7 changes: 7 additions & 0 deletions sdks/go/pkg/beam/core/util/reflectx/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@ package reflectx

import (
"reflect"
"strings"
"testing"
)

func testFunction() int64 {
return 42
}

func TestFunctionName(t *testing.T) {
if got, want := FunctionName(testFunction), "reflectx.testFunction"; !strings.Contains(got, want) {
t.Fatalf("FunctionName(testFunction)=%v, should contain %v", got, want)
}
}

func TestLoadFunction(t *testing.T) {
val := reflect.ValueOf(testFunction)
fi := uintptr(val.Pointer())
Expand Down
67 changes: 67 additions & 0 deletions sdks/go/pkg/beam/core/util/reflectx/structs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package reflectx

import (
"reflect"
"testing"
)

type nameFn struct {
Name string `json:"name,omitempty"`
}

func (f *nameFn) PrintName() string {
return f.Name
}

func NameFnMaker(fn interface{}) map[string]Func {
dfn := fn.(*nameFn)
return map[string]Func{
"PrintName": MakeFunc(func() string { return dfn.PrintName() }),
}
}

func TestWrapMethods_Registered(t *testing.T) {
RegisterStructWrapper(reflect.TypeOf((*nameFn)(nil)).Elem(), NameFnMaker)
wrapper, exists := WrapMethods(&nameFn{Name: "testName"})

if got, want := exists, true; got != want {
t.Errorf("WrapMethods(&nameFn{Name: \"testName\"}), nameFn registered = %v, want %v", got, want)
}
fn, ok := wrapper["PrintName"]
if ok != true {
t.Errorf("WrapMethods(&nameFn{Name: \"testName\"}) doesn't contain PrintName, want a function")
}
if got, want := fn.Call([]interface{}{})[0].(string), "testName"; got != want {
t.Errorf("WrapMethods(&nameFn{Name: \"testName\"}) invoked PrintName, got %v, want %v", got, want)
}
}

type unregisteredFn struct {
Name string `json:"name,omitempty"`
}

func TestWrapMethods_Unregistered(t *testing.T) {
wrapper, exists := WrapMethods(&unregisteredFn{Name: "testName"})

if got, want := exists, false; got != want {
t.Fatalf("WrapMethods(&unregisteredFn{Name: \"testName\"}), unregisteredFn registered = %v, want %v", got, want)
}
if got := wrapper; got != nil {
t.Fatalf("WrapMethods(&unregisteredFn{Name: \"testName\"}), wrapper = %v, want nil", got)
}
}
Loading

0 comments on commit eeccbb0

Please sign in to comment.