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