#!/usr/bin/env python3 """ Test pylint compliance for deb-bootc-image-builder. This module tests code quality and pylint compliance, including: - Code style validation - Pylint score checking - Debian-specific code standards """ import pytest import os import tempfile import shutil import subprocess import logging # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class TestPylintCompliance: """Test cases for pylint compliance.""" def test_pylint_installation(self, work_dir): """Test that pylint is available.""" try: result = subprocess.run( ["pylint", "--version"], capture_output=True, text=True, timeout=10 ) assert result.returncode == 0, "pylint is not properly installed" logger.info("pylint is available") except FileNotFoundError: pytest.skip("pylint not installed") def test_pylint_basic_usage(self, work_dir): """Test basic pylint functionality.""" # Create a simple test file test_file = os.path.join(work_dir, "test_pylint.py") with open(test_file, 'w') as f: f.write('''#!/usr/bin/env python3 """ Test file for pylint validation. """ def test_function(): """Test function for pylint.""" return "test" if __name__ == "__main__": print(test_function()) ''') # Run pylint on the test file try: result = subprocess.run( ["pylint", test_file], capture_output=True, text=True, timeout=30 ) # Pylint should run without errors assert result.returncode in [0, 1], f"pylint failed with return code {result.returncode}" logger.info("pylint basic functionality test passed") except subprocess.TimeoutExpired: pytest.fail("pylint timed out") except Exception as e: pytest.fail(f"pylint test failed: {e}") def test_debian_specific_code_standards(self, work_dir): """Test Debian-specific code standards.""" # Create a test file with Debian-specific patterns test_file = os.path.join(work_dir, "debian_test.py") with open(test_file, 'w') as f: f.write('''#!/usr/bin/env python3 """ Debian-specific test file for pylint validation. """ import os import subprocess import logging from typing import Dict, List, Any, Optional # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class DebianBootcBuilder: """Debian bootc image builder class.""" def __init__(self, work_dir: str): """Initialize the builder.""" self.work_dir = work_dir self.packages: List[str] = [] self.release = "trixie" self.arch = "amd64" def add_package(self, package: str) -> None: """Add a package to the installation list.""" if package not in self.packages: self.packages.append(package) logger.info(f"Added package: {package}") def set_release(self, release: str) -> None: """Set the Debian release.""" valid_releases = ["trixie", "bookworm", "bullseye"] if release in valid_releases: self.release = release logger.info(f"Set release to: {release}") else: raise ValueError(f"Invalid release: {release}") def build_image(self) -> Dict[str, Any]: """Build the Debian image.""" logger.info("Starting Debian image build") # Validate configuration if not self.packages: raise ValueError("No packages specified") # Build process would go here result = { "status": "success", "packages": self.packages, "release": self.release, "arch": self.arch } logger.info("Debian image build completed") return result def main() -> None: """Main function.""" builder = DebianBootcBuilder("/tmp/test") builder.add_package("linux-image-amd64") builder.add_package("systemd") builder.set_release("trixie") try: result = builder.build_image() print(f"Build result: {result}") except Exception as e: logger.error(f"Build failed: {e}") if __name__ == "__main__": main() ''') # Run pylint with Debian-specific configuration try: result = subprocess.run( ["pylint", "--disable=C0114,C0116", test_file], capture_output=True, text=True, timeout=30 ) # Check pylint output for Debian-specific patterns output = result.stdout + result.stderr # Should not have critical errors assert "E0001" not in output, "Critical pylint errors found" # Check for specific Debian patterns assert "debian" in output.lower() or "bootc" in output.lower(), \ "Debian-specific content not detected" logger.info("Debian-specific code standards test passed") except subprocess.TimeoutExpired: pytest.fail("pylint Debian test timed out") except Exception as e: pytest.fail(f"pylint Debian test failed: {e}") def test_pylint_score_threshold(self, work_dir): """Test that pylint score meets minimum threshold.""" # Create a high-quality test file test_file = os.path.join(work_dir, "high_quality_test.py") with open(test_file, 'w') as f: f.write('''#!/usr/bin/env python3 """ High-quality test file for pylint scoring. """ import os import logging from typing import Dict, List, Any, Optional # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class HighQualityClass: """A high-quality class for testing.""" def __init__(self, name: str): """Initialize the class.""" self.name = name self.data: List[str] = [] def add_item(self, item: str) -> None: """Add an item to the data list.""" if item and item not in self.data: self.data.append(item) logger.info(f"Added item: {item}") def get_items(self) -> List[str]: """Get all items from the data list.""" return self.data.copy() def clear_items(self) -> None: """Clear all items from the data list.""" self.data.clear() logger.info("Cleared all items") def high_quality_function(param: str) -> str: """A high-quality function for testing.""" if not param: return "" result = param.upper() logger.info(f"Processed parameter: {param} -> {result}") return result def main() -> None: """Main function.""" obj = HighQualityClass("test") obj.add_item("item1") obj.add_item("item2") items = obj.get_items() print(f"Items: {items}") result = high_quality_function("hello") print(f"Function result: {result}") if __name__ == "__main__": main() ''') # Run pylint and check score try: result = subprocess.run( ["pylint", "--score=yes", test_file], capture_output=True, text=True, timeout=30 ) output = result.stdout + result.stderr # Extract score from output score_line = [line for line in output.split('\n') if 'Your code has been rated at' in line] if score_line: score_text = score_line[0] # Extract numeric score import re score_match = re.search(r'(\d+\.\d+)', score_text) if score_match: score = float(score_match.group(1)) # Check if score meets minimum threshold (8.0) assert score >= 8.0, f"Pylint score {score} is below minimum threshold 8.0" logger.info(f"Pylint score: {score} (meets minimum threshold)") else: pytest.fail("Could not extract pylint score") else: pytest.fail("Could not find pylint score in output") except subprocess.TimeoutExpired: pytest.fail("pylint score test timed out") except Exception as e: pytest.fail(f"pylint score test failed: {e}") def test_pylint_configuration(self, work_dir): """Test pylint configuration and custom rules.""" # Create a pylint configuration file pylintrc = os.path.join(work_dir, ".pylintrc") with open(pylintrc, 'w') as f: f.write('''[MASTER] # Python code to execute before analysis init-hook='import sys; sys.path.append(".")' [REPORTS] # Set the output format output-format=text # Include a brief explanation of each error msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} # Include a brief explanation of each error include-naming-hint=yes [MESSAGES CONTROL] # Disable specific warnings disable=C0114,C0116,R0903 [FORMAT] # Maximum number of characters on a single line max-line-length=120 # Maximum number of lines in a module max-module-lines=1000 [SIMILARITIES] # Minimum lines number of a similarity min-similarity-lines=4 # Ignore imports when computing similarities ignore-imports=yes ''') # Create a test file test_file = os.path.join(work_dir, "config_test.py") with open(test_file, 'w') as f: f.write('''#!/usr/bin/env python3 """ Test file for pylint configuration. """ def test_function(): return "test" if __name__ == "__main__": print(test_function()) ''') # Run pylint with custom configuration try: result = subprocess.run( ["pylint", "--rcfile", pylintrc, test_file], capture_output=True, text=True, timeout=30 ) # Should run without configuration errors assert result.returncode in [0, 1], f"pylint with custom config failed: {result.returncode}" logger.info("Pylint configuration test passed") except subprocess.TimeoutExpired: pytest.fail("pylint configuration test timed out") except Exception as e: pytest.fail(f"pylint configuration test failed: {e}") if __name__ == "__main__": pytest.main([__file__])