1package diodes_test
2
3import (
4	"code.cloudfoundry.org/go-diodes"
5
6	. "github.com/onsi/ginkgo"
7	. "github.com/onsi/gomega"
8)
9
10var _ = Describe("OneToOne", func() {
11	var (
12		d    *diodes.OneToOne
13		data []byte
14
15		spy *spyAlerter
16	)
17
18	BeforeEach(func() {
19		spy = newSpyAlerter()
20
21		d = diodes.NewOneToOne(5, spy)
22
23		data = []byte("some-data")
24		d.Set(diodes.GenericDataType(&data))
25	})
26
27	Context("multiple data slices", func() {
28		var (
29			secondData []byte
30		)
31
32		BeforeEach(func() {
33			secondData = []byte("some-other-data")
34			d.Set(diodes.GenericDataType(&secondData))
35		})
36
37		Describe("TryNext()", func() {
38			It("returns true", func() {
39				_, ok := d.TryNext()
40
41				Expect(ok).To(BeTrue())
42			})
43
44			Context("reads exceed writes", func() {
45				BeforeEach(func() {
46					d.TryNext()
47					d.TryNext()
48				})
49
50				It("returns false", func() {
51					_, ok := d.TryNext()
52
53					Expect(ok).To(BeFalse())
54				})
55			})
56		})
57
58		Context("buffer size exceeded", func() {
59			BeforeEach(func() {
60				for i := 0; i < 4; i++ {
61					d.Set(diodes.GenericDataType(&secondData))
62				}
63			})
64
65			It("wraps", func() {
66				data, _ := d.TryNext()
67				Expect(*(*[]byte)(data)).To(Equal(secondData))
68			})
69
70			It("alerts for each dropped point", func() {
71				d.TryNext()
72				Expect(spy.AlertInput.Missed).To(Receive(Equal(5)))
73			})
74
75			It("it updates the read index", func() {
76				d.TryNext()
77				Expect(spy.AlertInput.Missed).To(Receive(Equal(5)))
78
79				for i := 0; i < 6; i++ {
80					d.Set(diodes.GenericDataType(&secondData))
81				}
82
83				d.TryNext()
84				Expect(spy.AlertInput.Missed).To(Receive(Equal(5)))
85			})
86
87			Context("read catches up with write", func() {
88				BeforeEach(func() {
89					d.TryNext()
90					<-spy.AlertInput.Missed
91				})
92
93				It("does not alert", func() {
94					d.TryNext()
95					Expect(spy.AlertInput.Missed).To(Not(Receive()))
96				})
97			})
98
99			Context("writer laps reader", func() {
100				BeforeEach(func() {
101					for i := 0; i < 5; i++ {
102						d.Set(diodes.GenericDataType(&secondData))
103					}
104					d.TryNext()
105				})
106
107				It("sends an alert for each set", func() {
108					Expect(spy.AlertInput.Missed).To(Receive(Equal(10)))
109				})
110			})
111
112			Context("writer laps reader with nil alerter", func() {
113				It("drops the alert", func() {
114					d = diodes.NewOneToOne(5, nil)
115					for i := 0; i < 10; i++ {
116						d.Set(diodes.GenericDataType(&secondData))
117					}
118
119					Expect(func() {
120						d.TryNext()
121					}).ToNot(Panic())
122				})
123			})
124		})
125	})
126
127})
128
129var _ = Describe("reader ahead of writer", func() {
130	It("must not occur after alerting", func() {
131		length := 4
132		spy := newSpyAlerter()
133		d := diodes.NewOneToOne(length, spy)
134		data := []byte("some-data")
135		genData := diodes.GenericDataType(&data)
136
137		By("filling up the buffer")
138		for i := 0; i < length; i++ {
139			d.Set(genData)
140		}
141
142		By("overwriting the first index")
143		d.Set(genData)
144
145		By("having reader fast forward")
146		Expect(spy.AlertInput.Missed).To(BeEmpty())
147		_, ok := d.TryNext()
148		Expect(ok).To(BeTrue())
149		Expect(spy.AlertInput.Missed).To(Receive(Equal(4)))
150
151		By("failing reads until the writer writes over skipped values")
152		_, ok = d.TryNext()
153		Expect(ok).To(BeFalse())
154		d.Set(genData)
155		_, ok = d.TryNext()
156		Expect(ok).To(BeTrue())
157	})
158})
159
160type spyAlerter struct {
161	AlertCalled chan bool
162	AlertInput  struct {
163		Missed chan int
164	}
165}
166
167func newSpyAlerter() *spyAlerter {
168	m := &spyAlerter{}
169	m.AlertCalled = make(chan bool, 100)
170	m.AlertInput.Missed = make(chan int, 100)
171	return m
172}
173func (m *spyAlerter) Alert(missed int) {
174	m.AlertCalled <- true
175	m.AlertInput.Missed <- missed
176}
177