@@ -133,6 +133,23 @@ def execute_process(cmd, timeout=TIMEOUT):
133133else :
134134 raise Exception ('Unknown architecture in testing' )
135135
136+ GCS_PAUSE = 'PAUSE'
137+ GCS_PROPAGATE = 'PROPAGATE'
138+ GCS_ATOMIC = 'ATOMIC'
139+ GCS_SWEEPSTRING = 'SWEEPSTRING'
140+ GCS_SWEEP = 'SWEEP'
141+ GCS_FINALIZE = 'FINALIZE'
142+
143+ RX_VALID_GC_STATES = r'({})' .format ('|' .join ([
144+ GCS_PAUSE ,
145+ GCS_PROPAGATE ,
146+ GCS_ATOMIC ,
147+ GCS_SWEEPSTRING ,
148+ GCS_SWEEP ,
149+ GCS_FINALIZE ,
150+ ]))
151+ RX_INVALID_GC_STATE = r'INVALID'
152+
136153
137154class TestCaseBase (unittest .TestCase ):
138155 @classmethod
@@ -283,23 +300,24 @@ class TestLJArch(TestCaseBase):
283300]))
284301
285302
286- class TestLJState (TestCaseBase ):
287- extension_cmds = 'lj-state'
288- location = 'lj_cf_print'
289- lua_script = 'print(1)'
290- pattern = (
303+ def compose_lj_state_rx (rx_gc_state ):
304+ return (
291305 r'VM state: ' + RX_VM_STATE + '\n '
292- r'GC state: [A-Z]+ \n'
306+ r'GC state: ' + rx_gc_state + ' \n '
293307 r'JIT state: [A-Z]+\n'
294308 )
295309
296310
297- class TestLJGC (TestCaseBase ):
298- extension_cmds = 'lj-gc '
311+ class TestLJState (TestCaseBase ):
312+ extension_cmds = 'lj-state '
299313 location = 'lj_cf_print'
300314 lua_script = 'print(1)'
301- pattern = (
302- r'GC stats: [A-Z]+\n'
315+ pattern = compose_lj_state_rx (RX_VALID_GC_STATES )
316+
317+
318+ def compose_gc_stats_rx (rx_gc_state ):
319+ return (
320+ r'GC stats: ' + rx_gc_state + '\n '
303321 r'\ttotal: \d+\n'
304322 r'\tthreshold: \d+\n'
305323 r'\tdebt: \d+\n'
@@ -315,6 +333,81 @@ class TestLJGC(TestCaseBase):
315333 )
316334
317335
336+ class TestLJGC (TestCaseBase ):
337+ extension_cmds = 'lj-gc'
338+ location = 'lj_cf_print'
339+ lua_script = 'print(1)'
340+ pattern = compose_gc_stats_rx (RX_VALID_GC_STATES )
341+
342+
343+ class TestGCStateBase (TestCaseBase ):
344+ extension_cmds = (
345+ 'lj-state\n '
346+ 'lj-gc\n '
347+ )
348+
349+ # The same lua code is used to check all the GC states because
350+ # it allows to reach any of them:
351+ # 1) `gc_mark_start()` (PAUSE) is always present in the full
352+ # GC cycle.
353+ # 2) `propagatemark()` (PROPAGATE) is reachable because our
354+ # build has FFI and the finalizer table is marked as a weak
355+ # table, i.e. it is gray. Hence, the g->gc.gray list is not
356+ # empty.
357+ # 3) `atomic()` (ATOMIC) is always present in the full GC
358+ # cycle.
359+ # 4) `gc_sweep_str_chain()` (SWEEPSTRING) is always present in
360+ # the full GC cycle.
361+ # 5) `gc_sweep()` (SWEEP) is always present in the full GC
362+ # cycle.
363+ # 6) `gc_finalize()` (FINALIZE) is met due to the finalizer to
364+ # userdata in the script.
365+ lua_script = (
366+ 'local obj = newproxy(true)\n '
367+ 'getmetatable(obj).__gc = function() end\n '
368+ 'obj = nil\n '
369+ 'collectgarbage()\n '
370+ )
371+
372+ @classmethod
373+ def setUpClass (cls ):
374+ cls .pattern = (
375+ compose_lj_state_rx (cls .gc_state ) +
376+ compose_gc_stats_rx (cls .gc_state )
377+ )
378+ super (TestGCStateBase , cls ).setUpClass ()
379+
380+
381+ class TestGCStatePause (TestGCStateBase ):
382+ gc_state = GCS_PAUSE
383+ location = 'gc_mark_start'
384+
385+
386+ class TestGCStatePropagate (TestGCStateBase ):
387+ gc_state = GCS_PROPAGATE
388+ location = 'propagatemark'
389+
390+
391+ class TestGCStateAtomic (TestGCStateBase ):
392+ gc_state = GCS_ATOMIC
393+ location = 'atomic'
394+
395+
396+ class TestGCStateSweepString (TestGCStateBase ):
397+ gc_state = GCS_SWEEPSTRING
398+ location = 'gc_sweep_str_chain'
399+
400+
401+ class TestGCStateSweep (TestGCStateBase ):
402+ gc_state = GCS_SWEEP
403+ location = 'gc_sweep'
404+
405+
406+ class TestGCStateFinalize (TestGCStateBase ):
407+ gc_state = GCS_FINALIZE
408+ location = 'gc_finalize'
409+
410+
318411STACK_RX = (
319412 r'-+ Red zone:\s+\d+ slots -+\n'
320413 r'(' + RX_ADDR + r'\s+' + RX_FRAME + r' VALUE: nil\n?)*\n'
0 commit comments