364 lines
11 KiB
Python
364 lines
11 KiB
Python
#!/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__])
|