github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/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  }