blob: c54572d59ca32610b6a6e10a79bacfcb11f73e2d [file] [log] [blame]
Ian Cottrellf8d1dee2019-05-10 16:36:20 -04001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package memoize_test
6
7import (
Ian Cottrellf8d1dee2019-05-10 16:36:20 -04008 "context"
Robert Findley56116ec2022-06-26 15:17:46 -04009 "sync"
Ian Cottrellf8d1dee2019-05-10 16:36:20 -040010 "testing"
Robert Findley56116ec2022-06-26 15:17:46 -040011 "time"
Ian Cottrellf8d1dee2019-05-10 16:36:20 -040012
13 "golang.org/x/tools/internal/memoize"
14)
15
Heschi Kreinickc1903db2020-07-24 17:17:13 -040016func TestGet(t *testing.T) {
Alan Donovan53ead672022-07-05 10:30:58 -040017 var store memoize.Store
Rebecca Stamblerecc01b72019-06-11 21:16:35 -040018
Heschi Kreinickc1903db2020-07-24 17:17:13 -040019 evaled := 0
Ian Cottrellf8d1dee2019-05-10 16:36:20 -040020
Alan Donovandb8f89b2022-07-13 16:34:32 -040021 h, release := store.Promise("key", func(context.Context, interface{}) interface{} {
Heschi Kreinickc1903db2020-07-24 17:17:13 -040022 evaled++
23 return "res"
Alan Donovanf0427992022-07-01 10:35:27 -040024 })
Alan Donovan53ead672022-07-05 10:30:58 -040025 defer release()
26 expectGet(t, h, "res")
27 expectGet(t, h, "res")
Heschi Kreinickc1903db2020-07-24 17:17:13 -040028 if evaled != 1 {
29 t.Errorf("got %v calls to function, wanted 1", evaled)
Ian Cottrellf8d1dee2019-05-10 16:36:20 -040030 }
31}
32
Alan Donovandb8f89b2022-07-13 16:34:32 -040033func expectGet(t *testing.T, h *memoize.Promise, wantV interface{}) {
Rob Findleyd36b6f62020-10-20 11:10:46 -040034 t.Helper()
Alan Donovan53ead672022-07-05 10:30:58 -040035 gotV, gotErr := h.Get(context.Background(), nil)
Heschi Kreinickc1903db2020-07-24 17:17:13 -040036 if gotV != wantV || gotErr != nil {
37 t.Fatalf("Get() = %v, %v, wanted %v, nil", gotV, gotErr, wantV)
Ian Cottrellf8d1dee2019-05-10 16:36:20 -040038 }
39}
40
Alan Donovandb8f89b2022-07-13 16:34:32 -040041func TestNewPromise(t *testing.T) {
42 calls := 0
43 f := func(context.Context, interface{}) interface{} {
44 calls++
45 return calls
46 }
47
48 // All calls to Get on the same promise return the same result.
49 p1 := memoize.NewPromise("debug", f)
50 expectGet(t, p1, 1)
51 expectGet(t, p1, 1)
52
53 // A new promise calls the function again.
54 p2 := memoize.NewPromise("debug", f)
55 expectGet(t, p2, 2)
56 expectGet(t, p2, 2)
57
58 // The original promise is unchanged.
59 expectGet(t, p1, 1)
60}
61
62func TestStoredPromiseRefCounting(t *testing.T) {
Alan Donovan53ead672022-07-05 10:30:58 -040063 var store memoize.Store
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000064 v1 := false
65 v2 := false
Alan Donovandb8f89b2022-07-13 16:34:32 -040066 p1, release1 := store.Promise("key1", func(context.Context, interface{}) interface{} {
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000067 return &v1
Robert Findley56116ec2022-06-26 15:17:46 -040068 })
Alan Donovandb8f89b2022-07-13 16:34:32 -040069 p2, release2 := store.Promise("key2", func(context.Context, interface{}) interface{} {
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000070 return &v2
Robert Findley56116ec2022-06-26 15:17:46 -040071 })
Alan Donovandb8f89b2022-07-13 16:34:32 -040072 expectGet(t, p1, &v1)
73 expectGet(t, p2, &v2)
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000074
Alan Donovandb8f89b2022-07-13 16:34:32 -040075 expectGet(t, p1, &v1)
76 expectGet(t, p2, &v2)
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000077
Alan Donovandb8f89b2022-07-13 16:34:32 -040078 p2Copy, release2Copy := store.Promise("key2", func(context.Context, interface{}) interface{} {
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000079 return &v1
Robert Findley56116ec2022-06-26 15:17:46 -040080 })
Alan Donovandb8f89b2022-07-13 16:34:32 -040081 if p2 != p2Copy {
82 t.Error("Promise returned a new value while old is not destroyed yet")
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000083 }
Alan Donovandb8f89b2022-07-13 16:34:32 -040084 expectGet(t, p2Copy, &v2)
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000085
86 release2()
87 if got, want := v2, false; got != want {
Robert Findley56116ec2022-06-26 15:17:46 -040088 t.Errorf("after destroying first v2 ref, got %v, want %v", got, want)
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000089 }
90 release2Copy()
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000091 if got, want := v1, false; got != want {
Robert Findley56116ec2022-06-26 15:17:46 -040092 t.Errorf("after destroying v2, got %v, want %v", got, want)
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000093 }
94 release1()
95
Alan Donovandb8f89b2022-07-13 16:34:32 -040096 p2Copy, release2Copy = store.Promise("key2", func(context.Context, interface{}) interface{} {
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +000097 return &v2
Robert Findley56116ec2022-06-26 15:17:46 -040098 })
Alan Donovandb8f89b2022-07-13 16:34:32 -040099 if p2 == p2Copy {
100 t.Error("Promise returned previously destroyed value")
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +0000101 }
102 release2Copy()
Ruslan Nigmatullinf60e9bc2022-06-22 00:22:43 +0000103}
Robert Findley56116ec2022-06-26 15:17:46 -0400104
Alan Donovandb8f89b2022-07-13 16:34:32 -0400105func TestPromiseDestroyedWhileRunning(t *testing.T) {
106 // Test that calls to Promise.Get return even if the promise is destroyed while running.
Robert Findley56116ec2022-06-26 15:17:46 -0400107
Alan Donovan53ead672022-07-05 10:30:58 -0400108 var store memoize.Store
Robert Findley56116ec2022-06-26 15:17:46 -0400109 c := make(chan int)
110
111 var v int
Alan Donovandb8f89b2022-07-13 16:34:32 -0400112 h, release := store.Promise("key", func(ctx context.Context, _ interface{}) interface{} {
Robert Findley56116ec2022-06-26 15:17:46 -0400113 <-c
114 <-c
115 if err := ctx.Err(); err != nil {
116 t.Errorf("ctx.Err() = %v, want nil", err)
117 }
118 return &v
119 })
120
121 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) // arbitrary timeout; may be removed if it causes flakes
122 defer cancel()
123
124 var wg sync.WaitGroup
125 wg.Add(1)
126 var got interface{}
127 var err error
128 go func() {
Alan Donovan53ead672022-07-05 10:30:58 -0400129 got, err = h.Get(ctx, nil)
Robert Findley56116ec2022-06-26 15:17:46 -0400130 wg.Done()
131 }()
132
Alan Donovandb8f89b2022-07-13 16:34:32 -0400133 c <- 0 // send once to enter the promise function
134 release() // release before the promise function returns
135 c <- 0 // let the promise function proceed
Robert Findley56116ec2022-06-26 15:17:46 -0400136
137 wg.Wait()
138
139 if err != nil {
140 t.Errorf("Get() failed: %v", err)
141 }
142 if got != &v {
143 t.Errorf("Get() = %v, want %v", got, v)
144 }
145}
Robert Findley98bfcd12022-07-27 15:59:52 -0400146
147func TestDoubleReleasePanics(t *testing.T) {
148 var store memoize.Store
149 _, release := store.Promise("key", func(ctx context.Context, _ interface{}) interface{} { return 0 })
150
151 panicked := false
152
153 func() {
154 defer func() {
155 if recover() != nil {
156 panicked = true
157 }
158 }()
159 release()
160 release()
161 }()
162
163 if !panicked {
164 t.Errorf("calling release() twice did not panic")
165 }
166}