import abc import os from contextlib import contextmanager from typing import ( cast, TYPE_CHECKING, ) from galaxy.app_unittest_utils.tools_support import ( MockContext, UsesApp, ) from galaxy.jobs import ( JobWrapper, TaskWrapper, ) from galaxy.jobs.handler import BaseJobHandlerQueue from galaxy.model import ( Base, Job, Task, User, ) from galaxy.objectstore import BaseObjectStore from galaxy.tools import ToolBox from galaxy.util.bunch import Bunch from galaxy.util.unittest import TestCase if TYPE_CHECKING: from sqlalchemy.orm import scoped_session TEST_TOOL_ID = "cufftest" TEST_VERSION_COMMAND = "bwa --version" TEST_DEPENDENCIES_COMMANDS = ". /galaxy/modules/bwa/0.5.9/env.sh" TEST_COMMAND = "" class AbstractTestCases: """Test classes that should not be collected. Classes derived from unittest.TestCase are collected only if they are at the module level: https://stackoverflow.com/a/25695512/4503125 This workaround is needed because unittest/pytest try to collect test classes even if they are abstract, and therefore their tests fails. """ class BaseWrapperTestCase(TestCase, UsesApp): def setUp(self): self.setup_app() job = Job() job.id = 345 job.tool_id = TEST_TOOL_ID job.user = User() job.object_store_id = "foo" self.model_objects: dict[type[Base], dict[int, Base]] = {Job: {345: job}} self.app.model.session = cast("scoped_session", MockContext(self.model_objects)) self.app._toolbox = cast(ToolBox, MockToolbox(MockTool(self))) self.working_directory = os.path.join(self.test_directory, "working") self.app.object_store = cast(BaseObjectStore, MockObjectStore(self.working_directory)) self.queue = cast(BaseJobHandlerQueue, MockJobQueue(self.app)) self.job = job def tearDown(self): self.tear_down_app() @contextmanager def _prepared_wrapper(self): wrapper = self._wrapper() wrapper._get_tool_evaluator = lambda *args, **kwargs: MockEvaluator(wrapper.app, wrapper.tool, wrapper.get_job(), wrapper.working_directory) # type: ignore[method-assign] wrapper.prepare() yield wrapper def test_version_path(self): wrapper = self._wrapper() version_path = wrapper.get_version_string_path() expected_path = os.path.join(self.working_directory, "outputs", "COMMAND_VERSION") assert version_path == expected_path def test_prepare_sets_command_line(self): with self._prepared_wrapper() as wrapper: assert TEST_COMMAND in wrapper.command_line def test_prepare_sets_dependency_shell_commands(self): with self._prepared_wrapper() as wrapper: assert TEST_DEPENDENCIES_COMMANDS == wrapper.dependency_shell_commands @abc.abstractmethod def _wrapper(self) -> JobWrapper: pass class TestJobWrapper(AbstractTestCases.BaseWrapperTestCase): def _wrapper(self): return JobWrapper(self.job, self.queue) class TestTaskWrapper(AbstractTestCases.BaseWrapperTestCase): def setUp(self): super().setUp() self.task = Task(self.job, self.working_directory, "prepare_bwa_job.sh") self.task.id = 4 self.model_objects[Task] = {4: self.task} def _wrapper(self): return TaskWrapper(self.task, self.queue) class MockEvaluator: def __init__(self, app, tool, job, local_working_directory): self.app = app self.tool = tool self.job = job self.local_working_directory = local_working_directory self.param_dict = {} self.use_cached_job = False def set_compute_environment(self, *args, **kwds): pass def build(self): return TEST_COMMAND, "", [], [], [] class MockJobQueue: def __init__(self, app): self.app = app self.dispatcher = MockJobDispatcher(app) class MockJobDispatcher: def __init__(self, app): pass def url_to_destination(self): pass class MockTool: def __init__(self, app): self.version_string_cmd = TEST_VERSION_COMMAND self.tool_dir = "/path/to/tools" self.dependencies = [] self.requires_galaxy_python_environment = False self.id = "mock_id" self.home_target = None self.tmp_target = None self.tool_source = Bunch(to_string=lambda: "") def get_job_destination(self, params): return Bunch(runner="local", id="local", params={}) def build_dependency_shell_commands(self, job_directory): return TEST_DEPENDENCIES_COMMANDS class MockToolbox: def __init__(self, test_tool): self.test_tool = test_tool def get(self, tool_id, default=None): assert tool_id == TEST_TOOL_ID return self.test_tool def get_tool(self, tool_id, tool_version, exact=False): tool = self.get(tool_id) return tool def tool_for_job(self, job, exact, check_access=True, user=None): tool = self.get(job.tool_id) return tool class MockObjectStore: def __init__(self, working_directory): self.working_directory = working_directory os.makedirs(working_directory) def create(self, *args, **kwds): pass def exists(self, *args, **kwargs): return True def construct_path(self, *args, **kwds): return self.working_directory def get_filename(self, *args, **kwds): if kwds.get("base_dir", "") == "job_work": return self.working_directory return None