package threadsafeset import ( "bufio" "context" "math/rand/v2" "os" "runtime" "slices" "strings" "sync" "testing" "time" ) var Words = func() []string { f, err := os.Open("../words") if err != nil { panic("error opening ../words: " + err.Error()) } defer f.Close() scanner := bufio.NewScanner(f) results := make([]string, 0) for scanner.Scan() { if line := strings.TrimSpace(scanner.Text()); len(line) > 0 { results = append(results, line) } } if err := scanner.Err(); err != nil { panic("error from scanner.Err(): " + err.Error()) } return results }() func CloneWords() []string { results := make([]string, 0, len(Words)) for _, word := range Words { results = append(results, strings.Clone(word)) } return results } func RandomWord() string { return Words[rand.IntN(len(Words))] } func ContextNotDone(ctx context.Context) bool { select { case <-ctx.Done(): return false default: return true } } func TestAdd(t *testing.T) { set := New[string]() var wg sync.WaitGroup ctx, cancel := context.WithTimeout(t.Context(), time.Second*2) defer cancel() for range runtime.NumCPU() * 10 { wg.Go(func() { for i := 0; i < len(Words) && ContextNotDone(ctx); i++ { set.Add(Words[i]) } }) } wg.Wait() } func TestContains(t *testing.T) { set := New(Words...) var wg sync.WaitGroup ctx, cancel := context.WithTimeout(t.Context(), time.Second*2) defer cancel() for range runtime.NumCPU() * 10 { wg.Go(func() { for i := 0; i < len(Words) && ContextNotDone(ctx); i++ { if !set.Contains(Words[i]) { panic("error: " + Words[i] + " not found") } } }) } wg.Wait() } func TestRemove(t *testing.T) { set := New(Words...) var wg sync.WaitGroup for range runtime.NumCPU() * 10 { wg.Go(func() { for _, word := range Words { set.Remove(word) } }) } wg.Wait() } func TestSize(t *testing.T) { set := New[string]() for index, word := range Words { if want, got := index, set.Size(); want != got { t.Fatalf("error: wanted size %d; got %d\n", want, got) } set.Add(word) if want, got := index+1, set.Size(); want != got { t.Fatalf("error: wanted size %d; got %d\n", want, got) } } } func TestToSlice(t *testing.T) { want := CloneWords() slices.Sort(want) set := New(Words...) got := set.ToSlice() slices.Sort(got) if !slices.Equal(want, got) { t.Fatalf("error: mismatched ToSlice() output\n") } } func BenchmarkAdd(b *testing.B) { set := New[string]() for b.Loop() { set.Add(RandomWord()) } } func BenchmarkContains(b *testing.B) { set := New(Words...) for b.Loop() { set.Contains(RandomWord()) } }