1#!/usr/bin/python3 2# 3# This file and its contents are supplied under the terms of the 4# Common Development and Distribution License ("CDDL"), version 1.0. 5# You may only use this file in accordance with the terms of version 6# 1.0 of the CDDL. 7# 8# A full copy of the text of the CDDL should have accompanied this 9# source. A copy of the CDDL is also available via the Internet at 10# http://www.illumos.org/license/CDDL. 11# 12 13# 14# Copyright 2026 Gordon W. Ross 15# 16 17""" 18Unit tests for SymConfig._parse() - the symbols config file parser. 19 20Tests use canned string input via io.StringIO so no external files are needed. 21""" 22 23import contextlib 24import io 25import unittest 26 27from symbol_test import SymConfig, SymEntry 28 29# --------------------------------------------------------------------------- 30# Canned input: exercises all four directive types, continuation lines, 31# multiple headers (;-separated), multiple arg types (;-separated), 32# comments, and blank lines. 33# --------------------------------------------------------------------------- 34 35CANNED_SYM = """\ 36# 37# Canned symbol test cfg for unit testing. 38# 39 40# A type test. 41type | size_t | test.h | C11+ 42 43# A value test. 44value | M_PI | double | test.h | C99+ 45 46# A define test with no expected value. 47define | INFINITY | | test.h | C99+ 48 49# A define test with an expected value. 50define | FLT_RADIX | 2 | test.h | C99+ 51 52# A simple func test (single arg, single header). 53func | log | double | double | test.h | C99+ 54 55# A func test with continuation lines and multiple args. 56func | hypot |\ 57 double |\ 58 double; double |\ 59 test.h | C99+ 60 61# A func test with multiple headers (;-separated). 62func | acosh |\ 63 double |\ 64 double |\ 65 test.h; helper.h | C99+ 66""" 67 68# --------------------------------------------------------------------------- 69# Expected results 70# --------------------------------------------------------------------------- 71 72EXPECTED = [ 73 dict(directive='type', symbol='size_t', rtype='size_t', 74 atypes=[], headers=['test.h'], env_spec='C11+'), 75 dict(directive='value', symbol='M_PI', rtype='double', 76 atypes=[], headers=['test.h'], env_spec='C99+'), 77 dict(directive='define', symbol='INFINITY', rtype=None, 78 atypes=[], headers=['test.h'], defval=None, env_spec='C99+'), 79 dict(directive='define', symbol='FLT_RADIX', rtype=None, 80 atypes=[], headers=['test.h'], defval='2', env_spec='C99+'), 81 dict(directive='func', symbol='log', rtype='double', 82 atypes=['double'], headers=['test.h'], env_spec='C99+'), 83 dict(directive='func', symbol='hypot', rtype='double', 84 atypes=['double', 'double'], headers=['test.h'], env_spec='C99+'), 85 dict(directive='func', symbol='acosh', rtype='double', 86 atypes=['double'], headers=['test.h', 'helper.h'], env_spec='C99+'), 87] 88 89 90class TestParseSymCfg(unittest.TestCase): 91 92 def setUp(self): 93 self.cfg = SymConfig() 94 self.cfg._parse(io.StringIO(CANNED_SYM), filename='<test>') 95 96 def test_entry_count(self): 97 self.assertEqual(len(self.cfg.entries), len(EXPECTED)) 98 99 def test_primary_header(self): 100 self.assertEqual(self.cfg.primary_header, 'test.h') 101 102 def test_entries(self): 103 for i, exp in enumerate(EXPECTED): 104 with self.subTest(i=i, symbol=exp['symbol']): 105 e = self.cfg.entries[i] 106 self.assertEqual(e.directive, exp['directive']) 107 self.assertEqual(e.symbol, exp['symbol']) 108 self.assertEqual(e.env_spec, exp['env_spec']) 109 self.assertEqual(e.headers, exp['headers']) 110 self.assertEqual(e.atypes, exp['atypes']) 111 if exp['directive'] == 'define': 112 self.assertEqual(e.defval, exp.get('defval')) 113 else: 114 self.assertEqual(e.rtype, exp['rtype']) 115 116 def test_primary_header_mismatch(self): 117 text = ( 118 'type | int | first.h | ALL\n' 119 'type | int | second.h | ALL\n' 120 ) 121 cfg = SymConfig() 122 err = io.StringIO() 123 with contextlib.redirect_stderr(err): 124 cfg._parse(io.StringIO(text), filename='one.cfg') 125 self.assertIn('Only one primary header per configuration file', 126 err.getvalue()) 127 self.assertNotEqual(cfg.entries[1].headers[0], cfg.primary_header) 128 129 with self.assertRaises(SystemExit): 130 cfg._parse(io.StringIO('type | int | other.h | ALL\n'), 131 filename='two.cfg') 132 133if __name__ == '__main__': 134 unittest.main() 135