oficina / slides Public

Clone
git clone https://amadan.net/oficina/slides

Plain git — no account needed to clone.

Download

Download this file

1package present_test
2
3import (
4 "testing"
5 "time"
6
7 "amadan.net/oficina/slides/internal/slides/present"
8)
9
10// recv reads one event, or fails: every assertion here is about
11// something arriving, and a test that hangs waiting for it tells the
12// person running it nothing.
13func recv(t *testing.T, ch <-chan present.Event, what string) present.Event {
14 t.Helper()
15 select {
16 case e, ok := <-ch:
17 if !ok {
18 t.Fatalf("%s: the channel was closed, want an event", what)
19 }
20 return e
21 case <-time.After(2 * time.Second):
22 t.Fatalf("%s: no event within two seconds", what)
23 }
24 return present.Event{}
25}
26
27// TestPublishReachesEverySubscriber: a session's event reaches every
28// page watching that session — the presenter's laptop, the phone in
29// their hand, and every follower — and nobody watching another one.
30func TestPublishReachesEverySubscriber(t *testing.T) {
31 h := present.New()
32
33 laptop, stopLaptop := h.Subscribe("talk")
34 defer stopLaptop()
35 phone, stopPhone := h.Subscribe("talk")
36 defer stopPhone()
37 elsewhere, stopElsewhere := h.Subscribe("other-talk")
38 defer stopElsewhere()
39
40 h.Publish("talk", present.Event{Slide: 3, Blackout: true})
41
42 if e := recv(t, laptop, "the laptop"); e.Slide != 3 || !e.Blackout {
43 t.Errorf("the laptop got %+v, want slide 3 blacked out", e)
44 }
45 if e := recv(t, phone, "the phone"); e.Slide != 3 || !e.Blackout {
46 t.Errorf("the phone got %+v, want slide 3 blacked out", e)
47 }
48 select {
49 case e := <-elsewhere:
50 t.Errorf("a subscriber to another session got %+v", e)
51 default:
52 }
53
54 // Cancelling really unsubscribes: the next event reaches the phone
55 // and not the laptop.
56 stopLaptop()
57 h.Publish("talk", present.Event{Slide: 4})
58 if e := recv(t, phone, "the phone after the laptop left"); e.Slide != 4 {
59 t.Errorf("the phone got %+v, want slide 4", e)
60 }
61 select {
62 case e, ok := <-laptop:
63 if ok {
64 t.Errorf("a cancelled subscriber got %+v", e)
65 }
66 default:
67 }
68
69 // Cancelling twice is a no-op, not a panic: the stream handler
70 // defers its stop and may also have reason to stop early.
71 stopLaptop()
72}
73
74// TestSlowSubscriberIsDropped: a page that has stopped reading — a
75// laptop asleep with the tab open — must never hold up the phone in
76// somebody's hand. Past the buffer it is dropped, and its channel is
77// closed so the handler holding it returns instead of blocking on a
78// stream that will never move again.
79func TestSlowSubscriberIsDropped(t *testing.T) {
80 h := present.New()
81
82 slow, stopSlow := h.Subscribe("talk")
83 defer stopSlow()
84 quick, stopQuick := h.Subscribe("talk")
85 defer stopQuick()
86
87 for i := 0; i < 12; i++ {
88 h.Publish("talk", present.Event{Slide: i})
89 // The quick subscriber keeps up, so only the slow one is ever
90 // behind: this test is about one subscriber's back pressure,
91 // not about both.
92 if e := recv(t, quick, "the quick subscriber"); e.Slide != i {
93 t.Fatalf("the quick subscriber got %+v, want slide %d", e, i)
94 }
95 }
96
97 // The buffer it did get is intact — dropping is not a rewrite of
98 // what was already delivered.
99 for i := 0; i < 8; i++ {
100 if e := recv(t, slow, "the slow subscriber's buffer"); e.Slide != i {
101 t.Fatalf("the slow subscriber's buffered event %d is %+v", i, e)
102 }
103 }
104 select {
105 case _, ok := <-slow:
106 if ok {
107 t.Fatal("the slow subscriber is still delivered to past its buffer")
108 }
109 case <-time.After(2 * time.Second):
110 t.Fatal("the slow subscriber was neither delivered to nor closed")
111 }
112
113 // And the session is still live for everyone who kept up.
114 h.Publish("talk", present.Event{Slide: 99, Ended: true})
115 if e := recv(t, quick, "the quick subscriber at the end"); e.Slide != 99 || !e.Ended {
116 t.Errorf("the quick subscriber got %+v, want the ending event", e)
117 }
118}
119