1package lua
2
3import (
4	"context"
5	"reflect"
6	"sync"
7	"testing"
8	"time"
9)
10
11func TestChannelMake(t *testing.T) {
12	L := NewState()
13	defer L.Close()
14	errorIfScriptFail(t, L, `
15    ch = channel.make()
16    `)
17	obj := L.GetGlobal("ch")
18	ch, ok := obj.(LChannel)
19	errorIfFalse(t, ok, "channel expected")
20	errorIfNotEqual(t, 0, reflect.ValueOf(ch).Cap())
21	close(ch)
22
23	errorIfScriptFail(t, L, `
24    ch = channel.make(10)
25    `)
26	obj = L.GetGlobal("ch")
27	ch, _ = obj.(LChannel)
28	errorIfNotEqual(t, 10, reflect.ValueOf(ch).Cap())
29	close(ch)
30}
31
32func TestChannelSelectError(t *testing.T) {
33	L := NewState()
34	defer L.Close()
35	errorIfScriptFail(t, L, `ch = channel.make()`)
36	errorIfScriptNotFail(t, L, `channel.select({1,2,3})`, "invalid select case")
37	errorIfScriptNotFail(t, L, `channel.select({"<-|", 1, 3})`, "invalid select case")
38	errorIfScriptNotFail(t, L, `channel.select({"<-|", ch, function() end})`, "can not send a function")
39	errorIfScriptNotFail(t, L, `channel.select({"|<-", 1, 3})`, "invalid select case")
40	errorIfScriptNotFail(t, L, `channel.select({"<-->", 1, 3})`, "invalid channel direction")
41	errorIfScriptFail(t, L, `ch:close()`)
42}
43
44func TestChannelSelect1(t *testing.T) {
45	var result LValue
46	var wg sync.WaitGroup
47	receiver := func(ch, quit chan LValue) {
48		defer wg.Done()
49		L := NewState()
50		defer L.Close()
51		L.SetGlobal("ch", LChannel(ch))
52		L.SetGlobal("quit", LChannel(quit))
53		if err := L.DoString(`
54    buf = ""
55    local exit = false
56    while not exit do
57      channel.select(
58        {"|<-", ch, function(ok, v)
59          if not ok then
60            buf = buf .. "channel closed"
61            exit = true
62          else
63            buf = buf .. "received:" .. v
64          end
65        end},
66        {"|<-", quit, function(ok, v)
67            buf = buf .. "quit"
68        end}
69      )
70    end
71  `); err != nil {
72			panic(err)
73		}
74		result = L.GetGlobal("buf")
75	}
76
77	sender := func(ch, quit chan LValue) {
78		defer wg.Done()
79		L := NewState()
80		defer L.Close()
81		L.SetGlobal("ch", LChannel(ch))
82		L.SetGlobal("quit", LChannel(quit))
83		if err := L.DoString(`
84    ch:send("1")
85    ch:send("2")
86  `); err != nil {
87			panic(err)
88		}
89		ch <- LString("3")
90		quit <- LTrue
91		time.Sleep(1 * time.Second)
92		close(ch)
93	}
94
95	ch := make(chan LValue)
96	quit := make(chan LValue)
97	wg.Add(2)
98	go receiver(ch, quit)
99	go sender(ch, quit)
100	wg.Wait()
101	lstr, ok := result.(LString)
102	errorIfFalse(t, ok, "must be string")
103	str := string(lstr)
104	errorIfNotEqual(t, "received:1received:2received:3quitchannel closed", str)
105
106}
107
108func TestChannelSelect2(t *testing.T) {
109	var wg sync.WaitGroup
110	receiver := func(ch, quit chan LValue) {
111		defer wg.Done()
112		L := NewState()
113		defer L.Close()
114		L.SetGlobal("ch", LChannel(ch))
115		L.SetGlobal("quit", LChannel(quit))
116		errorIfScriptFail(t, L, `
117           idx, rcv, ok = channel.select(
118               {"|<-", ch},
119               {"|<-", quit}
120           )
121           assert(idx == 1)
122           assert(rcv == "1")
123           assert(ok)
124           idx, rcv, ok = channel.select(
125               {"|<-", ch},
126               {"|<-", quit}
127           )
128           assert(idx == 1)
129           assert(rcv == nil)
130           assert(not ok)
131       `)
132	}
133
134	sender := func(ch, quit chan LValue) {
135		defer wg.Done()
136		L := NewState()
137		defer L.Close()
138		L.SetGlobal("ch", LChannel(ch))
139		L.SetGlobal("quit", LChannel(quit))
140		errorIfScriptFail(t, L, `ch:send("1")`)
141		errorIfScriptFail(t, L, `ch:close()`)
142	}
143
144	ch := make(chan LValue)
145	quit := make(chan LValue)
146	wg.Add(2)
147	go receiver(ch, quit)
148	go sender(ch, quit)
149	wg.Wait()
150}
151
152func TestChannelSelect3(t *testing.T) {
153	var wg sync.WaitGroup
154	receiver := func(ch chan LValue) {
155		defer wg.Done()
156		L := NewState()
157		defer L.Close()
158		L.SetGlobal("ch", LChannel(ch))
159		errorIfScriptFail(t, L, `
160           ok = true
161           while ok do
162             idx, rcv, ok = channel.select(
163                 {"|<-", ch}
164             )
165           end
166       `)
167	}
168
169	sender := func(ch chan LValue) {
170		defer wg.Done()
171		L := NewState()
172		defer L.Close()
173		L.SetGlobal("ch", LChannel(ch))
174		errorIfScriptFail(t, L, `
175           ok = false
176           channel.select(
177               {"<-|", ch, "1", function(v)
178                 ok = true
179               end}
180           )
181           assert(ok)
182           idx, rcv, ok = channel.select(
183               {"<-|", ch, "1"}
184           )
185           assert(idx == 1)
186           ch:close()
187       `)
188	}
189
190	ch := make(chan LValue)
191	wg.Add(2)
192	go receiver(ch)
193	time.Sleep(1)
194	go sender(ch)
195	wg.Wait()
196}
197
198func TestChannelSelect4(t *testing.T) {
199	var wg sync.WaitGroup
200	receiver := func(ch chan LValue) {
201		defer wg.Done()
202		L := NewState()
203		defer L.Close()
204		L.SetGlobal("ch", LChannel(ch))
205		errorIfScriptFail(t, L, `
206           idx, rcv, ok = channel.select(
207                 {"|<-", ch},
208                 {"default"}
209           )
210           assert(idx == 2)
211           called = false
212           idx, rcv, ok = channel.select(
213                 {"|<-", ch},
214                 {"default", function()
215                    called = true
216                 end}
217           )
218           assert(called)
219           ch:close()
220       `)
221	}
222
223	ch := make(chan LValue)
224	wg.Add(1)
225	go receiver(ch)
226	wg.Wait()
227}
228
229func TestChannelSendReceive1(t *testing.T) {
230	var wg sync.WaitGroup
231	receiver := func(ch chan LValue) {
232		defer wg.Done()
233		L := NewState()
234		defer L.Close()
235		L.SetGlobal("ch", LChannel(ch))
236		errorIfScriptFail(t, L, `
237          local ok, v = ch:receive()
238          assert(ok)
239          assert(v == "1")
240        `)
241		time.Sleep(1 * time.Second)
242		errorIfScriptFail(t, L, `
243          local ok, v = ch:receive()
244          assert(not ok)
245          assert(v == nil)
246        `)
247	}
248	sender := func(ch chan LValue) {
249		defer wg.Done()
250		L := NewState()
251		defer L.Close()
252		L.SetGlobal("ch", LChannel(ch))
253		errorIfScriptFail(t, L, `ch:send("1")`)
254		errorIfScriptNotFail(t, L, `ch:send(function() end)`, "can not send a function")
255		errorIfScriptFail(t, L, `ch:close()`)
256	}
257	ch := make(chan LValue)
258	wg.Add(2)
259	go receiver(ch)
260	go sender(ch)
261	wg.Wait()
262}
263
264func TestCancelChannelReceive(t *testing.T) {
265	done := make(chan struct{})
266	ctx, cancel := context.WithCancel(context.Background())
267	go func() {
268		defer close(done)
269		L := NewState()
270		L.SetContext(ctx)
271		defer L.Close()
272		L.SetGlobal("ch", LChannel(make(chan LValue)))
273		errorIfScriptNotFail(t, L, `ch:receive()`, context.Canceled.Error())
274	}()
275	time.Sleep(time.Second)
276	cancel()
277	<-done
278}
279
280func TestCancelChannelReceive2(t *testing.T) {
281	done := make(chan struct{})
282	ctx, cancel := context.WithCancel(context.Background())
283	go func() {
284		defer close(done)
285		L := NewState()
286		L.SetContext(ctx)
287		defer L.Close()
288		L.SetGlobal("ch", LChannel(make(chan LValue)))
289		errorIfScriptNotFail(t, L, `channel.select({"|<-", ch})`, context.Canceled.Error())
290	}()
291	time.Sleep(time.Second)
292	cancel()
293	<-done
294}
295