// Copyright 2019 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package android import ( "testing" "time" ) func TestOncePer_Once(t *testing.T) { once := OncePer{} key := NewOnceKey("key") a := once.Once(key, func() interface{} { return "a" }).(string) b := once.Once(key, func() interface{} { return "b" }).(string) if a != "a" { t.Errorf(`first call to Once should return "a": %q`, a) } if b != "a" { t.Errorf(`second call to Once with the same key should return "a": %q`, b) } } func TestOncePer_Once_wait(t *testing.T) { once := OncePer{} key := NewOnceKey("key") ch := make(chan bool) go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" }) <-ch a := once.Once(key, func() interface{} { return "bar" }).(string) if a != "foo" { t.Errorf("expect %q, got %q", "foo", a) } } func TestOncePer_Get(t *testing.T) { once := OncePer{} key := NewOnceKey("key") a := once.Once(key, func() interface{} { return "a" }).(string) b := once.Get(key).(string) if a != "a" { t.Errorf(`first call to Once should return "a": %q`, a) } if b != "a" { t.Errorf(`Get with the same key should return "a": %q`, b) } } func TestOncePer_Get_panic(t *testing.T) { once := OncePer{} key := NewOnceKey("key") defer func() { p := recover() if p == nil { t.Error("call to Get for unused key should panic") } }() once.Get(key) } func TestOncePer_Get_wait(t *testing.T) { once := OncePer{} key := NewOnceKey("key") ch := make(chan bool) go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" }) <-ch a := once.Get(key).(string) if a != "foo" { t.Errorf("expect %q, got %q", "foo", a) } } func TestOncePer_OnceStringSlice(t *testing.T) { once := OncePer{} key := NewOnceKey("key") a := once.OnceStringSlice(key, func() []string { return []string{"a"} }) b := once.OnceStringSlice(key, func() []string { return []string{"a"} }) if a[0] != "a" { t.Errorf(`first call to OnceStringSlice should return ["a"]: %q`, a) } if b[0] != "a" { t.Errorf(`second call to OnceStringSlice with the same key should return ["a"]: %q`, b) } } func TestOncePer_Once2StringSlice(t *testing.T) { once := OncePer{} key := NewOnceKey("key") a, b := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"a"}, []string{"b"} }) c, d := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"c"}, []string{"d"} }) if a[0] != "a" || b[0] != "b" { t.Errorf(`first call to Once2StringSlice should return ["a"], ["b"]: %q, %q`, a, b) } if c[0] != "a" || d[0] != "b" { t.Errorf(`second call to Once2StringSlice with the same key should return ["a"], ["b"]: %q, %q`, c, d) } } func TestNewOnceKey(t *testing.T) { once := OncePer{} key1 := NewOnceKey("key") key2 := NewOnceKey("key") a := once.Once(key1, func() interface{} { return "a" }).(string) b := once.Once(key2, func() interface{} { return "b" }).(string) if a != "a" { t.Errorf(`first call to Once should return "a": %q`, a) } if b != "b" { t.Errorf(`second call to Once with the NewOnceKey from same string should return "b": %q`, b) } } func TestNewCustomOnceKey(t *testing.T) { type key struct { key string } once := OncePer{} key1 := NewCustomOnceKey(key{"key"}) key2 := NewCustomOnceKey(key{"key"}) a := once.Once(key1, func() interface{} { return "a" }).(string) b := once.Once(key2, func() interface{} { return "b" }).(string) if a != "a" { t.Errorf(`first call to Once should return "a": %q`, a) } if b != "a" { t.Errorf(`second call to Once with the NewCustomOnceKey from equal key should return "a": %q`, b) } } func TestOncePerReentrant(t *testing.T) { once := OncePer{} key1 := NewOnceKey("key") key2 := NewOnceKey("key") a := once.Once(key1, func() interface{} { return once.Once(key2, func() interface{} { return "a" }) }) if a != "a" { t.Errorf(`reentrant Once should return "a": %q`, a) } } // Test that a recovered panic in a Once function doesn't deadlock func TestOncePerPanic(t *testing.T) { once := OncePer{} key := NewOnceKey("key") ch := make(chan interface{}) var a interface{} go func() { defer func() { ch <- recover() }() a = once.Once(key, func() interface{} { panic("foo") }) }() p := <-ch if p.(string) != "foo" { t.Errorf(`expected panic with "foo", got %#v`, p) } if a != nil { t.Errorf(`expected a to be nil, got %#v`, a) } // If the call to Once that panicked leaves the key in a bad state this will deadlock b := once.Once(key, func() interface{} { return "bar" }) // The second call to Once should return nil inserted by the first call that panicked. if b != nil { t.Errorf(`expected b to be nil, got %#v`, b) } }