Skip to content
quarry Artifact [7c896363ad]
Login

Artifact [7c896363ad]

Artifact 7c896363ad43ffc56af00bf36825381198ed83dcaa24288446e8f1dbeac81e44:


package chat

import (
	"sync"
	"testing"

	"craftdesign.group/quarry/internal/events"
)

// A presence change is fanned out while readers come and go: a chat tab
// opens or closes at any moment, including mid-beat. The fan-out must
// neither crash the process (the runtime aborts on a map ranged over
// while another goroutine writes it) nor lose the tick owed to a reader
// who stayed put the whole time.
func TestAPresenceBeatSurvivesTabsOpeningAndClosing(t *testing.T) {
	ev, err := events.Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	defer ev.Close()
	p, err := NewPresence(ev.Conn())
	if err != nil {
		t.Fatal(err)
	}
	defer p.Close()

	steady, stopSteady := p.Changed("A")
	defer stopSteady()

	// The count is high because CI runs without -race: a plain run only
	// catches the race when the runtime's own map check trips, and at a
	// few hundred rounds that happened in some runs and not others.
	const rounds = 5000
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 0; i < rounds; i++ {
			p.notify("A")
		}
	}()
	go func() {
		defer wg.Done()
		for i := 0; i < rounds; i++ {
			_, stop := p.Changed("A")
			stop()
		}
	}()
	wg.Wait()

	select {
	case <-steady:
		t.Log("ok: the reader who stayed was told of the change")
	default:
		t.Error("FAIL: the reader who stayed through the churn was never ticked")
	}
}