conftest.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import asyncio
  2. import os
  3. import time
  4. import pytest
  5. from pytest import TempPathFactory
  6. from openhands.core.config import AppConfig, SandboxConfig, load_from_env
  7. from openhands.events import EventStream
  8. from openhands.runtime.client.runtime import EventStreamRuntime
  9. from openhands.runtime.plugins import AgentSkillsRequirement, JupyterRequirement
  10. from openhands.runtime.runtime import Runtime
  11. from openhands.storage import get_file_store
  12. @pytest.fixture(autouse=True)
  13. def print_method_name(request):
  14. print('\n########################################################################')
  15. print(f'Running test: {request.node.name}')
  16. print('########################################################################')
  17. yield
  18. @pytest.fixture
  19. def temp_dir(tmp_path_factory: TempPathFactory) -> str:
  20. return str(tmp_path_factory.mktemp('test_runtime'))
  21. TEST_RUNTIME = os.getenv('TEST_RUNTIME', 'eventstream')
  22. # Depending on TEST_RUNTIME, feed the appropriate box class(es) to the test.
  23. def get_box_classes():
  24. runtime = TEST_RUNTIME
  25. if runtime.lower() == 'eventstream':
  26. return [EventStreamRuntime]
  27. else:
  28. raise ValueError(f'Invalid runtime: {runtime}')
  29. # This assures that all tests run together per runtime, not alternating between them,
  30. # which cause errors (especially outside GitHub actions).
  31. @pytest.fixture(scope='module', params=get_box_classes())
  32. def box_class(request):
  33. time.sleep(2)
  34. return request.param
  35. # TODO: We will change this to `run_as_user` when `ServerRuntime` is deprecated.
  36. # since `EventStreamRuntime` supports running as an arbitrary user.
  37. @pytest.fixture(scope='module', params=[True, False])
  38. def run_as_openhands(request):
  39. time.sleep(1)
  40. return request.param
  41. @pytest.fixture(scope='module', params=[True, False])
  42. def enable_auto_lint(request):
  43. time.sleep(1)
  44. return request.param
  45. @pytest.fixture(scope='module', params=None)
  46. def base_container_image(request):
  47. time.sleep(1)
  48. env_image = os.environ.get('SANDBOX_BASE_CONTAINER_IMAGE')
  49. if env_image:
  50. request.param = env_image
  51. else:
  52. if not hasattr(request, 'param'): # prevent runtime AttributeError
  53. request.param = None
  54. if request.param is None and hasattr(request.config, 'sandbox'):
  55. try:
  56. request.param = request.config.sandbox.getoption(
  57. '--base_container_image'
  58. )
  59. except ValueError:
  60. request.param = None
  61. if request.param is None:
  62. request.param = pytest.param(
  63. 'nikolaik/python-nodejs:python3.11-nodejs22',
  64. 'python:3.11-bookworm',
  65. 'node:22-bookworm',
  66. 'golang:1.23-bookworm',
  67. )
  68. print(f'Container image: {request.param}')
  69. return request.param
  70. @pytest.fixture
  71. async def runtime(temp_dir, box_class, run_as_openhands):
  72. runtime = await _load_runtime(temp_dir, box_class, run_as_openhands)
  73. yield runtime
  74. await runtime.close()
  75. await asyncio.sleep(1)
  76. async def _load_runtime(
  77. temp_dir,
  78. box_class,
  79. run_as_openhands: bool = True,
  80. enable_auto_lint: bool = False,
  81. base_container_image: str | None = None,
  82. browsergym_eval_env: str | None = None,
  83. ) -> Runtime:
  84. sid = 'test'
  85. cli_session = 'main_test'
  86. # AgentSkills need to be initialized **before** Jupyter
  87. # otherwise Jupyter will not access the proper dependencies installed by AgentSkills
  88. plugins = [AgentSkillsRequirement(), JupyterRequirement()]
  89. config = AppConfig(
  90. workspace_base=temp_dir,
  91. workspace_mount_path=temp_dir,
  92. sandbox=SandboxConfig(
  93. use_host_network=True,
  94. browsergym_eval_env=browsergym_eval_env,
  95. ),
  96. )
  97. load_from_env(config, os.environ)
  98. config.run_as_openhands = run_as_openhands
  99. config.sandbox.enable_auto_lint = enable_auto_lint
  100. if base_container_image is not None:
  101. config.sandbox.base_container_image = base_container_image
  102. file_store = get_file_store(config.file_store, config.file_store_path)
  103. event_stream = EventStream(cli_session, file_store)
  104. runtime = box_class(
  105. config=config,
  106. event_stream=event_stream,
  107. sid=sid,
  108. plugins=plugins,
  109. )
  110. await runtime.ainit()
  111. await asyncio.sleep(1)
  112. return runtime
  113. # Export necessary function
  114. __all__ = ['_load_runtime']