mirror of
https://github.com/containers/podman.git
synced 2026-05-29 19:07:12 -04:00
The Quadlet documentation rewrite added a render() pass that handles
`<<if VAR>>...<<endif>>` and `<<X if cond else Y>>` conditionals before
the existing `<<a|b>>` substitution and `<<subcommand>>` replacement run.
Tokens that didn't match the new conditional grammar were silently
consumed instead of passed through, so any `<<a|b>>` whose content
didn't fit the conditional shape disappeared from the rendered output.
The user-visible regression (containers/podman#28645) was the line in
options/sysctl.md:
Note: <<if using the **--ipc=host** option|...>>, the above sysctls
are not allowed.
Both halves of the substitution start with "if", so the inner string
matched `inner.startswith("if ")`, but the rest of the conditional
recogniser fell through and the whole token was dropped. The same
codepath also dropped `<<subcommand>>` tokens, which insert_file()
expects to replace later.
Fix: when none of the conditional/inline-if branches match, append the
original `<<...>>` token verbatim so downstream replace_type and
`<<subcommand>>` replacement can handle it. Add render() tests covering
the conditional grammar plus the regression case.
Verified by rendering all 307 files under docs/source/markdown/options/
in both is_quadlet=True and is_quadlet=False contexts; no failures.
Closes #28645
Signed-off-by: Matt Van Horn <mvanhorn@gmail.com>
144 lines
5.7 KiB
Python
Executable File
144 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Tests for markdown-preprocess
|
|
"""
|
|
|
|
import unittest
|
|
|
|
# https://stackoverflow.com/questions/66665217/how-to-import-a-python-script-without-a-py-extension
|
|
from importlib.util import spec_from_loader, module_from_spec
|
|
from importlib.machinery import SourceFileLoader
|
|
|
|
spec = spec_from_loader("mp", SourceFileLoader("mp", "hack/markdown-preprocess"))
|
|
mp = module_from_spec(spec)
|
|
spec.loader.exec_module(mp)
|
|
|
|
pp = mp.Preprocessor()
|
|
|
|
class TestPodReplacer(unittest.TestCase):
|
|
def check_4_way(self, containerstring: str, podstring: str):
|
|
types = ['container', 'pod']
|
|
strings = [ containerstring, podstring ]
|
|
for i in 0, 1:
|
|
pp.pod_or_container = types[i]
|
|
for j in 0, 1:
|
|
s = '<<' + strings[j] + '|' + strings[(j+1)%2] + '>>'
|
|
self.assertEqual(pp.replace_type(s), strings[i])
|
|
|
|
def test_basic(self):
|
|
"""basic pod|container and vice-versa"""
|
|
self.check_4_way('container', 'pod')
|
|
|
|
def test_case_insensitive(self):
|
|
"""test case-insensitive replacement of Pod, Container"""
|
|
self.check_4_way('Container', 'Pod')
|
|
|
|
def test_dont_care_about_podman(self):
|
|
"""we ignore 'podman'"""
|
|
self.check_4_way('podman container', 'pod in podman')
|
|
|
|
def test_not_at_beginning(self):
|
|
"""oops - test for 'pod' other than at beginning of string"""
|
|
self.check_4_way('container', 'container or pod')
|
|
|
|
def test_blank(self):
|
|
"""test that either side of '|' can be empty"""
|
|
s_lblank = 'abc container<<| or pod>> def'
|
|
s_rblank = 'abc container<< or pod|>> def'
|
|
|
|
pp.pod_or_container = 'container'
|
|
self.assertEqual(pp.replace_type(s_lblank), 'abc container def')
|
|
self.assertEqual(pp.replace_type(s_rblank), 'abc container def')
|
|
|
|
pp.pod_or_container = 'pod'
|
|
self.assertEqual(pp.replace_type(s_lblank), 'abc container or pod def')
|
|
self.assertEqual(pp.replace_type(s_rblank), 'abc container or pod def')
|
|
|
|
def test_exception_both(self):
|
|
"""test that 'pod' on both sides raises exception"""
|
|
for word in ['pod', 'container']:
|
|
pp.pod_or_container = word
|
|
with self.assertRaisesRegex(Exception, "in both left and right sides"):
|
|
pp.replace_type('<<pod 123|pod 321>>')
|
|
|
|
def test_exception_neither(self):
|
|
"""test that 'pod' on neither side raises exception"""
|
|
for word in ['pod', 'container']:
|
|
pp.pod_or_container = word
|
|
with self.assertRaisesRegex(Exception, "in either side"):
|
|
pp.replace_type('<<container 123|container 321>>')
|
|
|
|
class TestPodmanSubcommand(unittest.TestCase):
|
|
def test_basic(self):
|
|
"""podman subcommand basic test"""
|
|
pp.infile = 'podman-foo.1.md.in'
|
|
self.assertEqual(pp.podman_subcommand(), "foo")
|
|
|
|
pp.infile = 'podman-foo-bar.1.md.in'
|
|
self.assertEqual(pp.podman_subcommand(), "foo bar")
|
|
|
|
pp.infile = 'podman-pod-rm.1.md.in'
|
|
self.assertEqual(pp.podman_subcommand(), "rm")
|
|
self.assertEqual(pp.podman_subcommand("full"), "pod rm")
|
|
|
|
class TestRender(unittest.TestCase):
|
|
def test_if_then_endif(self):
|
|
"""basic if/endif conditional"""
|
|
text = 'before <<if is_quadlet>>quadlet text<<endif>> after'
|
|
self.assertEqual(pp.render(text, {'is_quadlet': True}),
|
|
'before quadlet text after')
|
|
self.assertEqual(pp.render(text, {'is_quadlet': False}),
|
|
'before after')
|
|
|
|
def test_if_else_endif(self):
|
|
"""if/else/endif"""
|
|
text = '<<if is_quadlet>>q<<else>>c<<endif>>'
|
|
self.assertEqual(pp.render(text, {'is_quadlet': True}), 'q')
|
|
self.assertEqual(pp.render(text, {'is_quadlet': False}), 'c')
|
|
|
|
def test_if_not(self):
|
|
"""if not variable"""
|
|
text = '<<if not is_quadlet>>c<<endif>>'
|
|
self.assertEqual(pp.render(text, {'is_quadlet': True}), '')
|
|
self.assertEqual(pp.render(text, {'is_quadlet': False}), 'c')
|
|
|
|
def test_inline_if_else(self):
|
|
"""inline X if cond else Y"""
|
|
text = '<<"q" if is_quadlet else "c">>'
|
|
self.assertEqual(pp.render(text, {'is_quadlet': True}), 'q')
|
|
self.assertEqual(pp.render(text, {'is_quadlet': False}), 'c')
|
|
|
|
def test_preserves_pod_container_substitution(self):
|
|
"""`<<a|b>>` tokens are passed through render verbatim for replace_type"""
|
|
text = '<<container|pod>> writable filesystem'
|
|
self.assertEqual(pp.render(text, {'is_quadlet': False}),
|
|
'<<container|pod>> writable filesystem')
|
|
|
|
def test_preserves_subcommand_token(self):
|
|
"""`<<subcommand>>` tokens are passed through render verbatim"""
|
|
text = 'Use podman <<subcommand>> --foo'
|
|
self.assertEqual(pp.render(text, {'is_quadlet': False}),
|
|
'Use podman <<subcommand>> --foo')
|
|
|
|
def test_preserves_pod_container_starting_with_if(self):
|
|
"""`<<if ...|...>>` substitutions where one side starts with 'if' are
|
|
preserved (not silently consumed by the conditional matcher).
|
|
|
|
Regression test for containers/podman#28645: text starting with 'if'
|
|
inside a `<<a|b>>` substitution was being interpreted as the start of
|
|
an `<<if VAR>>` conditional and dropped.
|
|
"""
|
|
text = ('Note: <<if using the **--ipc=host** option|'
|
|
'if the ipc namespace is not shared within the pod>>, '
|
|
'the above sysctls are not allowed.')
|
|
self.assertEqual(pp.render(text, {'is_quadlet': False}), text)
|
|
|
|
def test_unclosed_if_raises(self):
|
|
"""unclosed if blocks still raise"""
|
|
with self.assertRaisesRegex(ValueError, "unclosed"):
|
|
pp.render('<<if is_quadlet>>q', {'is_quadlet': True})
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|