diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index 9f9fe9a5dc2..14d584d632c 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -91,7 +91,6 @@ class Configuration( object ): self.tool_data_path = resolve_path( kwargs.get( "tool_data_path", "tool-data" ), os.getcwd() ) self.builds_file_path = resolve_path( kwargs.get( "builds_file_path", os.path.join( self.tool_data_path, 'shared', 'ucsc', 'builds.txt') ), self.root ) self.len_file_path = resolve_path( kwargs.get( "len_file_path", os.path.join( self.tool_data_path, 'shared', 'ucsc', 'chrom') ), self.root ) - self.test_conf = resolve_path( kwargs.get( "test_conf", "" ), self.root ) # The value of migrated_tools_config is the file reserved for containing only those tools that have been eliminated from the distribution # and moved to the tool shed. self.integrated_tool_panel_config = resolve_path( kwargs.get( 'integrated_tool_panel_config', 'integrated_tool_panel.xml' ), self.root ) diff --git a/lib/galaxy/jobs/__init__.py b/lib/galaxy/jobs/__init__.py index 5fd087be476..91e4dfce46a 100644 --- a/lib/galaxy/jobs/__init__.py +++ b/lib/galaxy/jobs/__init__.py @@ -1662,10 +1662,13 @@ class JobWrapper( object ): **kwds ) if resolve_metadata_dependencies: metadata_tool = self.app.toolbox.get_tool("__SET_METADATA__") - dependency_shell_commands = metadata_tool.build_dependency_shell_commands(job_directory=self.working_directory) - if dependency_shell_commands: - dependency_shell_commands = "; ".join(dependency_shell_commands) - command = "%s; %s" % (dependency_shell_commands, command) + if metadata_tool is not None: + # Due to tool shed hacks for migrate and installed tool tests... + # see (``setup_shed_tools_for_test`` in test/base/driver_util.py). + dependency_shell_commands = metadata_tool.build_dependency_shell_commands(job_directory=self.working_directory) + if dependency_shell_commands: + dependency_shell_commands = "; ".join(dependency_shell_commands) + command = "%s; %s" % (dependency_shell_commands, command) return command @property diff --git a/lib/galaxy/webapps/tool_shed/config.py b/lib/galaxy/webapps/tool_shed/config.py index d9f69cf8e37..43ae3d4a928 100644 --- a/lib/galaxy/webapps/tool_shed/config.py +++ b/lib/galaxy/webapps/tool_shed/config.py @@ -66,7 +66,6 @@ class Configuration( object ): self.new_file_path = resolve_path( kwargs.get( "new_file_path", "database/tmp" ), self.root ) self.cookie_path = kwargs.get( "cookie_path", "/" ) self.enable_quotas = string_as_bool( kwargs.get( 'enable_quotas', False ) ) - self.test_conf = resolve_path( kwargs.get( "test_conf", "" ), self.root ) self.id_secret = kwargs.get( "id_secret", "USING THE DEFAULT IS NOT SECURE!" ) # Tool stuff self.tool_path = resolve_path( kwargs.get( "tool_path", "tools" ), self.root ) diff --git a/run_tests.sh b/run_tests.sh index c4cf50e493a..e5c13aa9e20 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -35,16 +35,117 @@ Run a specific API test: ./run_tests.sh -api test/api/test_tools.py:ToolsTestCase.test_map_over_with_output_format_actions +External Tests: + +A small subset of tests can be run against an existing Galxy +instance. The external Galaxy instance URL can be configured with +--external_url. If this is set, either --external_master_key or +--external_user_key must be set as well - more tests can be executed +with --external_master_key than with a user key. + Extra options: + --verbose_errors Force some tests produce more verbose error reporting. - --no_cleanup Do not delete temp files for Python functional tests (-toolshed, -framework, etc...) - --debug On python test error or failure invoke a pdb shell for interactive debugging of the test - --report_file Path of HTML report to produce (for Python Galaxy functional tests). - --xunit_report_file Path of XUnit report to produce (for Python Galaxy functional tests). - --skip-venv Do not create .venv (passes this flag to common_startup.sh) - --dockerize Run tests in a pre-configured Docker container (must be first argument if present). - --db For use with --dockerize, run tests using partially migrated 'postgres', 'mysql', - or 'sqlite' databases. + --no_cleanup Do not delete temp files for Python functional tests + (-toolshed, -framework, etc...) + --debug On python test error or failure invoke a pdb shell for + interactive debugging of the test + --report_file Path of HTML report to produce (for Python Galaxy + functional tests). + --xunit_report_file Path of XUnit report to produce (for Python Galaxy + functional tests). + --skip-venv Do not create .venv (passes this flag to + common_startup.sh) + --dockerize Run tests in a pre-configured Docker container (must be + first argument if present). + --db For use with --dockerize, run tests using partially + migrated 'postgres', 'mysql', or 'sqlite' databases. + --external_url External URL to use for Galaxy testing (only certain + tests). + --external_master_key Master API key used to configure external tests. + --external_user_key User API used for external tests - not required if + external_master_key is specified. + +Environment Variables: + +In addition to the above command-line options, many environment variables +can be used to control the Galaxy functional testing processing. Command-line +options above like (--external_url) will set environment variables - in such +cases the command line argument takes precedent over environment variables set +at the time of running this script. + +Functional Test Environment Variables + +GALAXY_TEST_DBURI Database connection string used for functional + test database for Galaxy. +GALAXY_TEST_INSTALL_DBURI Database connection string used for functional + test database for Galaxy's install framework. +GALAXY_TEST_INSTALL_DB_MERGED Set to use same database for Galaxy and install + framework, this defaults to True for Galaxy + tests an False for shed tests. +GALAXY_TEST_DB_TEMPLATE If GALAXY_TEST_DBURI is unset, this URL can be + retrieved and should be an sqlite database that + will be upgraded and tested against. +GALAXY_TEST_TMP_DIR Temp directory used for files required by + Galaxy server setup for Galaxy functional tests. +GALAXY_TEST_SAVE Location to save certain test files (such as + tool outputs). +GALAXY_TEST_EXTERNAL Target an external Galaxy as part of testing. +GALAXY_CONFIG_MASTER_KEY Master or admin API key to use as part of + testing with GALAXY_TEST_EXTERNAL. +GALAXY_TEST_USER_API_KEY User API key to use as part of testing with + GALAXY_TEST_EXTERNAL. +GALAXY_TEST_HISTORY_ID Point casperjs tests at specific external + history for testing. +GALAXY_TEST_WORKFLOW_FILE Point casperjs tests at specific workflow + file for testing. +GALAXY_TEST_VERBOSE_ERRORS Enable more verbose errors during API tests. +GALAXY_TEST_UPLOAD_ASYNC Upload tool test inputs asynchronously (may + overwhelm sqlite database). +GALAXY_TEST_RAW_DIFF Don't slice up tool test diffs to keep output + managable - print all output. (default off) +GALAXY_TEST_DEFAULT_WAIT Max time allowed for a tool test before Galaxy + gives up (default 86400) - tools may define a + maxseconds attribute to extend this. +GALAXY_TEST_TOOL_DEPENDENCY_DIR tool dependency dir to use for Galaxy during + functional tests. +GALAXY_TEST_FILE_DIR Test data sources (default to + test-data,https://github.com/galaxyproject/galaxy-test-data.git) +GALAXY_TEST_DIRECTORY $GALAXY_ROOT/test +GALAXY_TEST_TOOL_DATA_PATH Set to override tool data path during tool + shed tests. +GALAXY_TEST_FETCH_DATA Fetch remote test data to + GALAXY_TEST_DATA_REPO_CACHE as part of tool + tests if it is not available locally (default + to True). Requires git to be available on the + command-line. +GALAXY_TEST_DATA_REPO_CACHE Where to cache remote test data to (default to + test-data-cache). +HTTP_ACCEPT_LANGUAGE Defaults to 'en' +GALAXY_TEST_NO_CLEANUP Do not cleanup main test directory after tests, + the deprecated option TOOL_SHED_TEST_NO_CLEANUP + does the same thing. +GALAXY_TEST_HOST Host to use for Galaxy server setup for + testing. +GALAXY_TEST_PORT Port to use for Galaxy server setup for + testing. +GALAXY_TEST_TOOL_PATH Path defaulting to 'tools'. +GALAXY_TEST_SHED_TOOL_CONF Shed toolbox conf (defaults to + config/shed_tool_conf.xml) used when testing + installed to tools with -installed. +TOOL_SHED_TEST_HOST Host to use for shed server setup for testing. +TOOL_SHED_TEST_PORT Port to use for shed server setup for testing. +TOOL_SHED_TEST_FILE_DIR Defaults to test/tool_shed/test_data. +TOOL_SHED_TEST_TMP_DIR Defaults to random /tmp directory - place for + tool shed test server files to be placed. +TOOL_SHED_TEST_OMIT_GALAXY Do not launch a Galaxy server for tool shed + testing. + +Unit Test Environment Variables + +GALAXY_TEST_INCLUDE_SLOW - Used in unit tests to trigger slower tests that + aren't included by default with --unit/-u. + EOF } @@ -160,6 +261,18 @@ do with_framework_test_tools_arg="-with_framework_test_tools" shift ;; + --external_url) + GALAXY_TEST_EXTERNAL=$2 + shift 2 + ;; + --external_master_key) + GALAXY_CONFIG_MASTER_KEY=$2 + shift 2 + ;; + --external_user_key) + GALAXY_TEST_USER_API_KEY=$2 + shift 2 + ;; -w|-workflow|--workflow) if [ $# -gt 1 ]; then workflow_file=$2 @@ -349,7 +462,8 @@ elif [ -n "$data_managers_test" ] ; then [ -n "$test_id" ] && class=":TestForDataManagerTool_$test_id" || class="" extra_args="functional.test_data_managers$class -data_managers" elif [ -n "$workflow_test" ]; then - extra_args="functional.workflow:WorkflowTestCase $workflow_file" + GALAXY_TEST_WORKFLOW_FILE="$workflow_file" + extra_args="functional.workflow:WorkflowTestCase" elif [ -n "$toolshed_script" ]; then extra_args="$toolshed_script" elif [ -n "$api_script" ]; then @@ -387,7 +501,11 @@ if [ "$driver" = "python" ]; then else structured_data_args="" fi - python $test_script $coverage_arg -v --with-nosehtml --html-report-file $report_file $xunit_args $structured_data_args $with_framework_test_tools_arg $extra_args + if [ -n "$with_framework_test_tools_arg" ]; then + GALAXY_TEST_TOOL_CONF="config/tool_conf.xml.sample,test/functional/tools/samples_tool_conf.xml" + export GALAXY_TEST_TOOL_CONF + fi + python $test_script $coverage_arg -v --with-nosehtml --html-report-file $report_file $xunit_args $structured_data_args $extra_args else ensure_grunt if [ -n "$watch" ]; then diff --git a/scripts/functional_tests.py b/scripts/functional_tests.py index 192dfc17206..b1817912fa1 100644 --- a/scripts/functional_tests.py +++ b/scripts/functional_tests.py @@ -1,531 +1,121 @@ #!/usr/bin/env python -import httplib -import logging +"""Test driver for many Galaxy Python functional tests. + +Launch this script by running ``run_tests.sh`` from GALAXY_ROOT, see +that script for a list of options. +""" + import os import os.path -import random -import re -import shutil -import socket import sys -import tempfile -import threading -import time -import urllib -from ConfigParser import SafeConfigParser -from json import dumps - -import nose.core -import nose.config -import nose.loader -import nose.plugins.manager -from paste import httpserver galaxy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) sys.path[1:1] = [ os.path.join( galaxy_root, "lib" ), os.path.join( galaxy_root, "test" ) ] +from base import driver_util +log = driver_util.build_logger() + from base.api_util import get_master_api_key, get_user_api_key -from base.nose_util import run -from base.instrument import StructuredTestDataPlugin -from base.test_logging import logging_config_file -from base.tool_shed_util import parse_tool_panel_config -from functional import database_contexts -from galaxy import tools -from galaxy.app import UniverseApplication -from galaxy.util.properties import load_app_properties -from galaxy.web import buildapp - -log = logging.getLogger( "functional_tests.py" ) - -default_galaxy_test_host = "localhost" -default_galaxy_test_port_min = 8000 -default_galaxy_test_port_max = 9999 -default_galaxy_locales = 'en' -default_galaxy_test_file_dir = "test-data,https://github.com/galaxyproject/galaxy-test-data.git" -migrated_tool_panel_config = 'config/migrated_tools_conf.xml' -installed_tool_panel_configs = [ - os.environ.get('GALAXY_TEST_SHED_TOOL_CONF', 'config/shed_tool_conf.xml') -] -# should this serve static resources (scripts, images, styles, etc.) -STATIC_ENABLED = True +class MigratedToolsGalaxyTestDriver(driver_util.GalaxyTestDriver): + """Instantiate a Galaxy-style nose TestDriver for testing migrated Galaxy tools.""" -# Set up a job_conf.xml that explicitly limits jobs to 10 minutes. -job_conf_xml = ''' - - - - - - - - - - - - - 00:10:00 - - -''' + testing_shed_tools = True - -def get_static_settings(): - """Returns dictionary of the settings necessary for a galaxy App - to be wrapped in the static middleware. - - This mainly consists of the filesystem locations of url-mapped - static resources. - """ - cwd = os.getcwd() - static_dir = os.path.join( cwd, 'static' ) - # TODO: these should be copied from config/galaxy.ini - return dict( - # TODO: static_enabled needed here? - static_enabled=True, - static_cache_time=360, - static_dir=static_dir, - static_images_dir=os.path.join( static_dir, 'images', '' ), - static_favicon_dir=os.path.join( static_dir, 'favicon.ico' ), - static_scripts_dir=os.path.join( static_dir, 'scripts', '' ), - static_style_dir=os.path.join( static_dir, 'june_2007_style', 'blue' ), - static_robots_txt=os.path.join( static_dir, 'robots.txt' ), - ) - - -def get_webapp_global_conf(): - """Get the global_conf dictionary sent as the first argument to app_factory. - """ - # (was originally sent 'dict()') - nothing here for now except static settings - global_conf = dict() - if STATIC_ENABLED: - global_conf.update( get_static_settings() ) - return global_conf - - -def generate_config_file( input_filename, output_filename, config_items ): - ''' - Generate a config file with the configuration that has been defined for the embedded web application. - This is mostly relevant when setting metadata externally, since the script for doing that does not - have access to app.config. - ''' - cp = SafeConfigParser() - cp.read( input_filename ) - config_items_by_section = [] - for label, value in config_items: - found = False - # Attempt to determine the correct section for this configuration option. - for section in cp.sections(): - if cp.has_option( section, label ): - config_tuple = section, label, value - config_items_by_section.append( config_tuple ) - found = True - continue - # Default to app:main if no section was found. - if not found: - config_tuple = 'app:main', label, value - config_items_by_section.append( config_tuple ) - print( config_items_by_section ) - - # Replace the default values with the provided configuration. - for section, label, value in config_items_by_section: - if cp.has_option( section, label ): - cp.remove_option( section, label ) - cp.set( section, label, str( value ) ) - fh = open( output_filename, 'w' ) - cp.write( fh ) - fh.close() - - -def run_tests( test_config ): - return run( test_config ) - - -def __copy_database_template( source, db_path ): - """ - Copy a 'clean' sqlite template database (from file or URL) to specified - database path. - """ - os.makedirs( os.path.dirname( db_path ) ) - if os.path.exists( source ): - shutil.copy( source, db_path ) - assert os.path.exists( db_path ) - elif source.lower().startswith( ( "http://", "https://", "ftp://" ) ): - urllib.urlretrieve( source, db_path ) - else: - raise Exception( "Failed to copy database template from source %s" % source ) - - -def main(): - # ---- Configuration ------------------------------------------------------ - galaxy_test_host = os.environ.get( 'GALAXY_TEST_HOST', default_galaxy_test_host ) - galaxy_test_port = os.environ.get( 'GALAXY_TEST_PORT', None ) - galaxy_test_save = os.environ.get( 'GALAXY_TEST_SAVE', None) - tool_path = os.environ.get( 'GALAXY_TEST_TOOL_PATH', 'tools' ) - if 'HTTP_ACCEPT_LANGUAGE' not in os.environ: - os.environ[ 'HTTP_ACCEPT_LANGUAGE' ] = default_galaxy_locales - testing_migrated_tools = __check_arg( '-migrated' ) - testing_installed_tools = __check_arg( '-installed' ) - datatypes_conf_override = None - - if testing_migrated_tools or testing_installed_tools: - # Store a jsonified dictionary of tool_id : GALAXY_TEST_FILE_DIR pairs. - galaxy_tool_shed_test_file = 'shed_tools_dict' - # We need the upload tool for functional tests, so we'll create a temporary tool panel config that defines it. - fd, tmp_tool_panel_conf = tempfile.mkstemp() - os.write( fd, '\n' ) - os.write( fd, '\n' ) - os.write( fd, '\n' ) - os.write( fd, '\n' ) - os.close( fd ) - tool_config_file = tmp_tool_panel_conf - galaxy_test_file_dir = None - library_import_dir = None - user_library_import_dir = None - # Exclude all files except test_toolbox.py. - ignore_files = ( re.compile( r'^test_[adghlmsu]*' ), re.compile( r'^test_ta*' ) ) - else: - framework_tool_dir = os.path.join('test', 'functional', 'tools') - framework_test = __check_arg( '-framework' ) # Run through suite of tests testing framework. - if framework_test: - tool_conf = os.path.join( framework_tool_dir, 'samples_tool_conf.xml' ) - datatypes_conf_override = os.path.join( framework_tool_dir, 'sample_datatypes_conf.xml' ) - else: - # Use tool_conf.xml toolbox. - tool_conf = None - if __check_arg( '-with_framework_test_tools' ): - tool_conf = "%s,%s" % ( 'config/tool_conf.xml.sample', os.path.join( framework_tool_dir, 'samples_tool_conf.xml' ) ) - test_dir = default_galaxy_test_file_dir - tool_config_file = os.environ.get( 'GALAXY_TEST_TOOL_CONF', tool_conf ) - galaxy_test_file_dir = os.environ.get( 'GALAXY_TEST_FILE_DIR', test_dir ) - first_test_file_dir = galaxy_test_file_dir.split(",")[0] - if not os.path.isabs( first_test_file_dir ): - first_test_file_dir = os.path.join( os.getcwd(), first_test_file_dir ) - library_import_dir = first_test_file_dir - import_dir = os.path.join( first_test_file_dir, 'users' ) - if os.path.exists(import_dir): - user_library_import_dir = import_dir - else: - user_library_import_dir = None - ignore_files = () - - start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ - tool_data_table_config_path = None - if os.path.exists( 'tool_data_table_conf.test.xml' ): - # If explicitly defined tables for test, use those. - tool_data_table_config_path = 'tool_data_table_conf.test.xml' - else: - # ... otherise find whatever Galaxy would use as the default and - # the sample data for fucntional tests to that. - default_tool_data_config = 'config/tool_data_table_conf.xml.sample' - for tool_data_config in ['config/tool_data_table_conf.xml', 'tool_data_table_conf.xml' ]: - if os.path.exists( tool_data_config ): - default_tool_data_config = tool_data_config - tool_data_table_config_path = '%s,test/functional/tool-data/sample_tool_data_tables.xml' % default_tool_data_config - - default_data_manager_config = 'config/data_manager_conf.xml.sample' - for data_manager_config in ['config/data_manager_conf.xml', 'data_manager_conf.xml' ]: - if os.path.exists( data_manager_config ): - default_data_manager_config = data_manager_config - data_manager_config_file = "%s,test/functional/tools/sample_data_manager_conf.xml" % default_data_manager_config - shed_tool_data_table_config = 'config/shed_tool_data_table_conf.xml' - tool_dependency_dir = os.environ.get( 'GALAXY_TOOL_DEPENDENCY_DIR', None ) - use_distributed_object_store = os.environ.get( 'GALAXY_USE_DISTRIBUTED_OBJECT_STORE', False ) - galaxy_test_tmp_dir = os.environ.get( 'GALAXY_TEST_TMP_DIR', None ) - if galaxy_test_tmp_dir is None: - galaxy_test_tmp_dir = tempfile.mkdtemp() - - galaxy_job_conf_file = os.environ.get( 'GALAXY_TEST_JOB_CONF', - os.path.join( galaxy_test_tmp_dir, 'test_job_conf.xml' ) ) - # Generate the job_conf.xml file. - file( galaxy_job_conf_file, 'w' ).write( job_conf_xml ) - - database_auto_migrate = False - - galaxy_test_proxy_port = None - if start_server: - tempdir = tempfile.mkdtemp( dir=galaxy_test_tmp_dir ) - # Configure the database path. - if 'GALAXY_TEST_DBPATH' in os.environ: - galaxy_db_path = os.environ[ 'GALAXY_TEST_DBPATH' ] - else: - galaxy_db_path = os.path.join( tempdir, 'database' ) - # Configure the paths Galaxy needs to test tools. - file_path = os.path.join( galaxy_db_path, 'files' ) - template_cache_path = os.path.join( galaxy_db_path, 'compiled_templates' ) - new_file_path = tempfile.mkdtemp( prefix='new_files_path_', dir=tempdir ) - job_working_directory = tempfile.mkdtemp( prefix='job_working_directory_', dir=tempdir ) - install_database_connection = os.environ.get( 'GALAXY_TEST_INSTALL_DBURI', None ) - if 'GALAXY_TEST_DBURI' in os.environ: - database_connection = os.environ['GALAXY_TEST_DBURI'] - else: - db_path = os.path.join( galaxy_db_path, 'universe.sqlite' ) - if 'GALAXY_TEST_DB_TEMPLATE' in os.environ: - # Middle ground between recreating a completely new - # database and pointing at existing database with - # GALAXY_TEST_DBURI. The former requires a lot of setup - # time, the latter results in test failures in certain - # cases (namely tool shed tests expecting clean database). - log.debug( "Copying database template from %s.", os.environ['GALAXY_TEST_DB_TEMPLATE'] ) - __copy_database_template(os.environ['GALAXY_TEST_DB_TEMPLATE'], db_path) - database_auto_migrate = True - database_connection = 'sqlite:///%s' % db_path - kwargs = {} - for dir in file_path, new_file_path, template_cache_path: - try: - if not os.path.exists( dir ): - os.makedirs( dir ) - except OSError: - pass - - # Data Manager testing temp path - # For storing Data Manager outputs and .loc files so that real ones don't get clobbered - data_manager_test_tmp_path = tempfile.mkdtemp( prefix='data_manager_test_tmp', dir=galaxy_test_tmp_dir ) - galaxy_data_manager_data_path = tempfile.mkdtemp( prefix='data_manager_tool-data', dir=data_manager_test_tmp_path ) - - # ---- Build Application -------------------------------------------------- - master_api_key = get_master_api_key() - app = None - if start_server: - kwargs = dict( admin_users='test@bx.psu.edu', - api_allow_run_as='test@bx.psu.edu', - allow_library_path_paste=True, - allow_user_creation=True, - allow_user_deletion=True, - database_connection=database_connection, - database_auto_migrate=database_auto_migrate, - datatype_converters_config_file="datatype_converters_conf.xml.sample", - file_path=file_path, - id_secret='changethisinproductiontoo', - job_queue_workers=5, - job_working_directory=job_working_directory, - library_import_dir=library_import_dir, - log_destination="stdout", - new_file_path=new_file_path, - template_cache_path=template_cache_path, - running_functional_tests=True, - shed_tool_data_table_config=shed_tool_data_table_config, - template_path="templates", - test_conf="test.conf", - tool_config_file=tool_config_file, - tool_data_table_config_path=tool_data_table_config_path, - tool_path=tool_path, - galaxy_data_manager_data_path=galaxy_data_manager_data_path, - tool_parse_help=False, - update_integrated_tool_panel=False, - use_heartbeat=False, - user_library_import_dir=user_library_import_dir, - master_api_key=master_api_key, - use_tasked_jobs=True, - check_migrate_tools=False, - cleanup_job='onsuccess', - enable_beta_tool_formats=True, - auto_configure_logging=logging_config_file is None, - data_manager_config_file=data_manager_config_file ) - if install_database_connection is not None: - kwargs[ 'install_database_connection' ] = install_database_connection - if not database_connection.startswith( 'sqlite://' ): - kwargs[ 'database_engine_option_max_overflow' ] = '20' - kwargs[ 'database_engine_option_pool_size' ] = '10' - if tool_dependency_dir is not None: - kwargs[ 'tool_dependency_dir' ] = tool_dependency_dir - if use_distributed_object_store: - kwargs[ 'object_store' ] = 'distributed' - kwargs[ 'distributed_object_store_config_file' ] = 'distributed_object_store_conf.xml.sample' - if datatypes_conf_override: - kwargs[ 'datatypes_config_file' ] = datatypes_conf_override - # If the user has passed in a path for the .ini file, do not overwrite it. - galaxy_config_file = os.environ.get( 'GALAXY_TEST_INI_FILE', None ) - if not galaxy_config_file: - galaxy_config_file = os.path.join( galaxy_test_tmp_dir, 'functional_tests_wsgi.ini' ) - config_items = [] - for label in kwargs: - config_tuple = label, kwargs[ label ] - config_items.append( config_tuple ) - # Write a temporary file, based on config/galaxy.ini.sample, using the configuration options defined above. - generate_config_file( 'config/galaxy.ini.sample', galaxy_config_file, config_items ) - # Set the global_conf[ '__file__' ] option to the location of the temporary .ini file, which gets passed to set_metadata.sh. - kwargs[ 'global_conf' ] = get_webapp_global_conf() - kwargs[ 'global_conf' ][ '__file__' ] = galaxy_config_file - kwargs[ 'config_file' ] = galaxy_config_file - kwargs = load_app_properties( - kwds=kwargs + def build_tests(self): + """Build migrated tool test methods.""" + self.setup_shed_tools( + testing_migrated_tools=True, ) - # Build the Universe Application - app = UniverseApplication( **kwargs ) - database_contexts.galaxy_context = app.model.context - log.info( "Embedded Universe application started" ) - - # ---- Run webserver ------------------------------------------------------ - server = None - - if start_server: - webapp = buildapp.app_factory( kwargs[ 'global_conf' ], app=app, - use_translogger=False, static_enabled=STATIC_ENABLED ) - if galaxy_test_port is not None: - server = httpserver.serve( webapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False ) - else: - random.seed() - for i in range( 0, 9 ): - try: - galaxy_test_port = str( random.randint( default_galaxy_test_port_min, default_galaxy_test_port_max ) ) - log.debug( "Attempting to serve app on randomly chosen port: %s" % galaxy_test_port ) - server = httpserver.serve( webapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False ) - break - except socket.error, e: - if e[0] == 98: - continue - raise - else: - raise Exception( "Unable to open a port between %s and %s to start Galaxy server" % ( default_galaxy_test_port_min, default_galaxy_test_port_max ) ) - if galaxy_test_proxy_port: - os.environ['GALAXY_TEST_PORT'] = galaxy_test_proxy_port - else: - os.environ['GALAXY_TEST_PORT'] = galaxy_test_port - - t = threading.Thread( target=server.serve_forever ) - t.start() - # Test if the server is up - for i in range( 10 ): - conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port ) # directly test the app, not the proxy - conn.request( "GET", "/" ) - if conn.getresponse().status == 200: - break - time.sleep( 0.1 ) - else: - raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" ) - log.info( "Embedded web server started" ) - - # ---- Find tests --------------------------------------------------------- - if galaxy_test_proxy_port: - log.info( "Functional tests will be run against %s:%s" % ( galaxy_test_host, galaxy_test_proxy_port ) ) - else: - log.info( "Functional tests will be run against %s:%s" % ( galaxy_test_host, galaxy_test_port ) ) - success = False - try: - tool_configs = app.config.tool_configs - # What requires these? Handy for (eg) functional tests to save outputs? - if galaxy_test_save: - os.environ[ 'GALAXY_TEST_SAVE' ] = galaxy_test_save - # Pass in through script setenv, will leave a copy of ALL test validate files - os.environ[ 'GALAXY_TEST_HOST' ] = galaxy_test_host - - def _run_functional_test( testing_shed_tools=None ): - workflow_test = __check_arg( '-workflow', param=True ) - if workflow_test: - import functional.workflow - functional.workflow.WorkflowTestCase.workflow_test_file = workflow_test - functional.workflow.WorkflowTestCase.master_api_key = master_api_key - functional.workflow.WorkflowTestCase.user_api_key = get_user_api_key() - data_manager_test = __check_arg( '-data_managers', param=False ) - if data_manager_test: - import functional.test_data_managers - functional.test_data_managers.data_managers = app.data_managers # seems like a hack... - functional.test_data_managers.build_tests( - tmp_dir=data_manager_test_tmp_path, - testing_shed_tools=testing_shed_tools, - master_api_key=master_api_key, - user_api_key=get_user_api_key(), - ) - else: - # We must make sure that functional.test_toolbox is always imported after - # database_contexts.galaxy_content is set (which occurs in this method above). - # If functional.test_toolbox is imported before database_contexts.galaxy_content - # is set, sa_session will be None in all methods that use it. - import functional.test_toolbox - functional.test_toolbox.toolbox = app.toolbox - # When testing data managers, do not test toolbox. - functional.test_toolbox.build_tests( - app=app, - testing_shed_tools=testing_shed_tools, - master_api_key=master_api_key, - user_api_key=get_user_api_key(), - ) - test_config = nose.config.Config( env=os.environ, ignoreFiles=ignore_files, plugins=nose.plugins.manager.DefaultPluginManager() ) - test_config.plugins.addPlugin( StructuredTestDataPlugin() ) - test_config.configure( sys.argv ) - result = run_tests( test_config ) - success = result.wasSuccessful() - return success - - if testing_migrated_tools or testing_installed_tools: - shed_tools_dict = {} - if testing_migrated_tools: - has_test_data, shed_tools_dict = parse_tool_panel_config( migrated_tool_panel_config, shed_tools_dict ) - elif testing_installed_tools: - for shed_tool_config in installed_tool_panel_configs: - has_test_data, shed_tools_dict = parse_tool_panel_config( shed_tool_config, shed_tools_dict ) - # Persist the shed_tools_dict to the galaxy_tool_shed_test_file. - shed_tools_file = open( galaxy_tool_shed_test_file, 'w' ) - shed_tools_file.write( dumps( shed_tools_dict ) ) - shed_tools_file.close() - if not os.path.isabs( galaxy_tool_shed_test_file ): - galaxy_tool_shed_test_file = os.path.join( os.getcwd(), galaxy_tool_shed_test_file ) - os.environ[ 'GALAXY_TOOL_SHED_TEST_FILE' ] = galaxy_tool_shed_test_file - if testing_installed_tools: - # Eliminate the migrated_tool_panel_config from the app's tool_configs, append the list of installed_tool_panel_configs, - # and reload the app's toolbox. - relative_migrated_tool_panel_config = os.path.join( app.config.root, migrated_tool_panel_config ) - if relative_migrated_tool_panel_config in tool_configs: - tool_configs.remove( relative_migrated_tool_panel_config ) - for installed_tool_panel_config in installed_tool_panel_configs: - tool_configs.append( installed_tool_panel_config ) - app.toolbox = tools.ToolBox( tool_configs, app.config.tool_path, app ) - success = _run_functional_test( testing_shed_tools=True ) - try: - os.unlink( tmp_tool_panel_conf ) - except: - log.info( "Unable to remove temporary file: %s" % tmp_tool_panel_conf ) - try: - os.unlink( galaxy_tool_shed_test_file ) - except: - log.info( "Unable to remove file: %s" % galaxy_tool_shed_test_file ) - else: - if galaxy_test_file_dir: - os.environ[ 'GALAXY_TEST_FILE_DIR' ] = galaxy_test_file_dir - success = _run_functional_test( ) - except: - log.exception( "Failure running tests" ) - - log.info( "Shutting down" ) - # ---- Tear down ----------------------------------------------------------- - if server: - log.info( "Shutting down embedded web server" ) - server.server_close() - server = None - log.info( "Embedded web server stopped" ) - if app: - log.info( "Shutting down app" ) - app.shutdown() - app = None - log.info( "Embedded Universe application stopped" ) - try: - if os.path.exists( tempdir ) and 'GALAXY_TEST_NO_CLEANUP' not in os.environ: - log.info( "Cleaning up temporary files in %s" % tempdir ) - shutil.rmtree( tempdir ) - else: - log.info( "GALAXY_TEST_NO_CLEANUP is on. Temporary files in %s" % tempdir ) - except: - pass - if success: - return 0 - else: - return 1 + self.build_tool_tests() -def __check_arg( name, param=False ): +class InstalledToolsGalaxyTestDriver(driver_util.GalaxyTestDriver): + """Galaxy-style nose TestDriver for testing installed Galaxy tools.""" + + testing_shed_tools = True + + def build_tests(self): + """Build installed tool test methods.""" + self.setup_shed_tools( + testing_installed_tools=True, + ) + self.build_tool_tests() + + +class DefaultGalaxyTestDriver(driver_util.GalaxyTestDriver): + """Default Galaxy-style nose test driver. + + Just populate non-shed tool tests and run tests. Works + for tool tests, regular twill tests, and API testing. + """ + + def build_tests(self): + """Build framework tool test methods.""" + self.build_tool_tests() + + +class FrameworkToolsGalaxyTestDriver(DefaultGalaxyTestDriver): + """Galaxy-style nose TestDriver for testing framework Galaxy tools.""" + + default_tool_conf = driver_util.FRAMEWORK_SAMPLE_TOOLS_CONF + datatypes_conf_override = driver_util.FRAMEWORK_DATATYPES_CONF + + +class DataManagersGalaxyTestDriver(driver_util.GalaxyTestDriver): + """Galaxy-style nose TestDriver for testing framework Galaxy tools.""" + + def build_tests(self): + """Build data manager test methods.""" + import functional.test_data_managers + functional.test_data_managers.data_managers = self.app.data_managers + functional.test_data_managers.build_tests( + tmp_dir=self.galaxy_test_tmp_dir, + testing_shed_tools=self.testing_shed_tools, + master_api_key=get_master_api_key(), + user_api_key=get_user_api_key(), + ) + + +class WorkflowGalaxyTestDriver(driver_util.GalaxyTestDriver): + """Galaxy-style nose TestDriver for testing a Galaxy workflow.""" + + def build_tests(self): + """Setup WorkflowTestCase for test execution.""" + import functional.workflow + functional.workflow.WorkflowTestCase.master_api_key = get_master_api_key() + functional.workflow.WorkflowTestCase.user_api_key = get_user_api_key() + + +TEST_DRIVERS = { + '-migrated': MigratedToolsGalaxyTestDriver, + '-installed': InstalledToolsGalaxyTestDriver, + '-framework': FrameworkToolsGalaxyTestDriver, + '-data_managers': DataManagersGalaxyTestDriver, + '-workflow': WorkflowGalaxyTestDriver, +} + + +def find_test_driver(): + """Look at command-line args and find the correct Galaxy test driver.""" + test_driver = DefaultGalaxyTestDriver + + for key in TEST_DRIVERS.keys(): + if _check_arg(key): + test_driver = TEST_DRIVERS[key] + + return test_driver + + +def _check_arg( name ): try: index = sys.argv.index( name ) del sys.argv[ index ] - if param: - ret_val = sys.argv[ index ] - del sys.argv[ index ] - else: - ret_val = True + ret_val = True except ValueError: ret_val = False return ret_val if __name__ == "__main__": - sys.exit( main() ) + driver_util.drive_test(find_test_driver()) diff --git a/test/base/driver_util.py b/test/base/driver_util.py new file mode 100644 index 00000000000..2cbb026f6cb --- /dev/null +++ b/test/base/driver_util.py @@ -0,0 +1,663 @@ +"""Scripts for drivers of Galaxy functional tests.""" + +import collections +import httplib +import json +import logging +import os +import random +import shutil +import socket +import sys +import tempfile +import threading +import time + +from six.moves.urllib.request import urlretrieve + +import nose.config +import nose.core +import nose.loader +import nose.plugins.manager + +from paste import httpserver + +from .api_util import get_master_api_key, get_user_api_key +from .tool_shed_util import parse_tool_panel_config +from .nose_util import run +from .instrument import StructuredTestDataPlugin + +from functional import database_contexts + +from galaxy.app import UniverseApplication as GalaxyUniverseApplication +from galaxy.web import buildapp +from galaxy.webapps.tool_shed.app import UniverseApplication as ToolshedUniverseApplication +from galaxy.util import asbool +from galaxy.util.properties import load_app_properties + +from base.test_logging import logging_config_file + +galaxy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir)) +DEFAULT_WEB_HOST = "localhost" +GALAXY_TEST_DIRECTORY = os.path.join(galaxy_root, "test") +GALAXY_TEST_FILE_DIR = "test-data,https://github.com/galaxyproject/galaxy-test-data.git" +TOOL_SHED_TEST_DATA = os.path.join(GALAXY_TEST_DIRECTORY, "tool_shed", "test_data") +FRAMEWORK_TOOLS_DIR = os.path.join(GALAXY_TEST_DIRECTORY, "functional", "tools") +FRAMEWORK_UPLOAD_TOOL_CONF = os.path.join(FRAMEWORK_TOOLS_DIR, "upload_tool_conf.xml") +FRAMEWORK_SAMPLE_TOOLS_CONF = os.path.join(FRAMEWORK_TOOLS_DIR, "samples_tool_conf.xml") +FRAMEWORK_DATATYPES_CONF = os.path.join(FRAMEWORK_TOOLS_DIR, "sample_datatypes_conf.xml") +MIGRATED_TOOL_PANEL_CONFIG = 'config/migrated_tools_conf.xml' +INSTALLED_TOOL_PANEL_CONFIGS = [ + os.environ.get('GALAXY_TEST_SHED_TOOL_CONF', 'config/shed_tool_conf.xml') +] + +DEFAULT_LOCALES = "en" + +log = logging.getLogger("test_driver") + + +def setup_tool_shed_tmp_dir(): + tool_shed_test_tmp_dir = os.environ.get('TOOL_SHED_TEST_TMP_DIR', None) + if tool_shed_test_tmp_dir is None: + tool_shed_test_tmp_dir = tempfile.mkdtemp() + # Here's the directory where everything happens. Temporary directories are created within this directory to contain + # the hgweb.config file, the database, new repositories, etc. Since the tool shed browses repository contents via HTTP, + # the full path to the temporary directroy wher eht repositories are located cannot contain invalid url characters. + os.environ[ 'TOOL_SHED_TEST_TMP_DIR' ] = tool_shed_test_tmp_dir + return tool_shed_test_tmp_dir + + +def get_galaxy_test_tmp_dir(): + """Create test directory for use by Galaxy server being setup for testing.""" + galaxy_test_tmp_dir = os.environ.get('GALAXY_TEST_TMP_DIR', None) + if galaxy_test_tmp_dir is None: + galaxy_test_tmp_dir = tempfile.mkdtemp() + return galaxy_test_tmp_dir + + +def configure_environment(): + """Hack up environment for test cases.""" + # no op remove if unused + if 'HTTP_ACCEPT_LANGUAGE' not in os.environ: + os.environ[ 'HTTP_ACCEPT_LANGUAGE' ] = DEFAULT_LOCALES + + # Used by get_filename in tool shed's twilltestcase. + if "TOOL_SHED_TEST_FILE_DIR" not in os.environ: + os.environ["TOOL_SHED_TEST_FILE_DIR"] = TOOL_SHED_TEST_DATA + + +def build_logger(): + """Build a logger for test driver script.""" + return log + + +def setup_galaxy_config( + tmpdir, + use_test_file_dir=False, + default_install_db_merged=True, + default_tool_data_table_config_path=None, + default_shed_tool_data_table_config=None, + enable_tool_shed_check=False, + default_tool_conf=None, + shed_tool_conf=None, + datatypes_conf=None, + update_integrated_tool_panel=False, +): + """Setup environment and build config for test Galaxy instance.""" + if not os.path.exists(tmpdir): + os.makedirs(tmpdir) + file_path = os.path.join(tmpdir, 'files') + template_cache_path = tempfile.mkdtemp(prefix='compiled_templates_', dir=tmpdir) + new_file_path = tempfile.mkdtemp(prefix='new_files_path_', dir=tmpdir ) + job_working_directory = tempfile.mkdtemp(prefix='job_working_directory_', dir=tmpdir) + + if use_test_file_dir: + galaxy_test_file_dir = os.environ.get('GALAXY_TEST_FILE_DIR', GALAXY_TEST_FILE_DIR) + os.environ['GALAXY_TEST_FILE_DIR'] = galaxy_test_file_dir + first_test_file_dir = galaxy_test_file_dir.split(",")[0] + if not os.path.isabs(first_test_file_dir): + first_test_file_dir = os.path.join(galaxy_root, first_test_file_dir) + library_import_dir = first_test_file_dir + import_dir = os.path.join(first_test_file_dir, 'users') + if os.path.exists(import_dir): + user_library_import_dir = import_dir + else: + user_library_import_dir = None + else: + user_library_import_dir = None + library_import_dir = None + tool_path = os.environ.get('GALAXY_TEST_TOOL_PATH', 'tools') + tool_dependency_dir = os.environ.get('GALAXY_TOOL_DEPENDENCY_DIR', None) + if tool_dependency_dir is None: + tool_dependency_dir = tempfile.mkdtemp(dir=tmpdir, prefix="tool_dependencies") + tool_data_table_config_path = _tool_data_table_config_path(default_tool_data_table_config_path) + default_data_manager_config = 'config/data_manager_conf.xml.sample' + for data_manager_config in ['config/data_manager_conf.xml', 'data_manager_conf.xml' ]: + if os.path.exists( data_manager_config ): + default_data_manager_config = data_manager_config + data_manager_config_file = "%s,test/functional/tools/sample_data_manager_conf.xml" % default_data_manager_config + master_api_key = get_master_api_key() + + # Data Manager testing temp path + # For storing Data Manager outputs and .loc files so that real ones don't get clobbered + galaxy_data_manager_data_path = tempfile.mkdtemp(prefix='data_manager_tool-data', dir=tmpdir) + + tool_conf = os.environ.get('GALAXY_TEST_TOOL_CONF', default_tool_conf) + if tool_conf is None: + # As a fallback always at least allow upload. + tool_conf = FRAMEWORK_UPLOAD_TOOL_CONF + + if shed_tool_conf is not None: + tool_conf = "%s,%s" % (tool_conf, shed_tool_conf) + + shed_tool_data_table_config = default_shed_tool_data_table_config + if shed_tool_data_table_config is None: + shed_tool_data_table_config = 'config/shed_tool_data_table_conf.xml' + + config = dict( + admin_users='test@bx.psu.edu', + allow_library_path_paste=True, + allow_user_creation=True, + allow_user_deletion=True, + api_allow_run_as='test@bx.psu.edu', + auto_configure_logging=logging_config_file is None , + check_migrate_tools=False, + cleanup_job='onsuccess', + data_manager_config_file=data_manager_config_file, + enable_beta_tool_formats=True, + file_path=file_path, + galaxy_data_manager_data_path=galaxy_data_manager_data_path, + id_secret='changethisinproductiontoo', + job_working_directory=job_working_directory, + job_queue_workers=5, + library_import_dir=library_import_dir, + log_destination="stdout", + new_file_path=new_file_path, + master_api_key=master_api_key, + running_functional_tests=True, + shed_tool_data_table_config=shed_tool_data_table_config, + template_cache_path=template_cache_path, + template_path='templates', + tool_config_file=tool_conf, + tool_data_table_config_path=tool_data_table_config_path, + tool_parse_help=False, + tool_path=tool_path, + update_integrated_tool_panel=update_integrated_tool_panel, + use_tasked_jobs=True, + use_heartbeat=False, + user_library_import_dir=user_library_import_dir, + ) + config.update(database_conf(tmpdir)) + config.update(install_database_conf(tmpdir, default_merged=default_install_db_merged)) + if datatypes_conf is not None: + config['datatypes_config_file'] = datatypes_conf + if enable_tool_shed_check: + config["enable_tool_shed_check"] = enable_tool_shed_check + config["hours_between_check"] = 0.001 + if tool_dependency_dir: + config["tool_dependency_dir"] = tool_dependency_dir + # Used by shed's twill dependency stuff - todo read from + # Galaxy's config API. + os.environ["GALAXY_TEST_TOOL_DEPENDENCY_DIR"] = tool_dependency_dir + return config + + +def _tool_data_table_config_path(default_tool_data_table_config_path=None): + tool_data_table_config_path = os.environ.get('GALAXY_TEST_TOOL_DATA_TABLE_CONF', default_tool_data_table_config_path) + if tool_data_table_config_path is None: + # ... otherise find whatever Galaxy would use as the default and + # the sample data for fucntional tests to that. + default_tool_data_config = 'config/tool_data_table_conf.xml.sample' + for tool_data_config in ['config/tool_data_table_conf.xml', 'tool_data_table_conf.xml' ]: + if os.path.exists( tool_data_config ): + default_tool_data_config = tool_data_config + tool_data_table_config_path = '%s,test/functional/tool-data/sample_tool_data_tables.xml' % default_tool_data_config + return tool_data_table_config_path + + +def nose_config_and_run( argv=None, env=None, ignore_files=[], plugins=None ): + """Setup a nose context and run tests. + + Tests are specified by argv (defaulting to sys.argv). + """ + if env is None: + env = os.environ + if plugins is None: + plugins = nose.plugins.manager.DefaultPluginManager() + if argv is None: + argv = sys.argv + + test_config = nose.config.Config( + env=os.environ, + ignoreFiles=ignore_files, + plugins=plugins, + ) + + # Add custom plugin to produce JSON data used by planemo. + test_config.plugins.addPlugin( StructuredTestDataPlugin() ) + test_config.configure( argv ) + + result = run( test_config ) + + success = result.wasSuccessful() + return success + + +def copy_database_template( source, db_path ): + """Copy a 'clean' sqlite template database. + + From file or URL to specified path for sqlite database. + """ + db_path_dir = os.path.dirname(db_path) + if not os.path.exists(db_path_dir): + os.makedirs(db_path_dir) + if os.path.exists(source): + shutil.copy(source, db_path) + assert os.path.exists(db_path) + elif source.lower().startswith(("http://", "https://", "ftp://")): + urlretrieve(source, db_path) + else: + raise Exception( "Failed to copy database template from source %s" % source ) + + +def database_conf(db_path, prefix="GALAXY"): + """Find (and populate if needed) Galaxy database connection.""" + database_auto_migrate = False + dburi_var = "%s_TEST_DBURI" % prefix + if dburi_var in os.environ: + database_connection = os.environ[dburi_var] + else: + default_db_filename = "%s.sqlite" % prefix.lower() + template_var = "%s_TEST_DB_TEMPLATE" % prefix + db_path = os.path.join(db_path, default_db_filename) + if template_var in os.environ: + # Middle ground between recreating a completely new + # database and pointing at existing database with + # GALAXY_TEST_DBURI. The former requires a lot of setup + # time, the latter results in test failures in certain + # cases (namely tool shed tests expecting clean database). + copy_database_template(os.environ[template_var], db_path) + database_auto_migrate = True + database_connection = 'sqlite:///%s' % db_path + config = { + "database_connection": database_connection, + "database_auto_migrate": database_auto_migrate + } + if not database_connection.startswith("sqlite://"): + config["database_engine_option_max_overflow"] = "20" + config["database_engine_option_pool_size"] = "10" + return config + + +def install_database_conf(db_path, default_merged=False): + if 'GALAXY_TEST_INSTALL_DBURI' in os.environ: + install_galaxy_database_connection = os.environ['GALAXY_TEST_INSTALL_DBURI'] + elif asbool(os.environ.get('GALAXY_TEST_INSTALL_DB_MERGED', default_merged)): + install_galaxy_database_connection = None + else: + install_galaxy_db_path = os.path.join(db_path, 'install.sqlite') + install_galaxy_database_connection = 'sqlite:///%s' % install_galaxy_db_path + conf = {} + if install_galaxy_database_connection is not None: + conf["install_database_connection"] = install_galaxy_database_connection + return conf + + +def database_files_path(test_tmpdir, prefix="GALAXY"): + """Create a mock database/ directory like in GALAXY_ROOT. + + Use prefix to default this if TOOL_SHED_TEST_DBPATH or + GALAXY_TEST_DBPATH is set in the environment. + """ + environ_var = "%s_TEST_DBPATH" % prefix + if environ_var in os.environ: + db_path = os.environ[environ_var] + else: + tempdir = tempfile.mkdtemp(dir=test_tmpdir) + db_path = os.path.join(tempdir, 'database') + return db_path + + +def _get_static_settings(): + """Configuration required for Galaxy static middleware. + + Returns dictionary of the settings necessary for a galaxy App + to be wrapped in the static middleware. + + This mainly consists of the filesystem locations of url-mapped + static resources. + """ + static_dir = os.path.join(galaxy_root, "static") + + # TODO: these should be copied from config/galaxy.ini + return dict( + static_enabled=True, + static_cache_time=360, + static_dir=static_dir, + static_images_dir=os.path.join(static_dir, 'images', ''), + static_favicon_dir=os.path.join(static_dir, 'favicon.ico'), + static_scripts_dir=os.path.join(static_dir, 'scripts', ''), + static_style_dir=os.path.join(static_dir, 'june_2007_style', 'blue'), + static_robots_txt=os.path.join(static_dir, 'robots.txt'), + ) + + +def get_webapp_global_conf(): + """Get the global_conf dictionary sent to ``app_factory``.""" + # (was originally sent 'dict()') - nothing here for now except static settings + global_conf = dict() + global_conf.update( _get_static_settings() ) + return global_conf + + +def wait_for_http_server(host, port): + """Wait for an HTTP server to boot up.""" + # Test if the server is up + for i in range( 10 ): + # directly test the app, not the proxy + conn = httplib.HTTPConnection(host, port) + conn.request( "GET", "/" ) + if conn.getresponse().status == 200: + break + time.sleep( 0.1 ) + else: + template = "Test HTTP server on host %s and port %s did not return '200 OK' after 10 tries" + message = template % (host, port) + raise Exception(message) + + +def serve_webapp(webapp, port=None, host=None): + """Serve the webapp on a recommend port or a free one. + + Return the port the webapp is running one. + """ + server = None + if port is not None: + server = httpserver.serve( webapp, host=host, port=port, start_loop=False ) + else: + random.seed() + for i in range( 0, 9 ): + try: + port = str( random.randint( 8000, 10000 ) ) + server = httpserver.serve( webapp, host=host, port=port, start_loop=False ) + break + except socket.error, e: + if e[0] == 98: + continue + raise + else: + raise Exception( "Unable to open a port between %s and %s to start Galaxy server" % ( 8000, 1000 ) ) + + t = threading.Thread( target=server.serve_forever ) + t.start() + + return server, port + + +def cleanup_directory(tempdir): + """Clean up temporary files used by test unless GALAXY_TEST_NO_CLEANUP is set. + + Also respect TOOL_SHED_TEST_NO_CLEANUP for legacy reasons. + """ + skip_cleanup = "GALAXY_TEST_NO_CLEANUP" in os.environ or "TOOL_SHED_TEST_NO_CLEANUP" in os.environ + if skip_cleanup: + log.info( "GALAXY_TEST_NO_CLEANUP is on. Temporary files in %s" % tempdir ) + return + try: + if os.path.exists(tempdir) and skip_cleanup: + shutil.rmtree(tempdir) + except Exception: + pass + + +def setup_shed_tools_for_test(app, tmpdir, testing_migrated_tools, testing_installed_tools): + """Modify Galaxy app's toolbox for migrated or installed tool tests.""" + # Store a jsonified dictionary of tool_id : GALAXY_TEST_FILE_DIR pairs. + galaxy_tool_shed_test_file = os.path.join(tmpdir, 'shed_tools_dict') + shed_tools_dict = {} + if testing_migrated_tools: + has_test_data, shed_tools_dict = parse_tool_panel_config(MIGRATED_TOOL_PANEL_CONFIG, shed_tools_dict) + elif testing_installed_tools: + for shed_tool_config in INSTALLED_TOOL_PANEL_CONFIGS: + has_test_data, shed_tools_dict = parse_tool_panel_config(shed_tool_config, shed_tools_dict) + # Persist the shed_tools_dict to the galaxy_tool_shed_test_file. + with open(galaxy_tool_shed_test_file, 'w') as shed_tools_file: + shed_tools_file.write(json.dumps(shed_tools_dict)) + if not os.path.isabs(galaxy_tool_shed_test_file): + galaxy_tool_shed_test_file = os.path.join(galaxy_root, galaxy_tool_shed_test_file) + os.environ['GALAXY_TOOL_SHED_TEST_FILE'] = galaxy_tool_shed_test_file + if testing_installed_tools: + # TODO: Do this without modifying app - that is a pretty violation + # of Galaxy's abstraction - we shouldn't require app at all let alone + # be modifying it. + + tool_configs = app.config.tool_configs + # Eliminate the migrated_tool_panel_config from the app's tool_configs, append the list of installed_tool_panel_configs, + # and reload the app's toolbox. + relative_migrated_tool_panel_config = os.path.join(app.config.root, MIGRATED_TOOL_PANEL_CONFIG) + if relative_migrated_tool_panel_config in tool_configs: + tool_configs.remove(relative_migrated_tool_panel_config) + for installed_tool_panel_config in INSTALLED_TOOL_PANEL_CONFIGS: + tool_configs.append(installed_tool_panel_config) + from galaxy import tools # noqa, delay import because this brings in so many modules for small tests + app.toolbox = tools.ToolBox(tool_configs, app.config.tool_path, app) + + +def build_galaxy_app(simple_kwargs): + """Build a Galaxy app object from a simple keyword arguments. + + Construct paste style complex dictionary and use load_app_properties so + Galaxy override variables are respected. Also setup "global" references + to sqlalchemy database context for Galaxy and install databases. + """ + log.info("Galaxy database connection:", simple_kwargs["database_connection"]) + simple_kwargs['global_conf'] = get_webapp_global_conf() + simple_kwargs['global_conf']['__file__'] = "config/galaxy.ini.sample" + simple_kwargs = load_app_properties( + kwds=simple_kwargs + ) + # Build the Universe Application + app = GalaxyUniverseApplication( **simple_kwargs ) + log.info( "Embedded Galaxy application started" ) + database_contexts.galaxy_context = app.model.context + database_contexts.install_context = app.install_model.context + return app + + +def build_shed_app(simple_kwargs): + """Build a Galaxy app object from a simple keyword arguments. + + Construct paste style complex dictionary. Also setup "global" reference + to sqlalchemy database context for tool shed database. + """ + log.info("Tool shed database connection:", simple_kwargs["database_connection"]) + # TODO: Simplify global_conf to match Galaxy above... + simple_kwargs['__file__'] = 'tool_shed_wsgi.ini.sample' + simple_kwargs['global_conf'] = get_webapp_global_conf() + + app = ToolshedUniverseApplication( **simple_kwargs ) + database_contexts.tool_shed_context = app.model.context + log.info( "Embedded Toolshed application started" ) + return app + +ServerWrapper = collections.namedtuple('ServerWrapper', ['app', 'server', 'name', 'host', 'port']) + + +def _stop(self): + if self.server is not None: + log.info("Shutting down embedded %s web server" % self.name) + self.server.server_close() + log.info("Embedded web server %s stopped" % self.name) + + if self.app is not None: + log.info("Stopping application %s" % self.name) + self.app.shutdown() + log.info("Application %s stopped." % self.name) + +ServerWrapper.stop = _stop + + +def launch_server(app, webapp_factory, kwargs, prefix="GALAXY"): + """Launch a web server for a given app using supplied factory. + + Consistently read either GALAXY_TEST_HOST and GALAXY_TEST_PORT or + TOOL_SHED_TEST_HOST and TOOL_SHED_TEST_PORT and ensure these are + all set after this method has been called. + """ + name = prefix.lower() + + host_env_key = "%s_TEST_HOST" % prefix + port_env_key = "%s_TEST_PORT" % prefix + host = os.environ.get(host_env_key, DEFAULT_WEB_HOST) + port = os.environ.get(port_env_key, None) + + webapp = webapp_factory( + kwargs[ 'global_conf' ], + app=app, + use_translogger=False, + static_enabled=True + ) + server, port = serve_webapp( + webapp, + host=host, port=port + ) + os.environ[host_env_key] = host + os.environ[port_env_key] = port + wait_for_http_server(host, port) + log.info("Embedded web server for %s started" % name) + return ServerWrapper( + app, server, name, host, port + ) + + +class TestDriver(object): + """Responsible for the life-cycle of a Galaxy-style functional test. + + Sets up servers, configures tests, runs nose, and tears things + down. This is somewhat like a Python TestCase - but different + because it is meant to provide a main() endpoint. + """ + + def __init__(self): + """Setup tracked resources.""" + self.server_wrappers = [] + self.temp_directories = [] + + def setup(self): + """Called before tests are built.""" + + def build_tests(self): + """After environment is setup, setup nose tests.""" + + def tear_down(self): + """Cleanup resources tracked by this object.""" + for server_wrapper in self.server_wrappers: + server_wrapper.stop() + for temp_directory in self.temp_directories: + cleanup_directory(temp_directory) + + def run(self): + """Driver whole test. + + Setup environment, build tests (if needed), run test, + and finally cleanup resources. + """ + configure_environment() + self.setup() + self.build_tests() + try: + success = nose_config_and_run() + return 0 if success else 1 + except Exception as e: + log.info("Failure running tests") + raise e + finally: + log.info( "Shutting down") + self.tear_down() + + +class GalaxyTestDriver(TestDriver): + """Instantial a Galaxy-style nose TestDriver for testing Galaxy.""" + + testing_shed_tools = False + + def setup(self): + """Setup a Galaxy server for functional test (if needed).""" + self.external_galaxy = os.environ.get('GALAXY_TEST_EXTERNAL', None) + self.galaxy_test_tmp_dir = get_galaxy_test_tmp_dir() + self.temp_directories.append(self.galaxy_test_tmp_dir) + + testing_shed_tools = getattr(self, "testing_shed_tools", False) + default_tool_conf = getattr(self, "default_tool_conf", None) + datatypes_conf_override = getattr(self, "datatypes_conf_override", None) + + if self.external_galaxy is None: + tempdir = tempfile.mkdtemp(dir=self.galaxy_test_tmp_dir) + # Configure the database path. + galaxy_db_path = database_files_path(tempdir) + galaxy_config = setup_galaxy_config( + galaxy_db_path, + use_test_file_dir=not testing_shed_tools, + default_install_db_merged=True, + default_tool_conf=default_tool_conf, + datatypes_conf=datatypes_conf_override, + ) + + # ---- Build Application -------------------------------------------------- + self.app = build_galaxy_app(galaxy_config) + server_wrapper = launch_server( + self.app, + buildapp.app_factory, + galaxy_config, + ) + self.server_wrappers.append(server_wrapper) + log.info("Functional tests will be run against %s:%s" % (server_wrapper.host, server_wrapper.port)) + else: + log.info("Functional tests will be run against %s" % self.external_galaxy) + + def setup_shed_tools(self, testing_migrated_tools=False, testing_installed_tools=True): + setup_shed_tools_for_test( + self.app, + self.galaxy_test_tmp_dir, + testing_migrated_tools, + testing_installed_tools + ) + + def build_tool_tests(self): + if self.app is None: + return + + # We must make sure that functional.test_toolbox is always imported after + # database_contexts.galaxy_content is set (which occurs in this method above). + # If functional.test_toolbox is imported before database_contexts.galaxy_content + # is set, sa_session will be None in all methods that use it. + import functional.test_toolbox + functional.test_toolbox.toolbox = self.app.toolbox + # When testing data managers, do not test toolbox. + functional.test_toolbox.build_tests( + app=self.app, + testing_shed_tools=self.testing_shed_tools, + master_api_key=get_master_api_key(), + user_api_key=get_user_api_key(), + ) + + +def drive_test(test_driver_class): + """Instantiate driver class, run, and exit appropriately.""" + sys.exit(test_driver_class().run()) + + +__all__ = [ + "copy_database_template", + "build_logger", + "drive_test", + "FRAMEWORK_UPLOAD_TOOL_CONF", + "FRAMEWORK_SAMPLE_TOOLS_CONF", + "FRAMEWORK_DATATYPES_CONF", + "database_conf", + "get_webapp_global_conf", + "nose_config_and_run", + "setup_galaxy_config", + "TestDriver", + "wait_for_http_server", +] diff --git a/test/base/twilltestcase.py b/test/base/twilltestcase.py index 0a9af77d3c6..3fe965d1835 100644 --- a/test/base/twilltestcase.py +++ b/test/base/twilltestcase.py @@ -48,7 +48,8 @@ class TwillTestCase( unittest.TestCase ): self.history_id = os.environ.get( 'GALAXY_TEST_HISTORY_ID', None ) self.host = os.environ.get( 'GALAXY_TEST_HOST' ) self.port = os.environ.get( 'GALAXY_TEST_PORT' ) - self.url = "http://%s:%s" % ( self.host, self.port ) + default_url = "http://%s:%s" % (self.host, self.port) + self.url = os.environ.get('GALAXY_TEST_EXTERNAL', default_url) self.test_data_resolver = TestDataResolver( ) self.tool_shed_test_file = os.environ.get( 'GALAXY_TOOL_SHED_TEST_FILE', None ) if self.tool_shed_test_file: diff --git a/test/functional/tools/upload_tool_conf.xml b/test/functional/tools/upload_tool_conf.xml new file mode 100644 index 00000000000..0865acc83cd --- /dev/null +++ b/test/functional/tools/upload_tool_conf.xml @@ -0,0 +1,6 @@ + + +
+ +
+
diff --git a/test/functional/workflow.py b/test/functional/workflow.py index c75d8940265..9dbba1fbe7e 100644 --- a/test/functional/workflow.py +++ b/test/functional/workflow.py @@ -17,7 +17,7 @@ class WorkflowTestCase( TwillTestCase ): Kind of a shell of a test case for running workflow tests. Probably needs to look more like test_toolbox. """ - workflow_test_file = None + workflow_test_file = os.environ.get("GALAXY_TEST_WORKFLOW_FILE", None) user_api_key = None master_api_key = None diff --git a/test/tool_shed/functional_tests.py b/test/tool_shed/functional_tests.py index 6d3da018b51..dca6e24db31 100644 --- a/test/tool_shed/functional_tests.py +++ b/test/tool_shed/functional_tests.py @@ -1,96 +1,26 @@ #!/usr/bin/env python +"""Test driver for tool shed functional tests. + +Launch this script by running ``run_tests.sh -t`` from GALAXY_ROOT. +""" from __future__ import absolute_import -import httplib -import logging import os -import random -import shutil -import socket import string import sys import tempfile -import threading -import time -import urllib galaxy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir)) -tool_shed_home_directory = os.path.join( galaxy_root, 'test', 'tool_shed' ) -default_tool_shed_test_file_dir = os.path.join( tool_shed_home_directory, 'test_data' ) -# Here's the directory where everything happens. Temporary directories are created within this directory to contain -# the hgweb.config file, the database, new repositories, etc. Since the tool shed browses repository contents via HTTP, -# the full path to the temporary directroy wher eht repositories are located cannot contain invalid url characters. -tool_shed_test_tmp_dir = os.path.join( tool_shed_home_directory, 'tmp' ) -os.environ[ 'TOOL_SHED_TEST_TMP_DIR' ] = tool_shed_test_tmp_dir # Need to remove this directory from sys.path sys.path[0:1] = [ os.path.join( galaxy_root, "lib" ), os.path.join( galaxy_root, "test" ) ] -from paste import httpserver -import nose.config -import nose.core -import nose.loader -import nose.plugins.manager +from base import driver_util +log = driver_util.build_logger() # This is for the tool shed application. from galaxy.webapps.tool_shed import buildapp as toolshedbuildapp -from galaxy.webapps.tool_shed.app import UniverseApplication as ToolshedUniverseApplication -# This is for the galaxy application. -from galaxy.app import UniverseApplication as GalaxyUniverseApplication -from galaxy.util import asbool from galaxy.web import buildapp as galaxybuildapp -from base import nose_util -from functional import database_contexts - -log = logging.getLogger( "tool_shed_functional_tests.py" ) - -default_tool_shed_test_host = "localhost" -default_tool_shed_test_port_min = 8000 -default_tool_shed_test_port_max = 8999 -default_tool_shed_locales = 'en' -default_galaxy_test_port_min = 9000 -default_galaxy_test_port_max = 9999 -default_galaxy_test_host = 'localhost' - -# Use separate databases for Galaxy and tool shed install info by default, -# set GALAXY_TEST_INSTALL_DB_MERGED to True to revert to merged databases -# behavior. -default_install_db_merged = False - -# should this serve static resources (scripts, images, styles, etc.) -STATIC_ENABLED = True - - -def get_static_settings(): - """Returns dictionary of the settings necessary for a galaxy App - to be wrapped in the static middleware. - - This mainly consists of the filesystem locations of url-mapped - static resources. - """ - static_dir = os.path.join( galaxy_root, 'static' ) - # TODO: these should be copied from galaxy.ini - return dict( - # TODO: static_enabled needed here? - static_enabled=True, - static_cache_time=360, - static_dir=static_dir, - static_images_dir=os.path.join( static_dir, 'images', '' ), - static_favicon_dir=os.path.join( static_dir, 'favicon.ico' ), - static_scripts_dir=os.path.join( static_dir, 'scripts', '' ), - static_style_dir=os.path.join( static_dir, 'june_2007_style', 'blue' ), - static_robots_txt=os.path.join( static_dir, 'robots.txt' ), - ) - - -def get_webapp_global_conf(): - """Get the global_conf dictionary sent as the first argument to app_factory. - """ - # (was originally sent 'dict()') - nothing here for now except static settings - global_conf = dict() - if STATIC_ENABLED: - global_conf.update( get_static_settings() ) - return global_conf tool_sheds_conf_xml_template = ''' @@ -105,14 +35,6 @@ shed_tool_conf_xml_template = ''' ''' -tool_conf_xml = ''' - -
- -
-
-''' - tool_data_table_conf_xml_template = ''' @@ -124,372 +46,124 @@ shed_data_manager_conf_xml_template = ''' ''' -def run_tests( test_config ): - return nose_util.run( test_config ) +class ToolShedTestDriver(driver_util.TestDriver): + """Instantial a Galaxy-style nose TestDriver for testing the tool shed.""" - -def main(): - # ---- Configuration ------------------------------------------------------ - tool_shed_test_host = os.environ.get( 'TOOL_SHED_TEST_HOST', default_tool_shed_test_host ) - tool_shed_test_port = os.environ.get( 'TOOL_SHED_TEST_PORT', None ) - galaxy_test_host = os.environ.get( 'GALAXY_TEST_HOST', default_galaxy_test_host ) - galaxy_test_port = os.environ.get( 'GALAXY_TEST_PORT', None ) - tool_path = os.environ.get( 'TOOL_SHED_TEST_TOOL_PATH', 'tools' ) - if 'HTTP_ACCEPT_LANGUAGE' not in os.environ: - os.environ[ 'HTTP_ACCEPT_LANGUAGE' ] = default_tool_shed_locales - tool_shed_test_file_dir = os.environ.get( 'TOOL_SHED_TEST_FILE_DIR', default_tool_shed_test_file_dir ) - if not os.path.isabs( tool_shed_test_file_dir ): - tool_shed_test_file_dir = tool_shed_test_file_dir - ignore_files = () - tool_dependency_dir = os.environ.get( 'TOOL_SHED_TOOL_DEPENDENCY_DIR', None ) - use_distributed_object_store = os.environ.get( 'TOOL_SHED_USE_DISTRIBUTED_OBJECT_STORE', False ) - if not os.path.isdir( tool_shed_test_tmp_dir ): - os.mkdir( tool_shed_test_tmp_dir ) - tool_shed_test_proxy_port = None - galaxy_test_proxy_port = None - if 'TOOL_SHED_TEST_DBPATH' in os.environ: - shed_db_path = os.environ[ 'TOOL_SHED_TEST_DBPATH' ] - else: - tempdir = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - shed_db_path = os.path.join( tempdir, 'database' ) - shed_tool_data_table_conf_file = os.environ.get( 'TOOL_SHED_TEST_TOOL_DATA_TABLE_CONF', os.path.join( tool_shed_test_tmp_dir, 'shed_tool_data_table_conf.xml' ) ) - galaxy_shed_data_manager_conf_file = os.environ.get( 'GALAXY_SHED_DATA_MANAGER_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_shed_data_manager_conf.xml' ) ) - galaxy_tool_data_table_conf_file = os.environ.get( 'GALAXY_TEST_TOOL_DATA_TABLE_CONF', os.path.join( tool_shed_test_tmp_dir, 'tool_data_table_conf.xml' ) ) - galaxy_tool_conf_file = os.environ.get( 'GALAXY_TEST_TOOL_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_tool_conf.xml' ) ) - galaxy_shed_tool_conf_file = os.environ.get( 'GALAXY_TEST_SHED_TOOL_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_shed_tool_conf.xml' ) ) - galaxy_migrated_tool_conf_file = os.environ.get( 'GALAXY_TEST_MIGRATED_TOOL_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_migrated_tool_conf.xml' ) ) - galaxy_tool_sheds_conf_file = os.environ.get( 'GALAXY_TEST_TOOL_SHEDS_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_sheds_conf.xml' ) ) - if 'GALAXY_TEST_TOOL_DATA_PATH' in os.environ: - tool_data_path = os.environ.get( 'GALAXY_TEST_TOOL_DATA_PATH' ) - else: - tool_data_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - os.environ[ 'GALAXY_TEST_TOOL_DATA_PATH' ] = tool_data_path - if 'GALAXY_TEST_DBPATH' in os.environ: - galaxy_db_path = os.environ[ 'GALAXY_TEST_DBPATH' ] - else: - tempdir = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - galaxy_db_path = os.path.join( tempdir, 'database' ) - shed_file_path = os.path.join( shed_db_path, 'files' ) - galaxy_file_path = os.path.join( galaxy_db_path, 'files' ) - hgweb_config_file_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - new_repos_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - galaxy_tempfiles = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - galaxy_shed_tool_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - galaxy_migrated_tool_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - galaxy_tool_dependency_dir = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) - os.environ[ 'GALAXY_TEST_TOOL_DEPENDENCY_DIR' ] = galaxy_tool_dependency_dir - hgweb_config_dir = hgweb_config_file_path - os.environ[ 'TEST_HG_WEB_CONFIG_DIR' ] = hgweb_config_dir - print "Directory location for hgweb.config:", hgweb_config_dir - if 'TOOL_SHED_TEST_DBURI' in os.environ: - toolshed_database_connection = os.environ[ 'TOOL_SHED_TEST_DBURI' ] - else: - toolshed_database_connection = 'sqlite:///' + os.path.join( shed_db_path, 'community_test.sqlite' ) - galaxy_database_auto_migrate = False - if 'GALAXY_TEST_DBURI' in os.environ: - galaxy_database_connection = os.environ[ 'GALAXY_TEST_DBURI' ] - else: - db_path = os.path.join( galaxy_db_path, 'universe.sqlite' ) - if 'GALAXY_TEST_DB_TEMPLATE' in os.environ: - # Middle ground between recreating a completely new - # database and pointing at existing database with - # GALAXY_TEST_DBURI. The former requires a lot of setup - # time, the latter results in test failures in certain - # cases (namely tool shed tests expecting clean database). - __copy_database_template(os.environ['GALAXY_TEST_DB_TEMPLATE'], db_path) - galaxy_database_auto_migrate = True - if not os.path.exists(galaxy_db_path): - os.makedirs(galaxy_db_path) - galaxy_database_connection = 'sqlite:///%s' % db_path - if 'GALAXY_TEST_INSTALL_DBURI' in os.environ: - install_galaxy_database_connection = os.environ[ 'GALAXY_TEST_INSTALL_DBURI' ] - elif asbool( os.environ.get( 'GALAXY_TEST_INSTALL_DB_MERGED', default_install_db_merged ) ): - install_galaxy_database_connection = galaxy_database_connection - else: - install_galaxy_db_path = os.path.join( galaxy_db_path, 'install.sqlite' ) - install_galaxy_database_connection = 'sqlite:///%s' % install_galaxy_db_path - tool_shed_global_conf = get_webapp_global_conf() - tool_shed_global_conf[ '__file__' ] = 'tool_shed_wsgi.ini.sample' - kwargs = dict( admin_users='test@bx.psu.edu', - allow_user_creation=True, - allow_user_deletion=True, - database_connection=toolshed_database_connection, - datatype_converters_config_file='datatype_converters_conf.xml.sample', - file_path=shed_file_path, - global_conf=tool_shed_global_conf, - hgweb_config_dir=hgweb_config_dir, - job_queue_workers=5, - id_secret='changethisinproductiontoo', - log_destination="stdout", - new_file_path=new_repos_path, - running_functional_tests=True, - shed_tool_data_table_config=shed_tool_data_table_conf_file, - smtp_server='smtp.dummy.string.tld', - email_from='functional@localhost', - template_path='templates', - tool_path=tool_path, - tool_parse_help=False, - tool_data_table_config_path=galaxy_tool_data_table_conf_file, - use_heartbeat=False ) - for dir in [ tool_shed_test_tmp_dir ]: - try: - os.makedirs( dir ) - except OSError: - pass - - print "Tool shed database connection:", toolshed_database_connection - print "Galaxy database connection:", galaxy_database_connection - - # Generate the tool_data_table_conf.xml file. - file( galaxy_tool_data_table_conf_file, 'w' ).write( tool_data_table_conf_xml_template ) - # Generate the shed_tool_data_table_conf.xml file. - file( shed_tool_data_table_conf_file, 'w' ).write( tool_data_table_conf_xml_template ) - os.environ[ 'TOOL_SHED_TEST_TOOL_DATA_TABLE_CONF' ] = shed_tool_data_table_conf_file - # ---- Build Tool Shed Application -------------------------------------------------- - toolshedapp = None -# if not toolshed_database_connection.startswith( 'sqlite://' ): -# kwargs[ 'database_engine_option_max_overflow' ] = '20' - if tool_dependency_dir is not None: - kwargs[ 'tool_dependency_dir' ] = tool_dependency_dir - if use_distributed_object_store: - kwargs[ 'object_store' ] = 'distributed' - kwargs[ 'distributed_object_store_config_file' ] = 'distributed_object_store_conf.xml.sample' - - kwargs[ 'global_conf' ] = tool_shed_global_conf - - if not toolshed_database_connection.startswith( 'sqlite://' ): - kwargs[ 'database_engine_option_pool_size' ] = '10' - - toolshedapp = ToolshedUniverseApplication( **kwargs ) - database_contexts.tool_shed_context = toolshedapp.model.context - log.info( "Embedded Toolshed application started" ) - - # ---- Run tool shed webserver ------------------------------------------------------ - tool_shed_server = None - tool_shed_global_conf[ 'database_connection' ] = toolshed_database_connection - toolshedwebapp = toolshedbuildapp.app_factory( tool_shed_global_conf, - use_translogger=False, - static_enabled=True, - app=toolshedapp ) - if tool_shed_test_port is not None: - tool_shed_server = httpserver.serve( toolshedwebapp, host=tool_shed_test_host, port=tool_shed_test_port, start_loop=False ) - else: - random.seed() - for i in range( 0, 9 ): - try: - tool_shed_test_port = str( random.randint( default_tool_shed_test_port_min, default_tool_shed_test_port_max ) ) - log.debug( "Attempting to serve app on randomly chosen port: %s" % tool_shed_test_port ) - tool_shed_server = httpserver.serve( toolshedwebapp, host=tool_shed_test_host, port=tool_shed_test_port, start_loop=False ) - break - except socket.error, e: - if e[0] == 98: - continue - raise + def setup(self): + """Entry point for test driver script.""" + # ---- Configuration ------------------------------------------------------ + tool_shed_test_tmp_dir = driver_util.setup_tool_shed_tmp_dir() + if not os.path.isdir( tool_shed_test_tmp_dir ): + os.mkdir( tool_shed_test_tmp_dir ) + self.temp_directories.append(tool_shed_test_tmp_dir) + shed_db_path = driver_util.database_files_path(tool_shed_test_tmp_dir, prefix="TOOL_SHED") + shed_tool_data_table_conf_file = os.environ.get( 'TOOL_SHED_TEST_TOOL_DATA_TABLE_CONF', os.path.join( tool_shed_test_tmp_dir, 'shed_tool_data_table_conf.xml' ) ) + galaxy_shed_data_manager_conf_file = os.environ.get( 'GALAXY_SHED_DATA_MANAGER_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_shed_data_manager_conf.xml' ) ) + default_tool_data_table_config_path = os.path.join( tool_shed_test_tmp_dir, 'tool_data_table_conf.xml' ) + galaxy_shed_tool_conf_file = os.environ.get( 'GALAXY_TEST_SHED_TOOL_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_shed_tool_conf.xml' ) ) + galaxy_migrated_tool_conf_file = os.environ.get( 'GALAXY_TEST_MIGRATED_TOOL_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_migrated_tool_conf.xml' ) ) + galaxy_tool_sheds_conf_file = os.environ.get( 'GALAXY_TEST_TOOL_SHEDS_CONF', os.path.join( tool_shed_test_tmp_dir, 'test_sheds_conf.xml' ) ) + if 'GALAXY_TEST_TOOL_DATA_PATH' in os.environ: + tool_data_path = os.environ.get( 'GALAXY_TEST_TOOL_DATA_PATH' ) else: - raise Exception( "Unable to open a port between %s and %s to start Galaxy server" % ( default_tool_shed_test_port_min, default_tool_shed_test_port_max ) ) - if tool_shed_test_proxy_port: - os.environ[ 'TOOL_SHED_TEST_PORT' ] = tool_shed_test_proxy_port - else: - os.environ[ 'TOOL_SHED_TEST_PORT' ] = tool_shed_test_port - t = threading.Thread( target=tool_shed_server.serve_forever ) - t.start() - # Test if the server is up - for i in range( 10 ): - # Directly test the app, not the proxy. - conn = httplib.HTTPConnection( tool_shed_test_host, tool_shed_test_port ) - conn.request( "GET", "/" ) - if conn.getresponse().status == 200: - break - time.sleep( 0.1 ) - else: - raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" ) - log.info( "Embedded web server started" ) - - # ---- Optionally start up a Galaxy instance ------------------------------------------------------ - if 'TOOL_SHED_TEST_OMIT_GALAXY' not in os.environ: - # Generate the tool_conf.xml file. - file( galaxy_tool_conf_file, 'w' ).write( tool_conf_xml ) - # Generate the shed_tool_conf.xml file. - tool_sheds_conf_template_parser = string.Template( tool_sheds_conf_xml_template ) - tool_sheds_conf_xml = tool_sheds_conf_template_parser.safe_substitute( shed_url=tool_shed_test_host, shed_port=tool_shed_test_port ) - file( galaxy_tool_sheds_conf_file, 'w' ).write( tool_sheds_conf_xml ) - # Generate the tool_sheds_conf.xml file. - shed_tool_conf_template_parser = string.Template( shed_tool_conf_xml_template ) - shed_tool_conf_xml = shed_tool_conf_template_parser.safe_substitute( shed_tool_path=galaxy_shed_tool_path ) - file( galaxy_shed_tool_conf_file, 'w' ).write( shed_tool_conf_xml ) - # Generate the migrated_tool_conf.xml file. - migrated_tool_conf_xml = shed_tool_conf_template_parser.safe_substitute( shed_tool_path=galaxy_migrated_tool_path ) - file( galaxy_migrated_tool_conf_file, 'w' ).write( migrated_tool_conf_xml ) - os.environ[ 'GALAXY_TEST_SHED_TOOL_CONF' ] = galaxy_shed_tool_conf_file - # Generate shed_data_manager_conf.xml - if not os.environ.get( 'GALAXY_SHED_DATA_MANAGER_CONF' ): - open( galaxy_shed_data_manager_conf_file, 'wb' ).write( shed_data_manager_conf_xml_template ) - galaxy_global_conf = get_webapp_global_conf() - galaxy_global_conf[ '__file__' ] = 'config/galaxy.ini.sample' - - kwargs = dict( allow_user_creation=True, + tool_data_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) + os.environ[ 'GALAXY_TEST_TOOL_DATA_PATH' ] = tool_data_path + galaxy_db_path = driver_util.database_files_path(tool_shed_test_tmp_dir) + shed_file_path = os.path.join( shed_db_path, 'files' ) + hgweb_config_file_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) + new_repos_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) + galaxy_shed_tool_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) + galaxy_migrated_tool_path = tempfile.mkdtemp( dir=tool_shed_test_tmp_dir ) + hgweb_config_dir = hgweb_config_file_path + os.environ[ 'TEST_HG_WEB_CONFIG_DIR' ] = hgweb_config_dir + print "Directory location for hgweb.config:", hgweb_config_dir + toolshed_database_conf = driver_util.database_conf(shed_db_path, prefix="TOOL_SHED") + kwargs = dict( admin_users='test@bx.psu.edu', + allow_user_creation=True, allow_user_deletion=True, - admin_users='test@bx.psu.edu', - allow_library_path_paste=True, - install_database_connection=install_galaxy_database_connection, - database_connection=galaxy_database_connection, - database_auto_migrate=galaxy_database_auto_migrate, - datatype_converters_config_file="datatype_converters_conf.xml.sample", - check_migrate_tools=False, - enable_tool_shed_check=True, - file_path=galaxy_file_path, - global_conf=galaxy_global_conf, - hours_between_check=0.001, - id_secret='changethisinproductiontoo', + datatype_converters_config_file='datatype_converters_conf.xml.sample', + file_path=shed_file_path, + hgweb_config_dir=hgweb_config_dir, job_queue_workers=5, + id_secret='changethisinproductiontoo', log_destination="stdout", - migrated_tools_config=galaxy_migrated_tool_conf_file, - new_file_path=galaxy_tempfiles, + new_file_path=new_repos_path, running_functional_tests=True, - shed_data_manager_config_file=galaxy_shed_data_manager_conf_file, shed_tool_data_table_config=shed_tool_data_table_conf_file, - shed_tool_path=galaxy_shed_tool_path, - template_path="templates", - tool_data_path=tool_data_path, - tool_dependency_dir=galaxy_tool_dependency_dir, - tool_path=tool_path, - tool_config_file=[ galaxy_tool_conf_file, galaxy_shed_tool_conf_file ], - tool_sheds_config_file=galaxy_tool_sheds_conf_file, + smtp_server='smtp.dummy.string.tld', + email_from='functional@localhost', + template_path='templates', tool_parse_help=False, - tool_data_table_config_path=galaxy_tool_data_table_conf_file, - update_integrated_tool_panel=False, use_heartbeat=False ) + kwargs.update(toolshed_database_conf) + # Generate the tool_data_table_conf.xml file. + file( default_tool_data_table_config_path, 'w' ).write( tool_data_table_conf_xml_template ) + # Generate the shed_tool_data_table_conf.xml file. + file( shed_tool_data_table_conf_file, 'w' ).write( tool_data_table_conf_xml_template ) + os.environ[ 'TOOL_SHED_TEST_TOOL_DATA_TABLE_CONF' ] = shed_tool_data_table_conf_file + # ---- Build Tool Shed Application -------------------------------------------------- + toolshedapp = driver_util.build_shed_app(kwargs) - # ---- Build Galaxy Application -------------------------------------------------- - if not galaxy_database_connection.startswith( 'sqlite://' ) and not install_galaxy_database_connection.startswith( 'sqlite://' ): - kwargs[ 'database_engine_option_pool_size' ] = '10' - kwargs[ 'database_engine_option_max_overflow' ] = '20' - galaxyapp = GalaxyUniverseApplication( **kwargs ) - - log.info( "Embedded Galaxy application started" ) - - # ---- Run galaxy webserver ------------------------------------------------------ - galaxy_server = None - galaxy_global_conf[ 'database_file' ] = galaxy_database_connection - galaxywebapp = galaxybuildapp.app_factory( galaxy_global_conf, - use_translogger=False, - static_enabled=True, - app=galaxyapp ) - database_contexts.galaxy_context = galaxyapp.model.context - database_contexts.install_context = galaxyapp.install_model.context - if galaxy_test_port is not None: - galaxy_server = httpserver.serve( galaxywebapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False ) - else: - random.seed() - for i in range( 0, 9 ): - try: - galaxy_test_port = str( random.randint( default_galaxy_test_port_min, default_galaxy_test_port_max ) ) - log.debug( "Attempting to serve app on randomly chosen port: %s" % galaxy_test_port ) - galaxy_server = httpserver.serve( galaxywebapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False ) - break - except socket.error, e: - if e[0] == 98: - continue - raise - else: - raise Exception( "Unable to open a port between %s and %s to start Galaxy server" % - ( default_galaxy_test_port_min, default_galaxy_test_port_max ) ) - if galaxy_test_proxy_port: - os.environ[ 'GALAXY_TEST_PORT' ] = galaxy_test_proxy_port - else: - os.environ[ 'GALAXY_TEST_PORT' ] = galaxy_test_port - t = threading.Thread( target=galaxy_server.serve_forever ) - t.start() - # Test if the server is up - for i in range( 10 ): - # Directly test the app, not the proxy. - conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port ) - conn.request( "GET", "/" ) - if conn.getresponse().status == 200: - break - time.sleep( 0.1 ) - else: - raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" ) - log.info( "Embedded galaxy web server started" ) - # ---- Find tests --------------------------------------------------------- - if tool_shed_test_proxy_port: - log.info( "Functional tests will be run against %s:%s" % ( tool_shed_test_host, tool_shed_test_proxy_port ) ) - else: + # ---- Run tool shed webserver ------------------------------------------------------ + # TODO: Needed for hg middleware ('lib/galaxy/webapps/tool_shed/framework/middleware/hg.py') + kwargs['global_conf']['database_connection'] = kwargs["database_connection"] + tool_shed_server_wrapper = driver_util.launch_server( + toolshedapp, + toolshedbuildapp.app_factory, + kwargs, + prefix="TOOL_SHED", + ) + self.server_wrappers.append(tool_shed_server_wrapper) + tool_shed_test_host = tool_shed_server_wrapper.host + tool_shed_test_port = tool_shed_server_wrapper.port log.info( "Functional tests will be run against %s:%s" % ( tool_shed_test_host, tool_shed_test_port ) ) - if galaxy_test_proxy_port: - log.info( "Galaxy tests will be run against %s:%s" % ( galaxy_test_host, galaxy_test_proxy_port ) ) - else: - log.info( "Galaxy tests will be run against %s:%s" % ( galaxy_test_host, galaxy_test_port ) ) - success = False - try: - # Pass in through script set env, will leave a copy of ALL test validate files. - os.environ[ 'TOOL_SHED_TEST_HOST' ] = tool_shed_test_host - os.environ[ 'GALAXY_TEST_HOST' ] = galaxy_test_host - if tool_shed_test_file_dir: - os.environ[ 'TOOL_SHED_TEST_FILE_DIR' ] = tool_shed_test_file_dir - test_config = nose.config.Config( env=os.environ, ignoreFiles=ignore_files, plugins=nose.plugins.manager.DefaultPluginManager() ) - test_config.configure( sys.argv ) - # Run the tests. - result = run_tests( test_config ) - success = result.wasSuccessful() - except: - log.exception( "Failure running tests" ) - log.info( "Shutting down" ) - # ---- Tear down ----------------------------------------------------------- - if tool_shed_server: - log.info( "Shutting down embedded web server" ) - tool_shed_server.server_close() - tool_shed_server = None - log.info( "Embedded web server stopped" ) - if toolshedapp: - log.info( "Shutting down tool shed app" ) - toolshedapp.shutdown() - toolshedapp = None - log.info( "Embedded tool shed application stopped" ) - if 'TOOL_SHED_TEST_OMIT_GALAXY' not in os.environ: - if galaxy_server: - log.info( "Shutting down galaxy web server" ) - galaxy_server.server_close() - galaxy_server = None - log.info( "Embedded galaxy server stopped" ) - if galaxyapp: - log.info( "Shutting down galaxy app" ) - galaxyapp.shutdown() - galaxyapp = None - log.info( "Embedded galaxy application stopped" ) - if 'TOOL_SHED_TEST_NO_CLEANUP' not in os.environ: - try: - for dir in [ tool_shed_test_tmp_dir ]: - if os.path.exists( dir ): - log.info( "Cleaning up temporary files in %s" % dir ) - shutil.rmtree( dir ) - except: - pass - if success: - return 0 - else: - return 1 + # ---- Optionally start up a Galaxy instance ------------------------------------------------------ + if 'TOOL_SHED_TEST_OMIT_GALAXY' not in os.environ: + # Generate the shed_tool_conf.xml file. + tool_sheds_conf_template_parser = string.Template( tool_sheds_conf_xml_template ) + tool_sheds_conf_xml = tool_sheds_conf_template_parser.safe_substitute( shed_url=tool_shed_test_host, shed_port=tool_shed_test_port ) + file( galaxy_tool_sheds_conf_file, 'w' ).write( tool_sheds_conf_xml ) + # Generate the tool_sheds_conf.xml file. + shed_tool_conf_template_parser = string.Template( shed_tool_conf_xml_template ) + shed_tool_conf_xml = shed_tool_conf_template_parser.safe_substitute( shed_tool_path=galaxy_shed_tool_path ) + file( galaxy_shed_tool_conf_file, 'w' ).write( shed_tool_conf_xml ) + # Generate the migrated_tool_conf.xml file. + migrated_tool_conf_xml = shed_tool_conf_template_parser.safe_substitute( shed_tool_path=galaxy_migrated_tool_path ) + file( galaxy_migrated_tool_conf_file, 'w' ).write( migrated_tool_conf_xml ) + os.environ[ 'GALAXY_TEST_SHED_TOOL_CONF' ] = galaxy_shed_tool_conf_file + # Generate shed_data_manager_conf.xml + if not os.environ.get( 'GALAXY_SHED_DATA_MANAGER_CONF' ): + open( galaxy_shed_data_manager_conf_file, 'wb' ).write( shed_data_manager_conf_xml_template ) + kwargs = dict( migrated_tools_config=galaxy_migrated_tool_conf_file, + shed_data_manager_config_file=galaxy_shed_data_manager_conf_file, + shed_tool_path=galaxy_shed_tool_path, + tool_data_path=tool_data_path, + tool_sheds_config_file=galaxy_tool_sheds_conf_file ) + kwargs.update( + driver_util.setup_galaxy_config( + galaxy_db_path, + use_test_file_dir=False, + default_install_db_merged=False, + default_tool_data_table_config_path=default_tool_data_table_config_path, + default_shed_tool_data_table_config=shed_tool_data_table_conf_file, + enable_tool_shed_check=True, + shed_tool_conf=galaxy_shed_tool_conf_file, + update_integrated_tool_panel=True, + ) + ) + print "Galaxy database connection:", kwargs["database_connection"] - -def __copy_database_template( source, db_path ): - """ - Copy a 'clean' sqlite template database (from file or URL) to specified - database path. - """ - os.makedirs( os.path.dirname( db_path ) ) - if os.path.exists( source ): - shutil.copy( source, db_path ) - assert os.path.exists( db_path ) - elif source.startswith("http"): - urllib.urlretrieve( source, db_path ) - else: - raise Exception( "Failed to copy database template from source %s" % source ) + # ---- Run galaxy webserver ------------------------------------------------------ + galaxyapp = driver_util.build_galaxy_app(kwargs) + galaxy_server_wrapper = driver_util.launch_server( + galaxyapp, + galaxybuildapp.app_factory, + kwargs, + ) + log.info("Galaxy tests will be run against %s:%s" % (galaxy_server_wrapper.host, galaxy_server_wrapper.port)) + self.server_wrappers.append(galaxy_server_wrapper) if __name__ == "__main__": - try: - sys.exit( main() ) - except Exception, e: - log.exception( str( e ) ) - exit(1) + driver_util.drive_test(ToolShedTestDriver)