initial commit

This commit is contained in:
2026-02-12 00:45:31 -08:00
commit 5f168f370b
3024 changed files with 804889 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,464 @@
#!/usr/bin/env python3
"""
Integration tests for xcache.py using real clang compiler.
This test suite verifies that xcache works correctly with actual compilation
scenarios, including response files and direct execution modes.
"""
import os
import shutil
import subprocess
import sys
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
@dataclass
class TestResults:
"""Test results container."""
passed: int = 0
failed: int = 0
errors: Optional[list[str]] = None
def __post_init__(self) -> None:
if self.errors is None:
self.errors = []
def find_clang() -> Optional[str]:
"""Find clang compiler in PATH."""
clang_path = shutil.which("clang")
if clang_path:
return clang_path
# Check common locations
common_paths = [
"/usr/bin/clang",
"/usr/local/bin/clang",
"/opt/local/bin/clang",
"C:/Program Files/LLVM/bin/clang.exe",
"C:/msys64/mingw64/bin/clang.exe",
]
for path in common_paths:
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return None
def find_sccache() -> Optional[str]:
"""Find sccache in PATH."""
return shutil.which("sccache")
def create_test_source_file(temp_dir: Path) -> Path:
"""Create a simple C source file for testing."""
source_file = temp_dir / "test.c"
source_content = """
#include <stdio.h>
int main() {
printf("Hello from xcache test!\\n");
return 0;
}
"""
source_file.write_text(source_content)
return source_file
def create_response_file(temp_dir: Path, args: list[str]) -> Path:
"""Create a response file with the given arguments."""
response_file = temp_dir / "compile_args.rsp"
# Write arguments to response file (one per line or space-separated)
content = " ".join(args)
response_file.write_text(content)
return response_file
def test_xcache_direct_execution(results: TestResults) -> None:
"""Test xcache with direct execution (no response files)."""
print("🧪 Testing xcache direct execution...")
clang_path = find_clang()
if not clang_path:
results.errors.append(
"clang compiler not found - skipping direct execution test"
)
return
sccache_path = find_sccache()
if not sccache_path:
results.errors.append("sccache not found - skipping direct execution test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file
source_file = create_test_source_file(temp_path)
output_file = temp_path / "test_direct.o"
# Test xcache with direct arguments (no response files)
env = os.environ.copy()
env["XCACHE_DEBUG"] = "1"
cmd = [
sys.executable,
"ci/util/xcache.py",
clang_path,
"-c",
str(source_file),
"-o",
str(output_file),
]
try:
result = subprocess.run(
cmd, capture_output=True, text=True, env=env, timeout=30
)
if result.returncode == 0 and output_file.exists():
print(" ✅ Direct execution test passed")
print(f" Output file created: {output_file.stat().st_size} bytes")
results.passed += 1
else:
print(
f" ❌ Direct execution test failed (return code: {result.returncode})"
)
print(f" Stderr: {result.stderr}")
results.failed += 1
results.errors.append(f"Direct execution failed: {result.stderr}")
except subprocess.TimeoutExpired:
print(" ❌ Direct execution test timed out")
results.failed += 1
results.errors.append("Direct execution test timed out")
except Exception as e:
print(f" ❌ Direct execution test error: {e}")
results.failed += 1
results.errors.append(f"Direct execution error: {e}")
def test_xcache_response_file_execution(results: TestResults) -> None:
"""Test xcache with response file execution."""
print("\n🧪 Testing xcache response file execution...")
clang_path = find_clang()
if not clang_path:
results.errors.append("clang compiler not found - skipping response file test")
return
sccache_path = find_sccache()
if not sccache_path:
results.errors.append("sccache not found - skipping response file test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file
source_file = create_test_source_file(temp_path)
output_file = temp_path / "test_response.o"
# Create response file with compilation arguments
compile_args = ["-c", str(source_file), "-o", str(output_file), "-O2", "-Wall"]
response_file = create_response_file(temp_path, compile_args)
# Test xcache with response file
env = os.environ.copy()
env["XCACHE_DEBUG"] = "1"
cmd = [sys.executable, "ci/util/xcache.py", clang_path, f"@{response_file}"]
try:
result = subprocess.run(
cmd, capture_output=True, text=True, env=env, timeout=30
)
if result.returncode == 0 and output_file.exists():
print(" ✅ Response file execution test passed")
print(f" Output file created: {output_file.stat().st_size} bytes")
print(f" Response file handled: @{response_file}")
# Verify that response file was passed through (not expanded)
if f"@{response_file}" in result.stderr:
print(" ✅ Response file passed through correctly")
else:
print(
" ⚠️ Response file passthrough not clearly visible in debug output"
)
results.passed += 1
else:
print(
f" ❌ Response file execution test failed (return code: {result.returncode})"
)
print(f" Stderr: {result.stderr}")
results.failed += 1
results.errors.append(
f"Response file execution failed: {result.stderr}"
)
except subprocess.TimeoutExpired:
print(" ❌ Response file execution test timed out")
results.failed += 1
results.errors.append("Response file execution test timed out")
except Exception as e:
print(f" ❌ Response file execution test error: {e}")
results.failed += 1
results.errors.append(f"Response file execution error: {e}")
def test_xcache_mixed_arguments(results: TestResults) -> None:
"""Test xcache with mixed regular arguments and response files."""
print("\n🧪 Testing xcache mixed arguments...")
clang_path = find_clang()
if not clang_path:
results.errors.append(
"clang compiler not found - skipping mixed arguments test"
)
return
sccache_path = find_sccache()
if not sccache_path:
results.errors.append("sccache not found - skipping mixed arguments test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file
source_file = create_test_source_file(temp_path)
output_file = temp_path / "test_mixed.o"
# Create response file with some arguments
response_args = ["-O2", "-Wall", "-Wextra"]
response_file = create_response_file(temp_path, response_args)
# Test xcache with mixed arguments
env = os.environ.copy()
env["XCACHE_DEBUG"] = "1"
cmd = [
sys.executable,
"ci/util/xcache.py",
clang_path,
"-c",
str(source_file),
f"@{response_file}",
"-o",
str(output_file),
]
try:
result = subprocess.run(
cmd, capture_output=True, text=True, env=env, timeout=30
)
if result.returncode == 0 and output_file.exists():
print(" ✅ Mixed arguments test passed")
print(f" Output file created: {output_file.stat().st_size} bytes")
print(f" Mixed args: regular + @{response_file}")
results.passed += 1
else:
print(
f" ❌ Mixed arguments test failed (return code: {result.returncode})"
)
print(f" Stderr: {result.stderr}")
results.failed += 1
results.errors.append(f"Mixed arguments failed: {result.stderr}")
except subprocess.TimeoutExpired:
print(" ❌ Mixed arguments test timed out")
results.failed += 1
results.errors.append("Mixed arguments test timed out")
except Exception as e:
print(f" ❌ Mixed arguments test error: {e}")
results.failed += 1
results.errors.append(f"Mixed arguments error: {e}")
def test_xcache_cache_effectiveness(results: TestResults) -> None:
"""Test that xcache actually caches compilation results."""
print("\n🧪 Testing xcache cache effectiveness...")
clang_path = find_clang()
if not clang_path:
results.errors.append(
"clang compiler not found - skipping cache effectiveness test"
)
return
sccache_path = find_sccache()
if not sccache_path:
results.errors.append("sccache not found - skipping cache effectiveness test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file
source_file = create_test_source_file(temp_path)
# Set up environment for caching
env = os.environ.copy()
env["XCACHE_DEBUG"] = "1"
cache_dir = temp_path / "sccache"
cache_dir.mkdir()
env["SCCACHE_DIR"] = str(cache_dir)
# First compilation (should be cache miss)
output_file1 = temp_path / "test_cache1.o"
cmd1 = [
sys.executable,
"ci/util/xcache.py",
clang_path,
"-c",
str(source_file),
"-o",
str(output_file1),
]
# Second compilation (should be cache hit)
output_file2 = temp_path / "test_cache2.o"
cmd2 = [
sys.executable,
"ci/util/xcache.py",
clang_path,
"-c",
str(source_file),
"-o",
str(output_file2),
]
try:
# First compilation
result1 = subprocess.run(
cmd1, capture_output=True, text=True, env=env, timeout=30
)
# Second compilation
result2 = subprocess.run(
cmd2, capture_output=True, text=True, env=env, timeout=30
)
if (
result1.returncode == 0
and output_file1.exists()
and result2.returncode == 0
and output_file2.exists()
):
print(" ✅ Cache effectiveness test passed")
print(f" First compilation: {output_file1.stat().st_size} bytes")
print(f" Second compilation: {output_file2.stat().st_size} bytes")
# Check if sccache cache directory has content
cache_files = list(cache_dir.rglob("*"))
if cache_files:
print(f" Cache directory has {len(cache_files)} entries")
else:
print(" ⚠️ Cache directory appears empty")
results.passed += 1
else:
print(f" ❌ Cache effectiveness test failed")
print(
f" First result: {result1.returncode}, {output_file1.exists()}"
)
print(
f" Second result: {result2.returncode}, {output_file2.exists()}"
)
results.failed += 1
results.errors.append("Cache effectiveness test failed")
except subprocess.TimeoutExpired:
print(" ❌ Cache effectiveness test timed out")
results.failed += 1
results.errors.append("Cache effectiveness test timed out")
except Exception as e:
print(f" ❌ Cache effectiveness test error: {e}")
results.failed += 1
results.errors.append(f"Cache effectiveness error: {e}")
def run_all_tests() -> TestResults:
"""Run all xcache tests."""
print("🚀 XCACHE INTEGRATION TESTS WITH CLANG")
print("=" * 60)
results = TestResults()
# Check prerequisites
clang_path = find_clang()
sccache_path = find_sccache()
print(f"📋 Test Environment:")
print(f" Clang: {clang_path or 'NOT FOUND'}")
print(f" Sccache: {sccache_path or 'NOT FOUND'}")
print(f" xcache: ci/util/xcache.py")
print()
if not clang_path:
print("❌ Clang compiler not found - install clang to run tests")
results.errors.append("Clang compiler not available")
return results
if not sccache_path:
print("❌ Sccache not found - install sccache to run tests")
results.errors.append("Sccache not available")
return results
# Run tests
test_xcache_direct_execution(results)
test_xcache_response_file_execution(results)
test_xcache_mixed_arguments(results)
test_xcache_cache_effectiveness(results)
return results
def main() -> int:
"""Main test function."""
results = run_all_tests()
print("\n" + "=" * 60)
print("📊 TEST RESULTS SUMMARY")
print("=" * 60)
total_tests = results.passed + results.failed
if total_tests > 0:
success_rate = (results.passed / total_tests) * 100
print(f" Tests passed: {results.passed}")
print(f" Tests failed: {results.failed}")
print(f" Success rate: {success_rate:.1f}%")
else:
print(" No tests were executed")
if results.errors:
print(f"\n🚨 Errors encountered:")
for i, error in enumerate(results.errors, 1):
print(f" {i}. {error}")
if results.failed == 0 and results.passed > 0:
print("\n✅ All xcache tests passed! Ready for ESP32S3 integration.")
return 0
elif results.passed > 0:
print(
f"\n⚠️ Some tests passed but {results.failed} failed. Check errors above."
)
return 1
else:
print("\n❌ No tests passed. Check prerequisites and errors above.")
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,437 @@
#!/usr/bin/env python3
"""
Standalone tests for xcache.py using clang compiler without requiring sccache server.
This test suite verifies that xcache works correctly by testing the wrapper
functionality and ensuring proper command construction and execution.
"""
import os
import shutil
import subprocess
import sys
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
@dataclass
class TestResults:
"""Test results container."""
passed: int = 0
failed: int = 0
errors: Optional[list[str]] = None
def __post_init__(self) -> None:
if self.errors is None:
self.errors = []
def find_clang() -> Optional[str]:
"""Find clang compiler in PATH."""
clang_path = shutil.which("clang")
if clang_path:
return clang_path
# Check common locations
common_paths = [
"/usr/bin/clang",
"/usr/local/bin/clang",
"/opt/local/bin/clang",
"C:/Program Files/LLVM/bin/clang.exe",
"C:/msys64/mingw64/bin/clang.exe",
]
for path in common_paths:
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return None
def create_test_source_file(temp_dir: Path) -> Path:
"""Create a simple C source file for testing."""
source_file = temp_dir / "test.c"
source_content = """
#include <stdio.h>
int main() {
printf("Hello from xcache test!\\n");
return 0;
}
"""
source_file.write_text(source_content)
return source_file
def create_response_file(temp_dir: Path, args: list[str]) -> Path:
"""Create a response file with the given arguments."""
response_file = temp_dir / "compile_args.rsp"
# Write arguments to response file (space-separated)
# Properly quote arguments that contain spaces (Windows paths)
# Use forward slashes for Windows paths to avoid backslash escape issues
quoted_args: list[str] = []
for arg in args:
# Convert Windows backslashes to forward slashes for response files
normalized_arg = arg.replace("\\", "/")
if " " in normalized_arg and not (
normalized_arg.startswith('"') and normalized_arg.endswith('"')
):
quoted_args.append(f'"{normalized_arg}"')
else:
quoted_args.append(normalized_arg)
content = " ".join(quoted_args)
response_file.write_text(content)
return response_file
def test_clang_direct_compilation(results: TestResults) -> None:
"""Test that clang compilation works directly (baseline test)."""
print("🧪 Testing direct clang compilation (baseline)...")
clang_path = find_clang()
if not clang_path:
results.errors.append("clang compiler not found - skipping baseline test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file
source_file = create_test_source_file(temp_path)
output_file = temp_path / "test_baseline.o"
# Test direct clang compilation
cmd = [clang_path, "-c", str(source_file), "-o", str(output_file)]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0 and output_file.exists():
print(" ✅ Direct clang compilation works")
print(f" Output file created: {output_file.stat().st_size} bytes")
results.passed += 1
else:
print(
f" ❌ Direct clang compilation failed (return code: {result.returncode})"
)
print(f" Stderr: {result.stderr}")
results.failed += 1
results.errors.append(
f"Direct clang compilation failed: {result.stderr}"
)
except subprocess.TimeoutExpired:
print(" ❌ Direct clang compilation timed out")
results.failed += 1
results.errors.append("Direct clang compilation timed out")
except Exception as e:
print(f" ❌ Direct clang compilation error: {e}")
results.failed += 1
results.errors.append(f"Direct clang compilation error: {e}")
def test_clang_response_file_compilation(results: TestResults) -> None:
"""Test that clang can handle response files directly."""
print("\n🧪 Testing clang response file compilation...")
clang_path = find_clang()
if not clang_path:
results.errors.append("clang compiler not found - skipping response file test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file
source_file = create_test_source_file(temp_path)
output_file = temp_path / "test_response.o"
# Create response file with compilation arguments
compile_args = ["-c", str(source_file), "-o", str(output_file)]
response_file = create_response_file(temp_path, compile_args)
# Test clang with response file
cmd = [clang_path, f"@{response_file}"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0 and output_file.exists():
print(" ✅ Clang response file compilation works")
print(f" Output file created: {output_file.stat().st_size} bytes")
print(f" Response file: @{response_file}")
results.passed += 1
else:
print(
f" ❌ Clang response file compilation failed (return code: {result.returncode})"
)
print(f" Stderr: {result.stderr}")
results.failed += 1
results.errors.append(
f"Clang response file compilation failed: {result.stderr}"
)
except subprocess.TimeoutExpired:
print(" ❌ Clang response file compilation timed out")
results.failed += 1
results.errors.append("Clang response file compilation timed out")
except Exception as e:
print(f" ❌ Clang response file compilation error: {e}")
results.failed += 1
results.errors.append(f"Clang response file compilation error: {e}")
def test_xcache_wrapper_script_creation(results: TestResults) -> None:
"""Test that xcache creates wrapper scripts correctly."""
print("\n🧪 Testing xcache wrapper script creation...")
clang_path = find_clang()
if not clang_path:
results.errors.append("clang compiler not found - skipping wrapper script test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file and response file
source_file = create_test_source_file(temp_path)
compile_args = ["-c", str(source_file), "-o", "test.o"]
response_file = create_response_file(temp_path, compile_args)
# Create a mock sccache script that just echoes its arguments
mock_sccache = (
temp_path / "sccache.bat" if os.name == "nt" else temp_path / "sccache"
)
if os.name == "nt":
mock_content = """@echo off
echo MOCK_SCCACHE_CALLED: %*
"""
else:
mock_content = """#!/bin/bash
echo "MOCK_SCCACHE_CALLED: $@"
"""
mock_sccache.write_text(mock_content)
if os.name != "nt":
mock_sccache.chmod(0o755)
# Set up environment to use mock sccache
env = os.environ.copy()
env["PATH"] = f"{temp_path}{os.pathsep}{env.get('PATH', '')}"
env["XCACHE_DEBUG"] = "1"
# Test xcache with response file (should create wrapper script)
cmd = [sys.executable, "ci/util/xcache.py", clang_path, f"@{response_file}"]
try:
result = subprocess.run(
cmd, capture_output=True, text=True, env=env, timeout=30
)
# Check if wrapper script was created and executed
if "Created compiler wrapper:" in result.stderr:
print(" ✅ Wrapper script creation detected")
# Check if wrapper was executed
if "Executing with wrapper:" in result.stderr:
print(" ✅ Wrapper script execution detected")
# Check if mock sccache was called
if "MOCK_SCCACHE_CALLED:" in result.stdout:
print(" ✅ Mock sccache was called through wrapper")
print(f" Sccache called with: {result.stdout.strip()}")
results.passed += 1
else:
print(" ❌ Mock sccache was not called")
results.failed += 1
results.errors.append(
"Mock sccache was not called through wrapper"
)
else:
print(" ❌ Wrapper script was not executed")
results.failed += 1
results.errors.append("Wrapper script was not executed")
else:
print(" ❌ Wrapper script was not created")
print(f" Stderr: {result.stderr}")
results.failed += 1
results.errors.append(
f"Wrapper script creation failed: {result.stderr}"
)
except subprocess.TimeoutExpired:
print(" ❌ Wrapper script test timed out")
results.failed += 1
results.errors.append("Wrapper script test timed out")
except Exception as e:
print(f" ❌ Wrapper script test error: {e}")
results.failed += 1
results.errors.append(f"Wrapper script test error: {e}")
def test_xcache_direct_mode(results: TestResults) -> None:
"""Test xcache direct mode (no response files)."""
print("\n🧪 Testing xcache direct mode...")
clang_path = find_clang()
if not clang_path:
results.errors.append("clang compiler not found - skipping direct mode test")
return
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test source file
source_file = create_test_source_file(temp_path)
# Create a mock sccache script that just passes through to real compiler
mock_sccache = (
temp_path / "sccache.bat" if os.name == "nt" else temp_path / "sccache"
)
if os.name == "nt":
mock_content = f'''@echo off
echo MOCK_SCCACHE_DIRECT: %*
"{clang_path}" %*
'''
else:
mock_content = f'''#!/bin/bash
echo "MOCK_SCCACHE_DIRECT: $@"
"{clang_path}" "$@"
'''
mock_sccache.write_text(mock_content)
if os.name != "nt":
mock_sccache.chmod(0o755)
# Set up environment to use mock sccache
env = os.environ.copy()
env["PATH"] = f"{temp_path}{os.pathsep}{env.get('PATH', '')}"
env["XCACHE_DEBUG"] = "1"
output_file = temp_path / "test_direct.o"
# Test xcache without response files (direct mode)
cmd = [
sys.executable,
"ci/util/xcache.py",
clang_path,
"-c",
str(source_file),
"-o",
str(output_file),
]
try:
result = subprocess.run(
cmd, capture_output=True, text=True, env=env, timeout=30
)
if result.returncode == 0 and output_file.exists():
print(" ✅ Direct mode compilation succeeded")
print(f" Output file created: {output_file.stat().st_size} bytes")
# Check if direct execution was used (no wrapper)
if "Direct execution:" in result.stderr:
print(" ✅ Direct execution mode confirmed")
results.passed += 1
else:
print(" ⚠️ Direct execution mode not clearly indicated")
results.passed += 1 # Still pass since compilation worked
else:
print(
f" ❌ Direct mode compilation failed (return code: {result.returncode})"
)
print(f" Stderr: {result.stderr}")
results.failed += 1
results.errors.append(
f"Direct mode compilation failed: {result.stderr}"
)
except subprocess.TimeoutExpired:
print(" ❌ Direct mode test timed out")
results.failed += 1
results.errors.append("Direct mode test timed out")
except Exception as e:
print(f" ❌ Direct mode test error: {e}")
results.failed += 1
results.errors.append(f"Direct mode test error: {e}")
def run_all_tests() -> TestResults:
"""Run all xcache standalone tests."""
print("🚀 XCACHE STANDALONE TESTS WITH CLANG")
print("=" * 60)
results = TestResults()
# Check prerequisites
clang_path = find_clang()
print(f"📋 Test Environment:")
print(f" Clang: {clang_path or 'NOT FOUND'}")
print(f" xcache: ci/util/xcache.py")
print(f" Note: Using mock sccache for testing wrapper functionality")
print()
if not clang_path:
print("❌ Clang compiler not found - install clang to run tests")
results.errors.append("Clang compiler not available")
return results
# Run tests
test_clang_direct_compilation(results)
test_clang_response_file_compilation(results)
test_xcache_wrapper_script_creation(results)
test_xcache_direct_mode(results)
return results
def main() -> int:
"""Main test function."""
results = run_all_tests()
print("\n" + "=" * 60)
print("📊 TEST RESULTS SUMMARY")
print("=" * 60)
total_tests = results.passed + results.failed
if total_tests > 0:
success_rate = (results.passed / total_tests) * 100
print(f" Tests passed: {results.passed}")
print(f" Tests failed: {results.failed}")
print(f" Success rate: {success_rate:.1f}%")
else:
print(" No tests were executed")
if results.errors:
print(f"\n🚨 Errors encountered:")
for i, error in enumerate(results.errors, 1):
print(f" {i}. {error}")
if results.failed == 0 and results.passed > 0:
print(
"\n✅ All xcache standalone tests passed! Wrapper functionality verified."
)
return 0
elif results.passed > 0:
print(
f"\n⚠️ Some tests passed but {results.failed} failed. Check errors above."
)
return 1
else:
print("\n❌ No tests passed. Check prerequisites and errors above.")
return 1
if __name__ == "__main__":
sys.exit(main())