github.com/xmx/lua@v0.0.0-20230324063450-8a298e091302/channellib_test.go (about) 1 package lua 2 3 import ( 4 "context" 5 "reflect" 6 "sync" 7 "testing" 8 "time" 9 ) 10 11 func 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 32 func 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 44 func 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 func TestChannelSelect2(t *testing.T) { 108 var wg sync.WaitGroup 109 receiver := func(ch, quit chan LValue) { 110 defer wg.Done() 111 L := NewState() 112 defer L.Close() 113 L.SetGlobal("ch", LChannel(ch)) 114 L.SetGlobal("quit", LChannel(quit)) 115 errorIfScriptFail(t, L, ` 116 idx, rcv, ok = channel.select( 117 {"|<-", ch}, 118 {"|<-", quit} 119 ) 120 assert(idx == 1) 121 assert(rcv == "1") 122 assert(ok) 123 idx, rcv, ok = channel.select( 124 {"|<-", ch}, 125 {"|<-", quit} 126 ) 127 assert(idx == 1) 128 assert(rcv == nil) 129 assert(not ok) 130 `) 131 } 132 133 sender := func(ch, quit chan LValue) { 134 defer wg.Done() 135 L := NewState() 136 defer L.Close() 137 L.SetGlobal("ch", LChannel(ch)) 138 L.SetGlobal("quit", LChannel(quit)) 139 errorIfScriptFail(t, L, `ch:send("1")`) 140 errorIfScriptFail(t, L, `ch:close()`) 141 } 142 143 ch := make(chan LValue) 144 quit := make(chan LValue) 145 wg.Add(2) 146 go receiver(ch, quit) 147 go sender(ch, quit) 148 wg.Wait() 149 } 150 151 func TestChannelSelect3(t *testing.T) { 152 var wg sync.WaitGroup 153 receiver := func(ch chan LValue) { 154 defer wg.Done() 155 L := NewState() 156 defer L.Close() 157 L.SetGlobal("ch", LChannel(ch)) 158 errorIfScriptFail(t, L, ` 159 ok = true 160 while ok do 161 idx, rcv, ok = channel.select( 162 {"|<-", ch} 163 ) 164 end 165 `) 166 } 167 168 sender := func(ch chan LValue) { 169 defer wg.Done() 170 L := NewState() 171 defer L.Close() 172 L.SetGlobal("ch", LChannel(ch)) 173 errorIfScriptFail(t, L, ` 174 ok = false 175 channel.select( 176 {"<-|", ch, "1", function(v) 177 ok = true 178 end} 179 ) 180 assert(ok) 181 idx, rcv, ok = channel.select( 182 {"<-|", ch, "1"} 183 ) 184 assert(idx == 1) 185 ch:close() 186 `) 187 } 188 189 ch := make(chan LValue) 190 wg.Add(2) 191 go receiver(ch) 192 time.Sleep(1) 193 go sender(ch) 194 wg.Wait() 195 } 196 197 func TestChannelSelect4(t *testing.T) { 198 var wg sync.WaitGroup 199 receiver := func(ch chan LValue) { 200 defer wg.Done() 201 L := NewState() 202 defer L.Close() 203 L.SetGlobal("ch", LChannel(ch)) 204 errorIfScriptFail(t, L, ` 205 idx, rcv, ok = channel.select( 206 {"|<-", ch}, 207 {"default"} 208 ) 209 assert(idx == 2) 210 called = false 211 idx, rcv, ok = channel.select( 212 {"|<-", ch}, 213 {"default", function() 214 called = true 215 end} 216 ) 217 assert(called) 218 ch:close() 219 `) 220 } 221 222 ch := make(chan LValue) 223 wg.Add(1) 224 go receiver(ch) 225 wg.Wait() 226 } 227 228 func TestChannelSendReceive1(t *testing.T) { 229 var wg sync.WaitGroup 230 receiver := func(ch chan LValue) { 231 defer wg.Done() 232 L := NewState() 233 defer L.Close() 234 L.SetGlobal("ch", LChannel(ch)) 235 errorIfScriptFail(t, L, ` 236 local ok, v = ch:receive() 237 assert(ok) 238 assert(v == "1") 239 `) 240 time.Sleep(1 * time.Second) 241 errorIfScriptFail(t, L, ` 242 local ok, v = ch:receive() 243 assert(not ok) 244 assert(v == nil) 245 `) 246 } 247 sender := func(ch chan LValue) { 248 defer wg.Done() 249 L := NewState() 250 defer L.Close() 251 L.SetGlobal("ch", LChannel(ch)) 252 errorIfScriptFail(t, L, `ch:send("1")`) 253 errorIfScriptNotFail(t, L, `ch:send(function() end)`, "can not send a function") 254 errorIfScriptFail(t, L, `ch:close()`) 255 } 256 ch := make(chan LValue) 257 wg.Add(2) 258 go receiver(ch) 259 go sender(ch) 260 wg.Wait() 261 } 262 263 func TestCancelChannelReceive(t *testing.T) { 264 done := make(chan struct{}) 265 ctx, cancel := context.WithCancel(context.Background()) 266 go func() { 267 defer close(done) 268 L := NewState() 269 L.SetContext(ctx) 270 defer L.Close() 271 L.SetGlobal("ch", LChannel(make(chan LValue))) 272 errorIfScriptNotFail(t, L, `ch:receive()`, context.Canceled.Error()) 273 }() 274 time.Sleep(time.Second) 275 cancel() 276 <-done 277 } 278 279 func TestCancelChannelReceive2(t *testing.T) { 280 done := make(chan struct{}) 281 ctx, cancel := context.WithCancel(context.Background()) 282 go func() { 283 defer close(done) 284 L := NewState() 285 L.SetContext(ctx) 286 defer L.Close() 287 L.SetGlobal("ch", LChannel(make(chan LValue))) 288 errorIfScriptNotFail(t, L, `channel.select({"|<-", ch})`, context.Canceled.Error()) 289 }() 290 time.Sleep(time.Second) 291 cancel() 292 <-done 293 }