summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-06-04 22:03:10 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-06-12 02:58:55 +0000
commitdfb2c9a331e841faf3cae040439519ecf85a84a1 (patch)
tree3b84c044c66dfceea7dd91b86b7773ca0669a3df
parent2402ac762883774e3ba207d9cbb8018a46314360 (diff)
downloadmeta-virtualization-dfb2c9a331e841faf3cae040439519ecf85a84a1.tar.gz
tests: parametrize boot tests over container profiles for full coverage
The boot tests (TestBundledContainersBoot, TestCustomServiceFileBoot) previously depended on whatever CONTAINER_PROFILE was in local.conf, skipping docker checks when podman was configured and vice versa. Getting complete coverage required manually editing local.conf and running the tests twice. Add --container-profiles pytest option (default: docker,podman) and a profiled_session fixture that builds container-image-host with each profile via a temporary bitbake -R conf file, boots it, and runs all checks. Both docker and podman get a full build-boot-verify cycle in a single test run with no local.conf changes. Also fix: - run_bitbake() gains extra_vars parameter for bitbake -R overrides - Strip OSC 3008 shell integration escape sequences from run_command output (BusyBox shell emits these, corrupting file paths used in subsequent commands) - Use known file paths for service file content checks instead of extracting paths from ls output (avoids escape sequence corruption) - Add test_bundle_class_unpack_enabled to catch do_unpack[noexec] regression Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--tests/conftest.py10
-rw-r--r--tests/test_container_cross_install.py426
2 files changed, 227 insertions, 209 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
index 2028ef32..6258611b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -259,6 +259,13 @@ def pytest_addoption(parser):
259 default=False, 259 default=False,
260 help="Run secure registry tests (requires openssl, htpasswd)", 260 help="Run secure registry tests (requires openssl, htpasswd)",
261 ) 261 )
262 # Container cross-install coverage options
263 parser.addoption(
264 "--container-profiles",
265 action="store",
266 default="docker,podman",
267 help="Comma-separated container profiles to test (default: docker,podman)",
268 )
262 269
263 270
264def _cleanup_stale_test_state(): 271def _cleanup_stale_test_state():
@@ -647,6 +654,9 @@ def pytest_configure(config):
647 "markers", "boot: marks tests that boot a QEMU image (requires built image)" 654 "markers", "boot: marks tests that boot a QEMU image (requires built image)"
648 ) 655 )
649 config.addinivalue_line( 656 config.addinivalue_line(
657 "markers", "container_profile: marks tests parametrized over container profiles"
658 )
659 config.addinivalue_line(
650 "markers", "k3s: marks k3s runtime tests" 660 "markers", "k3s: marks k3s runtime tests"
651 ) 661 )
652 config.addinivalue_line( 662 config.addinivalue_line(
diff --git a/tests/test_container_cross_install.py b/tests/test_container_cross_install.py
index f14bfbc5..52eceb0d 100644
--- a/tests/test_container_cross_install.py
+++ b/tests/test_container_cross_install.py
@@ -8,17 +8,22 @@ These tests verify that container-cross-install correctly bundles
8OCI containers into Yocto images. 8OCI containers into Yocto images.
9 9
10Run with: 10Run with:
11 pytest tests/test_container_cross_install.py -v 11 pytest tests/test_container_cross_install.py -v --poky-dir /opt/bruce/poky
12 12
13Run boot tests (requires built image): 13Full coverage (builds + boots with both docker and podman profiles):
14 pytest tests/test_container_cross_install.py::TestBundledContainersBoot -v 14 pytest tests/test_container_cross_install.py -v --poky-dir /opt/bruce/poky --container-profiles docker,podman
15
16Single profile only:
17 pytest tests/test_container_cross_install.py -v --poky-dir /opt/bruce/poky --container-profiles docker
15 18
16Environment variables: 19Environment variables:
17 POKY_DIR: Path to poky directory (default: /opt/bruce/poky) 20 POKY_DIR: Path to poky directory (default: /opt/bruce/poky)
18 BUILD_DIR: Path to build directory (default: $POKY_DIR/build) 21 BUILD_DIR: Path to build directory (default: $POKY_DIR/build)
19 MACHINE: Target machine (default: qemux86-64) 22 MACHINE: Target machine (default: qemux86-64)
20 23
21Note: These tests require a configured Yocto build environment. 24Note: Boot tests (TestBundledContainersBoot, TestCustomServiceFileBoot) build
25container-image-host with each --container-profiles profile, boot it, and
26verify containers are present and runnable. No local.conf editing required.
22""" 27"""
23 28
24import os 29import os
@@ -87,11 +92,35 @@ def meta_virt_dir(poky_dir):
87 return path 92 return path
88 93
89 94
90def run_bitbake(build_dir, recipe, task=None, extra_args=None, timeout=1800): 95def run_bitbake(build_dir, recipe, task=None, extra_args=None, extra_vars=None, timeout=1800):
91 """Run a bitbake command within the Yocto environment.""" 96 """Run a bitbake command within the Yocto environment.
97
98 Args:
99 build_dir: Path to build directory
100 recipe: Recipe or mc:target to build
101 task: Optional task (e.g., 'rootfs')
102 extra_args: Optional list of extra bitbake arguments
103 extra_vars: Optional dict of variable overrides (written to a
104 temporary conf file and passed via -R)
105 timeout: Command timeout in seconds
106 """
107 import tempfile
108
92 bb_cmd = "bitbake" 109 bb_cmd = "bitbake"
93 if task: 110 if task:
94 bb_cmd += f" -c {task}" 111 bb_cmd += f" -c {task}"
112
113 # Write variable overrides to a temporary conf file
114 conf_file = None
115 if extra_vars:
116 conf_file = tempfile.NamedTemporaryFile(
117 mode='w', suffix='.conf', prefix='pytest-bbvars-',
118 dir=str(build_dir / "conf"), delete=False)
119 for var, val in extra_vars.items():
120 conf_file.write(f'{var} = "{val}"\n')
121 conf_file.close()
122 bb_cmd += f" -R {conf_file.name}"
123
95 bb_cmd += f" {recipe}" 124 bb_cmd += f" {recipe}"
96 if extra_args: 125 if extra_args:
97 bb_cmd += " " + " ".join(extra_args) 126 bb_cmd += " " + " ".join(extra_args)
@@ -99,14 +128,19 @@ def run_bitbake(build_dir, recipe, task=None, extra_args=None, timeout=1800):
99 poky_dir = build_dir.parent 128 poky_dir = build_dir.parent
100 full_cmd = f"bash -c 'cd {poky_dir} && source oe-init-build-env {build_dir} >/dev/null 2>&1 && {bb_cmd}'" 129 full_cmd = f"bash -c 'cd {poky_dir} && source oe-init-build-env {build_dir} >/dev/null 2>&1 && {bb_cmd}'"
101 130
102 result = subprocess.run( 131 try:
103 full_cmd, 132 result = subprocess.run(
104 shell=True, 133 full_cmd,
105 cwd=build_dir, 134 shell=True,
106 timeout=timeout, 135 cwd=build_dir,
107 capture_output=True, 136 timeout=timeout,
108 text=True, 137 capture_output=True,
109 ) 138 text=True,
139 )
140 finally:
141 if conf_file:
142 os.unlink(conf_file.name)
143
110 return result 144 return result
111 145
112 146
@@ -439,6 +473,10 @@ class RunqemuSession:
439 # Get everything before the prompt 473 # Get everything before the prompt
440 raw_output = self.child.before 474 raw_output = self.child.before
441 475
476 # Strip OSC and other escape sequences from output
477 raw_output = re.sub(r'\x1b\]3008;[^\x07\x1b]*(?:\x07|\x1b\\)', '', raw_output)
478 raw_output = re.sub(r'\x1b\[[0-9;]*[A-Za-z]', '', raw_output)
479
442 # Parse: split by newlines, skip command echo (first line), take the rest 480 # Parse: split by newlines, skip command echo (first line), take the rest
443 lines = raw_output.replace('\r', '').split('\n') 481 lines = raw_output.replace('\r', '').split('\n')
444 482
@@ -589,6 +627,64 @@ def runqemu_session(request, poky_dir, build_dir, machine, check_rootfs_freshnes
589 session.stop() 627 session.stop()
590 628
591 629
630def _get_container_profiles(request):
631 """Parse --container-profiles option into a list."""
632 return [p.strip() for p in request.config.getoption("--container-profiles").split(",")]
633
634
635@pytest.fixture(scope="class", params=["docker", "podman"])
636def profiled_session(request, poky_dir, build_dir, machine):
637 """
638 Fixture that builds container-image-host with a specific CONTAINER_PROFILE,
639 boots it, and provides a session for testing.
640
641 Parametrized over profiles from --container-profiles (default: docker,podman).
642 Each profile gets a full build → boot → test cycle.
643 """
644 profile = request.param
645 profiles = _get_container_profiles(request)
646 if profile not in profiles:
647 pytest.skip(f"Profile {profile} not in --container-profiles={','.join(profiles)}")
648
649 if not PEXPECT_AVAILABLE:
650 pytest.skip("pexpect not installed. Run: pip install pexpect")
651
652 image = request.config.getoption("--image")
653 fstype = request.config.getoption("--image-fstype")
654 timeout = request.config.getoption("--boot-timeout")
655 use_kvm = not request.config.getoption("--no-kvm")
656
657 # Build the image with this profile
658 result = run_bitbake(
659 build_dir, image,
660 extra_vars={"CONTAINER_PROFILE": profile},
661 timeout=3600,
662 )
663 if result.returncode != 0:
664 pytest.fail(f"Build failed for profile {profile}: {result.stderr}")
665
666 session = RunqemuSession(poky_dir, build_dir, machine, image,
667 fstype=fstype, use_kvm=use_kvm, timeout=timeout)
668 session.container_profile = profile
669
670 try:
671 session.start()
672 yield session
673 finally:
674 session.stop()
675
676
677@pytest.fixture(scope="class")
678def profiled_containers(profiled_session, build_dir, machine):
679 """Detect containers in the booted image for the active profile."""
680 image = "container-image-host"
681 result = _detect_containers_from_rootfs(build_dir, machine, image)
682 if not result:
683 result = {'docker': [], 'podman': []}
684 result['profile'] = profiled_session.container_profile
685 return result
686
687
592def _detect_containers_from_rootfs(build_dir, machine, image): 688def _detect_containers_from_rootfs(build_dir, machine, image):
593 """ 689 """
594 Detect bundled containers by checking storage directories in the rootfs. 690 Detect bundled containers by checking storage directories in the rootfs.
@@ -775,135 +871,75 @@ class TestBundledContainersBoot:
775 """ 871 """
776 Boot tests to verify bundled containers are visible. 872 Boot tests to verify bundled containers are visible.
777 873
778 These tests boot the actual Yocto image and verify that 874 Parametrized over container profiles (--container-profiles, default: docker,podman).
779 `docker images` or `podman images` shows the bundled containers. 875 For each profile, builds container-image-host with that CONTAINER_PROFILE,
780 876 boots the image, and verifies the profile's containers are present and runnable.
781 Prerequisites:
782 - Image must be built with BUNDLED_CONTAINERS configured
783 - pexpect must be installed: pip install pexpect
784 877
785 Run with: 878 Run with:
786 pytest tests/test_container_cross_install.py::TestBundledContainersBoot -v 879 pytest tests/test_container_cross_install.py::TestBundledContainersBoot -v
787 880
788 Options: 881 Options:
789 --image IMAGE Image name to boot (default: core-image-minimal) 882 --container-profiles Profiles to test (default: docker,podman)
883 --image IMAGE Image name to boot (default: container-image-host)
790 --machine MACHINE Machine to use (default: qemux86-64) 884 --machine MACHINE Machine to use (default: qemux86-64)
791 --boot-timeout SECS Timeout for boot (default: 120) 885 --boot-timeout SECS Timeout for boot (default: 120)
792 """ 886 """
793 887
794 @pytest.mark.slow 888 @pytest.mark.slow
795 @pytest.mark.boot 889 @pytest.mark.boot
796 def test_system_boots(self, runqemu_session): 890 @pytest.mark.container_profile
891 def test_system_boots(self, profiled_session):
797 """Test that the system boots successfully.""" 892 """Test that the system boots successfully."""
798 assert runqemu_session.booted, "System failed to boot" 893 assert profiled_session.booted, \
894 f"System failed to boot with profile {profiled_session.container_profile}"
799 895
800 # Basic sanity check 896 output = profiled_session.run_command('uname -a')
801 output = runqemu_session.run_command('uname -a')
802 assert 'Linux' in output, f"Unexpected uname output: {output}" 897 assert 'Linux' in output, f"Unexpected uname output: {output}"
803 898
804 @pytest.mark.slow 899 @pytest.mark.slow
805 @pytest.mark.boot 900 @pytest.mark.boot
806 def test_docker_images_visible(self, runqemu_session, bundled_containers_config): 901 @pytest.mark.container_profile
807 """Test that bundled Docker containers are visible.""" 902 def test_containers_visible(self, profiled_session, profiled_containers):
808 expected = bundled_containers_config['docker'] 903 """Test that bundled containers are visible for the active profile."""
809 if not expected: 904 profile = profiled_session.container_profile
810 pytest.skip("No Docker containers in bundle packages or BUNDLED_CONTAINERS") 905 runtime = profile # docker or podman
811 906
812 # Check if docker is available 907 output = profiled_session.run_command(f'which {runtime}')
813 output = runqemu_session.run_command('which docker') 908 assert f'/{runtime}' in output, \
814 if '/docker' not in output: 909 f"{runtime} not installed in image (profile={profile})"
815 pytest.skip("docker not installed in image")
816 910
817 # Get docker images 911 output = profiled_session.run_command(f'{runtime} images', timeout=30)
818 output = runqemu_session.run_command('docker images', timeout=30) 912 print(f"{runtime} images output ({profile}):\n{output}")
819 print(f"docker images output:\n{output}")
820 913
821 # Verify each expected container is present 914 expected = profiled_containers.get(runtime, [])
822 missing = [] 915 assert expected, \
823 for container in expected: 916 f"No {runtime} containers detected in rootfs (profile={profile})"
824 if container not in output:
825 missing.append(container)
826 917
827 assert not missing, f"Missing Docker containers: {missing}\nOutput:\n{output}" 918 missing = [c for c in expected if c not in output]
919 assert not missing, \
920 f"Missing {runtime} containers: {missing}\nOutput:\n{output}"
828 921
829 @pytest.mark.slow 922 @pytest.mark.slow
830 @pytest.mark.boot 923 @pytest.mark.boot
831 def test_podman_images_visible(self, runqemu_session, bundled_containers_config): 924 @pytest.mark.container_profile
832 """Test that bundled Podman containers are visible.""" 925 def test_run_bundled_container(self, profiled_session, profiled_containers):
833 expected = bundled_containers_config['podman'] 926 """Test that a bundled container can actually run."""
834 if not expected: 927 profile = profiled_session.container_profile
835 pytest.skip("No Podman containers in bundle packages or BUNDLED_CONTAINERS") 928 runtime = profile
836
837 # Check if podman is available
838 output = runqemu_session.run_command('which podman')
839 if '/podman' not in output:
840 pytest.skip("podman not installed in image")
841
842 # Get podman images
843 output = runqemu_session.run_command('podman images', timeout=30)
844 print(f"podman images output:\n{output}")
845
846 # Verify each expected container is present
847 missing = []
848 for container in expected:
849 if container not in output:
850 missing.append(container)
851
852 assert not missing, f"Missing Podman containers: {missing}\nOutput:\n{output}"
853
854 @pytest.mark.slow
855 @pytest.mark.boot
856 def test_docker_run_bundled_container(self, runqemu_session, bundled_containers_config):
857 """Test that a bundled Docker container can actually run."""
858 expected = bundled_containers_config['docker']
859 if not expected:
860 pytest.skip("No Docker containers configured")
861
862 # Check if docker is available
863 output = runqemu_session.run_command('which docker')
864 if '/docker' not in output:
865 pytest.skip("docker not installed in image")
866
867 # Try to run the first bundled container with a simple command
868 # Use --entrypoint to override any image entrypoint, otherwise
869 # images with entrypoint like ["sh"] would interpret the command
870 # as a script argument rather than executing the binary directly
871 container = expected[0]
872 output = runqemu_session.run_command(
873 f'docker run --rm --entrypoint /bin/echo {container}:latest "CONTAINER_WORKS"',
874 timeout=60
875 )
876 print(f"docker run output:\n{output}")
877 929
878 assert 'CONTAINER_WORKS' in output, \ 930 expected = profiled_containers.get(runtime, [])
879 f"Container {container} failed to run.\nOutput:\n{output}" 931 assert expected, \
932 f"No {runtime} containers detected in rootfs (profile={profile})"
880 933
881 @pytest.mark.slow
882 @pytest.mark.boot
883 def test_podman_run_bundled_container(self, runqemu_session, bundled_containers_config):
884 """Test that a bundled Podman container can actually run."""
885 expected = bundled_containers_config['podman']
886 if not expected:
887 pytest.skip("No Podman containers in bundle packages or BUNDLED_CONTAINERS")
888
889 # Check if podman is available
890 output = runqemu_session.run_command('which podman')
891 if '/podman' not in output:
892 pytest.skip("podman not installed in image")
893
894 # Try to run the first bundled container with a simple command
895 # Use --entrypoint to override any image entrypoint, otherwise
896 # images with entrypoint like ["sh"] would interpret the command
897 # as a script argument rather than executing the binary directly
898 container = expected[0] 934 container = expected[0]
899 output = runqemu_session.run_command( 935 output = profiled_session.run_command(
900 f'podman run --rm --entrypoint /bin/echo {container}:latest "CONTAINER_WORKS"', 936 f'{runtime} run --rm --entrypoint /bin/echo {container}:latest "CONTAINER_WORKS"',
901 timeout=60 937 timeout=60
902 ) 938 )
903 print(f"podman run output:\n{output}") 939 print(f"{runtime} run output ({profile}):\n{output}")
904 940
905 assert 'CONTAINER_WORKS' in output, \ 941 assert 'CONTAINER_WORKS' in output, \
906 f"Container {container} failed to run.\nOutput:\n{output}" 942 f"Container {container} failed to run with {runtime}.\nOutput:\n{output}"
907 943
908 944
909# ============================================================================ 945# ============================================================================
@@ -933,6 +969,22 @@ class TestCustomServiceFileSupport:
933 assert "install_custom_service" in content, \ 969 assert "install_custom_service" in content, \
934 "install_custom_service function not found" 970 "install_custom_service function not found"
935 971
972 def test_bundle_class_unpack_enabled(self, meta_virt_dir):
973 """Test that do_unpack is NOT disabled in container-bundle.bbclass.
974
975 Custom service files use SRC_URI file:// entries which require
976 do_unpack to run. If do_unpack[noexec] = "1" is set, the files
977 never reach UNPACKDIR and CONTAINER_SERVICE_FILE references fail.
978 """
979 class_file = meta_virt_dir / "classes" / "container-bundle.bbclass"
980 content = class_file.read_text()
981 for line in content.splitlines():
982 stripped = line.strip()
983 if stripped.startswith("#"):
984 continue
985 assert 'do_unpack[noexec]' not in stripped, \
986 "do_unpack must not be noexec — custom service files need SRC_URI unpacking"
987
936 def test_bundle_class_has_service_file_support(self, meta_virt_dir): 988 def test_bundle_class_has_service_file_support(self, meta_virt_dir):
937 """Test that container-bundle.bbclass includes CONTAINER_SERVICE_FILE support.""" 989 """Test that container-bundle.bbclass includes CONTAINER_SERVICE_FILE support."""
938 class_file = meta_virt_dir / "classes" / "container-bundle.bbclass" 990 class_file = meta_virt_dir / "classes" / "container-bundle.bbclass"
@@ -983,118 +1035,74 @@ class TestCustomServiceFileBoot:
983 """ 1035 """
984 Boot tests for custom service files. 1036 Boot tests for custom service files.
985 1037
986 These tests verify that custom service files are properly installed 1038 Parametrized over container profiles — verifies that autostart services
987 and enabled in the booted system. 1039 (Docker systemd units or Podman Quadlet files) are correctly installed
1040 and enabled for each profile.
988 """ 1041 """
989 1042
990 @pytest.mark.slow 1043 @pytest.mark.slow
991 @pytest.mark.boot 1044 @pytest.mark.boot
992 def test_systemd_services_directory_exists(self, runqemu_session): 1045 @pytest.mark.container_profile
1046 def test_systemd_services_directory_exists(self, profiled_session):
993 """Test that systemd service directories exist.""" 1047 """Test that systemd service directories exist."""
994 output = runqemu_session.run_command('ls -la /lib/systemd/system/ | head -n 5') 1048 output = profiled_session.run_command('ls -la /lib/systemd/system/ | head -n 5')
995 assert 'systemd' in output or 'total' in output, \ 1049 assert 'systemd' in output or 'total' in output, \
996 "Systemd system directory not accessible" 1050 "Systemd system directory not accessible"
997 1051
998 @pytest.mark.slow 1052 @pytest.mark.slow
999 @pytest.mark.boot 1053 @pytest.mark.boot
1000 def test_container_services_present(self, runqemu_session, bundled_containers_config): 1054 @pytest.mark.container_profile
1001 """Test that container service files are present (custom or generated).""" 1055 def test_autostart_service_present(self, profiled_session):
1002 docker_containers = bundled_containers_config.get('docker', []) 1056 """Test that autostart service files are present for the active profile."""
1003 1057 profile = profiled_session.container_profile
1004 if not docker_containers: 1058
1005 pytest.skip("No Docker containers configured") 1059 if profile == 'docker':
1006 1060 output = profiled_session.run_command(
1007 # Check if docker is available 1061 'ls /lib/systemd/system/container-*.service 2>/dev/null || echo "NONE"')
1008 output = runqemu_session.run_command('which docker') 1062 assert 'NONE' not in output and '.service' in output, \
1009 if '/docker' not in output: 1063 f"No Docker autostart service files found (profile={profile})"
1010 pytest.skip("docker not installed in image") 1064
1011 1065 elif profile == 'podman':
1012 # Check for container service files 1066 output = profiled_session.run_command(
1013 output = runqemu_session.run_command('ls /lib/systemd/system/container-*.service 2>/dev/null || echo "NONE"') 1067 'ls /etc/containers/systemd/*.container 2>/dev/null || echo "NONE"')
1014 1068 assert 'NONE' not in output and '.container' in output, \
1015 if 'NONE' in output: 1069 f"No Podman Quadlet files found (profile={profile})"
1016 # No autostart services - check if any containers have autostart
1017 pytest.skip("No container autostart services found (containers may not have autostart enabled)")
1018
1019 # Verify at least one service file exists
1020 assert '.service' in output, \
1021 f"No container service files found. Output: {output}"
1022
1023 @pytest.mark.slow
1024 @pytest.mark.boot
1025 def test_container_service_enabled(self, runqemu_session, bundled_containers_config):
1026 """Test that container services are enabled (linked in wants directory)."""
1027 docker_containers = bundled_containers_config.get('docker', [])
1028
1029 if not docker_containers:
1030 pytest.skip("No Docker containers configured")
1031
1032 # Check for enabled services in multi-user.target.wants
1033 output = runqemu_session.run_command(
1034 'ls /etc/systemd/system/multi-user.target.wants/container-*.service 2>/dev/null || echo "NONE"'
1035 )
1036
1037 if 'NONE' in output:
1038 pytest.skip("No container autostart services enabled")
1039
1040 # Verify services are symlinked
1041 assert '.service' in output, \
1042 f"No enabled container services found. Output: {output}"
1043 1070
1044 @pytest.mark.slow 1071 @pytest.mark.slow
1045 @pytest.mark.boot 1072 @pytest.mark.boot
1046 def test_custom_service_content(self, runqemu_session, bundled_containers_config): 1073 @pytest.mark.container_profile
1047 """Test that custom service files have expected content markers.""" 1074 def test_autostart_service_running(self, profiled_session):
1048 docker_containers = bundled_containers_config.get('docker', []) 1075 """Test that the autostart container is actually running."""
1049 1076 profile = profiled_session.container_profile
1050 if not docker_containers: 1077 runtime = profile
1051 pytest.skip("No Docker containers configured")
1052 1078
1053 # Find a container service file 1079 output = profiled_session.run_command(f'{runtime} ps', timeout=30)
1054 output = runqemu_session.run_command( 1080 print(f"{runtime} ps output ({profile}):\n{output}")
1055 'ls /lib/systemd/system/container-*.service 2>/dev/null | head -1'
1056 )
1057
1058 if not output or 'container-' not in output:
1059 pytest.skip("No container service files found")
1060
1061 service_file = output.strip().split('\n')[0]
1062 1081
1063 # Read the service file content 1082 assert 'autostart-test-container' in output, \
1064 content = runqemu_session.run_command(f'cat {service_file}') 1083 f"autostart-test-container not running under {runtime} (profile={profile})\nOutput:\n{output}"
1065
1066 # Verify it has expected systemd service structure
1067 assert '[Unit]' in content, f"Service file missing [Unit] section: {service_file}"
1068 assert '[Service]' in content, f"Service file missing [Service] section: {service_file}"
1069 assert '[Install]' in content, f"Service file missing [Install] section: {service_file}"
1070
1071 # Check for docker-related content
1072 assert 'docker' in content.lower(), \
1073 f"Service file doesn't reference docker: {content}"
1074 1084
1075 @pytest.mark.slow 1085 @pytest.mark.slow
1076 @pytest.mark.boot 1086 @pytest.mark.boot
1077 def test_podman_quadlet_directory(self, runqemu_session, bundled_containers_config): 1087 @pytest.mark.container_profile
1078 """Test Podman Quadlet directory exists for Podman containers.""" 1088 def test_autostart_service_content(self, profiled_session):
1079 podman_containers = bundled_containers_config.get('podman', []) 1089 """Test that autostart service files have expected content."""
1080 1090 profile = profiled_session.container_profile
1081 if not podman_containers: 1091
1082 pytest.skip("No Podman containers configured") 1092 if profile == 'docker':
1083 1093 content = profiled_session.run_command(
1084 # Check if podman is available 1094 'cat /lib/systemd/system/container-autostart-test-container.service')
1085 output = runqemu_session.run_command('which podman') 1095 assert '[Unit]' in content, \
1086 if '/podman' not in output: 1096 f"Missing [Unit] section in docker service file.\nContent:\n{content}"
1087 pytest.skip("podman not installed in image") 1097 assert '[Service]' in content, \
1088 1098 f"Missing [Service] section in docker service file"
1089 # Check for Quadlet directory 1099 assert 'docker' in content.lower(), \
1090 output = runqemu_session.run_command('ls -la /etc/containers/systemd/ 2>/dev/null || echo "NONE"') 1100 f"Service doesn't reference docker"
1091 1101
1092 if 'NONE' in output: 1102 elif profile == 'podman':
1093 pytest.skip("Quadlet directory not found (containers may not have autostart enabled)") 1103 content = profiled_session.run_command(
1094 1104 'cat /etc/containers/systemd/container-autostart-test-container.container')
1095 # Check for .container files 1105 assert '[Container]' in content, \
1096 output = runqemu_session.run_command('ls /etc/containers/systemd/*.container 2>/dev/null || echo "NONE"') 1106 f"Missing [Container] section in podman quadlet file.\nContent:\n{content}"
1097 1107 assert 'Image=' in content, \
1098 if 'NONE' not in output: 1108 f"Missing Image= directive in podman quadlet file"
1099 assert '.container' in output, \
1100 f"No Quadlet container files found. Output: {output}"