github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/runtime/slots_test.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package grumpy 16 17 import ( 18 "reflect" 19 "testing" 20 ) 21 22 func TestSlotMakeCallable(t *testing.T) { 23 fooType := newTestClass("Foo", []*Type{ObjectType}, NewDict()) 24 foo := newObject(fooType) 25 // fun returns a tuple: (ret, args) where ret is the return value of 26 // the callable produced by makeCallable and args are the arguments 27 // that were passed into the callable. 28 fun := wrapFuncForTest(func(f *Frame, s slot, ret *Object, args ...*Object) (*Object, *BaseException) { 29 gotArgs := None 30 prepareTestSlot(s, &gotArgs, ret) 31 callable := s.makeCallable(fooType, "__slot__") 32 if callable == nil { 33 // This slot does not produce a callable, so just 34 // return None. 35 return None, nil 36 } 37 result, raised := callable.Call(f, args, nil) 38 if raised != nil { 39 return nil, raised 40 } 41 return NewTuple(result, gotArgs).ToObject(), nil 42 }) 43 cases := []invokeTestCase{ 44 {args: wrapArgs(&basisSlot{}, None), want: None}, 45 {args: wrapArgs(&binaryOpSlot{}, "foo", foo, 123), want: newTestTuple("foo", newTestTuple(foo, 123)).ToObject()}, 46 {args: wrapArgs(&binaryOpSlot{}, None, "abc", 123), wantExc: mustCreateException(TypeErrorType, "'__slot__' requires a 'Foo' object but received a 'str'")}, 47 {args: wrapArgs(&delAttrSlot{}, None, foo, "bar"), want: newTestTuple(None, newTestTuple(foo, "bar")).ToObject()}, 48 {args: wrapArgs(&delAttrSlot{}, None, foo, 3.14), wantExc: mustCreateException(TypeErrorType, "'__slot__' requires a 'str' object but received a 'float'")}, 49 {args: wrapArgs(&delAttrSlot{}, RuntimeErrorType, foo, "bar"), wantExc: mustCreateException(RuntimeErrorType, "")}, 50 {args: wrapArgs(&deleteSlot{}, None, foo, "bar"), want: newTestTuple(None, newTestTuple(foo, "bar")).ToObject()}, 51 {args: wrapArgs(&deleteSlot{}, None, foo, 1, 2, 3), wantExc: mustCreateException(TypeErrorType, "'__slot__' of 'Foo' requires 2 arguments")}, 52 {args: wrapArgs(&deleteSlot{}, RuntimeErrorType, foo, "bar"), wantExc: mustCreateException(RuntimeErrorType, "")}, 53 {args: wrapArgs(&delItemSlot{}, None, foo, "bar"), want: newTestTuple(None, newTestTuple(foo, "bar")).ToObject()}, 54 {args: wrapArgs(&delItemSlot{}, None, foo, 1, 2, 3), wantExc: mustCreateException(TypeErrorType, "'__slot__' of 'Foo' requires 2 arguments")}, 55 {args: wrapArgs(&delItemSlot{}, RuntimeErrorType, foo, "bar"), wantExc: mustCreateException(RuntimeErrorType, "")}, 56 {args: wrapArgs(&getAttributeSlot{}, None, foo, "bar"), want: newTestTuple(None, newTestTuple(foo, "bar")).ToObject()}, 57 {args: wrapArgs(&getAttributeSlot{}, None, foo, 3.14), wantExc: mustCreateException(TypeErrorType, "'__slot__' requires a 'str' object but received a 'float'")}, 58 {args: wrapArgs(&getAttributeSlot{}, RuntimeErrorType, foo, "bar"), wantExc: mustCreateException(RuntimeErrorType, "")}, 59 {args: wrapArgs(&getSlot{}, 3.14, foo, 123, IntType), want: newTestTuple(3.14, newTestTuple(foo, 123, IntType)).ToObject()}, 60 {args: wrapArgs(&getSlot{}, None, foo, "bar", "baz"), wantExc: mustCreateException(TypeErrorType, "'__slot__' requires a 'type' object but received a 'str'")}, 61 {args: wrapArgs(&nativeSlot{}, None), want: None}, 62 {args: wrapArgs(&setAttrSlot{}, None, foo, "bar", 123), want: newTestTuple(None, newTestTuple(foo, "bar", 123)).ToObject()}, 63 {args: wrapArgs(&setAttrSlot{}, None, foo, true, None), wantExc: mustCreateException(TypeErrorType, "'__slot__' requires a 'str' object but received a 'bool'")}, 64 {args: wrapArgs(&setAttrSlot{}, RuntimeErrorType, foo, "bar", "baz"), wantExc: mustCreateException(RuntimeErrorType, "")}, 65 {args: wrapArgs(&setItemSlot{}, None, foo, "bar", true), want: newTestTuple(None, newTestTuple(foo, "bar", true)).ToObject()}, 66 {args: wrapArgs(&setItemSlot{}, None, foo, 1, 2, 3), wantExc: mustCreateException(TypeErrorType, "'__slot__' of 'Foo' requires 3 arguments")}, 67 {args: wrapArgs(&setItemSlot{}, RuntimeErrorType, foo, "bar", "baz"), wantExc: mustCreateException(RuntimeErrorType, "")}, 68 {args: wrapArgs(&setSlot{}, None, foo, 3.14, false), want: newTestTuple(None, newTestTuple(foo, 3.14, false)).ToObject()}, 69 {args: wrapArgs(&setSlot{}, RuntimeErrorType, foo, "bar", "baz"), wantExc: mustCreateException(RuntimeErrorType, "")}, 70 {args: wrapArgs(&setSlot{}, None, foo, 1, 2, 3), wantExc: mustCreateException(TypeErrorType, "'__slot__' of 'Foo' requires 3 arguments")}, 71 {args: wrapArgs(&unaryOpSlot{}, 42, foo), want: newTestTuple(42, NewTuple(foo)).ToObject()}, 72 {args: wrapArgs(&unaryOpSlot{}, None, foo, "bar"), wantExc: mustCreateException(TypeErrorType, "'__slot__' of 'Foo' requires 1 arguments")}, 73 } 74 for _, cas := range cases { 75 if err := runInvokeTestCase(fun, &cas); err != "" { 76 t.Error(err) 77 } 78 } 79 } 80 81 func TestSlotWrapCallable(t *testing.T) { 82 // fun returns a tuple: (ret, args, kwargs) where ret is the return 83 // value of the slot, args and kwargs are the positional and keyword 84 // parameters passed to it. 85 fun := wrapFuncForTest(func(f *Frame, s slot, ret *Object, args ...*Object) (*Object, *BaseException) { 86 gotArgs := None 87 gotKWArgs := None 88 wrapped := newBuiltinFunction("wrapped", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 89 if ret.isInstance(TypeType) && toTypeUnsafe(ret).isSubclass(BaseExceptionType) { 90 return nil, f.Raise(ret, nil, nil) 91 } 92 gotArgs = NewTuple(args.makeCopy()...).ToObject() 93 gotKWArgs = kwargs.makeDict().ToObject() 94 return ret, nil 95 }).ToObject() 96 s.wrapCallable(wrapped) 97 fnField := reflect.ValueOf(s).Elem().Field(0) 98 if fnField.IsNil() { 99 // Return None to denote the slot was empty. 100 return None, nil 101 } 102 // Wrap the underlying slot function (s.Fn) and call it. This 103 // is more convenient than using reflection to call it. 104 fn, raised := WrapNative(f, fnField) 105 if raised != nil { 106 return nil, raised 107 } 108 result, raised := fn.Call(f, append(Args{f.ToObject()}, args...), nil) 109 if raised != nil { 110 return nil, raised 111 } 112 return NewTuple(result, gotArgs, gotKWArgs).ToObject(), nil 113 }) 114 o := newObject(ObjectType) 115 cases := []invokeTestCase{ 116 {args: wrapArgs(&basisSlot{}, "no"), want: None}, 117 {args: wrapArgs(&binaryOpSlot{}, "ret", "foo", "bar"), want: newTestTuple("ret", newTestTuple("foo", "bar"), NewDict()).ToObject()}, 118 {args: wrapArgs(&binaryOpSlot{}, RuntimeErrorType, "foo", "bar"), wantExc: mustCreateException(RuntimeErrorType, "")}, 119 {args: wrapArgs(&callSlot{}, "ret", true, wrapArgs(1, 2), None), want: newTestTuple("ret", newTestTuple(true, 1, 2), NewDict()).ToObject()}, 120 {args: wrapArgs(&callSlot{}, "ret", "foo", None, wrapKWArgs("a", "b")), want: newTestTuple("ret", newTestTuple("foo"), newTestDict("a", "b")).ToObject()}, 121 {args: wrapArgs(&callSlot{}, "ret", 3.14, wrapArgs(false), wrapKWArgs("foo", 42)), want: newTestTuple("ret", newTestTuple(3.14, false), newTestDict("foo", 42)).ToObject()}, 122 {args: wrapArgs(&callSlot{}, RuntimeErrorType, true, wrapArgs(1, 2), None), wantExc: mustCreateException(RuntimeErrorType, "")}, 123 {args: wrapArgs(&delAttrSlot{}, "ret", o, "foo"), want: newTestTuple(None, newTestTuple(o, "foo"), NewDict()).ToObject()}, 124 {args: wrapArgs(&delAttrSlot{}, RuntimeErrorType, o, "foo"), wantExc: mustCreateException(RuntimeErrorType, "")}, 125 {args: wrapArgs(&deleteSlot{}, "ret", o, 3.14), want: newTestTuple(None, newTestTuple(o, 3.14), NewDict()).ToObject()}, 126 {args: wrapArgs(&deleteSlot{}, RuntimeErrorType, o, 3.14), wantExc: mustCreateException(RuntimeErrorType, "")}, 127 {args: wrapArgs(&delItemSlot{}, "ret", o, false), want: newTestTuple(None, newTestTuple(o, false), NewDict()).ToObject()}, 128 {args: wrapArgs(&delItemSlot{}, RuntimeErrorType, o, false), wantExc: mustCreateException(RuntimeErrorType, "")}, 129 {args: wrapArgs(&getAttributeSlot{}, "ret", o, "foo"), want: newTestTuple("ret", newTestTuple(o, "foo"), NewDict()).ToObject()}, 130 {args: wrapArgs(&getAttributeSlot{}, RuntimeErrorType, o, "foo"), wantExc: mustCreateException(RuntimeErrorType, "")}, 131 {args: wrapArgs(&getSlot{}, "ret", o, "foo", SetType), want: newTestTuple("ret", newTestTuple(o, "foo", SetType), NewDict()).ToObject()}, 132 {args: wrapArgs(&getSlot{}, RuntimeErrorType, o, "foo", SetType), wantExc: mustCreateException(RuntimeErrorType, "")}, 133 {args: wrapArgs(&initSlot{}, "ret", true, wrapArgs(1, 2), None), want: newTestTuple("ret", newTestTuple(true, 1, 2), NewDict()).ToObject()}, 134 {args: wrapArgs(&initSlot{}, "ret", "foo", None, wrapKWArgs("a", "b")), want: newTestTuple("ret", newTestTuple("foo"), newTestDict("a", "b")).ToObject()}, 135 {args: wrapArgs(&initSlot{}, "ret", 3.14, wrapArgs(false), wrapKWArgs("foo", 42)), want: newTestTuple("ret", newTestTuple(3.14, false), newTestDict("foo", 42)).ToObject()}, 136 {args: wrapArgs(&initSlot{}, RuntimeErrorType, true, wrapArgs(1, 2), None), wantExc: mustCreateException(RuntimeErrorType, "")}, 137 {args: wrapArgs(&nativeSlot{}, "no"), want: None}, 138 {args: wrapArgs(&newSlot{}, "ret", StrType, wrapArgs(1, 2), None), want: newTestTuple("ret", newTestTuple(StrType, 1, 2), NewDict()).ToObject()}, 139 {args: wrapArgs(&newSlot{}, "ret", ObjectType, None, wrapKWArgs("a", "b")), want: newTestTuple("ret", newTestTuple(ObjectType), newTestDict("a", "b")).ToObject()}, 140 {args: wrapArgs(&newSlot{}, "ret", ListType, wrapArgs(false), wrapKWArgs("foo", 42)), want: newTestTuple("ret", newTestTuple(ListType, false), newTestDict("foo", 42)).ToObject()}, 141 {args: wrapArgs(&newSlot{}, RuntimeErrorType, IntType, wrapArgs(1, 2), None), wantExc: mustCreateException(RuntimeErrorType, "")}, 142 {args: wrapArgs(&setAttrSlot{}, "ret", o, "foo", 42), want: newTestTuple(None, newTestTuple(o, "foo", 42), NewDict()).ToObject()}, 143 {args: wrapArgs(&setAttrSlot{}, RuntimeErrorType, o, "foo", 42), wantExc: mustCreateException(RuntimeErrorType, "")}, 144 {args: wrapArgs(&setItemSlot{}, "ret", o, "foo", 42), want: newTestTuple(None, newTestTuple(o, "foo", 42), NewDict()).ToObject()}, 145 {args: wrapArgs(&setItemSlot{}, RuntimeErrorType, o, "foo", 42), wantExc: mustCreateException(RuntimeErrorType, "")}, 146 {args: wrapArgs(&setSlot{}, "ret", o, "foo", 42), want: newTestTuple(None, newTestTuple(o, "foo", 42), NewDict()).ToObject()}, 147 {args: wrapArgs(&setSlot{}, RuntimeErrorType, o, "foo", 42), wantExc: mustCreateException(RuntimeErrorType, "")}, 148 {args: wrapArgs(&unaryOpSlot{}, "ret", "foo"), want: newTestTuple("ret", newTestTuple("foo"), NewDict()).ToObject()}, 149 {args: wrapArgs(&unaryOpSlot{}, RuntimeErrorType, "foo"), wantExc: mustCreateException(RuntimeErrorType, "")}, 150 } 151 for _, cas := range cases { 152 if err := runInvokeTestCase(fun, &cas); err != "" { 153 t.Error(err) 154 } 155 } 156 } 157 158 // prepareTestSlot sets the Fn field of s to a function that assigns its 159 // parameters to gotArgs and returns ret. If ret is type inheriting from 160 // BaseException, an exception of that type will be raised instead. 161 func prepareTestSlot(s slot, gotArgs **Object, ret *Object) { 162 fnField := reflect.ValueOf(s).Elem().Field(0) 163 slotFuncType := fnField.Type() 164 numIn := slotFuncType.NumIn() 165 numOut := slotFuncType.NumOut() 166 fnField.Set(reflect.MakeFunc(slotFuncType, func(argValues []reflect.Value) []reflect.Value { 167 f := argValues[0].Interface().(*Frame) 168 var raised *BaseException 169 if ret.isInstance(TypeType) && toTypeUnsafe(ret).isSubclass(BaseExceptionType) { 170 raised = f.Raise(ret, nil, nil) 171 } else { 172 // Copy the input args into *gotArgs. 173 elems := make([]*Object, numIn-1) 174 for i := 1; i < numIn; i++ { 175 if elems[i-1], raised = WrapNative(f, argValues[i]); raised != nil { 176 break 177 } 178 } 179 if raised == nil { 180 *gotArgs = NewTuple(elems...).ToObject() 181 } 182 } 183 raisedValue := reflect.ValueOf(raised) 184 if numOut == 1 { 185 // This slot does only returns an exception so return 186 // raised (which may be nil). 187 return []reflect.Value{raisedValue} 188 } 189 // Slot returns a single value and an exception. 190 retValue := reflect.ValueOf((*Object)(nil)) 191 if raised == nil { 192 retValue = reflect.ValueOf(ret) 193 } 194 return []reflect.Value{retValue, raisedValue} 195 })) 196 }