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