// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package sync_test import ( "sync" "testing" ) // We assume that the Once.Do tests have already covered parallelism. func TestOnceFunc(t *testing.T) { calls := 0 f := sync.OnceFunc(func() { calls++ }) allocs := testing.AllocsPerRun(10, f) if calls != 1 { t.Errorf("want calls==1, got %d", calls) } if allocs != 0 { t.Errorf("want 0 allocations per call, got %v", allocs) } } func TestOnceValue(t *testing.T) { calls := 0 f := sync.OnceValue(func() int { calls++ return calls }) allocs := testing.AllocsPerRun(10, func() { f() }) value := f() if calls != 1 { t.Errorf("want calls==1, got %d", calls) } if value != 1 { t.Errorf("want value==1, got %d", value) } if allocs != 0 { t.Errorf("want 0 allocations per call, got %v", allocs) } } func TestOnceValues(t *testing.T) { calls := 0 f := sync.OnceValues(func() (int, int) { calls++ return calls, calls + 1 }) allocs := testing.AllocsPerRun(10, func() { f() }) v1, v2 := f() if calls != 1 { t.Errorf("want calls==1, got %d", calls) } if v1 != 1 || v2 != 2 { t.Errorf("want v1==1 and v2==2, got %d and %d", v1, v2) } if allocs != 0 { t.Errorf("want 0 allocations per call, got %v", allocs) } } // TODO: need to implement more complete panic handling for these tests. // func testOncePanicX(t *testing.T, calls *int, f func()) { // testOncePanicWith(t, calls, f, func(label string, p any) { // if p != "x" { // t.Fatalf("%s: want panic %v, got %v", label, "x", p) // } // }) // } // func testOncePanicWith(t *testing.T, calls *int, f func(), check func(label string, p any)) { // // Check that the each call to f panics with the same value, but the // // underlying function is only called once. // for _, label := range []string{"first time", "second time"} { // var p any // panicked := true // func() { // defer func() { // p = recover() // }() // f() // panicked = false // }() // if !panicked { // t.Fatalf("%s: f did not panic", label) // } // check(label, p) // } // if *calls != 1 { // t.Errorf("want calls==1, got %d", *calls) // } // } // func TestOnceFuncPanic(t *testing.T) { // calls := 0 // f := sync.OnceFunc(func() { // calls++ // panic("x") // }) // testOncePanicX(t, &calls, f) // } // func TestOnceValuePanic(t *testing.T) { // calls := 0 // f := sync.OnceValue(func() int { // calls++ // panic("x") // }) // testOncePanicX(t, &calls, func() { f() }) // } // func TestOnceValuesPanic(t *testing.T) { // calls := 0 // f := sync.OnceValues(func() (int, int) { // calls++ // panic("x") // }) // testOncePanicX(t, &calls, func() { f() }) // } // // func TestOnceFuncPanicNil(t *testing.T) { // calls := 0 // f := sync.OnceFunc(func() { // calls++ // panic(nil) // }) // testOncePanicWith(t, &calls, f, func(label string, p any) { // switch p.(type) { // case nil, *runtime.PanicNilError: // return // } // t.Fatalf("%s: want nil panic, got %v", label, p) // }) // } // // func TestOnceFuncGoexit(t *testing.T) { // // If f calls Goexit, the results are unspecified. But check that f doesn't // // get called twice. // calls := 0 // f := sync.OnceFunc(func() { // calls++ // runtime.Goexit() // }) // var wg sync.WaitGroup // for i := 0; i < 2; i++ { // wg.Add(1) // go func() { // defer wg.Done() // defer func() { recover() }() // f() // }() // wg.Wait() // } // if calls != 1 { // t.Errorf("want calls==1, got %d", calls) // } // }