1a5dea56eSMauro Carvalho Chehab#!/usr/bin/env python3 2a5dea56eSMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 3a5dea56eSMauro Carvalho Chehab# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>. 4a5dea56eSMauro Carvalho Chehab# 5a5dea56eSMauro Carvalho Chehab# pylint: disable=C0200,C0413,W0102,R0914 6a5dea56eSMauro Carvalho Chehab 7a5dea56eSMauro Carvalho Chehab""" 8a5dea56eSMauro Carvalho ChehabUnit tests for kernel-doc parser. 9a5dea56eSMauro Carvalho Chehab""" 10a5dea56eSMauro Carvalho Chehab 11eea0d807SMauro Carvalho Chehabimport logging 12a5dea56eSMauro Carvalho Chehabimport os 13a5dea56eSMauro Carvalho Chehabimport re 14eea0d807SMauro Carvalho Chehabimport shlex 15a5dea56eSMauro Carvalho Chehabimport sys 16eea0d807SMauro Carvalho Chehabimport unittest 17a5dea56eSMauro Carvalho Chehab 18a5dea56eSMauro Carvalho Chehabfrom textwrap import dedent 19a5dea56eSMauro Carvalho Chehabfrom unittest.mock import patch, MagicMock, mock_open 20a5dea56eSMauro Carvalho Chehab 21eea0d807SMauro Carvalho Chehabimport yaml 22eea0d807SMauro Carvalho Chehab 23a5dea56eSMauro Carvalho ChehabSRC_DIR = os.path.dirname(os.path.realpath(__file__)) 24a5dea56eSMauro Carvalho Chehabsys.path.insert(0, os.path.join(SRC_DIR, "../lib/python")) 25a5dea56eSMauro Carvalho Chehab 26eea0d807SMauro Carvalho Chehabfrom kdoc.kdoc_files import KdocConfig 27a5dea56eSMauro Carvalho Chehabfrom kdoc.kdoc_item import KdocItem 28eea0d807SMauro Carvalho Chehabfrom kdoc.kdoc_parser import KernelDoc 29eea0d807SMauro Carvalho Chehabfrom kdoc.kdoc_output import RestFormat, ManFormat 30eea0d807SMauro Carvalho Chehab 31a5dea56eSMauro Carvalho Chehabfrom kdoc.xforms_lists import CTransforms 32eea0d807SMauro Carvalho Chehab 333f049a5bSMauro Carvalho Chehabfrom unittest_helper import TestUnits 34a5dea56eSMauro Carvalho Chehab 35eea0d807SMauro Carvalho Chehab 36eea0d807SMauro Carvalho Chehab# 37eea0d807SMauro Carvalho Chehab# Test file 38eea0d807SMauro Carvalho Chehab# 39eea0d807SMauro Carvalho ChehabTEST_FILE = os.path.join(SRC_DIR, "kdoc-test.yaml") 40eea0d807SMauro Carvalho Chehab 413f049a5bSMauro Carvalho Chehabenv = { 423f049a5bSMauro Carvalho Chehab "yaml_file": TEST_FILE 433f049a5bSMauro Carvalho Chehab} 443f049a5bSMauro Carvalho Chehab 45eea0d807SMauro Carvalho Chehab# 46eea0d807SMauro Carvalho Chehab# Ancillary logic to clean whitespaces 47eea0d807SMauro Carvalho Chehab# 48a5dea56eSMauro Carvalho Chehab#: Regex to help cleaning whitespaces 49eea0d807SMauro Carvalho ChehabRE_WHITESPC = re.compile(r"[ \t]++") 50eea0d807SMauro Carvalho ChehabRE_BEGINSPC = re.compile(r"^\s+", re.MULTILINE) 51eea0d807SMauro Carvalho ChehabRE_ENDSPC = re.compile(r"\s+$", re.MULTILINE) 52a5dea56eSMauro Carvalho Chehab 53a5dea56eSMauro Carvalho Chehabdef clean_whitespc(val, relax_whitespace=False): 54a5dea56eSMauro Carvalho Chehab """ 55a5dea56eSMauro Carvalho Chehab Cleanup whitespaces to avoid false positives. 56a5dea56eSMauro Carvalho Chehab 57a5dea56eSMauro Carvalho Chehab By default, strip only bein/end whitespaces, but, when relax_whitespace 58a5dea56eSMauro Carvalho Chehab is true, also replace multiple whitespaces in the middle. 59a5dea56eSMauro Carvalho Chehab """ 60a5dea56eSMauro Carvalho Chehab 61a5dea56eSMauro Carvalho Chehab if isinstance(val, str): 62a5dea56eSMauro Carvalho Chehab val = val.strip() 63a5dea56eSMauro Carvalho Chehab if relax_whitespace: 64a5dea56eSMauro Carvalho Chehab val = RE_WHITESPC.sub(" ", val) 65eea0d807SMauro Carvalho Chehab val = RE_BEGINSPC.sub("", val) 66eea0d807SMauro Carvalho Chehab val = RE_ENDSPC.sub("", val) 67a5dea56eSMauro Carvalho Chehab elif isinstance(val, list): 68a5dea56eSMauro Carvalho Chehab val = [clean_whitespc(item, relax_whitespace) for item in val] 69a5dea56eSMauro Carvalho Chehab elif isinstance(val, dict): 70a5dea56eSMauro Carvalho Chehab val = {k: clean_whitespc(v, relax_whitespace) for k, v in val.items()} 71a5dea56eSMauro Carvalho Chehab return val 72a5dea56eSMauro Carvalho Chehab 73a5dea56eSMauro Carvalho Chehab# 74eea0d807SMauro Carvalho Chehab# Helper classes to help mocking with logger and config 75a5dea56eSMauro Carvalho Chehab# 76eea0d807SMauro Carvalho Chehabclass MockLogging(logging.Handler): 77eea0d807SMauro Carvalho Chehab """ 78eea0d807SMauro Carvalho Chehab Simple class to store everything on a list 79eea0d807SMauro Carvalho Chehab """ 80eea0d807SMauro Carvalho Chehab 81eea0d807SMauro Carvalho Chehab def __init__(self, level=logging.NOTSET): 82eea0d807SMauro Carvalho Chehab super().__init__(level) 83eea0d807SMauro Carvalho Chehab self.messages = [] 84eea0d807SMauro Carvalho Chehab self.formatter = logging.Formatter() 85eea0d807SMauro Carvalho Chehab 86eea0d807SMauro Carvalho Chehab def emit(self, record: logging.LogRecord) -> None: 87eea0d807SMauro Carvalho Chehab """ 88eea0d807SMauro Carvalho Chehab Append a formatted record to self.messages. 89eea0d807SMauro Carvalho Chehab """ 90eea0d807SMauro Carvalho Chehab try: 91eea0d807SMauro Carvalho Chehab # The `format` method uses the handler's formatter. 92eea0d807SMauro Carvalho Chehab message = self.format(record) 93eea0d807SMauro Carvalho Chehab self.messages.append(message) 94eea0d807SMauro Carvalho Chehab except Exception: 95eea0d807SMauro Carvalho Chehab self.handleError(record) 96eea0d807SMauro Carvalho Chehab 97eea0d807SMauro Carvalho Chehabclass MockKdocConfig(KdocConfig): 98eea0d807SMauro Carvalho Chehab def __init__(self, *args, **kwargs): 99eea0d807SMauro Carvalho Chehab super().__init__(*args, **kwargs) 100eea0d807SMauro Carvalho Chehab 101eea0d807SMauro Carvalho Chehab self.log = logging.getLogger(__file__) 102eea0d807SMauro Carvalho Chehab self.handler = MockLogging() 103eea0d807SMauro Carvalho Chehab self.log.addHandler(self.handler) 104eea0d807SMauro Carvalho Chehab 105eea0d807SMauro Carvalho Chehab def warning(self, msg): 106eea0d807SMauro Carvalho Chehab """Ancillary routine to output a warning and increment error count.""" 107eea0d807SMauro Carvalho Chehab 108eea0d807SMauro Carvalho Chehab self.log.warning(msg) 109eea0d807SMauro Carvalho Chehab 110eea0d807SMauro Carvalho Chehab# 111eea0d807SMauro Carvalho Chehab# Helper class to generate KdocItem and validate its contents 112eea0d807SMauro Carvalho Chehab# 113eea0d807SMauro Carvalho Chehab# TODO: check self.config.handler.messages content 114eea0d807SMauro Carvalho Chehab# 115eea0d807SMauro Carvalho Chehabclass GenerateKdocItem(unittest.TestCase): 116a5dea56eSMauro Carvalho Chehab """ 117a5dea56eSMauro Carvalho Chehab Base class to run KernelDoc parser class 118a5dea56eSMauro Carvalho Chehab """ 119a5dea56eSMauro Carvalho Chehab 120a5dea56eSMauro Carvalho Chehab DEFAULT = vars(KdocItem("", "", "", 0)) 121a5dea56eSMauro Carvalho Chehab 122eea0d807SMauro Carvalho Chehab config = MockKdocConfig() 123eea0d807SMauro Carvalho Chehab xforms = CTransforms() 124eea0d807SMauro Carvalho Chehab 125a5dea56eSMauro Carvalho Chehab def setUp(self): 126a5dea56eSMauro Carvalho Chehab self.maxDiff = None 127a5dea56eSMauro Carvalho Chehab 128a5dea56eSMauro Carvalho Chehab def run_test(self, source, __expected_list, exports={}, fname="test.c", 129a5dea56eSMauro Carvalho Chehab relax_whitespace=False): 130a5dea56eSMauro Carvalho Chehab """ 131a5dea56eSMauro Carvalho Chehab Stores expected values and patch the test to use source as 132a5dea56eSMauro Carvalho Chehab a "file" input. 133a5dea56eSMauro Carvalho Chehab """ 134a5dea56eSMauro Carvalho Chehab debug_level = int(os.getenv("VERBOSE", "0")) 135a5dea56eSMauro Carvalho Chehab source = dedent(source) 136a5dea56eSMauro Carvalho Chehab 137a5dea56eSMauro Carvalho Chehab # Ensure that default values will be there 138a5dea56eSMauro Carvalho Chehab expected_list = [] 139a5dea56eSMauro Carvalho Chehab for e in __expected_list: 140eea0d807SMauro Carvalho Chehab if not isinstance(e, dict): 141eea0d807SMauro Carvalho Chehab e = vars(e) 142eea0d807SMauro Carvalho Chehab 143a5dea56eSMauro Carvalho Chehab new_e = self.DEFAULT.copy() 144a5dea56eSMauro Carvalho Chehab new_e["fname"] = fname 145a5dea56eSMauro Carvalho Chehab for key, value in e.items(): 146a5dea56eSMauro Carvalho Chehab new_e[key] = value 147a5dea56eSMauro Carvalho Chehab 148a5dea56eSMauro Carvalho Chehab expected_list.append(new_e) 149a5dea56eSMauro Carvalho Chehab 150a5dea56eSMauro Carvalho Chehab patcher = patch('builtins.open', 151a5dea56eSMauro Carvalho Chehab new_callable=mock_open, read_data=source) 152a5dea56eSMauro Carvalho Chehab 153a5dea56eSMauro Carvalho Chehab kernel_doc = KernelDoc(self.config, fname, self.xforms) 154a5dea56eSMauro Carvalho Chehab 155a5dea56eSMauro Carvalho Chehab with patcher: 156a5dea56eSMauro Carvalho Chehab export_table, entries = kernel_doc.parse_kdoc() 157a5dea56eSMauro Carvalho Chehab 158a5dea56eSMauro Carvalho Chehab self.assertEqual(export_table, exports) 159a5dea56eSMauro Carvalho Chehab self.assertEqual(len(entries), len(expected_list)) 160a5dea56eSMauro Carvalho Chehab 161a5dea56eSMauro Carvalho Chehab for i in range(0, len(entries)): 162a5dea56eSMauro Carvalho Chehab 163a5dea56eSMauro Carvalho Chehab entry = entries[i] 164a5dea56eSMauro Carvalho Chehab expected = expected_list[i] 165a5dea56eSMauro Carvalho Chehab self.assertNotEqual(expected, None) 166a5dea56eSMauro Carvalho Chehab self.assertNotEqual(expected, {}) 167a5dea56eSMauro Carvalho Chehab self.assertIsInstance(entry, KdocItem) 168a5dea56eSMauro Carvalho Chehab 169a5dea56eSMauro Carvalho Chehab d = vars(entry) 170*99ec67a9SMauro Carvalho Chehab 171*99ec67a9SMauro Carvalho Chehab other_stuff = d.get("other_stuff", {}) 172*99ec67a9SMauro Carvalho Chehab if "source" in other_stuff: 173*99ec67a9SMauro Carvalho Chehab del other_stuff["source"] 174*99ec67a9SMauro Carvalho Chehab 175a5dea56eSMauro Carvalho Chehab for key, value in expected.items(): 176*99ec67a9SMauro Carvalho Chehab if key == "other_stuff": 177*99ec67a9SMauro Carvalho Chehab if "source" in value: 178*99ec67a9SMauro Carvalho Chehab del value["source"] 179*99ec67a9SMauro Carvalho Chehab 180a5dea56eSMauro Carvalho Chehab result = clean_whitespc(d[key], relax_whitespace) 181a5dea56eSMauro Carvalho Chehab value = clean_whitespc(value, relax_whitespace) 182a5dea56eSMauro Carvalho Chehab 183a5dea56eSMauro Carvalho Chehab if debug_level > 1: 184a5dea56eSMauro Carvalho Chehab sys.stderr.write(f"{key}: assert('{result}' == '{value}')\n") 185a5dea56eSMauro Carvalho Chehab 186a5dea56eSMauro Carvalho Chehab self.assertEqual(result, value, msg=f"at {key}") 187a5dea56eSMauro Carvalho Chehab 188eea0d807SMauro Carvalho Chehab# 189eea0d807SMauro Carvalho Chehab# Ancillary function that replicates kdoc_files way to generate output 190eea0d807SMauro Carvalho Chehab# 191eea0d807SMauro Carvalho Chehabdef cleanup_timestamp(text): 192eea0d807SMauro Carvalho Chehab lines = text.split("\n") 193eea0d807SMauro Carvalho Chehab 194eea0d807SMauro Carvalho Chehab for i, line in enumerate(lines): 195eea0d807SMauro Carvalho Chehab if not line.startswith('.TH'): 196eea0d807SMauro Carvalho Chehab continue 197eea0d807SMauro Carvalho Chehab 198eea0d807SMauro Carvalho Chehab parts = shlex.split(line) 199eea0d807SMauro Carvalho Chehab if len(parts) > 3: 200eea0d807SMauro Carvalho Chehab parts[3] = "" 201eea0d807SMauro Carvalho Chehab 202eea0d807SMauro Carvalho Chehab lines[i] = " ".join(parts) 203eea0d807SMauro Carvalho Chehab 204eea0d807SMauro Carvalho Chehab 205eea0d807SMauro Carvalho Chehab return "\n".join(lines) 206eea0d807SMauro Carvalho Chehab 207eea0d807SMauro Carvalho Chehabdef gen_output(fname, out_style, symbols, expected, 208eea0d807SMauro Carvalho Chehab config=None, relax_whitespace=False): 209eea0d807SMauro Carvalho Chehab """ 210eea0d807SMauro Carvalho Chehab Use the output class to return an output content from KdocItem symbols. 211eea0d807SMauro Carvalho Chehab """ 212eea0d807SMauro Carvalho Chehab 213eea0d807SMauro Carvalho Chehab if not config: 214eea0d807SMauro Carvalho Chehab config = MockKdocConfig() 215eea0d807SMauro Carvalho Chehab 216eea0d807SMauro Carvalho Chehab out_style.set_config(config) 217eea0d807SMauro Carvalho Chehab 218eea0d807SMauro Carvalho Chehab msg = out_style.output_symbols(fname, symbols) 219eea0d807SMauro Carvalho Chehab 220eea0d807SMauro Carvalho Chehab result = clean_whitespc(msg, relax_whitespace) 221eea0d807SMauro Carvalho Chehab result = cleanup_timestamp(result) 222eea0d807SMauro Carvalho Chehab 223eea0d807SMauro Carvalho Chehab expected = clean_whitespc(expected, relax_whitespace) 224eea0d807SMauro Carvalho Chehab expected = cleanup_timestamp(expected) 225eea0d807SMauro Carvalho Chehab 226eea0d807SMauro Carvalho Chehab return result, expected 227a5dea56eSMauro Carvalho Chehab 228a5dea56eSMauro Carvalho Chehab# 229eea0d807SMauro Carvalho Chehab# Classes to be used by dynamic test generation from YAML 230a5dea56eSMauro Carvalho Chehab# 231eea0d807SMauro Carvalho Chehabclass CToKdocItem(GenerateKdocItem): 232eea0d807SMauro Carvalho Chehab def setUp(self): 233eea0d807SMauro Carvalho Chehab self.maxDiff = None 234eea0d807SMauro Carvalho Chehab 235eea0d807SMauro Carvalho Chehab def run_parser_test(self, source, symbols, exports, fname): 236eea0d807SMauro Carvalho Chehab if isinstance(symbols, dict): 237eea0d807SMauro Carvalho Chehab symbols = [symbols] 238eea0d807SMauro Carvalho Chehab 239eea0d807SMauro Carvalho Chehab if isinstance(exports, str): 240eea0d807SMauro Carvalho Chehab exports=set([exports]) 241eea0d807SMauro Carvalho Chehab elif isinstance(exports, list): 242eea0d807SMauro Carvalho Chehab exports=set(exports) 243eea0d807SMauro Carvalho Chehab 244eea0d807SMauro Carvalho Chehab self.run_test(source, symbols, exports=exports, 245eea0d807SMauro Carvalho Chehab fname=fname, relax_whitespace=True) 246eea0d807SMauro Carvalho Chehab 247eea0d807SMauro Carvalho Chehabclass KdocItemToMan(unittest.TestCase): 248eea0d807SMauro Carvalho Chehab out_style = ManFormat() 249eea0d807SMauro Carvalho Chehab 250eea0d807SMauro Carvalho Chehab def setUp(self): 251eea0d807SMauro Carvalho Chehab self.maxDiff = None 252eea0d807SMauro Carvalho Chehab 253eea0d807SMauro Carvalho Chehab def run_out_test(self, fname, symbols, expected): 254a5dea56eSMauro Carvalho Chehab """ 255eea0d807SMauro Carvalho Chehab Generate output using out_style, 256eea0d807SMauro Carvalho Chehab """ 257eea0d807SMauro Carvalho Chehab result, expected = gen_output(fname, self.out_style, 258eea0d807SMauro Carvalho Chehab symbols, expected) 259eea0d807SMauro Carvalho Chehab 260eea0d807SMauro Carvalho Chehab self.assertEqual(result, expected) 261eea0d807SMauro Carvalho Chehab 262eea0d807SMauro Carvalho Chehabclass KdocItemToRest(unittest.TestCase): 263eea0d807SMauro Carvalho Chehab out_style = RestFormat() 264eea0d807SMauro Carvalho Chehab 265eea0d807SMauro Carvalho Chehab def setUp(self): 266eea0d807SMauro Carvalho Chehab self.maxDiff = None 267eea0d807SMauro Carvalho Chehab 268eea0d807SMauro Carvalho Chehab def run_out_test(self, fname, symbols, expected): 269eea0d807SMauro Carvalho Chehab """ 270eea0d807SMauro Carvalho Chehab Generate output using out_style, 271eea0d807SMauro Carvalho Chehab """ 272eea0d807SMauro Carvalho Chehab result, expected = gen_output(fname, self.out_style, symbols, 273eea0d807SMauro Carvalho Chehab expected, relax_whitespace=True) 274eea0d807SMauro Carvalho Chehab 275eea0d807SMauro Carvalho Chehab self.assertEqual(result, expected) 276eea0d807SMauro Carvalho Chehab 277eea0d807SMauro Carvalho Chehab 278eea0d807SMauro Carvalho Chehabclass CToMan(unittest.TestCase): 279eea0d807SMauro Carvalho Chehab out_style = ManFormat() 280eea0d807SMauro Carvalho Chehab config = MockKdocConfig() 281eea0d807SMauro Carvalho Chehab xforms = CTransforms() 282eea0d807SMauro Carvalho Chehab 283eea0d807SMauro Carvalho Chehab def setUp(self): 284eea0d807SMauro Carvalho Chehab self.maxDiff = None 285eea0d807SMauro Carvalho Chehab 286eea0d807SMauro Carvalho Chehab def run_out_test(self, fname, source, expected): 287eea0d807SMauro Carvalho Chehab """ 288eea0d807SMauro Carvalho Chehab Generate output using out_style, 289eea0d807SMauro Carvalho Chehab """ 290eea0d807SMauro Carvalho Chehab patcher = patch('builtins.open', 291eea0d807SMauro Carvalho Chehab new_callable=mock_open, read_data=source) 292eea0d807SMauro Carvalho Chehab 293eea0d807SMauro Carvalho Chehab kernel_doc = KernelDoc(self.config, fname, self.xforms) 294eea0d807SMauro Carvalho Chehab 295eea0d807SMauro Carvalho Chehab with patcher: 296eea0d807SMauro Carvalho Chehab export_table, entries = kernel_doc.parse_kdoc() 297eea0d807SMauro Carvalho Chehab 298eea0d807SMauro Carvalho Chehab result, expected = gen_output(fname, self.out_style, 299eea0d807SMauro Carvalho Chehab entries, expected, config=self.config) 300eea0d807SMauro Carvalho Chehab 301eea0d807SMauro Carvalho Chehab self.assertEqual(result, expected) 302eea0d807SMauro Carvalho Chehab 303eea0d807SMauro Carvalho Chehab 304eea0d807SMauro Carvalho Chehabclass CToRest(unittest.TestCase): 305eea0d807SMauro Carvalho Chehab out_style = RestFormat() 306eea0d807SMauro Carvalho Chehab config = MockKdocConfig() 307eea0d807SMauro Carvalho Chehab xforms = CTransforms() 308eea0d807SMauro Carvalho Chehab 309eea0d807SMauro Carvalho Chehab def setUp(self): 310eea0d807SMauro Carvalho Chehab self.maxDiff = None 311eea0d807SMauro Carvalho Chehab 312eea0d807SMauro Carvalho Chehab def run_out_test(self, fname, source, expected): 313eea0d807SMauro Carvalho Chehab """ 314eea0d807SMauro Carvalho Chehab Generate output using out_style, 315eea0d807SMauro Carvalho Chehab """ 316eea0d807SMauro Carvalho Chehab patcher = patch('builtins.open', 317eea0d807SMauro Carvalho Chehab new_callable=mock_open, read_data=source) 318eea0d807SMauro Carvalho Chehab 319eea0d807SMauro Carvalho Chehab kernel_doc = KernelDoc(self.config, fname, self.xforms) 320eea0d807SMauro Carvalho Chehab 321eea0d807SMauro Carvalho Chehab with patcher: 322eea0d807SMauro Carvalho Chehab export_table, entries = kernel_doc.parse_kdoc() 323eea0d807SMauro Carvalho Chehab 324eea0d807SMauro Carvalho Chehab result, expected = gen_output(fname, self.out_style, entries, 325eea0d807SMauro Carvalho Chehab expected, relax_whitespace=True, 326eea0d807SMauro Carvalho Chehab config=self.config) 327eea0d807SMauro Carvalho Chehab 328eea0d807SMauro Carvalho Chehab self.assertEqual(result, expected) 329eea0d807SMauro Carvalho Chehab 330eea0d807SMauro Carvalho Chehab 331eea0d807SMauro Carvalho Chehab# 332eea0d807SMauro Carvalho Chehab# Selftest class 333eea0d807SMauro Carvalho Chehab# 334eea0d807SMauro Carvalho Chehabclass TestSelfValidate(GenerateKdocItem): 335eea0d807SMauro Carvalho Chehab """ 336eea0d807SMauro Carvalho Chehab Tests to check if logic inside GenerateKdocItem.run_test() is working. 337a5dea56eSMauro Carvalho Chehab """ 338a5dea56eSMauro Carvalho Chehab 339a5dea56eSMauro Carvalho Chehab SOURCE = """ 340a5dea56eSMauro Carvalho Chehab /** 341a5dea56eSMauro Carvalho Chehab * function3: Exported function 342a5dea56eSMauro Carvalho Chehab * @arg1: @arg1 does nothing 343a5dea56eSMauro Carvalho Chehab * 344a5dea56eSMauro Carvalho Chehab * Does nothing 345a5dea56eSMauro Carvalho Chehab * 346a5dea56eSMauro Carvalho Chehab * return: 347a5dea56eSMauro Carvalho Chehab * always return 0. 348a5dea56eSMauro Carvalho Chehab */ 349a5dea56eSMauro Carvalho Chehab int function3(char *arg1) { return 0; }; 350a5dea56eSMauro Carvalho Chehab EXPORT_SYMBOL(function3); 351a5dea56eSMauro Carvalho Chehab """ 352a5dea56eSMauro Carvalho Chehab 353a5dea56eSMauro Carvalho Chehab EXPECTED = [{ 354a5dea56eSMauro Carvalho Chehab 'name': 'function3', 355a5dea56eSMauro Carvalho Chehab 'type': 'function', 356a5dea56eSMauro Carvalho Chehab 'declaration_start_line': 2, 357a5dea56eSMauro Carvalho Chehab 358a5dea56eSMauro Carvalho Chehab 'sections_start_lines': { 359a5dea56eSMauro Carvalho Chehab 'Description': 4, 360a5dea56eSMauro Carvalho Chehab 'Return': 7, 361a5dea56eSMauro Carvalho Chehab }, 362a5dea56eSMauro Carvalho Chehab 'sections': { 363a5dea56eSMauro Carvalho Chehab 'Description': 'Does nothing\n\n', 364a5dea56eSMauro Carvalho Chehab 'Return': '\nalways return 0.\n' 365a5dea56eSMauro Carvalho Chehab }, 366eea0d807SMauro Carvalho Chehab 367eea0d807SMauro Carvalho Chehab 'sections_start_lines': { 368eea0d807SMauro Carvalho Chehab 'Description': 4, 369eea0d807SMauro Carvalho Chehab 'Return': 7, 370eea0d807SMauro Carvalho Chehab }, 371eea0d807SMauro Carvalho Chehab 372eea0d807SMauro Carvalho Chehab 'parameterdescs': {'arg1': '@arg1 does nothing\n'}, 373eea0d807SMauro Carvalho Chehab 'parameterlist': ['arg1'], 374eea0d807SMauro Carvalho Chehab 'parameterdesc_start_lines': {'arg1': 3}, 375eea0d807SMauro Carvalho Chehab 'parametertypes': {'arg1': 'char *arg1'}, 376eea0d807SMauro Carvalho Chehab 377a5dea56eSMauro Carvalho Chehab 'other_stuff': { 378a5dea56eSMauro Carvalho Chehab 'func_macro': False, 379a5dea56eSMauro Carvalho Chehab 'functiontype': 'int', 380a5dea56eSMauro Carvalho Chehab 'purpose': 'Exported function', 381a5dea56eSMauro Carvalho Chehab 'typedef': False 382a5dea56eSMauro Carvalho Chehab }, 383a5dea56eSMauro Carvalho Chehab }] 384a5dea56eSMauro Carvalho Chehab 385a5dea56eSMauro Carvalho Chehab EXPORTS = {"function3"} 386a5dea56eSMauro Carvalho Chehab 387a5dea56eSMauro Carvalho Chehab def test_parse_pass(self): 388a5dea56eSMauro Carvalho Chehab """ 389a5dea56eSMauro Carvalho Chehab Test if export_symbol is properly handled. 390a5dea56eSMauro Carvalho Chehab """ 391a5dea56eSMauro Carvalho Chehab self.run_test(self.SOURCE, self.EXPECTED, self.EXPORTS) 392a5dea56eSMauro Carvalho Chehab 393a5dea56eSMauro Carvalho Chehab @unittest.expectedFailure 394a5dea56eSMauro Carvalho Chehab def test_no_exports(self): 395a5dea56eSMauro Carvalho Chehab """ 396a5dea56eSMauro Carvalho Chehab Test if export_symbol is properly handled. 397a5dea56eSMauro Carvalho Chehab """ 398a5dea56eSMauro Carvalho Chehab self.run_test(self.SOURCE, [], {}) 399a5dea56eSMauro Carvalho Chehab 400a5dea56eSMauro Carvalho Chehab @unittest.expectedFailure 401a5dea56eSMauro Carvalho Chehab def test_with_empty_expected(self): 402a5dea56eSMauro Carvalho Chehab """ 403a5dea56eSMauro Carvalho Chehab Test if export_symbol is properly handled. 404a5dea56eSMauro Carvalho Chehab """ 405a5dea56eSMauro Carvalho Chehab self.run_test(self.SOURCE, [], self.EXPORTS) 406a5dea56eSMauro Carvalho Chehab 407a5dea56eSMauro Carvalho Chehab @unittest.expectedFailure 408a5dea56eSMauro Carvalho Chehab def test_with_unfilled_expected(self): 409a5dea56eSMauro Carvalho Chehab """ 410a5dea56eSMauro Carvalho Chehab Test if export_symbol is properly handled. 411a5dea56eSMauro Carvalho Chehab """ 412a5dea56eSMauro Carvalho Chehab self.run_test(self.SOURCE, [{}], self.EXPORTS) 413a5dea56eSMauro Carvalho Chehab 414a5dea56eSMauro Carvalho Chehab @unittest.expectedFailure 415a5dea56eSMauro Carvalho Chehab def test_with_default_expected(self): 416a5dea56eSMauro Carvalho Chehab """ 417a5dea56eSMauro Carvalho Chehab Test if export_symbol is properly handled. 418a5dea56eSMauro Carvalho Chehab """ 419a5dea56eSMauro Carvalho Chehab self.run_test(self.SOURCE, [self.DEFAULT.copy()], self.EXPORTS) 420a5dea56eSMauro Carvalho Chehab 421a5dea56eSMauro Carvalho Chehab# 422eea0d807SMauro Carvalho Chehab# Class and logic to create dynamic tests from YAML 423eea0d807SMauro Carvalho Chehab# 424eea0d807SMauro Carvalho Chehab 425eea0d807SMauro Carvalho Chehabclass KernelDocDynamicTests(): 426eea0d807SMauro Carvalho Chehab """ 427eea0d807SMauro Carvalho Chehab Dynamically create a set of tests from a YAML file. 428eea0d807SMauro Carvalho Chehab """ 429eea0d807SMauro Carvalho Chehab 430eea0d807SMauro Carvalho Chehab @classmethod 431eea0d807SMauro Carvalho Chehab def create_parser_test(cls, name, fname, source, symbols, exports): 432eea0d807SMauro Carvalho Chehab """ 433eea0d807SMauro Carvalho Chehab Return a function that will be attached to the test class. 434eea0d807SMauro Carvalho Chehab """ 435eea0d807SMauro Carvalho Chehab def test_method(self): 436eea0d807SMauro Carvalho Chehab """Lambda-like function to run tests with provided vars""" 437eea0d807SMauro Carvalho Chehab self.run_parser_test(source, symbols, exports, fname) 438eea0d807SMauro Carvalho Chehab 439eea0d807SMauro Carvalho Chehab test_method.__name__ = f"test_gen_{name}" 440eea0d807SMauro Carvalho Chehab 441eea0d807SMauro Carvalho Chehab setattr(CToKdocItem, test_method.__name__, test_method) 442eea0d807SMauro Carvalho Chehab 443eea0d807SMauro Carvalho Chehab @classmethod 444eea0d807SMauro Carvalho Chehab def create_out_test(cls, name, fname, symbols, out_type, data): 445eea0d807SMauro Carvalho Chehab """ 446eea0d807SMauro Carvalho Chehab Return a function that will be attached to the test class. 447eea0d807SMauro Carvalho Chehab """ 448eea0d807SMauro Carvalho Chehab def test_method(self): 449eea0d807SMauro Carvalho Chehab """Lambda-like function to run tests with provided vars""" 450eea0d807SMauro Carvalho Chehab self.run_out_test(fname, symbols, data) 451eea0d807SMauro Carvalho Chehab 452eea0d807SMauro Carvalho Chehab test_method.__name__ = f"test_{out_type}_{name}" 453eea0d807SMauro Carvalho Chehab 454eea0d807SMauro Carvalho Chehab if out_type == "man": 455eea0d807SMauro Carvalho Chehab setattr(KdocItemToMan, test_method.__name__, test_method) 456eea0d807SMauro Carvalho Chehab else: 457eea0d807SMauro Carvalho Chehab setattr(KdocItemToRest, test_method.__name__, test_method) 458eea0d807SMauro Carvalho Chehab 459eea0d807SMauro Carvalho Chehab @classmethod 460eea0d807SMauro Carvalho Chehab def create_src2out_test(cls, name, fname, source, out_type, data): 461eea0d807SMauro Carvalho Chehab """ 462eea0d807SMauro Carvalho Chehab Return a function that will be attached to the test class. 463eea0d807SMauro Carvalho Chehab """ 464eea0d807SMauro Carvalho Chehab def test_method(self): 465eea0d807SMauro Carvalho Chehab """Lambda-like function to run tests with provided vars""" 466eea0d807SMauro Carvalho Chehab self.run_out_test(fname, source, data) 467eea0d807SMauro Carvalho Chehab 468eea0d807SMauro Carvalho Chehab test_method.__name__ = f"test_{out_type}_{name}" 469eea0d807SMauro Carvalho Chehab 470eea0d807SMauro Carvalho Chehab if out_type == "man": 471eea0d807SMauro Carvalho Chehab setattr(CToMan, test_method.__name__, test_method) 472eea0d807SMauro Carvalho Chehab else: 473eea0d807SMauro Carvalho Chehab setattr(CToRest, test_method.__name__, test_method) 474eea0d807SMauro Carvalho Chehab 475eea0d807SMauro Carvalho Chehab @classmethod 476eea0d807SMauro Carvalho Chehab def create_tests(cls): 477eea0d807SMauro Carvalho Chehab """ 478eea0d807SMauro Carvalho Chehab Iterate over all scenarios and add a method to the class for each. 479eea0d807SMauro Carvalho Chehab 480eea0d807SMauro Carvalho Chehab The logic in this function assumes a valid test that are compliant 481eea0d807SMauro Carvalho Chehab with kdoc-test-schema.yaml. There is an unit test to check that. 482eea0d807SMauro Carvalho Chehab As such, it picks mandatory values directly, and uses get() for the 483eea0d807SMauro Carvalho Chehab optional ones. 484eea0d807SMauro Carvalho Chehab """ 485eea0d807SMauro Carvalho Chehab 4863f049a5bSMauro Carvalho Chehab test_file = os.environ.get("yaml_file", TEST_FILE) 4873f049a5bSMauro Carvalho Chehab 4883f049a5bSMauro Carvalho Chehab with open(test_file, encoding="utf-8") as fp: 489eea0d807SMauro Carvalho Chehab testset = yaml.safe_load(fp) 490eea0d807SMauro Carvalho Chehab 491eea0d807SMauro Carvalho Chehab tests = testset["tests"] 492eea0d807SMauro Carvalho Chehab 493eea0d807SMauro Carvalho Chehab for idx, test in enumerate(tests): 494eea0d807SMauro Carvalho Chehab name = test["name"] 495eea0d807SMauro Carvalho Chehab fname = test["fname"] 496eea0d807SMauro Carvalho Chehab source = test["source"] 497eea0d807SMauro Carvalho Chehab expected_list = test["expected"] 498eea0d807SMauro Carvalho Chehab 499eea0d807SMauro Carvalho Chehab exports = test.get("exports", []) 500eea0d807SMauro Carvalho Chehab 501eea0d807SMauro Carvalho Chehab # 502eea0d807SMauro Carvalho Chehab # The logic below allows setting up to 5 types of test: 503eea0d807SMauro Carvalho Chehab # 1. from source to kdoc_item: test KernelDoc class; 504eea0d807SMauro Carvalho Chehab # 2. from kdoc_item to man: test ManOutput class; 505eea0d807SMauro Carvalho Chehab # 3. from kdoc_item to rst: test RestOutput class; 506eea0d807SMauro Carvalho Chehab # 4. from source to man without checking expected KdocItem; 507eea0d807SMauro Carvalho Chehab # 5. from source to rst without checking expected KdocItem. 508eea0d807SMauro Carvalho Chehab # 509eea0d807SMauro Carvalho Chehab for expected in expected_list: 510eea0d807SMauro Carvalho Chehab kdoc_item = expected.get("kdoc_item") 511eea0d807SMauro Carvalho Chehab man = expected.get("man", []) 512eea0d807SMauro Carvalho Chehab rst = expected.get("rst", []) 513eea0d807SMauro Carvalho Chehab 514eea0d807SMauro Carvalho Chehab if kdoc_item: 515eea0d807SMauro Carvalho Chehab if isinstance(kdoc_item, dict): 516eea0d807SMauro Carvalho Chehab kdoc_item = [kdoc_item] 517eea0d807SMauro Carvalho Chehab 518eea0d807SMauro Carvalho Chehab symbols = [] 519eea0d807SMauro Carvalho Chehab 520eea0d807SMauro Carvalho Chehab for arg in kdoc_item: 521eea0d807SMauro Carvalho Chehab arg["fname"] = fname 522eea0d807SMauro Carvalho Chehab arg["start_line"] = 1 523eea0d807SMauro Carvalho Chehab 524eea0d807SMauro Carvalho Chehab symbols.append(KdocItem.from_dict(arg)) 525eea0d807SMauro Carvalho Chehab 526eea0d807SMauro Carvalho Chehab if source: 527eea0d807SMauro Carvalho Chehab cls.create_parser_test(name, fname, source, 528eea0d807SMauro Carvalho Chehab symbols, exports) 529eea0d807SMauro Carvalho Chehab 530eea0d807SMauro Carvalho Chehab if man: 531eea0d807SMauro Carvalho Chehab cls.create_out_test(name, fname, symbols, "man", man) 532eea0d807SMauro Carvalho Chehab 533eea0d807SMauro Carvalho Chehab if rst: 534eea0d807SMauro Carvalho Chehab cls.create_out_test(name, fname, symbols, "rst", rst) 535eea0d807SMauro Carvalho Chehab 536eea0d807SMauro Carvalho Chehab elif source: 537eea0d807SMauro Carvalho Chehab if man: 538eea0d807SMauro Carvalho Chehab cls.create_src2out_test(name, fname, source, "man", man) 539eea0d807SMauro Carvalho Chehab 540eea0d807SMauro Carvalho Chehab if rst: 541eea0d807SMauro Carvalho Chehab cls.create_src2out_test(name, fname, source, "rst", rst) 542eea0d807SMauro Carvalho Chehab 543eea0d807SMauro Carvalho ChehabKernelDocDynamicTests.create_tests() 544eea0d807SMauro Carvalho Chehab 545eea0d807SMauro Carvalho Chehab# 546a5dea56eSMauro Carvalho Chehab# Run all tests 547a5dea56eSMauro Carvalho Chehab# 548a5dea56eSMauro Carvalho Chehabif __name__ == "__main__": 5493f049a5bSMauro Carvalho Chehab runner = TestUnits() 5503f049a5bSMauro Carvalho Chehab parser = runner.parse_args() 5513f049a5bSMauro Carvalho Chehab parser.add_argument("-y", "--yaml-file", "--yaml", 5523f049a5bSMauro Carvalho Chehab help='Name of the yaml file to load') 5533f049a5bSMauro Carvalho Chehab 5543f049a5bSMauro Carvalho Chehab args = parser.parse_args() 5553f049a5bSMauro Carvalho Chehab 5563f049a5bSMauro Carvalho Chehab if args.yaml_file: 5573f049a5bSMauro Carvalho Chehab env["yaml_file"] = os.path.expanduser(args.yaml_file) 5583f049a5bSMauro Carvalho Chehab 5593f049a5bSMauro Carvalho Chehab # Run tests with customized arguments 5603f049a5bSMauro Carvalho Chehab runner.run(__file__, parser=parser, args=args, env=env) 561