blob: da0b10d3d40e81493b53f19ddc049a4369fce6d4 [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) {
Colin Cross323dc602020-09-18 14:25:31 -070023 t.Parallel()
Colin Cross571cccf2019-02-04 11:22:08 -080024 once := OncePer{}
25 key := NewOnceKey("key")
26
27 a := once.Once(key, func() interface{} { return "a" }).(string)
28 b := once.Once(key, func() interface{} { return "b" }).(string)
29
30 if a != "a" {
31 t.Errorf(`first call to Once should return "a": %q`, a)
32 }
33
34 if b != "a" {
35 t.Errorf(`second call to Once with the same key should return "a": %q`, b)
36 }
37}
38
Colin Crossd7cfaee2019-02-15 23:00:48 -080039func TestOncePer_Once_wait(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -070040 t.Parallel()
Colin Crossd7cfaee2019-02-15 23:00:48 -080041 once := OncePer{}
42 key := NewOnceKey("key")
43
44 ch := make(chan bool)
45
46 go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
47 <-ch
48 a := once.Once(key, func() interface{} { return "bar" }).(string)
49
50 if a != "foo" {
51 t.Errorf("expect %q, got %q", "foo", a)
52 }
53}
54
Colin Cross571cccf2019-02-04 11:22:08 -080055func TestOncePer_Get(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -070056 t.Parallel()
Colin Cross571cccf2019-02-04 11:22:08 -080057 once := OncePer{}
58 key := NewOnceKey("key")
59
60 a := once.Once(key, func() interface{} { return "a" }).(string)
61 b := once.Get(key).(string)
62
63 if a != "a" {
64 t.Errorf(`first call to Once should return "a": %q`, a)
65 }
66
67 if b != "a" {
68 t.Errorf(`Get with the same key should return "a": %q`, b)
69 }
70}
71
72func TestOncePer_Get_panic(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -070073 t.Parallel()
Colin Cross571cccf2019-02-04 11:22:08 -080074 once := OncePer{}
75 key := NewOnceKey("key")
76
77 defer func() {
78 p := recover()
79
80 if p == nil {
81 t.Error("call to Get for unused key should panic")
82 }
83 }()
84
85 once.Get(key)
86}
87
Colin Crossd7cfaee2019-02-15 23:00:48 -080088func TestOncePer_Get_wait(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -070089 t.Parallel()
Colin Crossd7cfaee2019-02-15 23:00:48 -080090 once := OncePer{}
91 key := NewOnceKey("key")
92
93 ch := make(chan bool)
94
95 go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
96 <-ch
97 a := once.Get(key).(string)
98
99 if a != "foo" {
100 t.Errorf("expect %q, got %q", "foo", a)
101 }
102}
103
Colin Cross571cccf2019-02-04 11:22:08 -0800104func TestOncePer_OnceStringSlice(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -0700105 t.Parallel()
Colin Cross571cccf2019-02-04 11:22:08 -0800106 once := OncePer{}
107 key := NewOnceKey("key")
108
109 a := once.OnceStringSlice(key, func() []string { return []string{"a"} })
110 b := once.OnceStringSlice(key, func() []string { return []string{"a"} })
111
112 if a[0] != "a" {
113 t.Errorf(`first call to OnceStringSlice should return ["a"]: %q`, a)
114 }
115
116 if b[0] != "a" {
117 t.Errorf(`second call to OnceStringSlice with the same key should return ["a"]: %q`, b)
118 }
119}
120
121func TestOncePer_Once2StringSlice(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -0700122 t.Parallel()
Colin Cross571cccf2019-02-04 11:22:08 -0800123 once := OncePer{}
124 key := NewOnceKey("key")
125
126 a, b := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"a"}, []string{"b"} })
127 c, d := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"c"}, []string{"d"} })
128
129 if a[0] != "a" || b[0] != "b" {
130 t.Errorf(`first call to Once2StringSlice should return ["a"], ["b"]: %q, %q`, a, b)
131 }
132
133 if c[0] != "a" || d[0] != "b" {
134 t.Errorf(`second call to Once2StringSlice with the same key should return ["a"], ["b"]: %q, %q`, c, d)
135 }
136}
137
138func TestNewOnceKey(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -0700139 t.Parallel()
Colin Cross571cccf2019-02-04 11:22:08 -0800140 once := OncePer{}
141 key1 := NewOnceKey("key")
142 key2 := NewOnceKey("key")
143
144 a := once.Once(key1, func() interface{} { return "a" }).(string)
145 b := once.Once(key2, func() interface{} { return "b" }).(string)
146
147 if a != "a" {
148 t.Errorf(`first call to Once should return "a": %q`, a)
149 }
150
151 if b != "b" {
152 t.Errorf(`second call to Once with the NewOnceKey from same string should return "b": %q`, b)
153 }
154}
155
156func TestNewCustomOnceKey(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -0700157 t.Parallel()
Colin Cross571cccf2019-02-04 11:22:08 -0800158 type key struct {
159 key string
160 }
161 once := OncePer{}
162 key1 := NewCustomOnceKey(key{"key"})
163 key2 := NewCustomOnceKey(key{"key"})
164
165 a := once.Once(key1, func() interface{} { return "a" }).(string)
166 b := once.Once(key2, func() interface{} { return "b" }).(string)
167
168 if a != "a" {
169 t.Errorf(`first call to Once should return "a": %q`, a)
170 }
171
172 if b != "a" {
173 t.Errorf(`second call to Once with the NewCustomOnceKey from equal key should return "a": %q`, b)
174 }
175}
Colin Crosse5cdaf92019-02-11 15:06:16 -0800176
177func TestOncePerReentrant(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -0700178 t.Parallel()
Colin Crosse5cdaf92019-02-11 15:06:16 -0800179 once := OncePer{}
180 key1 := NewOnceKey("key")
181 key2 := NewOnceKey("key")
182
183 a := once.Once(key1, func() interface{} { return once.Once(key2, func() interface{} { return "a" }) })
184 if a != "a" {
185 t.Errorf(`reentrant Once should return "a": %q`, a)
186 }
187}
Colin Cross66bdb692019-05-14 11:33:05 -0700188
189// Test that a recovered panic in a Once function doesn't deadlock
190func TestOncePerPanic(t *testing.T) {
Colin Cross323dc602020-09-18 14:25:31 -0700191 t.Parallel()
Colin Cross66bdb692019-05-14 11:33:05 -0700192 once := OncePer{}
193 key := NewOnceKey("key")
194
195 ch := make(chan interface{})
196
197 var a interface{}
198
199 go func() {
200 defer func() {
201 ch <- recover()
202 }()
203
204 a = once.Once(key, func() interface{} {
205 panic("foo")
206 })
207 }()
208
209 p := <-ch
210
211 if p.(string) != "foo" {
212 t.Errorf(`expected panic with "foo", got %#v`, p)
213 }
214
215 if a != nil {
216 t.Errorf(`expected a to be nil, got %#v`, a)
217 }
218
219 // If the call to Once that panicked leaves the key in a bad state this will deadlock
220 b := once.Once(key, func() interface{} {
221 return "bar"
222 })
223
224 // The second call to Once should return nil inserted by the first call that panicked.
225 if b != nil {
226 t.Errorf(`expected b to be nil, got %#v`, b)
227 }
228}