diff options
Diffstat (limited to 'tests/basic.lua')
-rw-r--r-- | tests/basic.lua | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/tests/basic.lua b/tests/basic.lua new file mode 100644 index 0000000..ee31ed1 --- /dev/null +++ b/tests/basic.lua | |||
@@ -0,0 +1,331 @@ | |||
1 | -- | ||
2 | -- BASIC.LUA Copyright (c) 2007-08, Asko Kauppi <akauppi@gmail.com> | ||
3 | -- | ||
4 | -- Selftests for Lua Lanes | ||
5 | -- | ||
6 | -- To do: | ||
7 | -- - ... | ||
8 | -- | ||
9 | |||
10 | require "lanes" | ||
11 | require "assert" -- assert.fails() | ||
12 | |||
13 | local lanes_gen= assert( lanes.gen ) | ||
14 | local lanes_linda= assert( lanes.linda ) | ||
15 | |||
16 | local tostring= assert( tostring ) | ||
17 | |||
18 | local function PRINT(...) | ||
19 | local str="" | ||
20 | for i=1,select('#',...) do | ||
21 | str= str..tostring(select(i,...)).."\t" | ||
22 | end | ||
23 | if io then | ||
24 | io.stderr:write(str.."\n") | ||
25 | end | ||
26 | end | ||
27 | |||
28 | |||
29 | ---=== Local helpers ===--- | ||
30 | |||
31 | local tables_match | ||
32 | |||
33 | -- true if 'a' is a subtable of 'b' | ||
34 | -- | ||
35 | local function subtable( a, b ) | ||
36 | -- | ||
37 | assert( type(a)=="table" and type(b)=="table" ) | ||
38 | |||
39 | for k,v in pairs(b) do | ||
40 | if type(v)~=type(a[k]) then | ||
41 | return false -- not subtable (different types, or missing key) | ||
42 | elseif type(v)=="table" then | ||
43 | if not tables_match(v,a[k]) then return false end | ||
44 | else | ||
45 | if a[k] ~= v then return false end | ||
46 | end | ||
47 | end | ||
48 | return true -- is a subtable | ||
49 | end | ||
50 | |||
51 | -- true when contents of 'a' and 'b' are identific | ||
52 | -- | ||
53 | tables_match= function( a, b ) | ||
54 | return subtable( a, b ) and subtable( b, a ) | ||
55 | end | ||
56 | |||
57 | |||
58 | ---=== Tasking (basic) ===--- | ||
59 | |||
60 | local function task( a, b, c ) | ||
61 | --error "111" -- testing error messages | ||
62 | assert(hey) | ||
63 | local v=0 | ||
64 | for i=a,b,c do | ||
65 | v= v+i | ||
66 | end | ||
67 | return v, hey | ||
68 | end | ||
69 | |||
70 | local task_launch= lanes_gen( "", { globals={hey=true} }, task ) | ||
71 | -- base stdlibs, normal priority | ||
72 | |||
73 | -- 'task_launch' is a factory of multithreaded tasks, we can launch several: | ||
74 | |||
75 | local lane1= task_launch( 100,200,3 ) | ||
76 | local lane2= task_launch( 200,300,4 ) | ||
77 | |||
78 | -- At this stage, states may be "pending", "running" or "done" | ||
79 | |||
80 | local st1,st2= lane1.status, lane2.status | ||
81 | PRINT(st1,st2) | ||
82 | assert( st1=="pending" or st1=="running" or st1=="done" ) | ||
83 | assert( st2=="pending" or st2=="running" or st2=="done" ) | ||
84 | |||
85 | -- Accessing results ([1..N]) pends until they are available | ||
86 | -- | ||
87 | PRINT("waiting...") | ||
88 | local v1, v1_hey= lane1[1], lane1[2] | ||
89 | local v2, v2_hey= lane2[1], lane2[2] | ||
90 | |||
91 | PRINT( v1, v1_hey ) | ||
92 | assert( v1_hey == true ) | ||
93 | |||
94 | PRINT( v2, v2_hey ) | ||
95 | assert( v2_hey == true ) | ||
96 | |||
97 | assert( lane1.status == "done" ) | ||
98 | assert( lane1.status == "done" ) | ||
99 | |||
100 | |||
101 | ---=== Tasking (cancelling) ===--- | ||
102 | |||
103 | local task_launch2= lanes_gen( "", { cancelstep=100, globals={hey=true} }, task ) | ||
104 | |||
105 | local N=999999999 | ||
106 | local lane9= task_launch2(1,N,1) -- huuuuuuge... | ||
107 | |||
108 | -- Wait until state changes "pending"->"running" | ||
109 | -- | ||
110 | local st | ||
111 | local t0= os.time() | ||
112 | while os.time()-t0 < 5 do | ||
113 | st= lane9.status | ||
114 | io.stderr:write( (i==1) and st.." " or '.' ) | ||
115 | if st~="pending" then break end | ||
116 | end | ||
117 | PRINT(" "..st) | ||
118 | |||
119 | if st=="error" then | ||
120 | local _= lane9[0] -- propagate the error here | ||
121 | end | ||
122 | if st=="done" then | ||
123 | error( "Looping to "..N.." was not long enough (cannot test cancellation)" ) | ||
124 | end | ||
125 | assert( st=="running" ) | ||
126 | |||
127 | lane9:cancel() | ||
128 | |||
129 | local t0= os.time() | ||
130 | while os.time()-t0 < 5 do | ||
131 | st= lane9.status | ||
132 | io.stderr:write( (i==1) and st.." " or '.' ) | ||
133 | if st~="running" then break end | ||
134 | end | ||
135 | PRINT(" "..st) | ||
136 | assert( st == "cancelled" ) | ||
137 | |||
138 | |||
139 | ---=== Communications ===--- | ||
140 | |||
141 | local function WR(...) io.stderr:write(...) end | ||
142 | |||
143 | local chunk= function( linda ) | ||
144 | |||
145 | local function receive() return linda:receive( "->" ) end | ||
146 | local function send(...) linda:send( "<-", ... ) end | ||
147 | |||
148 | WR( "Lane starts!\n" ) | ||
149 | |||
150 | local v | ||
151 | v=receive(); WR( v.." received\n" ); assert( v==1 ) | ||
152 | v=receive(); WR( v.." received\n" ); assert( v==2 ) | ||
153 | v=receive(); WR( v.." received\n" ); assert( v==3 ) | ||
154 | |||
155 | send( 1,2,3 ); WR( "1,2,3 sent\n" ) | ||
156 | send 'a'; WR( "'a' sent\n" ) | ||
157 | send { 'a', 'b', 'c', d=10 }; WR( "{'a','b','c',d=10} sent\n" ) | ||
158 | |||
159 | v=receive(); WR( v.." received\n" ); assert( v==4 ) | ||
160 | |||
161 | WR( "Lane ends!\n" ) | ||
162 | end | ||
163 | |||
164 | local linda= lanes_linda() | ||
165 | assert( type(linda) == "userdata" ) | ||
166 | -- | ||
167 | -- ["->"] master -> slave | ||
168 | -- ["<-"] slave <- master | ||
169 | |||
170 | local function PEEK() return linda:get("<-") end | ||
171 | local function SEND(...) linda:send( "->", ... ) end | ||
172 | local function RECEIVE() return linda:receive( "<-" ) end | ||
173 | |||
174 | local t= lanes_gen("io",chunk)(linda) -- prepare & launch | ||
175 | |||
176 | SEND(1); WR( "1 sent\n" ) | ||
177 | SEND(2); WR( "2 sent\n" ) | ||
178 | for i=1,100 do | ||
179 | WR "." | ||
180 | assert( PEEK() == nil ) -- nothing coming in, yet | ||
181 | end | ||
182 | SEND(3); WR( "3 sent\n" ) | ||
183 | |||
184 | local a,b,c= RECEIVE(), RECEIVE(), RECEIVE() | ||
185 | WR( a..", "..b..", "..c.." received\n" ) | ||
186 | assert( a==1 and b==2 and c==3 ) | ||
187 | |||
188 | local a= RECEIVE(); WR( a.." received\n" ) | ||
189 | assert( a=='a' ) | ||
190 | |||
191 | local a= RECEIVE(); WR( type(a).." received\n" ) | ||
192 | assert( tables_match( a, {'a','b','c',d=10} ) ) | ||
193 | |||
194 | assert( PEEK() == nil ) | ||
195 | SEND(4) | ||
196 | |||
197 | |||
198 | ---=== Stdlib naming ===--- | ||
199 | |||
200 | local function io_os_f() | ||
201 | assert(io) | ||
202 | assert(os) | ||
203 | assert(print) | ||
204 | return true | ||
205 | end | ||
206 | |||
207 | local f1= lanes_gen( "io,os", io_os_f ) -- any delimiter will do | ||
208 | local f2= lanes_gen( "io+os", io_os_f ) | ||
209 | local f3= lanes_gen( "io,os,base", io_os_f ) | ||
210 | |||
211 | assert.fails( function() lanes_gen( "xxx", io_os_f ) end ) | ||
212 | |||
213 | assert( f1()[1] ) | ||
214 | assert( f2()[1] ) | ||
215 | assert( f3()[1] ) | ||
216 | |||
217 | |||
218 | ---=== Comms criss cross ===--- | ||
219 | |||
220 | -- We make two identical lanes, which are using the same Linda channel. | ||
221 | -- | ||
222 | local tc= lanes_gen( "io", | ||
223 | function( linda, ch_in, ch_out ) | ||
224 | |||
225 | local function STAGE(str) | ||
226 | io.stderr:write( ch_in..": "..str.."\n" ) | ||
227 | linda:send( nil, ch_out, str ) | ||
228 | local v= linda:receive( nil, ch_in ) | ||
229 | assert(v==str) | ||
230 | end | ||
231 | STAGE("Hello") | ||
232 | STAGE("I was here first!") | ||
233 | STAGE("So waht?") | ||
234 | end | ||
235 | ) | ||
236 | |||
237 | local linda= lanes_linda() | ||
238 | |||
239 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms | ||
240 | |||
241 | local _= a[1],b[1] -- waits until they are both ready | ||
242 | |||
243 | |||
244 | ---=== Receive & send of code ===--- | ||
245 | |||
246 | local upvalue="123" | ||
247 | |||
248 | local function chunk2( linda ) | ||
249 | assert( upvalue=="123" ) -- even when running as separate thread | ||
250 | |||
251 | -- function name & line number should be there even as separate thread | ||
252 | -- | ||
253 | local info= debug.getinfo(1) -- 1 = us | ||
254 | -- | ||
255 | for k,v in pairs(info) do PRINT(k,v) end | ||
256 | |||
257 | assert( info.nups == 2 ) -- one upvalue + PRINT | ||
258 | assert( info.what == "Lua" ) | ||
259 | |||
260 | --assert( info.name == "chunk2" ) -- name does not seem to come through | ||
261 | assert( string.match( info.source, "^@tests[/\\]basic.lua$" ) ) | ||
262 | assert( string.match( info.short_src, "^tests[/\\]basic.lua$" ) ) | ||
263 | |||
264 | -- These vary so let's not be picky (they're there..) | ||
265 | -- | ||
266 | assert( info.linedefined > 200 ) -- start of 'chunk2' | ||
267 | assert( info.currentline > info.linedefined ) -- line of 'debug.getinfo' | ||
268 | assert( info.lastlinedefined > info.currentline ) -- end of 'chunk2' | ||
269 | |||
270 | local func,k= linda:receive( "down" ) | ||
271 | assert( type(func)=="function" ) | ||
272 | assert( k=="down" ) | ||
273 | |||
274 | func(linda) | ||
275 | |||
276 | local str= linda:receive( "down" ) | ||
277 | assert( str=="ok" ) | ||
278 | |||
279 | linda:send( "up", function() return ":)" end, "ok2" ) | ||
280 | end | ||
281 | |||
282 | local linda= lanes.linda() | ||
283 | |||
284 | local t2= lanes_gen( "debug,string", chunk2 )(linda) -- prepare & launch | ||
285 | |||
286 | linda:send( "down", function(linda) linda:send( "up", "ready!" ) end, | ||
287 | "ok" ) | ||
288 | |||
289 | -- wait to see if the tiny function gets executed | ||
290 | -- | ||
291 | local s= linda:receive( "up" ) | ||
292 | PRINT(s) | ||
293 | assert( s=="ready!" ) | ||
294 | |||
295 | -- returns of the 'chunk2' itself | ||
296 | -- | ||
297 | local f= linda:receive( "up" ) | ||
298 | assert( type(f)=="function" ) | ||
299 | |||
300 | local s2= f() | ||
301 | assert( s2==":)" ) | ||
302 | |||
303 | local ok2= linda:receive( "up" ) | ||
304 | assert( ok2 == "ok2" ) | ||
305 | |||
306 | |||
307 | ---=== :join test ===--- | ||
308 | |||
309 | -- NOTE: 'unpack()' cannot be used on the lane handle; it will always return nil | ||
310 | -- (unless [1..n] has been read earlier, in which case it would seemingly | ||
311 | -- work). | ||
312 | |||
313 | local S= lanes_gen( "table", | ||
314 | function(arg) | ||
315 | aux= {} | ||
316 | for i, v in ipairs(arg) do | ||
317 | table.insert (aux, 1, v) | ||
318 | end | ||
319 | return unpack(aux) | ||
320 | end ) | ||
321 | |||
322 | h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values | ||
323 | |||
324 | local a,b,c,d= h:join() | ||
325 | assert(a==14) | ||
326 | assert(b==13) | ||
327 | assert(c==12) | ||
328 | assert(d==nil) | ||
329 | |||
330 | -- | ||
331 | io.stderr:write "Done! :)\n" | ||