ffbe098ef8
TLC (Twilight Commander) was missing from both ISO configs. Added
tlc = {} to [packages] in redbear-mini.toml and redbear-full.toml.
Created missing symlink: recipes/tui/tlc -> ../../local/recipes/tui/tlc.
207 lines
6.7 KiB
Python
207 lines
6.7 KiB
Python
"""
|
|
Tests for cleanup-kf6-noop-seds.sh
|
|
|
|
The script walks the list of NO-OP KF6 recipes and removes the
|
|
inline `sed -i` chains whose target line is absent from the
|
|
upstream 6.26.0 source. The python heredoc inside the script
|
|
consumes the `sed -i ...` line plus any continuation lines
|
|
(both `\` and `&& cd ...` forms).
|
|
|
|
These tests validate the python heredoc directly via a helper
|
|
function that mirrors the same loop.
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
SCRIPT = Path(__file__).parent.parent / "cleanup-kf6-noop-seds.sh"
|
|
|
|
|
|
def run_cleanup_python(recipe_text: str) -> str:
|
|
"""Run the same python heredoc that the bash script runs."""
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".toml", delete=False) as f:
|
|
f.write(recipe_text)
|
|
path = f.name
|
|
try:
|
|
proc = subprocess.run(
|
|
[
|
|
"python3", "-", path
|
|
],
|
|
input="""
|
|
import sys
|
|
from pathlib import Path
|
|
recipe_path = Path(sys.argv[1])
|
|
text = recipe_path.read_text()
|
|
lines = text.splitlines(keepends=True)
|
|
out = []
|
|
i = 0
|
|
while i < len(lines):
|
|
line = lines[i]
|
|
if 'sed -i' in line:
|
|
i += 1
|
|
just_consumed = line
|
|
while i < len(lines):
|
|
nxt_strip = lines[i].strip()
|
|
if just_consumed.rstrip('\\n').rstrip().endswith('\\\\'):
|
|
just_consumed = lines[i]
|
|
i += 1
|
|
continue
|
|
if nxt_strip.startswith('&&') and (' cd ' in nxt_strip or nxt_strip.startswith('&&\\\\')):
|
|
just_consumed = lines[i]
|
|
i += 1
|
|
continue
|
|
break
|
|
continue
|
|
out.append(line)
|
|
i += 1
|
|
recipe_path.write_text(''.join(out))
|
|
""",
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
)
|
|
return Path(path).read_text()
|
|
finally:
|
|
os.unlink(path)
|
|
|
|
|
|
class TestSedRemoval(unittest.TestCase):
|
|
def test_single_line_sed(self):
|
|
src = "header\nsed -i 'foo' bar\nfooter\n"
|
|
out = run_cleanup_python(src)
|
|
self.assertNotIn("sed -i", out)
|
|
self.assertIn("header", out)
|
|
self.assertIn("footer", out)
|
|
|
|
def test_multiline_sed_with_backslash(self):
|
|
src = (
|
|
"header\n"
|
|
"sed -i 's/foo/bar/' \\\n"
|
|
" file.txt\n"
|
|
"footer\n"
|
|
)
|
|
out = run_cleanup_python(src)
|
|
self.assertNotIn("sed -i", out)
|
|
self.assertNotIn("file.txt", out)
|
|
self.assertIn("header", out)
|
|
self.assertIn("footer", out)
|
|
|
|
def test_chained_seds_with_and_cd(self):
|
|
src = (
|
|
"header\n"
|
|
"sed -i 'foo' a.txt && \\\n"
|
|
" cd subdir && \\\n"
|
|
" sed -i 'bar' b.txt\n"
|
|
"footer\n"
|
|
)
|
|
out = run_cleanup_python(src)
|
|
self.assertNotIn("sed -i", out)
|
|
self.assertNotIn("a.txt", out)
|
|
self.assertNotIn("subdir", out)
|
|
self.assertNotIn("b.txt", out)
|
|
self.assertIn("header", out)
|
|
self.assertIn("footer", out)
|
|
|
|
def test_no_sed_unchanged(self):
|
|
src = "header\ncmake /src\nfooter\n"
|
|
out = run_cleanup_python(src)
|
|
self.assertEqual(src, out)
|
|
|
|
def test_real_kf6_attica_recipe(self):
|
|
# Actual recipe text from kf6-attica (lines 23-32 of original).
|
|
src = (
|
|
'redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules\n'
|
|
"\n"
|
|
'sed -i "s/^ecm_install_po_files_as_qm/#ecm_install_po_files_as_qm/" \\\n'
|
|
' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true\n'
|
|
"sed -i 's/^ki18n_install(po)/#ki18n_install(po)/' \\\n"
|
|
' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true\n'
|
|
'sed -i \'/include(ECMQmlModule)/s/^/#/\' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true\n'
|
|
'sed -i \'/add_subdirectory(autotests)/s/^/#/\' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true\n'
|
|
'sed -i \'/add_subdirectory(examples)/s/^/#/\' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true\n'
|
|
"\n"
|
|
"rm -f CMakeCache.txt\n"
|
|
)
|
|
out = run_cleanup_python(src)
|
|
self.assertNotIn("sed -i", out)
|
|
self.assertNotIn("${COOKBOOK_SOURCE}/CMakeLists.txt", out)
|
|
self.assertIn("redbear_qt_link_sysroot_dirs", out)
|
|
self.assertIn("rm -f CMakeCache.txt", out)
|
|
|
|
|
|
class TestScriptStructure(unittest.TestCase):
|
|
def test_script_exists_and_executable(self):
|
|
self.assertTrue(SCRIPT.exists(), f"{SCRIPT} does not exist")
|
|
self.assertTrue(
|
|
os.access(SCRIPT, os.X_OK),
|
|
f"{SCRIPT} is not executable (+x missing)",
|
|
)
|
|
|
|
def test_script_targets_known_noop_recipes(self):
|
|
# The script must list the 24 NO-OP recipes that have
|
|
# no ecm_install_po_files_as_qm line in upstream 6.26.0.
|
|
text = SCRIPT.read_text()
|
|
noop_recipes = [
|
|
"kf6-attica",
|
|
"kf6-kcolorscheme",
|
|
"kf6-kconfigwidgets",
|
|
"kf6-kcrash",
|
|
"kf6-kguiaddons",
|
|
"kf6-ki18n",
|
|
"kf6-kiconthemes",
|
|
"kf6-kidletime",
|
|
"kf6-kimageformats",
|
|
"kf6-kio",
|
|
"kf6-kitemmodels",
|
|
"kf6-knewstuff",
|
|
"kf6-kpackage",
|
|
"kf6-kservice",
|
|
"kf6-ksvg",
|
|
"kf6-ktexteditor",
|
|
"kf6-ktextwidgets",
|
|
"kf6-kwallet",
|
|
"kf6-kxmlgui",
|
|
"kf6-parts",
|
|
"kf6-plasma-activities",
|
|
"kf6-prison",
|
|
"kf6-pty",
|
|
"plasma-framework",
|
|
]
|
|
for recipe in noop_recipes:
|
|
self.assertIn(
|
|
f"local/recipes/kde/{recipe}",
|
|
text,
|
|
f"NO-OP recipe `{recipe}` missing from cleanup script",
|
|
)
|
|
|
|
def test_script_makes_timestamped_backup(self):
|
|
# Each cleanup must save a backup of the original recipe
|
|
# before modifying it, in case the user wants to roll
|
|
# back. The timestamp ensures multiple invocations
|
|
# don't clobber each other.
|
|
text = SCRIPT.read_text()
|
|
self.assertIn(
|
|
"recipe.bak.",
|
|
text,
|
|
"script must create a timestamped backup before modifying",
|
|
)
|
|
|
|
def test_script_handles_backslash_continuation(self):
|
|
# The `\` line continuation is the most common pattern
|
|
# in the actual recipes (a multi-line `sed -i ... \`
|
|
# followed by an indented file path). The script's
|
|
# python loop must consume both lines.
|
|
text = SCRIPT.read_text()
|
|
self.assertIn(
|
|
'endswith("\\\\")',
|
|
text,
|
|
"python loop must check for backslash continuation",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|