Files
galaxy/test/api/helpers.py
T
John Chilton ddeff4ec0c New model tool that filters failed datasets out of a collection.
This differs from a traditional tool in that its inputs don't need to be in an 'ok' state and instead of creating new datasets and duplicating data on disk, new HDAs are created from the existing datasets.
2016-06-10 13:34:18 -04:00

489 lines
19 KiB
Python

from base import api_asserts
from operator import itemgetter
import time
import json
import StringIO
from pkg_resources import resource_string
# Simple workflow that takes an input and call cat wrapper on it.
workflow_str = resource_string( __name__, "test_workflow_1.ga" )
# Simple workflow that takes an input and filters with random lines twice in a
# row - first grabbing 8 lines at random and then 6.
workflow_random_x2_str = resource_string( __name__, "test_workflow_2.ga" )
DEFAULT_TIMEOUT = 15 # Secs to wait for state to turn ok
def skip_without_tool( tool_id ):
""" Decorate an API test method as requiring a specific tool,
have nose skip the test case is the tool is unavailable.
"""
def method_wrapper( method ):
def get_tool_ids( api_test_case ):
index = api_test_case.galaxy_interactor.get( "tools", data=dict(in_panel=False) )
tools = index.json()
# In panels by default, so flatten out sections...
tool_ids = map( itemgetter( "id" ), tools )
return tool_ids
def wrapped_method( api_test_case, *args, **kwargs ):
if tool_id not in get_tool_ids( api_test_case ):
from nose.plugins.skip import SkipTest
raise SkipTest( )
return method( api_test_case, *args, **kwargs )
# Must preserve method name so nose can detect and report tests by
# name.
wrapped_method.__name__ = method.__name__
return wrapped_method
return method_wrapper
# Deprecated mixin, use dataset populator instead.
# TODO: Rework existing tests to target DatasetPopulator in a setup method instead.
class TestsDatasets:
def _new_dataset( self, history_id, content='TestData123', **kwds ):
return DatasetPopulator( self.galaxy_interactor ).new_dataset( history_id, content=content, **kwds)
def _wait_for_history( self, history_id, assert_ok=False ):
return DatasetPopulator( self.galaxy_interactor ).wait_for_history( history_id, assert_ok=assert_ok )
def _new_history( self, **kwds ):
return DatasetPopulator( self.galaxy_interactor ).new_history( **kwds )
def _upload_payload( self, history_id, content, **kwds ):
return DatasetPopulator( self.galaxy_interactor ).upload_payload( history_id, content, **kwds )
def _run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
return DatasetPopulator( self.galaxy_interactor ).run_tool_payload( tool_id, inputs, history_id, **kwds )
class BaseDatasetPopulator( object ):
""" Abstract description of API operations optimized for testing
Galaxy - implementations must implement _get and _post.
"""
def new_dataset( self, history_id, content='TestData123', wait=False, **kwds ):
payload = self.upload_payload( history_id, content, **kwds )
run_response = self._post( "tools", data=payload ).json()
if wait:
job = run_response["jobs"][0]
self.wait_for_job(job["id"])
self.wait_for_history(history_id, assert_ok=True)
return run_response["outputs"][0]
def wait_for_history( self, history_id, assert_ok=False, timeout=DEFAULT_TIMEOUT ):
try:
return wait_on_state( lambda: self._get( "histories/%s" % history_id ), assert_ok=assert_ok, timeout=timeout )
except AssertionError:
self._summarize_history_errors( history_id )
raise
def wait_for_job( self, job_id, assert_ok=False, timeout=DEFAULT_TIMEOUT ):
return wait_on_state( lambda: self.get_job_details( job_id ), assert_ok=assert_ok, timeout=timeout )
def get_job_details( self, job_id, full=False ):
return self._get( "jobs/%s?full=%s" % (job_id, full) )
def _summarize_history_errors( self, history_id ):
pass
def new_history( self, **kwds ):
name = kwds.get( "name", "API Test History" )
create_history_response = self._post( "histories", data=dict( name=name ) )
history_id = create_history_response.json()[ "id" ]
return history_id
def upload_payload( self, history_id, content, **kwds ):
name = kwds.get( "name", "Test Dataset" )
dbkey = kwds.get( "dbkey", "?" )
file_type = kwds.get( "file_type", 'txt' )
upload_params = {
'files_0|NAME': name,
'dbkey': dbkey,
'file_type': file_type,
}
if isinstance( content, file ):
upload_params[ "files_0|file_data"] = content
else:
upload_params[ 'files_0|url_paste' ] = content
if "to_posix_lines" in kwds:
upload_params[ "files_0|to_posix_lines"] = kwds[ "to_posix_lines" ]
if "space_to_tab" in kwds:
upload_params[ "files_0|space_to_tab" ] = kwds[ "space_to_tab" ]
return self.run_tool_payload(
tool_id='upload1',
inputs=upload_params,
history_id=history_id,
upload_type='upload_dataset'
)
def run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
if "files_0|file_data" in inputs:
kwds[ "__files" ] = { "files_0|file_data": inputs[ "files_0|file_data" ] }
del inputs[ "files_0|file_data" ]
return dict(
tool_id=tool_id,
inputs=json.dumps(inputs),
history_id=history_id,
**kwds
)
def run_tool( self, tool_id, inputs, history_id, **kwds ):
payload = self.run_tool_payload( tool_id, inputs, history_id, **kwds )
tool_response = self._post( "tools", data=payload )
api_asserts.assert_status_code_is( tool_response, 200 )
return tool_response.json()
def get_history_dataset_content( self, history_id, wait=True, **kwds ):
dataset_id = self.__history_content_id( history_id, wait=wait, **kwds )
display_response = self.__get_contents_request( history_id, "/%s/display" % dataset_id )
assert display_response.status_code == 200, display_response.content
return display_response.content
def get_history_dataset_details( self, history_id, **kwds ):
dataset_id = self.__history_content_id( history_id, **kwds )
details_response = self.__get_contents_request( history_id, "/datasets/%s" % dataset_id )
assert details_response.status_code == 200
return details_response.json()
def get_history_collection_details( self, history_id, **kwds ):
hdca_id = self.__history_content_id( history_id, **kwds )
details_response = self.__get_contents_request( history_id, "/dataset_collections/%s" % hdca_id )
assert details_response.status_code == 200, details_response.content
return details_response.json()
def __history_content_id( self, history_id, wait=True, **kwds ):
if wait:
assert_ok = kwds.get( "assert_ok", True )
self.wait_for_history( history_id, assert_ok=assert_ok )
# kwds should contain a 'dataset' object response, a 'dataset_id' or
# the last dataset in the history will be fetched.
if "dataset_id" in kwds:
dataset_id = kwds[ "dataset_id" ]
elif "dataset" in kwds:
dataset_id = kwds[ "dataset" ][ "id" ]
else:
hid = kwds.get( "hid", None ) # If not hid, just grab last dataset
if hid:
index = hid - 1
else:
# No hid specified - just grab most recent element.
index = -1
dataset_contents = self.__get_contents_request( history_id ).json()
dataset_id = dataset_contents[ index ][ "id" ]
return dataset_id
def __get_contents_request( self, history_id, suffix=""):
url = "histories/%s/contents" % history_id
if suffix:
url = "%s%s" % ( url, suffix )
return self._get( url )
class DatasetPopulator( BaseDatasetPopulator ):
def __init__( self, galaxy_interactor ):
self.galaxy_interactor = galaxy_interactor
def _post( self, route, data={}, files=None ):
files = data.get( "__files", None )
if files is not None:
del data[ "__files" ]
return self.galaxy_interactor.post( route, data, files=files )
def _get( self, route ):
return self.galaxy_interactor.get( route )
def _summarize_history_errors( self, history_id ):
self.galaxy_interactor._summarize_history_errors( history_id )
class BaseWorkflowPopulator( object ):
def load_workflow( self, name, content=workflow_str, add_pja=False ):
workflow = json.loads( content )
workflow[ "name" ] = name
if add_pja:
tool_step = workflow[ "steps" ][ "2" ]
tool_step[ "post_job_actions" ][ "RenameDatasetActionout_file1" ] = dict(
action_type="RenameDatasetAction",
output_name="out_file1",
action_arguments=dict( newname="foo ${replaceme}" ),
)
return workflow
def load_random_x2_workflow( self, name ):
return self.load_workflow( name, content=workflow_random_x2_str )
def load_workflow_from_resource( self, name, filename=None ):
if filename is None:
filename = "%s.ga" % name
content = resource_string( __name__, filename )
return self.load_workflow( name, content=content )
def simple_workflow( self, name, **create_kwds ):
workflow = self.load_workflow( name )
return self.create_workflow( workflow, **create_kwds )
def create_workflow( self, workflow, **create_kwds ):
upload_response = self.create_workflow_response( workflow, **create_kwds )
uploaded_workflow_id = upload_response.json()[ "id" ]
return uploaded_workflow_id
def create_workflow_response( self, workflow, **create_kwds ):
data = dict(
workflow=json.dumps( workflow ),
**create_kwds
)
upload_response = self._post( "workflows/upload", data=data )
return upload_response
def wait_for_invocation( self, workflow_id, invocation_id, timeout=DEFAULT_TIMEOUT ):
url = "workflows/%s/usage/%s" % ( workflow_id, invocation_id )
return wait_on_state( lambda: self._get( url ), timeout=timeout )
def wait_for_workflow( self, workflow_id, invocation_id, history_id, assert_ok=True, timeout=DEFAULT_TIMEOUT ):
""" Wait for a workflow invocation to completely schedule and then history
to be complete. """
self.wait_for_invocation( workflow_id, invocation_id, timeout=timeout )
self.dataset_populator.wait_for_history( history_id, assert_ok=assert_ok, timeout=timeout )
class WorkflowPopulator( BaseWorkflowPopulator ):
def __init__( self, galaxy_interactor ):
self.galaxy_interactor = galaxy_interactor
self.dataset_populator = DatasetPopulator( galaxy_interactor )
def _post( self, route, data={} ):
return self.galaxy_interactor.post( route, data )
def _get( self, route ):
return self.galaxy_interactor.get( route )
class LibraryPopulator( object ):
def __init__( self, api_test_case ):
self.api_test_case = api_test_case
self.galaxy_interactor = api_test_case.galaxy_interactor
def new_private_library( self, name ):
library = self.new_library( name )
library_id = library[ "id" ]
role_id = self.user_private_role_id()
self.set_permissions( library_id, role_id )
return library
def new_library( self, name ):
data = dict( name=name )
create_response = self.galaxy_interactor.post( "libraries", data=data, admin=True )
return create_response.json()
def set_permissions( self, library_id, role_id=None ):
if role_id:
perm_list = json.dumps( role_id )
else:
perm_list = json.dumps( [] )
permissions = dict(
LIBRARY_ACCESS_in=perm_list,
LIBRARY_MODIFY_in=perm_list,
LIBRARY_ADD_in=perm_list,
LIBRARY_MANAGE_in=perm_list,
)
self.galaxy_interactor.post( "libraries/%s/permissions" % library_id, data=permissions, admin=True )
def user_email( self ):
users_response = self.galaxy_interactor.get( "users" )
users = users_response.json()
assert len( users ) == 1
return users[ 0 ][ "email" ]
def user_private_role_id( self ):
user_email = self.user_email()
roles_response = self.api_test_case.galaxy_interactor.get( "roles", admin=True )
users_roles = [ r for r in roles_response.json() if r[ "name" ] == user_email ]
assert len( users_roles ) == 1
return users_roles[ 0 ][ "id" ]
def create_dataset_request( self, library, **kwds ):
create_data = {
"folder_id": kwds.get( "folder_id", library[ "root_folder_id" ] ),
"create_type": "file",
"files_0|NAME": kwds.get( "name", "NewFile" ),
"upload_option": kwds.get( "upload_option", "upload_file" ),
"file_type": kwds.get( "file_type", "auto" ),
"db_key": kwds.get( "db_key", "?" ),
}
files = {
"files_0|file_data": kwds.get( "file", StringIO.StringIO( kwds.get( "contents", "TestData" ) ) ),
}
return create_data, files
def new_library_dataset( self, name, **create_dataset_kwds ):
library = self.new_private_library( name )
payload, files = self.create_dataset_request( library, **create_dataset_kwds )
url_rel = "libraries/%s/contents" % ( library[ "id" ] )
dataset = self.api_test_case.galaxy_interactor.post( url_rel, payload, files=files ).json()[0]
def show():
return self.api_test_case.galaxy_interactor.get( "libraries/%s/contents/%s" % ( library[ "id" ], dataset[ "id" ] ) )
wait_on_state(show, timeout=DEFAULT_TIMEOUT)
return show().json()
class BaseDatasetCollectionPopulator( object ):
def create_list_from_pairs( self, history_id, pairs ):
element_identifiers = []
for i, pair in enumerate( pairs ):
element_identifiers.append( dict(
name="test%d" % i,
src="hdca",
id=pair
) )
payload = dict(
instance_type="history",
history_id=history_id,
element_identifiers=json.dumps(element_identifiers),
collection_type="list:paired",
)
return self.__create( payload )
def create_list_of_pairs_in_history( self, history_id, **kwds ):
pair1 = self.create_pair_in_history( history_id, **kwds ).json()["id"]
return self.create_list_from_pairs( history_id, [ pair1 ] )
def create_pair_in_history( self, history_id, **kwds ):
payload = self.create_pair_payload(
history_id,
instance_type="history",
**kwds
)
return self.__create( payload )
def create_list_in_history( self, history_id, **kwds ):
payload = self.create_list_payload(
history_id,
instance_type="history",
**kwds
)
return self.__create( payload )
def create_list_payload( self, history_id, **kwds ):
return self.__create_payload( history_id, identifiers_func=self.list_identifiers, collection_type="list", **kwds )
def create_pair_payload( self, history_id, **kwds ):
return self.__create_payload( history_id, identifiers_func=self.pair_identifiers, collection_type="paired", **kwds )
def __create_payload( self, history_id, identifiers_func, collection_type, **kwds ):
contents = None
if "contents" in kwds:
contents = kwds[ "contents" ]
del kwds[ "contents" ]
if "element_identifiers" not in kwds:
kwds[ "element_identifiers" ] = json.dumps( identifiers_func( history_id, contents=contents ) )
payload = dict(
history_id=history_id,
collection_type=collection_type,
**kwds
)
return payload
def pair_identifiers( self, history_id, contents=None ):
hda1, hda2 = self.__datasets( history_id, count=2, contents=contents )
element_identifiers = [
dict( name="forward", src="hda", id=hda1[ "id" ] ),
dict( name="reverse", src="hda", id=hda2[ "id" ] ),
]
return element_identifiers
def list_identifiers( self, history_id, contents=None ):
count = 3 if not contents else len( contents )
# Contents can be a list of strings (with name auto-assigned here) or a list of
# 2-tuples of form (name, dataset_content).
if contents and isinstance(contents[0], tuple):
hdas = self.__datasets( history_id, count=count, contents=[c[1] for c in contents] )
def hda_to_identifier(i, hda):
return dict(name=contents[i][0], src="hda", id=hda["id"])
else:
hdas = self.__datasets( history_id, count=count, contents=contents )
def hda_to_identifier(i, hda):
return dict(name="data%d" % (i + 1), src="hda", id=hda["id"])
element_identifiers = [hda_to_identifier(i, hda) for (i, hda) in enumerate(hdas)]
return element_identifiers
def __create( self, payload ):
return self._create_collection( payload )
def __datasets( self, history_id, count, contents=None ):
datasets = []
for i in xrange( count ):
new_kwds = {}
if contents:
new_kwds[ "content" ] = contents[ i ]
datasets.append( self.dataset_populator.new_dataset( history_id, **new_kwds ) )
return datasets
class DatasetCollectionPopulator( BaseDatasetCollectionPopulator ):
def __init__( self, galaxy_interactor ):
self.galaxy_interactor = galaxy_interactor
self.dataset_populator = DatasetPopulator( galaxy_interactor )
def _create_collection( self, payload ):
create_response = self.galaxy_interactor.post( "dataset_collections", data=payload )
return create_response
def wait_on_state( state_func, assert_ok=False, timeout=DEFAULT_TIMEOUT ):
def get_state( ):
response = state_func()
assert response.status_code == 200, "Failed to fetch state update while waiting."
state = response.json()[ "state" ]
if state not in [ "running", "queued", "new", "ready" ]:
if assert_ok:
assert state == "ok", "Final state - %s - not okay." % state
return state
else:
return None
return wait_on( get_state, desc="state", timeout=timeout)
def wait_on( function, desc, timeout=DEFAULT_TIMEOUT ):
delta = .25
iteration = 0
while True:
total_wait = delta * iteration
if total_wait > timeout:
timeout_message = "Timed out after %s seconds waiting on %s." % (
total_wait, desc
)
assert False, timeout_message
iteration += 1
value = function()
if value is not None:
return value
time.sleep( delta )