Outline new API based galaxy interactor for functional tests.

Add mechanism to tool test parser code to determine which Galaxy interactor should be used ('twill' or 'api' are current options). This maybe a stop gap to force usage of the API interactor until that becomes the default or may prove in the long term to be essential if there are certain tools that will always require Twill-specific functionality or if new browser-based JavaScript (e.g. w/selenium) are implemented. An interactor value can be specified at the tests level and/or at the level of individual test elements in the tool XML (using the 'interactor' attribute on either element).

The current default interactor is 'twill'. The default interactor app-wide can be overridden with the GALAXY_TEST_DEFAULT_INTERACTOR environment variable.
This commit is contained in:
John Chilton
2013-11-21 19:17:12 -06:00
parent a427e33788
commit a0bc80ed4c
3 changed files with 60 additions and 16 deletions
+2 -12
View File
@@ -52,7 +52,7 @@ from galaxy.tools.parameters.input_translation import ToolInputTranslator
from galaxy.tools.parameters.output import ToolOutputActionGroup
from galaxy.tools.parameters.validation import LateValidationError
from galaxy.tools.filters import FilterFactory
from galaxy.tools.test import ToolTestBuilder
from galaxy.tools.test import parse_tests_elem
from galaxy.util import listify, parse_xml, rst_to_html, string_as_bool, string_to_object, xml_text, xml_to_string
from galaxy.util.bunch import Bunch
from galaxy.util.expressions import ExpressionContext
@@ -1222,7 +1222,7 @@ class Tool( object, Dictifiable ):
tests_elem = root.find( "tests" )
if tests_elem:
try:
self.parse_tests( tests_elem )
self.tests = parse_tests_elem( self, tests_elem )
except:
log.exception( "Failed to parse tool tests" )
else:
@@ -1564,16 +1564,6 @@ class Tool( object, Dictifiable ):
log.error( "Traceback: %s" % trace_msg )
return return_level
def parse_tests( self, tests_elem ):
"""
Parse any "<test>" elements, create a `ToolTestBuilder` for each and
store in `self.tests`.
"""
self.tests = []
for i, test_elem in enumerate( tests_elem.findall( 'test' ) ):
test = ToolTestBuilder( self, test_elem, i )
self.tests.append( test )
def parse_input_page( self, input_elem, enctypes ):
"""
Parse a page of inputs. This basically just calls 'parse_input_elem',
+25 -3
View File
@@ -1,3 +1,4 @@
import os
import os.path
from parameters import basic
from parameters import grouping
@@ -8,6 +9,21 @@ log = logging.getLogger( __name__ )
DEFAULT_FTYPE = 'auto'
DEFAULT_DBKEY = 'hg17'
DEFAULT_INTERACTOR = "twill" # Default mechanism test code uses for interacting with Galaxy instance.
def parse_tests_elem(tool, tests_elem):
"""
Build ToolTestBuilder objects for each "<test>" elements and
return default interactor (if any).
"""
default_interactor = os.environ.get( 'GALAXY_TEST_DEFAULT_INTERACTOR', DEFAULT_INTERACTOR )
tests_default_interactor = tests_elem.get( 'interactor', default_interactor )
tests = []
for i, test_elem in enumerate( tests_elem.findall( 'test' ) ):
test = ToolTestBuilder( tool, test_elem, i, default_interactor=tests_default_interactor )
tests.append( test )
return tests
class ToolTestBuilder( object ):
@@ -17,7 +33,7 @@ class ToolTestBuilder( object ):
doing dynamic tests in this way allows better integration)
"""
def __init__( self, tool, test_elem, i ):
def __init__( self, tool, test_elem, i, default_interactor ):
name = test_elem.get( 'name', 'Test-%d' % (i + 1) )
maxseconds = int( test_elem.get( 'maxseconds', '120' ) )
@@ -30,7 +46,7 @@ class ToolTestBuilder( object ):
self.error = False
self.exception = None
self.__parse_elem( test_elem, i )
self.__parse_elem( test_elem, i, default_interactor )
def test_data( self ):
"""
@@ -57,12 +73,18 @@ class ToolTestBuilder( object ):
yield data_dict
def __parse_elem( self, test_elem, i ):
def __parse_elem( self, test_elem, i, default_interactor ):
# Composite datasets need a unique name: each test occurs in a fresh
# history, but we'll keep it unique per set of tests - use i (test #)
# and composite_data_names_counter (instance per test #)
composite_data_names_counter = 0
try:
# Mechanism test code uses for interacting with Galaxy instance,
# until 'api' is the default switch this to API to use its new
# features. Once 'api' is the default set to 'twill' to use legacy
# features or workarounds.
self.interactor = test_elem.get( 'interactor', default_interactor )
for param_elem in test_elem.findall( "param" ):
attrib = dict( param_elem.attrib )
if 'values' in attrib:
+33 -1
View File
@@ -1,6 +1,8 @@
import sys
import new
import os
from galaxy.tools.parameters import grouping
from galaxy.util import string_as_bool
from base.twilltestcase import TwillTestCase
import galaxy.model
from galaxy.model.orm import and_, desc
@@ -20,7 +22,7 @@ class ToolTestCase( TwillTestCase ):
self.__handle_test_def_errors( testdef )
galaxy_interactor = GalaxyInteractorTwill( self )
galaxy_interactor = self.__galaxy_interactor( testdef )
test_history = galaxy_interactor.new_history()
@@ -38,6 +40,11 @@ class ToolTestCase( TwillTestCase ):
galaxy_interactor.delete_history( test_history )
def __galaxy_interactor( self, testdef ):
interactor_key = testdef.interactor
interactor_class = GALAXY_INTERACTORS[ interactor_key ]
return interactor_class( self )
def __handle_test_def_errors(self, testdef):
# If the test generation had an error, raise
if testdef.error:
@@ -68,6 +75,25 @@ class ToolTestCase( TwillTestCase ):
raise
class GalaxyInteractorApi( object ):
def __init__( self, twill_test_case ):
self.twill_test_case = twill_test_case
self.master_api_key = twill_test_case.master_api_key
def new_history( self ):
return None
def stage_data_async( self, test_data, shed_tool_id, async=True ):
return lambda: True
def run_tool( self, testdef ):
return []
def delete_history( self, history ):
return None
class GalaxyInteractorTwill( object ):
def __init__( self, twill_test_case ):
@@ -231,3 +257,9 @@ def build_tests( testing_shed_tools=False, master_api_key=None ):
# from baseclasses (which should be a tuple of classes) and with namespace dict.
new_class_obj = new.classobj( name, baseclasses, namespace )
G[ name ] = new_class_obj
GALAXY_INTERACTORS = {
'api': GalaxyInteractorApi,
'twill': GalaxyInteractorTwill,
}