blob: 1a55ff4b194801c3b1f62786e07a3c1f5cffff66 [file] [log] [blame]
Colin Cross571cccf2019-02-04 11:22:08 -08001// Copyright 2019 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
15package android
16
17import (
18 "testing"
Colin Crossd7cfaee2019-02-15 23:00:48 -080019 "time"
Colin Cross571cccf2019-02-04 11:22:08 -080020)
21
22func TestOncePer_Once(t *testing.T) {
23 once := OncePer{}
24 key := NewOnceKey("key")
25
26 a := once.Once(key, func() interface{} { return "a" }).(string)
27 b := once.Once(key, func() interface{} { return "b" }).(string)
28
29 if a != "a" {
30 t.Errorf(`first call to Once should return "a": %q`, a)
31 }
32
33 if b != "a" {
34 t.Errorf(`second call to Once with the same key should return "a": %q`, b)
35 }
36}
37
Colin Crossd7cfaee2019-02-15 23:00:48 -080038func TestOncePer_Once_wait(t *testing.T) {
39 once := OncePer{}
40 key := NewOnceKey("key")
41
42 ch := make(chan bool)
43
44 go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
45 <-ch
46 a := once.Once(key, func() interface{} { return "bar" }).(string)
47
48 if a != "foo" {
49 t.Errorf("expect %q, got %q", "foo", a)
50 }
51}
52
Colin Cross571cccf2019-02-04 11:22:08 -080053func TestOncePer_Get(t *testing.T) {
54 once := OncePer{}
55 key := NewOnceKey("key")
56
57 a := once.Once(key, func() interface{} { return "a" }).(string)
58 b := once.Get(key).(string)
59
60 if a != "a" {
61 t.Errorf(`first call to Once should return "a": %q`, a)
62 }
63
64 if b != "a" {
65 t.Errorf(`Get with the same key should return "a": %q`, b)
66 }
67}
68
69func TestOncePer_Get_panic(t *testing.T) {
70 once := OncePer{}
71 key := NewOnceKey("key")
72
73 defer func() {
74 p := recover()
75
76 if p == nil {
77 t.Error("call to Get for unused key should panic")
78 }
79 }()
80
81 once.Get(key)
82}
83
Colin Crossd7cfaee2019-02-15 23:00:48 -080084func TestOncePer_Get_wait(t *testing.T) {
85 once := OncePer{}
86 key := NewOnceKey("key")
87
88 ch := make(chan bool)
89
90 go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
91 <-ch
92 a := once.Get(key).(string)
93
94 if a != "foo" {
95 t.Errorf("expect %q, got %q", "foo", a)
96 }
97}
98
Colin Cross571cccf2019-02-04 11:22:08 -080099func TestOncePer_OnceStringSlice(t *testing.T) {
100 once := OncePer{}
101 key := NewOnceKey("key")
102
103 a := once.OnceStringSlice(key, func() []string { return []string{"a"} })
104 b := once.OnceStringSlice(key, func() []string { return []string{"a"} })
105
106 if a[0] != "a" {
107 t.Errorf(`first call to OnceStringSlice should return ["a"]: %q`, a)
108 }
109
110 if b[0] != "a" {
111 t.Errorf(`second call to OnceStringSlice with the same key should return ["a"]: %q`, b)
112 }
113}
114
115func TestOncePer_Once2StringSlice(t *testing.T) {
116 once := OncePer{}
117 key := NewOnceKey("key")
118
119 a, b := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"a"}, []string{"b"} })
120 c, d := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"c"}, []string{"d"} })
121
122 if a[0] != "a" || b[0] != "b" {
123 t.Errorf(`first call to Once2StringSlice should return ["a"], ["b"]: %q, %q`, a, b)
124 }
125
126 if c[0] != "a" || d[0] != "b" {
127 t.Errorf(`second call to Once2StringSlice with the same key should return ["a"], ["b"]: %q, %q`, c, d)
128 }
129}
130
131func TestNewOnceKey(t *testing.T) {
132 once := OncePer{}
133 key1 := NewOnceKey("key")
134 key2 := NewOnceKey("key")
135
136 a := once.Once(key1, func() interface{} { return "a" }).(string)
137 b := once.Once(key2, func() interface{} { return "b" }).(string)
138
139 if a != "a" {
140 t.Errorf(`first call to Once should return "a": %q`, a)
141 }
142
143 if b != "b" {
144 t.Errorf(`second call to Once with the NewOnceKey from same string should return "b": %q`, b)
145 }
146}
147
148func TestNewCustomOnceKey(t *testing.T) {
149 type key struct {
150 key string
151 }
152 once := OncePer{}
153 key1 := NewCustomOnceKey(key{"key"})
154 key2 := NewCustomOnceKey(key{"key"})
155
156 a := once.Once(key1, func() interface{} { return "a" }).(string)
157 b := once.Once(key2, func() interface{} { return "b" }).(string)
158
159 if a != "a" {
160 t.Errorf(`first call to Once should return "a": %q`, a)
161 }
162
163 if b != "a" {
164 t.Errorf(`second call to Once with the NewCustomOnceKey from equal key should return "a": %q`, b)
165 }
166}
Colin Crosse5cdaf92019-02-11 15:06:16 -0800167
168func TestOncePerReentrant(t *testing.T) {
169 once := OncePer{}
170 key1 := NewOnceKey("key")
171 key2 := NewOnceKey("key")
172
173 a := once.Once(key1, func() interface{} { return once.Once(key2, func() interface{} { return "a" }) })
174 if a != "a" {
175 t.Errorf(`reentrant Once should return "a": %q`, a)
176 }
177}
Colin Cross66bdb692019-05-14 11:33:05 -0700178
179// Test that a recovered panic in a Once function doesn't deadlock
180func TestOncePerPanic(t *testing.T) {
181 once := OncePer{}
182 key := NewOnceKey("key")
183
184 ch := make(chan interface{})
185
186 var a interface{}
187
188 go func() {
189 defer func() {
190 ch <- recover()
191 }()
192
193 a = once.Once(key, func() interface{} {
194 panic("foo")
195 })
196 }()
197
198 p := <-ch
199
200 if p.(string) != "foo" {
201 t.Errorf(`expected panic with "foo", got %#v`, p)
202 }
203
204 if a != nil {
205 t.Errorf(`expected a to be nil, got %#v`, a)
206 }
207
208 // If the call to Once that panicked leaves the key in a bad state this will deadlock
209 b := once.Once(key, func() interface{} {
210 return "bar"
211 })
212
213 // The second call to Once should return nil inserted by the first call that panicked.
214 if b != nil {
215 t.Errorf(`expected b to be nil, got %#v`, b)
216 }
217}