channel_test.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package sharedspace
  2. import (
  3. "path/filepath"
  4. "sync"
  5. "testing"
  6. )
  7. func newChannelTestSpace(t *testing.T, access string) *Space {
  8. t.Helper()
  9. m := NewManager(filepath.Join(t.TempDir(), "spaces"), 0)
  10. space, err := m.CreateSpaceWithOptions("alice", "Channel test", SpaceOptions{Access: access})
  11. if err != nil {
  12. t.Fatalf("CreateSpaceWithOptions() error = %v", err)
  13. }
  14. return space
  15. }
  16. func TestChannelJoinLeave(t *testing.T) {
  17. space := newChannelTestSpace(t, AccessOpen)
  18. channel := space.Channel()
  19. if channel != space.Channel() {
  20. t.Fatalf("Channel() is not stable")
  21. }
  22. a, err := channel.Join("alice")
  23. if err != nil {
  24. t.Fatalf("Join(alice) error = %v", err)
  25. }
  26. b, err := channel.Join("bob")
  27. if err != nil {
  28. t.Fatalf("Join(bob) error = %v", err)
  29. }
  30. if a.ID == b.ID || a.ID < 1 || b.ID < 1 {
  31. t.Errorf("subscriber IDs invalid: %d, %d (must be unique, 1-based)", a.ID, b.ID)
  32. }
  33. if channel.Count() != 2 {
  34. t.Errorf("Count() = %d, want 2", channel.Count())
  35. }
  36. if got, ok := channel.Get(b.ID); !ok || got != b {
  37. t.Errorf("Get(%d) did not return bob", b.ID)
  38. }
  39. channel.Leave(b.ID)
  40. if channel.Count() != 1 {
  41. t.Errorf("Count() after leave = %d, want 1", channel.Count())
  42. }
  43. if _, open := <-b.Send; open {
  44. t.Errorf("left subscriber's send channel still open")
  45. }
  46. //Leaving twice or with an unknown ID must not panic
  47. channel.Leave(b.ID)
  48. channel.Leave(9999)
  49. }
  50. func TestChannelACL(t *testing.T) {
  51. space := newChannelTestSpace(t, AccessPrivate)
  52. channel := space.Channel()
  53. if _, err := channel.Join("stranger"); err != ErrPermissionDenied {
  54. t.Errorf("stranger Join error = %v, want ErrPermissionDenied", err)
  55. }
  56. if _, err := channel.Join("alice"); err != nil {
  57. t.Errorf("owner Join error = %v", err)
  58. }
  59. space.AddMember("alice", "mia", RoleMember)
  60. if _, err := channel.Join("mia"); err != nil {
  61. t.Errorf("member Join error = %v", err)
  62. }
  63. }
  64. func TestChannelBroadcastAndSendTo(t *testing.T) {
  65. space := newChannelTestSpace(t, AccessOpen)
  66. channel := space.Channel()
  67. a, _ := channel.Join("alice")
  68. b, _ := channel.Join("bob")
  69. c, _ := channel.Join("carol")
  70. channel.Broadcast([]byte("hello"), a.ID)
  71. for _, sub := range []*Subscriber{b, c} {
  72. select {
  73. case msg := <-sub.Send:
  74. if string(msg) != "hello" {
  75. t.Errorf("%s received %q, want hello", sub.Username, msg)
  76. }
  77. default:
  78. t.Errorf("%s received nothing from broadcast", sub.Username)
  79. }
  80. }
  81. select {
  82. case msg := <-a.Send:
  83. t.Errorf("excluded sender received %q", msg)
  84. default:
  85. }
  86. if !channel.SendTo(b.ID, []byte("direct")) {
  87. t.Errorf("SendTo(%d) = false, want true", b.ID)
  88. }
  89. if msg := <-b.Send; string(msg) != "direct" {
  90. t.Errorf("b received %q, want direct", msg)
  91. }
  92. if channel.SendTo(9999, []byte("direct")) {
  93. t.Errorf("SendTo(unknown) = true, want false")
  94. }
  95. //Broadcast must not alias the caller's buffer
  96. frame := []byte(`{"type":"chat"}`)
  97. channel.Broadcast(frame, -1)
  98. frame[2] = 'X'
  99. if msg := <-a.Send; string(msg) != `{"type":"chat"}` {
  100. t.Errorf("broadcast frame mutated by caller: %q", msg)
  101. }
  102. }
  103. func TestChannelBufferFullDrops(t *testing.T) {
  104. space := newChannelTestSpace(t, AccessOpen)
  105. channel := space.Channel()
  106. a, _ := channel.Join("alice")
  107. //Fill the buffer past capacity: the hub must not block
  108. for i := 0; i < SubscriberSendBuffer+10; i++ {
  109. channel.Broadcast([]byte("x"), -1)
  110. }
  111. if len(a.Send) != SubscriberSendBuffer {
  112. t.Errorf("send buffer = %d frames, want %d (overflow dropped)", len(a.Send), SubscriberSendBuffer)
  113. }
  114. }
  115. func TestChannelClose(t *testing.T) {
  116. space := newChannelTestSpace(t, AccessOpen)
  117. channel := space.Channel()
  118. a, _ := channel.Join("alice")
  119. channel.Join("bob")
  120. members := channel.Close()
  121. if len(members) != 2 {
  122. t.Errorf("Close() returned %d members, want 2", len(members))
  123. }
  124. if _, open := <-a.Send; open {
  125. t.Errorf("send channel still open after Close")
  126. }
  127. if _, err := channel.Join("late"); err != ErrSpaceClosed {
  128. t.Errorf("Join after Close error = %v, want ErrSpaceClosed", err)
  129. }
  130. //Closing twice must not panic
  131. if again := channel.Close(); len(again) != 0 {
  132. t.Errorf("second Close() returned %d members, want 0", len(again))
  133. }
  134. }
  135. func TestDeleteSpaceClosesChannel(t *testing.T) {
  136. m := NewManager(filepath.Join(t.TempDir(), "spaces"), 0)
  137. space := m.CreateSpace("alice", "")
  138. channel := space.Channel()
  139. a, _ := channel.Join("alice")
  140. m.DeleteSpace(space.ID)
  141. if _, open := <-a.Send; open {
  142. t.Errorf("subscriber send channel still open after DeleteSpace")
  143. }
  144. if _, err := channel.Join("late"); err != ErrSpaceClosed {
  145. t.Errorf("Join after DeleteSpace error = %v, want ErrSpaceClosed", err)
  146. }
  147. }
  148. func TestStandaloneChannel(t *testing.T) {
  149. channel := NewStandaloneChannel()
  150. //No space, no ACL: anyone joins
  151. a, err := channel.Join("anyone")
  152. if err != nil {
  153. t.Fatalf("standalone Join error = %v", err)
  154. }
  155. channel.Broadcast([]byte("ping"), -1)
  156. if msg := <-a.Send; string(msg) != "ping" {
  157. t.Errorf("standalone broadcast = %q", msg)
  158. }
  159. }
  160. func TestChannelPresenceHooks(t *testing.T) {
  161. space := newChannelTestSpace(t, AccessOpen)
  162. channel := space.Channel()
  163. var mu sync.Mutex
  164. joins := []string{}
  165. leaves := []string{}
  166. channel.SetPresenceHooks(
  167. func(sub *Subscriber) {
  168. //Hooks must run outside the channel lock: calling back into the
  169. //channel here must not deadlock
  170. channel.Count()
  171. mu.Lock()
  172. joins = append(joins, sub.Username)
  173. mu.Unlock()
  174. },
  175. func(sub *Subscriber) {
  176. channel.Count()
  177. mu.Lock()
  178. leaves = append(leaves, sub.Username)
  179. mu.Unlock()
  180. },
  181. )
  182. a, _ := channel.Join("alice")
  183. channel.Leave(a.ID)
  184. mu.Lock()
  185. defer mu.Unlock()
  186. if len(joins) != 1 || joins[0] != "alice" {
  187. t.Errorf("join hook calls = %v", joins)
  188. }
  189. if len(leaves) != 1 || leaves[0] != "alice" {
  190. t.Errorf("leave hook calls = %v", leaves)
  191. }
  192. }
  193. func TestChannelConcurrentUse(t *testing.T) {
  194. //Exercises the lock discipline under -race: concurrent joins, posts,
  195. //broadcasts and leaves must be data-race free and deadlock free.
  196. space := newChannelTestSpace(t, AccessOpen)
  197. channel := space.Channel()
  198. var wg sync.WaitGroup
  199. for worker := 0; worker < 8; worker++ {
  200. wg.Add(1)
  201. go func(n int) {
  202. defer wg.Done()
  203. for i := 0; i < 50; i++ {
  204. sub, err := channel.Join("user")
  205. if err != nil {
  206. return
  207. }
  208. channel.Broadcast([]byte("frame"), sub.ID)
  209. if n%2 == 0 {
  210. space.AddText("user", "message", "test")
  211. }
  212. //Drain a little so buffers do not saturate
  213. select {
  214. case <-sub.Send:
  215. default:
  216. }
  217. channel.Leave(sub.ID)
  218. }
  219. }(worker)
  220. }
  221. wg.Wait()
  222. if channel.Count() != 0 {
  223. t.Errorf("Count() after concurrent churn = %d, want 0", channel.Count())
  224. }
  225. }