mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-15 16:55:06 -05:00
tests: add util to run a function in separate process
This allows our tests to run in an isolated environment. For tests taht implicitly depend on import behaviour, this can prevent side-effects. The function should only be used for tests.
This commit is contained in:
46
tests/dangerously_run_function_in_subprocess.py
Normal file
46
tests/dangerously_run_function_in_subprocess.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import inspect
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
|
||||||
|
def dangerously_run_function_in_subprocess(func: Callable[[], Any]) -> tuple[str, str, int]:
|
||||||
|
"""**Use with caution! This should _only_ be used with trusted code!**
|
||||||
|
|
||||||
|
Extracts a function's source and runs it in a separate subprocess. Returns stdout, stderr, and return code
|
||||||
|
from the subprocess.
|
||||||
|
|
||||||
|
This is useful for tests where an isolated environment is required.
|
||||||
|
|
||||||
|
The function to be called must not have any arguments and must not have any closures over the scope in which is was
|
||||||
|
defined.
|
||||||
|
|
||||||
|
Any modules that the function depends on must be imported inside the function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
source_code = inspect.getsource(func)
|
||||||
|
|
||||||
|
# Must dedent the source code to avoid indentation errors
|
||||||
|
dedented_source_code = textwrap.dedent(source_code)
|
||||||
|
|
||||||
|
# Get the function name so we can call it in the subprocess
|
||||||
|
func_name = func.__name__
|
||||||
|
|
||||||
|
# Create a script that calls the function
|
||||||
|
script = f"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
{dedented_source_code}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
{func_name}()
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, "-c", textwrap.dedent(script)], # Run the script in a subprocess
|
||||||
|
capture_output=True, # Capture stdout and stderr
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return result.stdout, result.stderr, result.returncode
|
||||||
57
tests/test_dangerously_run_function_in_subprocess.py
Normal file
57
tests/test_dangerously_run_function_in_subprocess.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
from tests.dangerously_run_function_in_subprocess import dangerously_run_function_in_subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def test_simple_function():
|
||||||
|
def test_func():
|
||||||
|
print("Hello, Test!")
|
||||||
|
|
||||||
|
stdout, stderr, returncode = dangerously_run_function_in_subprocess(test_func)
|
||||||
|
|
||||||
|
assert returncode == 0
|
||||||
|
assert stdout.strip() == "Hello, Test!"
|
||||||
|
assert stderr == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_function_with_error():
|
||||||
|
def test_func():
|
||||||
|
raise ValueError("This is an error")
|
||||||
|
|
||||||
|
_stdout, stderr, returncode = dangerously_run_function_in_subprocess(test_func)
|
||||||
|
|
||||||
|
assert returncode != 0 # Should fail
|
||||||
|
assert "ValueError: This is an error" in stderr
|
||||||
|
|
||||||
|
|
||||||
|
def test_function_with_imports():
|
||||||
|
def test_func():
|
||||||
|
import math
|
||||||
|
|
||||||
|
print(math.sqrt(4))
|
||||||
|
|
||||||
|
stdout, stderr, returncode = dangerously_run_function_in_subprocess(test_func)
|
||||||
|
|
||||||
|
assert returncode == 0
|
||||||
|
assert stdout.strip() == "2.0"
|
||||||
|
assert stderr == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_function_with_sys_exit():
|
||||||
|
def test_func():
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.exit(42)
|
||||||
|
|
||||||
|
_stdout, _stderr, returncode = dangerously_run_function_in_subprocess(test_func)
|
||||||
|
|
||||||
|
assert returncode == 42 # Should return the custom exit code
|
||||||
|
|
||||||
|
|
||||||
|
def test_function_with_closure():
|
||||||
|
foo = "bar"
|
||||||
|
|
||||||
|
def test_func():
|
||||||
|
print(foo)
|
||||||
|
|
||||||
|
_stdout, _stderr, returncode = dangerously_run_function_in_subprocess(test_func)
|
||||||
|
|
||||||
|
assert returncode == 1 # Should fail because of closure
|
||||||
Reference in New Issue
Block a user