5
5
6
6
import os
7
7
import re
8
+ import json
8
9
import socket
9
10
import urllib .request
10
11
import sphinx
17
18
author = 'Linux Test Project'
18
19
release = '1.0'
19
20
ltp_repo = 'https://github.com/linux-test-project/ltp'
21
+ ltp_repo_base_url = f"{ ltp_repo } /tree/master"
20
22
21
23
# -- General configuration ---------------------------------------------------
22
24
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
25
27
'linuxdoc.rstKernelDoc' ,
26
28
'sphinxcontrib.spelling' ,
27
29
'sphinx.ext.autosectionlabel' ,
28
- 'sphinx.ext.extlinks'
30
+ 'sphinx.ext.extlinks' ,
29
31
]
30
32
31
33
exclude_patterns = ["html*" , '_static*' ]
@@ -138,7 +140,6 @@ def generate_syscalls_stats(_):
138
140
if error :
139
141
return
140
142
141
- syscalls_base_url = f"{ ltp_repo } /tree/master"
142
143
text = [
143
144
'Syscalls\n ' ,
144
145
'--------\n \n ' ,
@@ -176,7 +177,7 @@ def generate_syscalls_stats(_):
176
177
path = dirpath .replace ('../' , '' )
177
178
name = match .group ('name' )
178
179
179
- ltp_syscalls [name ] = f'{ syscalls_base_url } /{ path } '
180
+ ltp_syscalls [name ] = f'{ ltp_repo_base_url } /{ path } '
180
181
181
182
# compare kernel syscalls with LTP tested syscalls
182
183
syscalls = {}
@@ -186,7 +187,7 @@ def generate_syscalls_stats(_):
186
187
187
188
if kersc not in syscalls :
188
189
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 ]} '
190
191
continue
191
192
192
193
syscalls [kersc ] = None
@@ -256,10 +257,275 @@ def generate_syscalls_stats(_):
256
257
stats .writelines (text )
257
258
258
259
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
+
259
524
def setup (app ):
260
525
"""
261
526
Setup the current documentation, using self generated data and graphics
262
527
customizations.
263
528
"""
264
529
app .add_css_file ('custom.css' )
265
530
app .connect ('builder-inited' , generate_syscalls_stats )
531
+ app .connect ('builder-inited' , generate_test_catalog )
0 commit comments