import os
import subprocess
import json
def execute_command_with_timeout(command, timeout):
"""
Executes a shell command with a specified timeout.
Parameters:
- command: list or str, the command to execute.
- timeout: int or float, the timeout duration in seconds.
Returns:
- stdout: str, the standard output from the command.
- stderr: str, the standard error from the command.
- returncode: int, the return code from the command execution.
Raises:
- subprocess.TimeoutExpired: if the command execution exceeds the timeout.
- subprocess.CalledProcessError: if the command exits with a non-zero status.
- Exception: for any other exceptions that occur.
"""
try:
# Execute the command with the specified timeout
result = subprocess.run(
command,
check=True, # Raises CalledProcessError if return code is non-zero
capture_output=True, # Captures stdout and stderr
text=True, # Returns output as text strings
stdin=subprocess.DEVNULL, # Prevents the subprocess from waiting for input
timeout=timeout # Timeout for the command execution
)
# Return the output and return code
return result.stdout, result.stderr, result.returncode
except subprocess.TimeoutExpired as e:
print(f"Command '{e.cmd}' timed out after {timeout} seconds.")
# Re-raise the exception if you want the calling code to handle it
raise
except subprocess.CalledProcessError as e:
print(f"Command '{e.cmd}' exited with non-zero status {e.returncode}.")
print(f"Standard Error Output:\n{e.stderr}")
# Re-raise the exception if you want the calling code to handle it
raise
except Exception as e:
print(f"An unexpected error occurred: {e}")
# Re-raise the exception if you want the calling code to handle it
raise
def test_generated_code(path_file, path_test, test_file_name, output_path, debug):
""" Function that tests a generated file
Args:
path_file: The path to the generated file
path_test: The path to the test file
test_file_name: The name of the test executable
output_path: The directory where the executable and output will be placed
debug: Boolean flag for printing debug information
Returns:
passed: The number of tests that passed
total: The total number of tests
tests_output: The output from the tests, typically a summary dictionary
"""
# If debugging is true then print information that the file will be tested
if debug:
print(f"Testing the file {path_file}\n using the test file {path_test}")
# Normalize paths
path_file = os.path.normpath(path_file)
path_test = os.path.normpath(path_test)
# Create the output directory if it does not exist
if not os.path.exists(output_path):
os.makedirs(output_path)
path_to_executable = os.path.normpath(os.path.join(output_path, test_file_name))
# Compile the file and the test cases
command = [
"gcc",
path_file,
path_test,
"-o",
path_to_executable
]
try:
# Use the execute_command_with_timeout function
stdout, stderr, returncode = execute_command_with_timeout(command, timeout=120)
if debug:
print("Compilation output:", stdout)
except subprocess.TimeoutExpired as e:
print("Compilation timed out!")
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": f"Compilation timed out: {str(e)}"}}
except subprocess.CalledProcessError as e:
print("Compilation failed!")
print(e.stderr)
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": f"Compilation failed: {e.stderr}"}}
except Exception as e:
print(f"An unexpected error occurred during compilation: {e}")
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": f"Compilation error: {str(e)}"}}
# Run the test cases
command = [path_to_executable, f"{path_to_executable}.json"]
try:
# Use the execute_command_with_timeout function
stdout, stderr, returncode = execute_command_with_timeout(command, timeout=1200)
if stderr:
print("Test execution failed!")
print(stderr)
except subprocess.TimeoutExpired as e:
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": f"Test execution timed out: {str(e)}"}}
except subprocess.CalledProcessError as e:
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": f"Test execution failed: {e.stderr}"}}
except OSError as e:
print(f"Error: The test cases could not be run. {str(e)}")
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": f"Execution error: {str(e)}"}}
except Exception as e:
print(f"An unexpected error occurred during test execution: {e}")
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": f"Execution error: {str(e)}"}}
# Verify the JSON file existence
json_file_path = path_to_executable + ".json"
if not os.path.exists(json_file_path):
print(f"Error: The output JSON file '{json_file_path}' was not created.")
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": "Output JSON file missing."}}
# Read the output JSON file
try:
with open(json_file_path, "r", encoding="utf-8") as file:
tests_output = json.load(file)
test_information = tests_output[-1]['summary']
passed = test_information['passed']
total = test_information['total']
except (json.JSONDecodeError, KeyError, IndexError) as e:
print(f"Error reading JSON file: {str(e)}")
return 0, 0, {"summary": {"passed": 0, "failed": 0, "total": 0, "information": "Invalid JSON format."}}
# Clean up
try:
os.remove(path_to_executable)
os.remove(json_file_path)
except OSError as e:
print(f"Error during cleanup: {str(e)}")
return passed, total, tests_output