// Copyright 2020 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 intern import ( "fmt" "runtime" "testing" ) func TestBasics(t *testing.T) { clearMap() foo := Get("foo") bar := Get("bar") empty := Get("") nilEface := Get(nil) i := Get(0x7777777) foo2 := Get("foo") bar2 := Get("bar") empty2 := Get("") nilEface2 := Get(nil) i2 := Get(0x7777777) foo3 := GetByString("foo") empty3 := GetByString("") if foo.Get() != foo2.Get() { t.Error("foo/foo2 values differ") } if foo.Get() != foo3.Get() { t.Error("foo/foo3 values differ") } if foo.Get() != "foo" { t.Error("foo.Get not foo") } if foo != foo2 { t.Error("foo/foo2 pointers differ") } if foo != foo3 { t.Error("foo/foo3 pointers differ") } if bar.Get() != bar2.Get() { t.Error("bar values differ") } if bar.Get() != "bar" { t.Error("bar.Get not bar") } if bar != bar2 { t.Error("bar pointers differ") } if i.Get() != i.Get() { t.Error("i values differ") } if i.Get() != 0x7777777 { t.Error("i.Get not 0x7777777") } if i != i2 { t.Error("i pointers differ") } if empty.Get() != empty2.Get() { t.Error("empty/empty2 values differ") } if empty.Get() != empty.Get() { t.Error("empty/empty3 values differ") } if empty.Get() != "" { t.Error("empty.Get not empty string") } if empty != empty2 { t.Error("empty/empty2 pointers differ") } if empty != empty3 { t.Error("empty/empty3 pointers differ") } if nilEface.Get() != nilEface2.Get() { t.Error("nilEface values differ") } if nilEface.Get() != nil { t.Error("nilEface.Get not nil") } if nilEface != nilEface2 { t.Error("nilEface pointers differ") } if n := mapLen(); n != 5 { t.Errorf("map len = %d; want 4", n) } wantEmpty(t) } func wantEmpty(t testing.TB) { t.Helper() const gcTries = 5000 for try := 0; try < gcTries; try++ { runtime.GC() n := mapLen() if n == 0 { break } if try == gcTries-1 { t.Errorf("map len = %d after (%d GC tries); want 0, contents: %v", n, gcTries, mapKeys()) } } } func TestStress(t *testing.T) { iters := 10000 if testing.Short() { iters = 1000 } var sink []byte for i := 0; i < iters; i++ { _ = Get("foo") sink = make([]byte, 1<<20) } _ = sink } func BenchmarkStress(b *testing.B) { done := make(chan struct{}) defer close(done) go func() { for { select { case <-done: return default: } runtime.GC() } }() clearMap() v1 := Get("foo") b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { v2 := Get("foo") if v1 != v2 { b.Fatal("wrong value") } // And also a key we don't retain: _ = Get("bar") } }) runtime.GC() wantEmpty(b) } func mapLen() int { mu.Lock() defer mu.Unlock() return len(valMap) } func mapKeys() (keys []string) { mu.Lock() defer mu.Unlock() for k := range valMap { keys = append(keys, fmt.Sprint(k)) } return keys } func clearMap() { mu.Lock() defer mu.Unlock() clear(valMap) } var ( globalString = "not a constant" sink string ) func TestGetByStringAllocs(t *testing.T) { allocs := int(testing.AllocsPerRun(100, func() { GetByString(globalString) })) if allocs != 0 { t.Errorf("GetString allocated %d objects, want 0", allocs) } } func BenchmarkGetByString(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { v := GetByString(globalString) sink = v.Get().(string) } }