test: ProjectConf: Add unit tests
Add unit tests for the new ProjectConf module covering:
- Basic string and list value retrieval (get_str(), get_str_or_none(), get_list(), get_list_or_none()) - Quoted values with preserved spaces and comment delimiters - Inline comments outside quotes - Comma-separated lists with quoted commas - Line continuations - Multiple sections - Error cases: empty key, missing key/section, malformed sections, unfinished continuations, unclosed quotes - Error class is a subclass of ValueErrorAlso include a Makefile for running tests via `make test`.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.devSigned-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
24558f2b58
commit
2ca66e34ba
2 changed files with 260 additions and 0 deletions
7
test/unit/python/jw/pkg/lib/ProjectConf/Makefile
Normal file
7
test/unit/python/jw/pkg/lib/ProjectConf/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
TOPDIR = ../../../../../../..
|
||||||
|
|
||||||
|
include $(TOPDIR)/make/proj.mk
|
||||||
|
include $(JWBDIR)/make/py-run.mk
|
||||||
|
|
||||||
|
all:
|
||||||
|
test: run
|
||||||
253
test/unit/python/jw/pkg/lib/ProjectConf/test.py
Normal file
253
test/unit/python/jw/pkg/lib/ProjectConf/test.py
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
import tempfile
|
||||||
|
from jw.pkg.lib.ProjectConf import ProjectConf
|
||||||
|
|
||||||
|
# -- Helper: create temp file and read it --
|
||||||
|
|
||||||
|
def _load(text: str) -> ProjectConf:
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.conf', delete=False) as f:
|
||||||
|
f.write(text)
|
||||||
|
f.flush()
|
||||||
|
return ProjectConf.read(f.name)
|
||||||
|
|
||||||
|
# -- Basic construction --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = value\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'value'
|
||||||
|
|
||||||
|
# -- get_section returns raw section text --
|
||||||
|
|
||||||
|
pc = _load('[meta]\n# header comment\nkey = val\n\n')
|
||||||
|
section = pc.get_section('meta')
|
||||||
|
assert section is not None
|
||||||
|
assert '# header comment' in section
|
||||||
|
assert 'key = val' in section
|
||||||
|
|
||||||
|
# -- get_section returns None for missing section --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = value\n')
|
||||||
|
assert pc.get_section('nonexistent') is None
|
||||||
|
|
||||||
|
# -- get_str returns unquoted value --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = "hello world"\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'hello world'
|
||||||
|
|
||||||
|
# -- get_str returns raw value when not quoted --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = plain value\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'plain value'
|
||||||
|
|
||||||
|
# -- get_str raises Error when key missing --
|
||||||
|
|
||||||
|
pc = _load('[section]\nother = val\n')
|
||||||
|
try:
|
||||||
|
pc.get_str('section', 'missing')
|
||||||
|
assert False, 'Should have raised'
|
||||||
|
except ProjectConf.Error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -- get_str raises Error when section missing --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = val\n')
|
||||||
|
try:
|
||||||
|
pc.get_str('nonexistent', 'key')
|
||||||
|
assert False, 'Should have raised'
|
||||||
|
except ProjectConf.Error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -- get_str_or_none returns None for missing key --
|
||||||
|
|
||||||
|
pc = _load('[section]\nother = val\n')
|
||||||
|
assert pc.get_str_or_none('section', 'missing') is None
|
||||||
|
|
||||||
|
# -- get_str_or_none returns None for missing section --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = val\n')
|
||||||
|
assert pc.get_str_or_none('nonexistent', 'key') is None
|
||||||
|
|
||||||
|
# -- get_str returns string with trailing whitespace stripped --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = value \n')
|
||||||
|
assert pc.get_str('section', 'key') == 'value'
|
||||||
|
|
||||||
|
# -- Quoted value with spaces preserved --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = " spaced "\n')
|
||||||
|
assert pc.get_str('section', 'key') == ' spaced '
|
||||||
|
|
||||||
|
# -- Quoted hash inside quotes is preserved --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = "value # not a comment"\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'value # not a comment'
|
||||||
|
|
||||||
|
# -- Inline comment outside quotes --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = value # this is a comment\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'value'
|
||||||
|
|
||||||
|
# -- Quoted value containing an inline comment delimiter --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = "has # inside"\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'has # inside'
|
||||||
|
|
||||||
|
# -- get_list returns split values --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = a, b, c\n')
|
||||||
|
assert pc.get_list('section', 'list') == ['a', 'b', 'c']
|
||||||
|
|
||||||
|
# -- get_list returns single value --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = single\n')
|
||||||
|
assert pc.get_list('section', 'list') == ['single']
|
||||||
|
|
||||||
|
# -- get_list_or_none returns None for missing key --
|
||||||
|
|
||||||
|
pc = _load('[section]\nother = val\n')
|
||||||
|
assert pc.get_list_or_none('section', 'missing') is None
|
||||||
|
|
||||||
|
# -- get_list raises Error when missing key --
|
||||||
|
|
||||||
|
pc = _load('[section]\nother = val\n')
|
||||||
|
try:
|
||||||
|
pc.get_list('section', 'missing')
|
||||||
|
assert False, 'Should have raised'
|
||||||
|
except ProjectConf.Error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -- get_list handles quoted commas --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = "a, b", c, "d, e"\n')
|
||||||
|
assert pc.get_list('section', 'list') == ['a, b', 'c', 'd, e']
|
||||||
|
|
||||||
|
# -- get_list returns empty list for empty value --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = \n')
|
||||||
|
assert pc.get_list('section', 'list') == []
|
||||||
|
|
||||||
|
# -- get_list_or_none returns empty list for empty value --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = \n')
|
||||||
|
assert pc.get_list_or_none('section', 'list') == []
|
||||||
|
|
||||||
|
# -- Unclosed quote in list raises --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = "unclosed, b\n')
|
||||||
|
try:
|
||||||
|
pc.get_list('section', 'list')
|
||||||
|
assert False, 'Should have raised'
|
||||||
|
except ProjectConf.Error as exc:
|
||||||
|
assert 'Unclosed quote' in str(exc)
|
||||||
|
|
||||||
|
# -- Line continuation --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = first \\\n second \\\n third\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'first second third'
|
||||||
|
|
||||||
|
# -- Line continuation with inline comment at end --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = first \\\n second # comment\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'first second'
|
||||||
|
|
||||||
|
# -- get_section returns None for non-existent section --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = val\n')
|
||||||
|
assert pc.get_section('does_not_exist') is None
|
||||||
|
|
||||||
|
# -- Multiple keys in same section --
|
||||||
|
|
||||||
|
pc = _load('[section]\nfirst = one\nsecond = two\nthird = three\n')
|
||||||
|
assert pc.get_str('section', 'first') == 'one'
|
||||||
|
assert pc.get_str('section', 'second') == 'two'
|
||||||
|
assert pc.get_str('section', 'third') == 'three'
|
||||||
|
|
||||||
|
# -- get_str_or_none does not raise for missing section --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = val\n')
|
||||||
|
assert pc.get_str_or_none('nope', 'key') is None
|
||||||
|
|
||||||
|
# -- get_list_or_none does not raise for missing section --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = val\n')
|
||||||
|
assert pc.get_list_or_none('nope', 'key') is None
|
||||||
|
|
||||||
|
# -- get_list_or_none does not raise for missing key --
|
||||||
|
|
||||||
|
pc = _load('[section]\nother = val\n')
|
||||||
|
assert pc.get_list_or_none('section', 'nope') is None
|
||||||
|
|
||||||
|
# -- Empty key raises --
|
||||||
|
|
||||||
|
pc = _load('[section]\n= value\n')
|
||||||
|
try:
|
||||||
|
pc.get_str('section', '')
|
||||||
|
assert False, 'Should have raised'
|
||||||
|
except ProjectConf.Error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -- Not an assignment raises --
|
||||||
|
|
||||||
|
pc = _load('[section]\nnot an assignment\n')
|
||||||
|
try:
|
||||||
|
pc.get_str('section', 'not an assignment')
|
||||||
|
assert False, 'Should have raised'
|
||||||
|
except ProjectConf.Error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -- Unfinished continuation raises --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = value \\\n')
|
||||||
|
try:
|
||||||
|
pc.get_str('section', 'key')
|
||||||
|
assert False, 'Should have raised'
|
||||||
|
except ProjectConf.Error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -- Error class is ValueError --
|
||||||
|
|
||||||
|
pc = _load('[section]\nkey = val\n')
|
||||||
|
try:
|
||||||
|
pc.get_str('missing', 'key')
|
||||||
|
except ValueError as e:
|
||||||
|
assert isinstance(e, ValueError)
|
||||||
|
else:
|
||||||
|
assert False, 'Should have raised ValueError'
|
||||||
|
|
||||||
|
# -- Whitespace around key and value --
|
||||||
|
|
||||||
|
pc = _load('[section]\n key = value \n')
|
||||||
|
assert pc.get_str('section', 'key') == 'value'
|
||||||
|
|
||||||
|
# -- Whitespace around key in list --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = a , b , c \n')
|
||||||
|
assert pc.get_list('section', 'list') == ['a', 'b', 'c']
|
||||||
|
|
||||||
|
# -- Multiple sections --
|
||||||
|
|
||||||
|
pc = _load('[one]\nkey = val1\n[two]\nkey = val2\n')
|
||||||
|
assert pc.get_str('one', 'key') == 'val1'
|
||||||
|
assert pc.get_str('two', 'key') == 'val2'
|
||||||
|
|
||||||
|
# -- Section with only comments and blanks --
|
||||||
|
|
||||||
|
pc = _load('[empty]\n# just a comment\n\n')
|
||||||
|
section = pc.get_section('empty')
|
||||||
|
assert section is not None
|
||||||
|
assert pc.get_str_or_none('empty', 'key') is None
|
||||||
|
|
||||||
|
# -- Section with trailing comment on section header line --
|
||||||
|
|
||||||
|
pc = _load('[section] # inline comment\nkey = val\n')
|
||||||
|
assert pc.get_str('section', 'key') == 'val'
|
||||||
|
|
||||||
|
# -- Value that looks like a section header --
|
||||||
|
|
||||||
|
pc = _load('[section]\nnot_a_key = [bracket]\n')
|
||||||
|
assert pc.get_str('section', 'not_a_key') == '[bracket]'
|
||||||
|
|
||||||
|
# -- Multiple values with trailing backslash comments --
|
||||||
|
|
||||||
|
pc = _load('[section]\nlist = "a", "b", "c"\n')
|
||||||
|
assert pc.get_list('section', 'list') == ['a', 'b', 'c']
|
||||||
|
|
||||||
|
print('All ProjectConf tests passed')
|
||||||
Loading…
Add table
Add a link
Reference in a new issue