Files
2026-05-12 04:05:14 +02:00

316 lines
8.6 KiB
Python

#!/usr/bin/env python3
"""
Tests for the Binary/Object File API.
These tests verify:
1. Basic functionality of BinaryManager.from_bytes and BinaryManager.from_file
2. Memory safety with validity tokens
3. Iterator behavior after binary disposal
4. Error handling
"""
import sys
from pathlib import Path
import llvm
def test_create_binary_from_bytes():
"""Test creating a binary from bytes."""
# Read the test object file
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
data = obj_path.read_bytes()
with llvm.BinaryManager.from_bytes(data) as binary:
# Check binary type
assert binary.type == llvm.BinaryType.ELF64L, (
f"Expected ELF64L, got {binary.type}"
)
# Count sections
section_count = 0
for sect in binary.sections:
section_count += 1
# Access properties to ensure they work
_ = sect.name
_ = sect.address
_ = sect.size
assert section_count > 0, "Expected at least one section"
print(f" Found {section_count} sections")
print("PASS: test_create_binary_from_bytes")
def test_create_binary_from_file():
"""Test creating a binary from a file path."""
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
with llvm.BinaryManager.from_file(str(obj_path)) as binary:
assert binary.type == llvm.BinaryType.ELF64L
# Iterate sections
section_names = []
for sect in binary.sections:
section_names.append(sect.name)
assert ".text" in section_names, f"Expected .text section, got {section_names}"
print(f" Sections: {section_names}")
print("PASS: test_create_binary_from_file")
def test_section_iterator_after_binary_disposed():
"""Test that section iterator raises error after binary is disposed."""
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
data = obj_path.read_bytes()
# Get iterator inside context, try to use outside
sect_iter = None
with llvm.BinaryManager.from_bytes(data) as binary:
sect_iter = binary.sections
# Iterator should work inside the context
assert not sect_iter.is_at_end()
_ = sect_iter.name
# After context exit, iterator should raise error
try:
_ = sect_iter.name
assert False, "Expected LLVMMemoryError after binary disposed"
except llvm.LLVMMemoryError as e:
assert "disposed" in str(e).lower() or "invalid" in str(e).lower()
print(f" Got expected error: {e}")
print("PASS: test_section_iterator_after_binary_disposed")
def test_symbol_iterator_after_binary_disposed():
"""Test that symbol iterator raises error after binary is disposed."""
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
data = obj_path.read_bytes()
sym_iter = None
with llvm.BinaryManager.from_bytes(data) as binary:
sym_iter = binary.symbols
# Don't access it - just get it
# After context exit, iterator should raise error
try:
_ = sym_iter.is_at_end()
assert False, "Expected LLVMMemoryError after binary disposed"
except llvm.LLVMMemoryError as e:
assert "disposed" in str(e).lower() or "invalid" in str(e).lower()
print(f" Got expected error: {e}")
print("PASS: test_symbol_iterator_after_binary_disposed")
def test_invalid_binary_bytes():
"""Test error handling for invalid binary data."""
try:
with llvm.BinaryManager.from_bytes(b"not a valid object file") as binary:
pass
assert False, "Expected LLVMError for invalid data"
except llvm.LLVMError as e:
print(f" Got expected error: {e}")
print("PASS: test_invalid_binary_bytes")
def test_nonexistent_file():
"""Test error handling for non-existent file."""
try:
with llvm.BinaryManager.from_file("/nonexistent/path/to/file.o") as binary:
pass
assert False, "Expected LLVMError for non-existent file"
except llvm.LLVMError as e:
print(f" Got expected error: {e}")
print("PASS: test_nonexistent_file")
def test_pythonic_iteration():
"""Test that Pythonic for loops work correctly."""
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
data = obj_path.read_bytes()
with llvm.BinaryManager.from_bytes(data) as binary:
# Test that we can iterate multiple times
count1 = sum(1 for _ in binary.sections)
count2 = sum(1 for _ in binary.sections)
# Each call to binary.sections creates a new iterator
# So both counts should be the same
assert count1 == count2, f"Expected same count, got {count1} and {count2}"
print(f" Section count (both iterations): {count1}")
print("PASS: test_pythonic_iteration")
def test_section_contents():
"""Test accessing section contents as bytes."""
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
data = obj_path.read_bytes()
with llvm.BinaryManager.from_bytes(data) as binary:
for sect in binary.sections:
if sect.name == ".text":
contents = sect.contents
assert isinstance(contents, bytes)
assert len(contents) == sect.size
print(f" .text section: {sect.size} bytes")
break
print("PASS: test_section_contents")
def test_double_enter():
"""Test that entering a binary manager twice raises error."""
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
data = obj_path.read_bytes()
manager = llvm.BinaryManager.from_bytes(data)
with manager as binary:
pass # First enter/exit
# Now the manager is disposed, trying to enter again should fail
try:
with manager as binary:
pass
assert False, "Expected error on second enter"
except llvm.LLVMMemoryError as e:
print(f" Got expected error: {e}")
print("PASS: test_double_enter")
def test_dispose_before_enter():
"""Test dispose() without entering the context."""
obj_path = (
Path(__file__).parent.parent
/ "llvm-c"
/ "llvm-c-test"
/ "inputs"
/ "Inputs"
/ "simple.o"
)
if not obj_path.exists():
print(f"SKIP: Test object file not found: {obj_path}")
return
data = obj_path.read_bytes()
manager = llvm.BinaryManager.from_bytes(data)
# Dispose without entering - should work
manager.dispose()
# Now try to enter - should fail
try:
with manager as binary:
pass
assert False, "Expected error after dispose"
except llvm.LLVMMemoryError as e:
print(f" Got expected error: {e}")
print("PASS: test_dispose_before_enter")
def main():
"""Run all tests."""
print("Running Binary API tests...")
print()
test_create_binary_from_bytes()
test_create_binary_from_file()
test_section_iterator_after_binary_disposed()
test_symbol_iterator_after_binary_disposed()
test_invalid_binary_bytes()
test_nonexistent_file()
test_pythonic_iteration()
test_section_contents()
test_double_enter()
test_dispose_before_enter()
print()
print("All tests passed!")
if __name__ == "__main__":
main()