3 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/15e54243fcc3/ Changeset: 15e54243fcc3 User: jmchilton Date: 2014-01-24 05:11:06 Summary: Slight improvement to test_execution.py Affected #: 1 file diff -r b4abf0b73f7c7a89444fd5ec01d9d4e6d2fc8c7d -r 15e54243fcc3b11055aa418aab7cf01bf412646b test/unit/tools/test_execution.py --- a/test/unit/tools/test_execution.py +++ b/test/unit/tools/test_execution.py @@ -92,7 +92,7 @@ param1="moo", runtool_btn="dummy", ) - assert template == "tool_executed.mako" + self.__assert_exeuted( template, template_vars ) # Didn't specify a rerun_remap_id so this should be None assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] is None @@ -138,7 +138,7 @@ rerun_remap_job_id=self.app.security.encode_id(123), runtool_btn="dummy", ) - assert template == "tool_executed.mako" + self.__assert_exeuted( template, template_vars ) assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] == 123 def test_invalid_remap_job( self ): @@ -189,7 +189,7 @@ param1=1, runtool_btn="dummy", ) - assert template == "tool_executed.mako" + self.__assert_exeuted( template, template_vars ) # Tool 'executed' once, with hda as param1 assert len( self.tool_action.execution_call_args ) == 1 assert self.tool_action.execution_call_args[ 0 ][ "incoming" ][ "param1" ] == hda @@ -236,10 +236,20 @@ def __assert_rerenders_tool_without_errors( self, template, template_vars ): assert template == "tool_form.mako" - assert not template_vars[ "errors" ] + self.__assert_no_errors( template_vars ) state = template_vars[ "tool_state" ] return state + def __assert_exeuted( self, template, template_vars ): + if template == "tool_form.mako": + self.__assert_no_errors( template_vars ) + self.assertEquals(template, "tool_executed.mako") + + def __assert_no_errors( self, template_vars ): + assert "errors" in template_vars, "tool_form.mako rendered without errors defintion." + errors = template_vars[ "errors" ] + assert not errors, "Template rendered unexpected errors - %s" % errors + def __init_tool( self, tool_contents ): self.__write_tool( tool_contents ) self.__setup_tool( ) https://bitbucket.org/galaxy/galaxy-central/commits/1277dd1aa267/ Changeset: 1277dd1aa267 User: jmchilton Date: 2014-01-24 05:11:06 Summary: Refactor logic related to creating tools for unit tests. So it can be reused by tool actions unit test. Affected #: 2 files diff -r 15e54243fcc3b11055aa418aab7cf01bf412646b -r 1277dd1aa2671c9a738b38f88507c8c14b2fb1a6 test/unit/tools/test_execution.py --- a/test/unit/tools/test_execution.py +++ b/test/unit/tools/test_execution.py @@ -5,15 +5,13 @@ from unittest import TestCase import galaxy.model -from galaxy.tools import Tool from galaxy.tools import DefaultToolState from galaxy.tools.parameters import params_to_incoming -from galaxy.util import parse_xml from galaxy.util.bunch import Bunch from galaxy.util import string_to_object from galaxy.util import object_to_string from galaxy.util.odict import odict -from tools_support import UsesApp +import tools_support from galaxy import eggs eggs.require( "Paste" ) @@ -61,7 +59,7 @@ ''' -class ToolExecutionTestCase( TestCase, UsesApp ): +class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.UsesTools ): def setUp(self): self.setup_app() @@ -77,7 +75,7 @@ self.tear_down_app() def test_state_new( self ): - self.__init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", # no runtool_btn, just rerenders the form mako with tool @@ -87,7 +85,7 @@ assert state.inputs[ "param1" ] == "moo" def test_execute( self ): - self.__init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", runtool_btn="dummy", @@ -97,7 +95,7 @@ assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] is None def test_execute_exception( self ): - self.__init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( SIMPLE_TOOL_CONTENTS ) self.tool_action.raise_exception( ) template, template_vars = self.__handle_with_incoming( param1="moo", @@ -108,7 +106,7 @@ assert "Error executing tool" in template_vars[ "message" ] def test_execute_errors( self ): - self.__init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( SIMPLE_TOOL_CONTENTS ) self.tool_action.return_error( ) template, template_vars = self.__handle_with_incoming( param1="moo", @@ -119,7 +117,7 @@ assert "Test Error Message" in template_vars[ "message" ], template_vars def test_redirect( self ): - self.__init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( SIMPLE_TOOL_CONTENTS ) self.tool_action.expect_redirect = True redirect_raised = False try: @@ -132,7 +130,7 @@ assert redirect_raised def test_remap_job( self ): - self.__init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", rerun_remap_job_id=self.app.security.encode_id(123), @@ -142,7 +140,7 @@ assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] == 123 def test_invalid_remap_job( self ): - self.__init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", rerun_remap_job_id='123', # Not encoded @@ -153,7 +151,7 @@ assert "invalid job" in template_vars[ "message" ] def test_repeat_state_updates( self ): - self.__init_tool( REPEAT_TOOL_CONTENTS ) + self._init_tool( REPEAT_TOOL_CONTENTS ) # Fresh state contains no repeat elements template, template_vars = self.__handle_with_incoming() @@ -182,7 +180,7 @@ assert len( state.inputs[ "repeat1" ] ) == 1 def test_data_param_execute( self ): - self.__init_tool( SIMPLE_CAT_TOOL_CONTENTS ) + self._init_tool( SIMPLE_CAT_TOOL_CONTENTS ) hda = self.__add_dataset(1) # Execute tool action template, template_vars = self.__handle_with_incoming( @@ -195,7 +193,7 @@ assert self.tool_action.execution_call_args[ 0 ][ "incoming" ][ "param1" ] == hda def test_data_param_state_update( self ): - self.__init_tool( SIMPLE_CAT_TOOL_CONTENTS ) + self._init_tool( SIMPLE_CAT_TOOL_CONTENTS ) hda = self.__add_dataset( 1 ) # Update state template, template_vars = self.__handle_with_incoming( @@ -250,18 +248,6 @@ errors = template_vars[ "errors" ] assert not errors, "Template rendered unexpected errors - %s" % errors - def __init_tool( self, tool_contents ): - self.__write_tool( tool_contents ) - self.__setup_tool( ) - - def __setup_tool( self ): - tree = parse_xml( self.tool_file ) - self.tool = Tool( self.tool_file, tree.getroot(), self.app ) - self.tool.tool_action = self.tool_action - - def __write_tool( self, contents ): - open( self.tool_file, "w" ).write( contents ) - def __string_to_state( self, state_string ): encoded_state = string_to_object( state_string ) state = DefaultToolState() diff -r 15e54243fcc3b11055aa418aab7cf01bf412646b -r 1277dd1aa2671c9a738b38f88507c8c14b2fb1a6 test/unit/tools_support.py --- a/test/unit/tools_support.py +++ b/test/unit/tools_support.py @@ -12,6 +12,8 @@ from galaxy.web.security import SecurityHelper import galaxy.model from galaxy.model import mapping +from galaxy.tools import Tool +from galaxy.util import parse_xml class UsesApp( object ): @@ -28,6 +30,21 @@ shutil.rmtree( self.test_directory ) +class UsesTools( object ): + + def _init_tool( self, tool_contents ): + self.__write_tool( tool_contents ) + self.__setup_tool( ) + + def __setup_tool( self ): + tree = parse_xml( self.tool_file ) + self.tool = Tool( self.tool_file, tree.getroot(), self.app ) + self.tool.tool_action = self.tool_action + + def __write_tool( self, contents ): + open( self.tool_file, "w" ).write( contents ) + + class MockApp( object ): def __init__( self, test_directory ): https://bitbucket.org/galaxy/galaxy-central/commits/f443913ea499/ Changeset: f443913ea499 User: jmchilton Date: 2014-01-24 05:11:07 Summary: Create unit test for some simple DefaultToolAction functionality. Want to refactor some stuff around in DefaultToolAction so can be reused when dealing with dataset collections downstream in https://github.com/jmchilton/galaxy-central/tree/collections_1 - so creating unit tests to ensure functionality is not changing. This changeset also reworks test_execution.py moving more stuff to test/unit/tools_support.py to share between test files. Affected #: 3 files diff -r 1277dd1aa2671c9a738b38f88507c8c14b2fb1a6 -r f443913ea49987da0bb651621743fefd03328cea test/unit/tools/test_actions.py --- /dev/null +++ b/test/unit/tools/test_actions.py @@ -0,0 +1,133 @@ +import unittest + +from galaxy import model +from galaxy.tools.actions import DefaultToolAction + +import tools_support + +TEST_HANDLER_NAME = "test_handler_1" + + +# I cannot think of a saner way to test if data is being wrapped than use a +# data param in the output label - though you would probably never want to do +# this. +DATA_IN_LABEL_TOOL_CONTENTS = '''<tool id="test_tool" name="Test Tool"> + <command>echo "$param1" < $out1</command> + <inputs> + <repeat name="repeat1" label="The Repeat"> + <param type="data" name="param1" value="" /> + </repeat> + </inputs> + <outputs> + <data name="out1" format="data" label="Output (${repeat1[0].param1})" /> + </outputs> +</tool> +''' + + +class DefaultToolActionTestCase( unittest.TestCase, tools_support.UsesApp, tools_support.UsesTools ): + + def setUp( self ): + self.setup_app( mock_model=False ) + history = model.History() + self.history = history + self.trans = MockTrans( + self.app, + self.history + ) + self.app.model.context.add( history ) + self.app.model.context.flush() + self.action = DefaultToolAction() + self.app.config.len_file_path = "moocow" + self.app.job_config[ "get_handler" ] = lambda h: TEST_HANDLER_NAME + self.app.object_store = MockObjectStore() + + def test_output_created( self ): + _, output = self._simple_execute() + assert len( output ) == 1 + assert "out1" in output + + def test_output_label( self ): + _, output = self._simple_execute() + self.assertEquals( output[ "out1" ].name, "Output (moo)" ) + + def test_output_label_data( self ): + hda1 = self.__add_dataset() + hda2 = self.__add_dataset() + incoming = { + "param1": hda1, + "repeat1": [ + {"param2": hda2}, + ] + } + job, output = self._simple_execute( + tools_support.SIMPLE_CAT_TOOL_CONTENTS, + incoming, + ) + self.assertEquals( output[ "out1" ].name, "Test Tool on data 2 and data 1" ) + + def test_params_wrapped( self ): + hda1 = self.__add_dataset() + _, output = self._simple_execute( + contents=DATA_IN_LABEL_TOOL_CONTENTS, + incoming=dict( repeat1=[ dict( param1=hda1 ) ] ), + ) + # Again this is a stupid way to ensure data parameters are wrapped. + self.assertEquals( output[ "out1" ].name, "Output (%s)" % hda1.dataset.get_file_name() ) + + def test_handler_set( self ): + job, _ = self._simple_execute() + assert job.handler == TEST_HANDLER_NAME + + def __add_dataset( self, state='ok' ): + hda = model.HistoryDatasetAssociation() + hda.dataset = model.Dataset() + hda.dataset.state = 'ok' + hda.dataset.external_filename = "/tmp/datasets/dataset_001.dat" + self.history.add_dataset( hda ) + self.app.model.context.flush() + return hda + + def _simple_execute( self, contents=None, incoming=None ): + if contents is None: + contents = tools_support.SIMPLE_TOOL_CONTENTS + if incoming is None: + incoming = dict(param1="moo") + self._init_tool( contents ) + return self.action.execute( + tool=self.tool, + trans=self.trans, + history=self.history, + incoming=incoming, + ) + + +class MockTrans( object ): + + def __init__( self, app, history, user=None ): + self.app = app + self.history = history + self.user = user + self.sa_session = self.app.model.context + self.model = app.model + + def db_dataset_for( self, input_db_key ): + return None + + def get_galaxy_session( self ): + return model.GalaxySession() + + def get_current_user_roles( self ): + return [] + + def log_event( self, *args, **kwargs ): + pass + + +class MockObjectStore( object ): + + def __init__( self ): + self.created_datasets = [] + + def create( self, dataset ): + self.created_datasets.append( dataset ) diff -r 1277dd1aa2671c9a738b38f88507c8c14b2fb1a6 -r f443913ea49987da0bb651621743fefd03328cea test/unit/tools/test_execution.py --- a/test/unit/tools/test_execution.py +++ b/test/unit/tools/test_execution.py @@ -1,6 +1,5 @@ """ Test Tool execution and state handling logic. """ -import os from unittest import TestCase @@ -17,17 +16,6 @@ eggs.require( "Paste" ) from paste import httpexceptions -# Simple tool with just one text parameter and output. -SIMPLE_TOOL_CONTENTS = '''<tool id="test_tool" name="Test Tool"> - <command>echo "$param1" < $out1</command> - <inputs> - <param type="text" name="param1" value="" /> - </inputs> - <outputs> - <output name="out1" format="data" /> - </outputs> -</tool>''' - # Tool with a repeat parameter, to test state update. REPEAT_TOOL_CONTENTS = '''<tool id="test_tool" name="Test Tool"><command>echo "$param1" #for $r in $repeat# "$r.param2" #end for# < $out1</command> @@ -38,22 +26,7 @@ </repeat></inputs><outputs> - <output name="out1" format="data" /> - </outputs> -</tool> -''' - -# A tool with data parameters (kind of like cat1) my favorite test tool :) -SIMPLE_CAT_TOOL_CONTENTS = '''<tool id="test_tool" name="Test Tool"> - <command>cat "$param1" #for $r in $repeat# "$r.param2" #end for# < $out1</command> - <inputs> - <param type="data" format="tabular" name="param1" value="" /> - <repeat name="repeat1" label="Repeat 1"> - <param type="data" format="tabular" name="param2" value="" /> - </repeat> - </inputs> - <outputs> - <output name="out1" format="data" /> + <data name="out1" format="data" /></outputs></tool> ''' @@ -64,18 +37,14 @@ def setUp(self): self.setup_app() self.history = galaxy.model.History() - self.app.job_config["get_job_tool_configurations"] = lambda ids: None - self.app.config.drmaa_external_runjob_script = "" - self.app.config.tool_secret = "testsecret" self.trans = MockTrans( self.app, self.history ) self.tool_action = MockAction( self.trans ) - self.tool_file = os.path.join( self.test_directory, "tool.xml" ) def tearDown(self): self.tear_down_app() def test_state_new( self ): - self._init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", # no runtool_btn, just rerenders the form mako with tool @@ -85,7 +54,7 @@ assert state.inputs[ "param1" ] == "moo" def test_execute( self ): - self._init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", runtool_btn="dummy", @@ -95,7 +64,7 @@ assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] is None def test_execute_exception( self ): - self._init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) self.tool_action.raise_exception( ) template, template_vars = self.__handle_with_incoming( param1="moo", @@ -106,7 +75,7 @@ assert "Error executing tool" in template_vars[ "message" ] def test_execute_errors( self ): - self._init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) self.tool_action.return_error( ) template, template_vars = self.__handle_with_incoming( param1="moo", @@ -117,7 +86,7 @@ assert "Test Error Message" in template_vars[ "message" ], template_vars def test_redirect( self ): - self._init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) self.tool_action.expect_redirect = True redirect_raised = False try: @@ -130,7 +99,7 @@ assert redirect_raised def test_remap_job( self ): - self._init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", rerun_remap_job_id=self.app.security.encode_id(123), @@ -140,7 +109,7 @@ assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] == 123 def test_invalid_remap_job( self ): - self._init_tool( SIMPLE_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS ) template, template_vars = self.__handle_with_incoming( param1="moo", rerun_remap_job_id='123', # Not encoded @@ -180,7 +149,7 @@ assert len( state.inputs[ "repeat1" ] ) == 1 def test_data_param_execute( self ): - self._init_tool( SIMPLE_CAT_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_CAT_TOOL_CONTENTS ) hda = self.__add_dataset(1) # Execute tool action template, template_vars = self.__handle_with_incoming( @@ -193,7 +162,7 @@ assert self.tool_action.execution_call_args[ 0 ][ "incoming" ][ "param1" ] == hda def test_data_param_state_update( self ): - self._init_tool( SIMPLE_CAT_TOOL_CONTENTS ) + self._init_tool( tools_support.SIMPLE_CAT_TOOL_CONTENTS ) hda = self.__add_dataset( 1 ) # Update state template, template_vars = self.__handle_with_incoming( diff -r 1277dd1aa2671c9a738b38f88507c8c14b2fb1a6 -r f443913ea49987da0bb651621743fefd03328cea test/unit/tools_support.py --- a/test/unit/tools_support.py +++ b/test/unit/tools_support.py @@ -14,32 +14,64 @@ from galaxy.model import mapping from galaxy.tools import Tool from galaxy.util import parse_xml +from galaxy.jobs import NoopQueue class UsesApp( object ): - def setup_app( self ): - # The following line is needed in order to create - # HistoryDatasetAssociations - ideally the model classes would be - # usable without the ORM infrastructure in place. - mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True ) + def setup_app( self, mock_model=True ): self.test_directory = tempfile.mkdtemp() - self.app = MockApp(self.test_directory) + self.app = MockApp( self.test_directory, mock_model=mock_model ) def tear_down_app( self ): shutil.rmtree( self.test_directory ) +# Simple tool with just one text parameter and output. +SIMPLE_TOOL_CONTENTS = '''<tool id="test_tool" name="Test Tool"> + <command>echo "$param1" < $out1</command> + <inputs> + <param type="text" name="param1" value="" /> + </inputs> + <outputs> + <data name="out1" format="data" label="Output ($param1)" /> + </outputs> +</tool> +''' + + +# A tool with data parameters (kind of like cat1) my favorite test tool :) +SIMPLE_CAT_TOOL_CONTENTS = '''<tool id="test_tool" name="Test Tool"> + <command>cat "$param1" #for $r in $repeat# "$r.param2" #end for# < $out1</command> + <inputs> + <param type="data" format="tabular" name="param1" value="" /> + <repeat name="repeat1" label="Repeat 1"> + <param type="data" format="tabular" name="param2" value="" /> + </repeat> + </inputs> + <outputs> + <data name="out1" format="data" /> + </outputs> +</tool> +''' + + class UsesTools( object ): def _init_tool( self, tool_contents ): + self.tool_file = os.path.join( self.test_directory, "tool.xml" ) + self.app.config.drmaa_external_runjob_script = "" + self.app.config.tool_secret = "testsecret" + self.app.config.track_jobs_in_database = False + self.app.job_config["get_job_tool_configurations"] = lambda ids: [Bunch(handler=Bunch())] self.__write_tool( tool_contents ) self.__setup_tool( ) def __setup_tool( self ): tree = parse_xml( self.tool_file ) self.tool = Tool( self.tool_file, tree.getroot(), self.app ) - self.tool.tool_action = self.tool_action + if getattr( self, "tool_action", None ): + self.tool.tool_action = self.tool_action def __write_tool( self, contents ): open( self.tool_file, "w" ).write( contents ) @@ -47,7 +79,11 @@ class MockApp( object ): - def __init__( self, test_directory ): + 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', @@ -64,15 +100,27 @@ # Setup some attributes for downstream extension by specific tests. self.job_config = Bunch() - # 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 + + # 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.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 ) class MockContext(object): Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.