1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 5import os 6 7from kdoc.kdoc_output import ManFormat, RestFormat 8 9 10class KDocTestFile(): 11 """ 12 Handles the logic needed to store kernel‑doc output inside a YAML file. 13 Useful for unit tests and regression tests. 14 """ 15 16 def __init__(self, config, yaml_file, yaml_content): 17 # 18 # Bail out early if yaml is not available 19 # 20 try: 21 import yaml 22 except ImportError: 23 sys.exit("Warning: yaml package not available. Aborting it.") 24 25 self.config = config 26 self.test_file = os.path.expanduser(yaml_file) 27 self.yaml_content = yaml_content 28 29 self.tests = [] 30 31 out_dir = os.path.dirname(self.test_file) 32 if out_dir and not os.path.isdir(out_dir): 33 sys.exit(f"Directory {out_dir} doesn't exist.") 34 35 self.out_style = [] 36 37 if "man" in self.yaml_content: 38 out_style = ManFormat() 39 out_style.set_config(self.config) 40 41 self.out_style.append(out_style) 42 43 if "rst" in self.yaml_content: 44 out_style = RestFormat() 45 out_style.set_config(self.config) 46 47 self.out_style.append(out_style) 48 49 def set_filter(self, export, internal, symbol, nosymbol, 50 function_table, enable_lineno, no_doc_sections): 51 """ 52 Set filters at the output classes. 53 """ 54 for out_style in self.out_style: 55 out_style.set_filter(export, internal, symbol, 56 nosymbol, function_table, 57 enable_lineno, no_doc_sections) 58 59 @staticmethod 60 def get_kdoc_item(arg, start_line=1): 61 62 d = vars(arg) 63 64 declaration_start_line = d.get("declaration_start_line") 65 if not declaration_start_line: 66 return d 67 68 d["declaration_start_line"] = start_line 69 70 parameterdesc_start_lines = d.get("parameterdesc_start_lines") 71 if parameterdesc_start_lines: 72 for key in parameterdesc_start_lines: 73 ln = parameterdesc_start_lines[key] 74 ln += start_line - declaration_start_line 75 76 parameterdesc_start_lines[key] = ln 77 78 sections_start_lines = d.get("sections_start_lines") 79 if sections_start_lines: 80 for key in sections_start_lines: 81 ln = sections_start_lines[key] 82 ln += start_line - declaration_start_line 83 84 sections_start_lines[key] = ln 85 86 return d 87 88 def output_symbols(self, fname, symbols, source): 89 """ 90 Store source, symbols and output strings at self.tests. 91 """ 92 93 # 94 # KdocItem needs to be converted into dicts 95 # 96 kdoc_item = [] 97 expected = [] 98 99 if not symbols and not source: 100 return 101 102 if not source or len(symbols) != len(source): 103 print(f"Warning: lengths are different. Ignoring {fname}") 104 105 # Folding without line numbers is too hard. 106 # The right thing to do here to proceed would be to delete 107 # not-handled source blocks, as len(source) should be bigger 108 # than len(symbols) 109 return 110 111 base_name = "test_" + fname.replace(".", "_").replace("/", "_") 112 expected_dict = {} 113 start_line=1 114 115 for i in range(0, len(symbols)): 116 arg = symbols[i] 117 118 if "KdocItem" in self.yaml_content: 119 msg = self.get_kdoc_item(arg) 120 121 expected_dict["kdoc_item"] = msg 122 123 for out_style in self.out_style: 124 if isinstance(out_style, ManFormat): 125 key = "man" 126 else: 127 key = "rst" 128 129 expected_dict[key]= out_style.output_symbols(fname, [arg]) 130 131 name = f"{base_name}_{i:03d}" 132 133 test = { 134 "name": name, 135 "description": f"{fname} line {source[i]["ln"]}", 136 "fname": fname, 137 "source": source[i]["data"], 138 "expected": [expected_dict] 139 } 140 141 self.tests.append(test) 142 143 expected_dict = {} 144 145 def write(self): 146 """ 147 Output the content of self.tests to self.test_file. 148 """ 149 import yaml 150 151 data = {"tests": self.tests} 152 153 with open(self.test_file, "w", encoding="utf-8") as fp: 154 yaml.safe_dump(data, fp, sort_keys=False, default_style="|", 155 default_flow_style=False, allow_unicode=True) 156