55
66import os
77import re
8+ import json
89import socket
910import urllib .request
1011import sphinx
1718author = 'Linux Test Project'
1819release = '1.0'
1920ltp_repo = 'https://github.com/linux-test-project/ltp'
21+ ltp_repo_base_url = f"{ ltp_repo } /tree/master"
2022
2123# -- General configuration ---------------------------------------------------
2224# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
2527 'linuxdoc.rstKernelDoc' ,
2628 'sphinxcontrib.spelling' ,
2729 'sphinx.ext.autosectionlabel' ,
28- 'sphinx.ext.extlinks'
30+ 'sphinx.ext.extlinks' ,
2931]
3032
3133exclude_patterns = ["html*" , '_static*' ]
@@ -138,7 +140,6 @@ def generate_syscalls_stats(_):
138140 if error :
139141 return
140142
141- syscalls_base_url = f"{ ltp_repo } /tree/master"
142143 text = [
143144 'Syscalls\n ' ,
144145 '--------\n \n ' ,
@@ -176,7 +177,7 @@ def generate_syscalls_stats(_):
176177 path = dirpath .replace ('../' , '' )
177178 name = match .group ('name' )
178179
179- ltp_syscalls [name ] = f'{ syscalls_base_url } /{ path } '
180+ ltp_syscalls [name ] = f'{ ltp_repo_base_url } /{ path } '
180181
181182 # compare kernel syscalls with LTP tested syscalls
182183 syscalls = {}
@@ -186,7 +187,7 @@ def generate_syscalls_stats(_):
186187
187188 if kersc not in syscalls :
188189 if kersc in white_list :
189- syscalls [kersc ] = f'{ syscalls_base_url } /{ white_list [kersc ]} '
190+ syscalls [kersc ] = f'{ ltp_repo_base_url } /{ white_list [kersc ]} '
190191 continue
191192
192193 syscalls [kersc ] = None
@@ -256,10 +257,275 @@ def generate_syscalls_stats(_):
256257 stats .writelines (text )
257258
258259
260+ def _generate_tags_table (tags ):
261+ """
262+ Generate the tags table from tags hash.
263+ """
264+ supported_url_ref = {
265+ "linux-git" : "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=" ,
266+ "linux-stable-git" : "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=" ,
267+ "glibc-git" : "https://sourceware.org/git/?p=glibc.git;a=commit;h=" ,
268+ "musl-git" : "https://git.musl-libc.org/cgit/musl/commit/src/linux/clone.c?id=" ,
269+ "CVE" : "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-" ,
270+ }
271+
272+ table = [
273+ '.. list-table::' ,
274+ ' :header-rows: 1' ,
275+ '' ,
276+ ' * - Tag' ,
277+ ' - Info' ,
278+ ]
279+
280+ for tag in tags :
281+ tag_key = tag [0 ]
282+ tag_val = tag [1 ]
283+
284+ tag_url = supported_url_ref .get (tag_key , None )
285+ if tag_url :
286+ tag_val = f'`{ tag_val } <{ tag_url } { tag_val } >`_'
287+
288+ table .extend ([
289+ f' * - { tag_key } ' ,
290+ f' - { tag_val } ' ,
291+ ])
292+
293+ return table
294+
295+
296+ def _generate_options_table (options ):
297+ """
298+ Generate the options table from the options hash.
299+ """
300+ table = [
301+ '.. list-table::' ,
302+ ' :header-rows: 1' ,
303+ '' ,
304+ ' * - Option' ,
305+ ' - Description' ,
306+ ]
307+
308+ for opt in options :
309+ if not isinstance (opt , list ):
310+ table .clear ()
311+ break
312+
313+ key = opt [0 ]
314+ val = opt [2 ]
315+
316+ if key .endswith (':' ):
317+ key = key [:- 1 ] if key .endswith (':' ) else key
318+
319+ key = f'-{ key } '
320+
321+ table .extend ([
322+ f' * - { key } ' ,
323+ f' - { val } ' ,
324+ ])
325+
326+ return table
327+
328+
329+ def _generate_table_cell (key , values ):
330+ """
331+ Generate a cell which can be multiline if value is a list.
332+ """
333+ cell = []
334+
335+ if len (values ) > 1 :
336+ cell .extend ([
337+ f' * - { key } ' ,
338+ f' - | { values [0 ]} ' ,
339+ ])
340+
341+ for item in values [1 :]:
342+ cell .append (f' | { item } ' )
343+ else :
344+ cell .extend ([
345+ f' * - { key } ' ,
346+ f' - { values [0 ]} ' ,
347+ ])
348+
349+ return cell
350+
351+
352+ def _generate_setup_table (keys ):
353+ """
354+ Generate the table with test setup configuration.
355+ """
356+ exclude = [
357+ # following keys are already handled
358+ 'options' ,
359+ 'runtime' ,
360+ 'timeout' ,
361+ 'fname' ,
362+ 'doc' ,
363+ # following keys don't need to be shown
364+ 'child_needs_reinit' ,
365+ 'needs_checkpoints' ,
366+ 'forks_child' ,
367+ 'tags' ,
368+ ]
369+ my_keys = {k : v for k , v in keys .items () if k not in exclude }
370+ if len (my_keys ) == 0 :
371+ return []
372+
373+ table = [
374+ '.. list-table::' ,
375+ ' :header-rows: 1' ,
376+ '' ,
377+ ' * - Key' ,
378+ ' - Value' ,
379+ ]
380+
381+ values = []
382+
383+ for key , value in my_keys .items ():
384+ if key in exclude :
385+ continue
386+
387+ values .clear ()
388+
389+ if key == 'ulimit' :
390+ for item in value :
391+ values .append (f'{ item [0 ]} : { item [1 ]} ' )
392+ elif key == 'hugepages' :
393+ if len (value ) == 1 :
394+ values .append (f'{ value [0 ]} ' )
395+ else :
396+ values .append (f'{ value [0 ]} , { value [1 ]} ' )
397+ elif key == 'filesystems' :
398+ for v in value :
399+ for item in v :
400+ if isinstance (item , list ):
401+ continue
402+
403+ if item .startswith ('.type' ):
404+ values .append (item .replace ('.type=' , '' ))
405+ elif key == "save_restore" :
406+ for item in value :
407+ values .append (item [0 ])
408+ else :
409+ if isinstance (value , list ):
410+ values .extend (value )
411+ else :
412+ values .append (value )
413+
414+ table .extend (_generate_table_cell (key , values ))
415+
416+ return table
417+
418+
419+ def generate_test_catalog (_ ):
420+ """
421+ Generate the test catalog from ltp.json metadata file.
422+ """
423+ output = '_static/tests.rst'
424+ metadata_file = '../metadata/ltp.json'
425+ text = [
426+ '.. warning::' ,
427+ ' The following catalog has been generated using LTP metadata' ,
428+ ' which is including only tests using the new :ref:`LTP C API`.' ,
429+ ''
430+ ]
431+
432+ metadata = None
433+ with open (metadata_file , 'r' , encoding = 'utf-8' ) as data :
434+ metadata = json .load (data )
435+
436+ timeout_def = metadata ['defaults' ]['timeout' ]
437+
438+ for test_name , conf in metadata ['tests' ].items ():
439+ text .extend ([
440+ f'{ test_name } ' ,
441+ len (test_name ) * '-'
442+ ])
443+
444+ # source url location
445+ test_fname = conf .get ('fname' , None )
446+ if test_fname :
447+ text .extend ([
448+ '' ,
449+ f"`source <{ ltp_repo_base_url } /{ test_fname } >`__" ,
450+ ''
451+ ])
452+
453+ # test description
454+ desc = conf .get ('doc' , None )
455+ if desc :
456+ desc_text = []
457+ for line in desc :
458+ if line .startswith ("[Description]" ):
459+ desc_text .append ("**Description**" )
460+ elif line .startswith ("[Algorithm]" ):
461+ desc_text .append ("**Algorithm**" )
462+ else :
463+ desc_text .append (line )
464+
465+ text .extend ([
466+ '\n ' .join (desc_text ),
467+ ])
468+
469+ # timeout information
470+ timeout = conf .get ('timeout' , None )
471+ if timeout :
472+ text .extend ([
473+ '' ,
474+ f'Test timeout is { timeout } seconds.' ,
475+ ])
476+ else :
477+ text .extend ([
478+ '' ,
479+ f'Test timeout defaults is { timeout_def } seconds.' ,
480+ ])
481+
482+ # runtime information
483+ runtime = conf .get ('runtime' , None )
484+ if runtime :
485+ text .extend ([
486+ f'Maximum runtime is { runtime } seconds.' ,
487+ ''
488+ ])
489+ else :
490+ text .append ('' )
491+
492+ # options information
493+ opts = conf .get ('options' , None )
494+ if opts :
495+ text .append ('' )
496+ text .extend (_generate_options_table (opts ))
497+ text .append ('' )
498+
499+ # tags information
500+ tags = conf .get ('tags' , None )
501+ if tags :
502+ text .append ('' )
503+ text .extend (_generate_tags_table (tags ))
504+ text .append ('' )
505+
506+ # parse struct tst_test content
507+ text .append ('' )
508+ text .extend (_generate_setup_table (conf ))
509+ text .append ('' )
510+
511+ # small separator between tests
512+ text .extend ([
513+ '' ,
514+ '.. raw:: html' ,
515+ '' ,
516+ ' <hr>' ,
517+ '' ,
518+ ])
519+
520+ with open (output , 'w+' , encoding = 'utf-8' ) as new_tests :
521+ new_tests .write ('\n ' .join (text ))
522+
523+
259524def setup (app ):
260525 """
261526 Setup the current documentation, using self generated data and graphics
262527 customizations.
263528 """
264529 app .add_css_file ('custom.css' )
265530 app .connect ('builder-inited' , generate_syscalls_stats )
531+ app .connect ('builder-inited' , generate_test_catalog )
0 commit comments