diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 664d1c443c1..23fe5f1a43f 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -191,6 +191,24 @@ class ToolNotFoundException( Exception ): pass +def create_tool_from_source( app, tool_source, config_file=None, **kwds ): + # Allow specifying a different tool subclass to instantiate + tool_module = tool_source.parse_tool_module() + if tool_module is not None: + module, cls = tool_module + mod = __import__( module, globals(), locals(), [cls] ) + ToolClass = getattr( mod, cls ) + elif tool_source.parse_tool_type(): + tool_type = tool_source.parse_tool_type() + ToolClass = tool_types.get( tool_type ) + else: + # Normal tool + root = getattr( tool_source, 'root', None ) + ToolClass = Tool + tool = ToolClass( config_file, tool_source, app, **kwds ) + return tool + + class ToolBox( BaseGalaxyToolBox ): """ A derivative of AbstractToolBox with knowledge about Tool internals - how to construct them, action types, dependency management, etc.... @@ -228,7 +246,7 @@ class ToolBox( BaseGalaxyToolBox ): # Deprecated method, TODO - eliminate calls to this in test/. return self._tools_by_id - def create_tool( self, config_file, repository_id=None, guid=None, **kwds ): + def create_tool( self, config_file, **kwds ): try: tool_source = get_tool_source( config_file, @@ -239,21 +257,10 @@ class ToolBox( BaseGalaxyToolBox ): # capture and log parsing errors global_tool_errors.add_error(config_file, "Tool XML parsing", e) raise e - # Allow specifying a different tool subclass to instantiate - tool_module = tool_source.parse_tool_module() - if tool_module is not None: - module, cls = tool_module - mod = __import__( module, globals(), locals(), [cls] ) - ToolClass = getattr( mod, cls ) - elif tool_source.parse_tool_type(): - tool_type = tool_source.parse_tool_type() - ToolClass = tool_types.get( tool_type ) - else: - # Normal tool - root = getattr( tool_source, 'root', None ) - ToolClass = Tool - tool = ToolClass( config_file, tool_source, self.app, guid=guid, repository_id=repository_id, **kwds ) - return tool + return self._create_tool_from_source( tool_source, config_file=config_file, **kwds ) + + def _create_tool_from_source( self, tool_source, **kwds ): + return create_tool_from_source( self.app, tool_source, **kwds) def get_tool_components( self, tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=False ): """ diff --git a/test/unit/tools/test_actions.py b/test/unit/tools/test_actions.py index 8689ba55e7f..d2b8ea185ba 100644 --- a/test/unit/tools/test_actions.py +++ b/test/unit/tools/test_actions.py @@ -61,7 +61,7 @@ def test_on_text_for_names(): class DefaultToolActionTestCase( unittest.TestCase, tools_support.UsesApp, tools_support.UsesTools ): def setUp( self ): - self.setup_app( mock_model=False ) + self.setup_app( ) history = model.History() self.history = history self.trans = MockTrans( diff --git a/test/unit/tools/test_collect_primary_datasets.py b/test/unit/tools/test_collect_primary_datasets.py index 2ffa7f28155..d7f241bf7bd 100644 --- a/test/unit/tools/test_collect_primary_datasets.py +++ b/test/unit/tools/test_collect_primary_datasets.py @@ -17,7 +17,7 @@ DEFAULT_EXTRA_NAME = "test1" class CollectPrimaryDatasetsTestCase( unittest.TestCase, tools_support.UsesApp, tools_support.UsesTools ): def setUp( self ): - self.setup_app( mock_model=False ) + self.setup_app( ) object_store = MockObjectStore() self.app.object_store = object_store self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) diff --git a/test/unit/tools/test_execution.py b/test/unit/tools/test_execution.py index a71f6a8f168..182324af7df 100644 --- a/test/unit/tools/test_execution.py +++ b/test/unit/tools/test_execution.py @@ -134,8 +134,9 @@ class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.Uses hda.dataset = galaxy.model.Dataset() hda.dataset.state = 'ok' - self.trans.sa_session.model_objects[ galaxy.model.HistoryDatasetAssociation ][ id ] = hda + self.trans.sa_session.add( hda ) self.history.datasets.append( hda ) + self.trans.sa_session.flush() return hda def __add_collection_dataset( self, id, collection_type="paired", *hdas ): @@ -199,7 +200,7 @@ class MockTrans( object ): self.app = app self.history = history self.user = None - self.history._active_datasets_and_roles = [hda for hda in self.app.model.context.model_objects[ galaxy.model.HistoryDatasetAssociation ] if hda.active and hda.history == history] + self.history._active_datasets_and_roles = [hda for hda in self.app.model.context.query( galaxy.model.HistoryDatasetAssociation ).all() if hda.active and hda.history == history] self.workflow_building_mode = False self.webapp = Bunch( name="galaxy" ) self.sa_session = self.app.model.context diff --git a/test/unit/tools/test_parameter_parsing.py b/test/unit/tools/test_parameter_parsing.py index 3290d39f639..1d2e765b207 100644 --- a/test/unit/tools/test_parameter_parsing.py +++ b/test/unit/tools/test_parameter_parsing.py @@ -10,7 +10,7 @@ from galaxy.util import bunch class BaseParameterTestCase( TestCase, tools_support.UsesApp ): def setUp(self): - self.setup_app( mock_model=False ) + self.setup_app( ) self.mock_tool = bunch.Bunch( app=self.app, tool_type="default", diff --git a/test/unit/tools/test_toolbox.py b/test/unit/tools/test_toolbox.py index 84f67b49e99..d4cbf5f67f5 100644 --- a/test/unit/tools/test_toolbox.py +++ b/test/unit/tools/test_toolbox.py @@ -56,7 +56,7 @@ class BaseToolBoxTestCase( unittest.TestCase, tools_support.UsesApp, tools_supp def setUp( self ): self.reindexed = False - self.setup_app( mock_model=False ) + self.setup_app( ) install_model = mapping.init( "sqlite:///:memory:", create_tables=True ) self.app.tool_cache = ToolCache() self.app.install_model = install_model diff --git a/test/unit/tools_support.py b/test/unit/tools_support.py index 8545ac5add6..123c92a0e39 100644 --- a/test/unit/tools_support.py +++ b/test/unit/tools_support.py @@ -7,18 +7,18 @@ import os.path import shutil import string import tempfile + from collections import defaultdict +from unittest_utils import galaxy_mock + import galaxy.datatypes.registry import galaxy.model -from galaxy.jobs import NoopQueue -from galaxy.model import mapping -from galaxy.tools import Tool -from galaxy.tools.deps.containers import NullContainerFinder + +from galaxy.tools import create_tool_from_source from galaxy.tools.parser import get_tool_source from galaxy.util.bunch import Bunch -from galaxy.util.dbkeys import GenomeBuilds -from galaxy.web.security import SecurityHelper + datatypes_registry = galaxy.datatypes.registry.Registry() datatypes_registry.load_datatypes() @@ -27,9 +27,11 @@ galaxy.model.set_datatypes_registry(datatypes_registry) class UsesApp( object ): - def setup_app( self, mock_model=True ): + def setup_app( self ): self.test_directory = tempfile.mkdtemp() - self.app = MockApp( self.test_directory, mock_model=mock_model ) + self.app = galaxy_mock.MockApp( ) + self.app.config.new_file_path = os.path.join(self.test_directory, "new_files") + self.app.config.admin_users = "mary@example.com" def tear_down_app( self ): shutil.rmtree( self.test_directory ) @@ -90,7 +92,7 @@ class UsesTools( object ): def __setup_tool( self ): tool_source = get_tool_source( self.tool_file ) try: - self.tool = Tool( self.tool_file, tool_source, self.app ) + self.tool = create_tool_from_source(self.app, tool_source, config_file=self.tool_file) except Exception: self.tool = None if getattr( self, "tool_action", None and self.tool): @@ -101,79 +103,6 @@ class UsesTools( object ): open( self.tool_file, "w" ).write( contents ) -class MockApp( object ): - - def __init__( self, test_directory, mock_model=True ): - # The following line is needed in order to create - # HistoryDatasetAssociations - ideally the model classes would be - # usable without the ORM infrastructure in place. - in_memomry_model = mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True ) - - self.datatypes_registry = Bunch( - integrated_datatypes_configs='/galaxy/integrated_datatypes_configs.xml', - get_datatype_by_extension=lambda ext: Bunch(), - ) - - self.config = Bunch( - outputs_to_working_directory=False, - commands_in_new_shell=True, - new_file_path=os.path.join(test_directory, "new_files"), - tool_data_path=os.path.join(test_directory, "tools"), - root=os.path.join(test_directory, "galaxy"), - admin_users="mary@example.com", - len_file_path=os.path.join( 'tool-data', 'shared', 'ucsc', 'chrom' ), - builds_file_path=os.path.join( 'tool-data', 'shared', 'ucsc', 'builds.txt.sample' ), - migrated_tools_config=os.path.join(test_directory, "migrated_tools_conf.xml"), - server_name="test_server", - preserve_python_environment="always", - ) - - # Setup some attributes for downstream extension by specific tests. - self.job_config = Bunch( - dynamic_params=None, - ) - - # Two ways to handle model layer, one is to stub out some objects that - # have an interface similar to real model (mock_model) and can keep - # track of 'persisted' objects in a map. The other is to use a real - # sqlalchemy layer but target an in memory database. Depending on what - # is being tested. - if mock_model: - # Create self.model to mimic app.model. - self.model = Bunch( context=MockContext() ) - for module_member_name in dir( galaxy.model ): - module_member = getattr(galaxy.model, module_member_name) - if type( module_member ) == type: - self.model[ module_member_name ] = module_member - else: - self.model = in_memomry_model - self.genome_builds = GenomeBuilds( self ) - self.toolbox = None - self.object_store = None - self.security = SecurityHelper(id_secret="testing") - from galaxy.security import GalaxyRBACAgent - self.job_queue = NoopQueue() - self.security_agent = GalaxyRBACAgent( self.model ) - self.tool_data_tables = {} - self.dataset_collections_service = None - self.container_finder = NullContainerFinder() - self.name = "galaxy" - self._toolbox_lock = MockLock() - - def wait_for_toolbox_reload(self, toolbox): - # TODO: If the tpm test case passes, does the operation really - # need to wait. - return True - - -class MockLock( object ): - def __enter__(self): - pass - - def __exit__(self, type, value, traceback): - pass - - class MockContext(object): def __init__(self, model_objects=None): diff --git a/test/unit/unittest_utils/galaxy_mock.py b/test/unit/unittest_utils/galaxy_mock.py index fec8ffea9f9..a52c6dba3b4 100644 --- a/test/unit/unittest_utils/galaxy_mock.py +++ b/test/unit/unittest_utils/galaxy_mock.py @@ -11,9 +11,12 @@ from galaxy import ( quota ) from galaxy.datatypes import registry +from galaxy.jobs import NoopQueue from galaxy.managers import tags from galaxy.model import mapping +from galaxy.tools.deps.containers import NullContainerFinder from galaxy.util.bunch import Bunch +from galaxy.util.dbkeys import GenomeBuilds from galaxy.web import security @@ -63,22 +66,47 @@ class MockApp( object ): self.tag_handler = tags.GalaxyTagManager( self.model.context ) self.quota_agent = quota.QuotaAgent( self.model ) self.init_datatypes() + self.job_config = Bunch( + dynamic_params=None, + ) + self.tool_data_tables = {} + self.dataset_collections_service = None + self.container_finder = NullContainerFinder() + self._toolbox_lock = MockLock() + self.genome_builds = GenomeBuilds( self ) + self.job_queue = NoopQueue() def init_datatypes( self ): datatypes_registry = registry.Registry() datatypes_registry.load_datatypes() model.set_datatypes_registry( datatypes_registry ) + self.datatypes_registry = datatypes_registry + + def wait_for_toolbox_reload(self, toolbox): + # TODO: If the tpm test case passes, does the operation really + # need to wait. + return True + + +class MockLock( object ): + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + pass class MockAppConfig( Bunch ): def __init__( self, root=None, **kwargs ): Bunch.__init__( self, **kwargs ) + root = root or '/tmp' self.security = security.SecurityHelper( id_secret='bler' ) self.use_remote_user = kwargs.get( 'use_remote_user', False ) self.file_path = '/tmp' self.jobs_directory = '/tmp' self.new_file_path = '/tmp' + self.tool_data_path = '/tmp' self.object_store_config_file = '' self.object_store = 'disk' @@ -93,6 +121,13 @@ class MockAppConfig( Bunch ): self.umask = 0o77 + # Follow two required by GenomeBuilds + self.len_file_path = os.path.join( 'tool-data', 'shared', 'ucsc', 'chrom' ) + self.builds_file_path = os.path.join( 'tool-data', 'shared', 'ucsc', 'builds.txt.sample' ) + + self.migrated_tools_config = "/tmp/migrated_tools_conf.xml" + self.preserve_python_environment = "always" + # set by MockDir self.root = root