github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/runtime/weakref_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  	"fmt"
    19  	"runtime"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  func TestWeakRefCall(t *testing.T) {
    25  	aliveRef, alive, deadRef := makeWeakRefsForTest()
    26  	dupRef := newTestWeakRef(alive, nil)
    27  	cases := []invokeTestCase{
    28  		{args: wrapArgs(aliveRef), want: alive},
    29  		{args: wrapArgs(dupRef), want: alive},
    30  		{args: wrapArgs(deadRef), want: None},
    31  		{args: wrapArgs(aliveRef, 123), wantExc: mustCreateException(TypeErrorType, "'__call__' requires 0 arguments")},
    32  	}
    33  	for _, cas := range cases {
    34  		if err := runInvokeMethodTestCase(WeakRefType, "__call__", &cas); err != "" {
    35  			t.Error(err)
    36  		}
    37  	}
    38  	runtime.KeepAlive(alive)
    39  }
    40  
    41  func TestWeakRefHash(t *testing.T) {
    42  	aliveRef, alive, deadRef := makeWeakRefsForTest()
    43  	hashedRef, hashed, _ := makeWeakRefsForTest()
    44  	if _, raised := Hash(NewRootFrame(), hashedRef.ToObject()); raised != nil {
    45  		t.Fatal(raised)
    46  	}
    47  	runtime.KeepAlive(hashed)
    48  	hashed = nil
    49  	weakRefMustDie(hashedRef)
    50  	unhashable := NewList().ToObject()
    51  	unhashableRef := newTestWeakRef(unhashable, nil)
    52  	cases := []invokeTestCase{
    53  		{args: wrapArgs(aliveRef), want: NewInt(hashString("foo")).ToObject()},
    54  		{args: wrapArgs(deadRef), wantExc: mustCreateException(TypeErrorType, "weak object has gone away")},
    55  		{args: wrapArgs(hashedRef), want: NewInt(hashString("foo")).ToObject()},
    56  		{args: wrapArgs(unhashableRef), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")},
    57  	}
    58  	for _, cas := range cases {
    59  		if err := runInvokeMethodTestCase(WeakRefType, "__hash__", &cas); err != "" {
    60  			t.Error(err)
    61  		}
    62  	}
    63  	runtime.KeepAlive(alive)
    64  	runtime.KeepAlive(unhashable)
    65  }
    66  
    67  func TestWeakRefNew(t *testing.T) {
    68  	alive := NewStr("foo").ToObject()
    69  	aliveRef := newTestWeakRef(alive, nil)
    70  	cases := []invokeTestCase{
    71  		{args: wrapArgs(alive), want: aliveRef.ToObject()},
    72  		{wantExc: mustCreateException(TypeErrorType, "'__new__' requires 1 arguments")},
    73  		{args: wrapArgs("foo", "bar", "baz"), wantExc: mustCreateException(TypeErrorType, "__new__ expected at most 2 arguments, got 3")},
    74  	}
    75  	for _, cas := range cases {
    76  		if err := runInvokeTestCase(WeakRefType.ToObject(), &cas); err != "" {
    77  			t.Error(err)
    78  		}
    79  	}
    80  	runtime.KeepAlive(alive)
    81  }
    82  
    83  func TestWeakRefNewCallback(t *testing.T) {
    84  	callbackChannel := make(chan *WeakRef)
    85  	callback := wrapFuncForTest(func(f *Frame, r *WeakRef) {
    86  		callbackChannel <- r
    87  	})
    88  	r := newTestWeakRef(newObject(ObjectType), callback)
    89  	weakRefMustDie(r)
    90  	if r.get() != nil {
    91  		t.Fatalf("expected weakref %v to be dead", r)
    92  	}
    93  	if callbackGot := <-callbackChannel; callbackGot != r {
    94  		t.Fatalf("callback got %v, want %v", callbackGot, r)
    95  	}
    96  }
    97  
    98  func TestWeakRefNewCallbackRaises(t *testing.T) {
    99  	// It's not easy to verify that the exception is output properly, but
   100  	// we can at least make sure the program doesn't blow up if the
   101  	// callback raises.
   102  	callback := wrapFuncForTest(func(f *Frame, r *WeakRef) *BaseException {
   103  		return f.RaiseType(RuntimeErrorType, "foo")
   104  	})
   105  	r := newTestWeakRef(newObject(ObjectType), callback)
   106  	weakRefMustDie(r)
   107  	if r.get() != nil {
   108  		t.Fatalf("expected weakref %v to be dead", r)
   109  	}
   110  }
   111  
   112  func TestWeakRefStrRepr(t *testing.T) {
   113  	aliveRef, alive, deadRef := makeWeakRefsForTest()
   114  	cases := []invokeTestCase{
   115  		{args: wrapArgs(aliveRef), want: NewStr(fmt.Sprintf("<weakref at %p; to 'str' at %p>", aliveRef, alive)).ToObject()},
   116  		{args: wrapArgs(deadRef), want: NewStr(fmt.Sprintf("<weakref at %p; dead>", deadRef)).ToObject()},
   117  	}
   118  	for _, cas := range cases {
   119  		if err := runInvokeTestCase(wrapFuncForTest(ToStr), &cas); err != "" {
   120  			t.Error(err)
   121  		}
   122  		if err := runInvokeTestCase(wrapFuncForTest(Repr), &cas); err != "" {
   123  			t.Error(err)
   124  		}
   125  	}
   126  	runtime.KeepAlive(alive)
   127  }
   128  
   129  func newTestWeakRef(o, callback *Object) *WeakRef {
   130  	args := Args{o}
   131  	if callback != nil {
   132  		args = Args{o, callback}
   133  	}
   134  	return toWeakRefUnsafe(mustNotRaise(WeakRefType.Call(NewRootFrame(), args, nil)))
   135  }
   136  
   137  func makeWeakRefsForTest() (*WeakRef, *Object, *WeakRef) {
   138  	alive := NewStr("foo").ToObject()
   139  	aliveRef := newTestWeakRef(alive, nil)
   140  	dead := NewFloat(3.14).ToObject()
   141  	deadRef := newTestWeakRef(dead, nil)
   142  	dead = nil
   143  	weakRefMustDie(deadRef)
   144  	return aliveRef, alive, deadRef
   145  }
   146  
   147  func weakRefMustDie(r *WeakRef) {
   148  	r.mutex.Lock()
   149  	o := r.get()
   150  	r.mutex.Unlock()
   151  	if o == nil {
   152  		return
   153  	}
   154  	doneChannel := make(chan bool)
   155  	callback := wrapFuncForTest(func(f *Frame, r *WeakRef) {
   156  		close(doneChannel)
   157  	})
   158  	mustNotRaise(WeakRefType.Call(NewRootFrame(), Args{o, callback}, nil))
   159  	o = nil
   160  	timeoutChannel := make(chan bool)
   161  	go func() {
   162  		// Finalizers run some time after GC, thus the Sleep call. In
   163  		// our case o's finalizer will have to run twice, so loop and
   164  		// GC repeatedly. In theory, twice should be enough, but in
   165  		// practice there are race conditions and things to contend
   166  		// with so just loop a bunch of times.
   167  		wait := 10 * time.Millisecond
   168  		for t := time.Duration(0); t < time.Second; t += wait {
   169  			runtime.GC()
   170  			time.Sleep(wait)
   171  		}
   172  		close(timeoutChannel)
   173  	}()
   174  	select {
   175  	case <-doneChannel:
   176  		return
   177  	case <-timeoutChannel:
   178  		panic(fmt.Sprintf("weakref %v did not die", r))
   179  	}
   180  }