[2] | 1 | #!/usr/bin/env python |
---|
| 2 | |
---|
| 3 | import os, sys, shutil |
---|
| 4 | |
---|
| 5 | # Assume we are run from the galaxy root directory, add lib to the python path |
---|
| 6 | cwd = os.getcwd() |
---|
| 7 | new_path = [ os.path.join( cwd, "lib" ) ] |
---|
| 8 | new_path.extend( sys.path[1:] ) |
---|
| 9 | sys.path = new_path |
---|
| 10 | |
---|
| 11 | from galaxy import eggs |
---|
| 12 | |
---|
| 13 | eggs.require( "nose" ) |
---|
| 14 | eggs.require( "NoseHTML" ) |
---|
| 15 | eggs.require( "twill==0.9" ) |
---|
| 16 | eggs.require( "Paste" ) |
---|
| 17 | eggs.require( "PasteDeploy" ) |
---|
| 18 | eggs.require( "Cheetah" ) |
---|
| 19 | |
---|
| 20 | # this should not be required, but it is under certain conditions, thanks to this bug: |
---|
| 21 | # http://code.google.com/p/python-nose/issues/detail?id=284 |
---|
| 22 | eggs.require( "pysqlite" ) |
---|
| 23 | |
---|
| 24 | import atexit, logging, os, os.path, sys, tempfile |
---|
| 25 | import twill, unittest, time |
---|
| 26 | import subprocess, sys, threading, random |
---|
| 27 | import httplib, socket |
---|
| 28 | from paste import httpserver |
---|
| 29 | import galaxy.app |
---|
| 30 | from galaxy.app import UniverseApplication |
---|
| 31 | from galaxy.web import buildapp |
---|
| 32 | from galaxy import tools |
---|
| 33 | from galaxy.util import bunch |
---|
| 34 | |
---|
| 35 | log = logging.getLogger( "functional_tests.py" ) |
---|
| 36 | |
---|
| 37 | default_galaxy_test_host = "localhost" |
---|
| 38 | default_galaxy_test_port_min = 8000 |
---|
| 39 | default_galaxy_test_port_max = 9999 |
---|
| 40 | default_galaxy_locales = 'en' |
---|
| 41 | default_galaxy_test_file_dir = "test-data" |
---|
| 42 | |
---|
| 43 | def main(): |
---|
| 44 | |
---|
| 45 | # ---- Configuration ------------------------------------------------------ |
---|
| 46 | |
---|
| 47 | galaxy_test_host = os.environ.get( 'GALAXY_TEST_HOST', default_galaxy_test_host ) |
---|
| 48 | galaxy_test_port = os.environ.get( 'GALAXY_TEST_PORT', None ) |
---|
| 49 | if 'HTTP_ACCEPT_LANGUAGE' not in os.environ: |
---|
| 50 | os.environ['HTTP_ACCEPT_LANGUAGE'] = default_galaxy_locales |
---|
| 51 | galaxy_test_file_dir = os.environ.get( 'GALAXY_TEST_FILE_DIR', default_galaxy_test_file_dir ) |
---|
| 52 | if not os.path.isabs( galaxy_test_file_dir ): |
---|
| 53 | galaxy_test_file_dir = os.path.join( os.getcwd(), galaxy_test_file_dir ) |
---|
| 54 | start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ |
---|
| 55 | tool_path = os.environ.get( 'GALAXY_TEST_TOOL_PATH', 'tools' ) |
---|
| 56 | if start_server: |
---|
| 57 | psu_production = False |
---|
| 58 | galaxy_test_proxy_port = None |
---|
| 59 | if 'GALAXY_TEST_PSU_PRODUCTION' in os.environ: |
---|
| 60 | if not galaxy_test_port: |
---|
| 61 | raise Exception( 'Please set GALAXY_TEST_PORT to the port to which the proxy server will proxy' ) |
---|
| 62 | galaxy_test_proxy_port = os.environ.get( 'GALAXY_TEST_PROXY_PORT', None ) |
---|
| 63 | if not galaxy_test_proxy_port: |
---|
| 64 | raise Exception( 'Please set GALAXY_TEST_PROXY_PORT to the port on which the proxy server is listening' ) |
---|
| 65 | base_file_path = os.environ.get( 'GALAXY_TEST_BASE_FILE_PATH', None ) |
---|
| 66 | if not base_file_path: |
---|
| 67 | raise Exception( 'Please set GALAXY_TEST_BASE_FILE_PATH to the directory which will contain the dataset files directory' ) |
---|
| 68 | base_new_file_path = os.environ.get( 'GALAXY_TEST_BASE_NEW_FILE_PATH', None ) |
---|
| 69 | if not base_new_file_path: |
---|
| 70 | raise Exception( 'Please set GALAXY_TEST_BASE_NEW_FILE_PATH to the directory which will contain the temporary directory' ) |
---|
| 71 | database_connection = os.environ.get( 'GALAXY_TEST_DBURI', None ) |
---|
| 72 | if not database_connection: |
---|
| 73 | raise Exception( 'Please set GALAXY_TEST_DBURI to the URI of the database to be used for tests' ) |
---|
| 74 | nginx_upload_store = os.environ.get( 'GALAXY_TEST_NGINX_UPLOAD_STORE', None ) |
---|
| 75 | if not nginx_upload_store: |
---|
| 76 | raise Exception( 'Please set GALAXY_TEST_NGINX_UPLOAD_STORE to the path where the nginx upload module places uploaded files' ) |
---|
| 77 | default_cluster_job_runner = os.environ.get( 'GALAXY_TEST_DEFAULT_CLUSTER_JOB_RUNNER', 'pbs:///' ) |
---|
| 78 | file_path = tempfile.mkdtemp( dir=base_file_path ) |
---|
| 79 | new_file_path = tempfile.mkdtemp( dir=base_new_file_path ) |
---|
| 80 | cluster_files_directory = os.path.join( new_file_path, 'pbs' ) |
---|
| 81 | job_working_directory = os.path.join( new_file_path, 'job_working_directory' ) |
---|
| 82 | os.mkdir( cluster_files_directory ) |
---|
| 83 | os.mkdir( job_working_directory ) |
---|
| 84 | kwargs = dict( database_engine_option_pool_size = '10', |
---|
| 85 | database_engine_option_max_overflow = '20', |
---|
| 86 | database_engine_option_strategy = 'threadlocal', |
---|
| 87 | nginx_x_accel_redirect_base = '/_x_accel_redirect', |
---|
| 88 | nginx_upload_store = nginx_upload_store, |
---|
| 89 | nginx_upload_path = '/_upload', |
---|
| 90 | cluster_files_directory = cluster_files_directory, |
---|
| 91 | job_working_directory = job_working_directory, |
---|
| 92 | outputs_to_working_directory = 'True', |
---|
| 93 | set_metadata_externally = 'True', |
---|
| 94 | static_enabled = 'False', |
---|
| 95 | debug = 'False', |
---|
| 96 | track_jobs_in_database = 'True', |
---|
| 97 | job_scheduler_policy = 'FIFO', |
---|
| 98 | start_job_runners = 'pbs', |
---|
| 99 | default_cluster_job_runner = default_cluster_job_runner, ) |
---|
| 100 | psu_production = True |
---|
| 101 | else: |
---|
| 102 | if 'GALAXY_TEST_DBPATH' in os.environ: |
---|
| 103 | db_path = os.environ['GALAXY_TEST_DBPATH'] |
---|
| 104 | else: |
---|
| 105 | tempdir = tempfile.mkdtemp() |
---|
| 106 | db_path = os.path.join( tempdir, 'database' ) |
---|
| 107 | file_path = os.path.join( db_path, 'files' ) |
---|
| 108 | new_file_path = os.path.join( db_path, 'tmp' ) |
---|
| 109 | if 'GALAXY_TEST_DBURI' in os.environ: |
---|
| 110 | database_connection = os.environ['GALAXY_TEST_DBURI'] |
---|
| 111 | else: |
---|
| 112 | database_connection = 'sqlite:///' + os.path.join( db_path, 'universe.sqlite' ) |
---|
| 113 | kwargs = {} |
---|
| 114 | for dir in file_path, new_file_path: |
---|
| 115 | try: |
---|
| 116 | os.makedirs( dir ) |
---|
| 117 | except OSError: |
---|
| 118 | pass |
---|
| 119 | |
---|
| 120 | print "Database connection:", database_connection |
---|
| 121 | |
---|
| 122 | # What requires these? |
---|
| 123 | os.environ['GALAXY_TEST_HOST'] = galaxy_test_host |
---|
| 124 | os.environ['GALAXY_TEST_FILE_DIR'] = galaxy_test_file_dir |
---|
| 125 | |
---|
| 126 | # ---- Build Application -------------------------------------------------- |
---|
| 127 | |
---|
| 128 | app = None |
---|
| 129 | |
---|
| 130 | if start_server: |
---|
| 131 | |
---|
| 132 | global_conf = { '__file__' : 'universe_wsgi.ini.sample' } |
---|
| 133 | if psu_production: |
---|
| 134 | global_conf = None |
---|
| 135 | |
---|
| 136 | # Build the Universe Application |
---|
| 137 | app = UniverseApplication( job_queue_workers = 5, |
---|
| 138 | id_secret = 'changethisinproductiontoo', |
---|
| 139 | template_path = "templates", |
---|
| 140 | database_connection = database_connection, |
---|
| 141 | file_path = file_path, |
---|
| 142 | new_file_path = new_file_path, |
---|
| 143 | tool_path = tool_path, |
---|
| 144 | tool_config_file = "tool_conf.xml.sample", |
---|
| 145 | datatype_converters_config_file = "datatype_converters_conf.xml.sample", |
---|
| 146 | tool_parse_help = False, |
---|
| 147 | test_conf = "test.conf", |
---|
| 148 | log_destination = "stdout", |
---|
| 149 | use_heartbeat = False, |
---|
| 150 | allow_user_creation = True, |
---|
| 151 | allow_user_deletion = True, |
---|
| 152 | admin_users = 'test@bx.psu.edu', |
---|
| 153 | library_import_dir = galaxy_test_file_dir, |
---|
| 154 | user_library_import_dir = os.path.join( galaxy_test_file_dir, 'users' ), |
---|
| 155 | global_conf = global_conf, |
---|
| 156 | **kwargs ) |
---|
| 157 | |
---|
| 158 | log.info( "Embedded Universe application started" ); |
---|
| 159 | |
---|
| 160 | # ---- Run webserver ------------------------------------------------------ |
---|
| 161 | |
---|
| 162 | server = None |
---|
| 163 | |
---|
| 164 | if start_server: |
---|
| 165 | |
---|
| 166 | webapp = buildapp.app_factory( dict(), use_translogger = False, static_enabled = False, app=app ) |
---|
| 167 | |
---|
| 168 | if galaxy_test_port is not None: |
---|
| 169 | server = httpserver.serve( webapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False ) |
---|
| 170 | else: |
---|
| 171 | random.seed() |
---|
| 172 | for i in range( 0, 9 ): |
---|
| 173 | try: |
---|
| 174 | galaxy_test_port = str( random.randint( default_galaxy_test_port_min, default_galaxy_test_port_max ) ) |
---|
| 175 | log.debug( "Attempting to serve app on randomly chosen port: %s" % galaxy_test_port ) |
---|
| 176 | server = httpserver.serve( webapp, host=galaxy_test_host, port=galaxy_test_port, start_loop=False ) |
---|
| 177 | break |
---|
| 178 | except socket.error, e: |
---|
| 179 | if e[0] == 98: |
---|
| 180 | continue |
---|
| 181 | raise |
---|
| 182 | else: |
---|
| 183 | 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 ) ) |
---|
| 184 | if galaxy_test_proxy_port: |
---|
| 185 | os.environ['GALAXY_TEST_PORT'] = galaxy_test_proxy_port |
---|
| 186 | else: |
---|
| 187 | os.environ['GALAXY_TEST_PORT'] = galaxy_test_port |
---|
| 188 | |
---|
| 189 | t = threading.Thread( target=server.serve_forever ) |
---|
| 190 | t.start() |
---|
| 191 | |
---|
| 192 | # Test if the server is up |
---|
| 193 | for i in range( 10 ): |
---|
| 194 | conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port ) # directly test the app, not the proxy |
---|
| 195 | conn.request( "GET", "/" ) |
---|
| 196 | if conn.getresponse().status == 200: |
---|
| 197 | break |
---|
| 198 | time.sleep( 0.1 ) |
---|
| 199 | else: |
---|
| 200 | raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" ) |
---|
| 201 | |
---|
| 202 | # Test if the proxy server is up |
---|
| 203 | if psu_production: |
---|
| 204 | conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_proxy_port ) # directly test the app, not the proxy |
---|
| 205 | conn.request( "GET", "/" ) |
---|
| 206 | if not conn.getresponse().status == 200: |
---|
| 207 | raise Exception( "Test HTTP proxy server did not return '200 OK'" ) |
---|
| 208 | |
---|
| 209 | log.info( "Embedded web server started" ) |
---|
| 210 | |
---|
| 211 | |
---|
| 212 | # ---- Load toolbox for generated tests ----------------------------------- |
---|
| 213 | |
---|
| 214 | # We don't add the tests to the path until everything is up and running |
---|
| 215 | new_path = [ os.path.join( cwd, "test" ) ] |
---|
| 216 | new_path.extend( sys.path[1:] ) |
---|
| 217 | sys.path = new_path |
---|
| 218 | |
---|
| 219 | import functional.test_toolbox |
---|
| 220 | |
---|
| 221 | if app: |
---|
| 222 | # TODO: provisions for loading toolbox from file when using external server |
---|
| 223 | functional.test_toolbox.toolbox = app.toolbox |
---|
| 224 | functional.test_toolbox.build_tests() |
---|
| 225 | else: |
---|
| 226 | # FIXME: This doesn't work at all now that toolbox requires an 'app' instance |
---|
| 227 | # (to get at datatypes, might just pass a datatype registry directly) |
---|
| 228 | my_app = bunch.Bunch( datatypes_registry = galaxy.datatypes.registry.Registry() ) |
---|
| 229 | test_toolbox.toolbox = tools.ToolBox( 'tool_conf.xml.test', 'tools', my_app ) |
---|
| 230 | |
---|
| 231 | # ---- Find tests --------------------------------------------------------- |
---|
| 232 | |
---|
| 233 | if galaxy_test_proxy_port: |
---|
| 234 | log.info( "Functional tests will be run against %s:%s" % ( galaxy_test_host, galaxy_test_proxy_port ) ) |
---|
| 235 | else: |
---|
| 236 | log.info( "Functional tests will be run against %s:%s" % ( galaxy_test_host, galaxy_test_port ) ) |
---|
| 237 | |
---|
| 238 | success = False |
---|
| 239 | |
---|
| 240 | try: |
---|
| 241 | |
---|
| 242 | import nose.core |
---|
| 243 | import nose.config |
---|
| 244 | import nose.loader |
---|
| 245 | import nose.plugins.manager |
---|
| 246 | |
---|
| 247 | test_config = nose.config.Config( env = os.environ, plugins=nose.plugins.manager.DefaultPluginManager() ) |
---|
| 248 | test_config.configure( sys.argv ) |
---|
| 249 | |
---|
| 250 | loader = nose.loader.TestLoader( config = test_config ) |
---|
| 251 | |
---|
| 252 | plug_loader = test_config.plugins.prepareTestLoader( loader ) |
---|
| 253 | if plug_loader is not None: |
---|
| 254 | loader = plug_loader |
---|
| 255 | |
---|
| 256 | tests = loader.loadTestsFromNames( test_config.testNames ) |
---|
| 257 | |
---|
| 258 | test_runner = nose.core.TextTestRunner( |
---|
| 259 | stream = test_config.stream, |
---|
| 260 | verbosity = test_config.verbosity, |
---|
| 261 | config = test_config) |
---|
| 262 | |
---|
| 263 | plug_runner = test_config.plugins.prepareTestRunner( test_runner ) |
---|
| 264 | if plug_runner is not None: |
---|
| 265 | test_runner = plug_runner |
---|
| 266 | |
---|
| 267 | result = test_runner.run( tests ) |
---|
| 268 | |
---|
| 269 | success = result.wasSuccessful() |
---|
| 270 | |
---|
| 271 | except: |
---|
| 272 | log.exception( "Failure running tests" ) |
---|
| 273 | |
---|
| 274 | log.info( "Shutting down" ) |
---|
| 275 | |
---|
| 276 | # ---- Teardown ----------------------------------------------------------- |
---|
| 277 | |
---|
| 278 | if server: |
---|
| 279 | log.info( "Shutting down embedded web server" ) |
---|
| 280 | server.server_close() |
---|
| 281 | server = None |
---|
| 282 | log.info( "Embedded web server stopped" ) |
---|
| 283 | if app: |
---|
| 284 | log.info( "Shutting down app" ) |
---|
| 285 | app.shutdown() |
---|
| 286 | app = None |
---|
| 287 | log.info( "Embedded Universe application stopped" ) |
---|
| 288 | try: |
---|
| 289 | if os.path.exists( tempdir ) and 'GALAXY_TEST_NO_CLEANUP' not in os.environ: |
---|
| 290 | log.info( "Cleaning up temporary files in %s" % tempdir ) |
---|
| 291 | shutil.rmtree( tempdir ) |
---|
| 292 | except: |
---|
| 293 | pass |
---|
| 294 | if psu_production and 'GALAXY_TEST_NO_CLEANUP' not in os.environ: |
---|
| 295 | for dir in ( file_path, new_file_path ): |
---|
| 296 | try: |
---|
| 297 | if os.path.exists( dir ): |
---|
| 298 | log.info( 'Cleaning up temporary files in %s' % dir ) |
---|
| 299 | shutil.rmtree( dir ) |
---|
| 300 | except: |
---|
| 301 | pass |
---|
| 302 | |
---|
| 303 | if success: |
---|
| 304 | return 0 |
---|
| 305 | else: |
---|
| 306 | return 1 |
---|
| 307 | |
---|
| 308 | if __name__ == "__main__": |
---|
| 309 | sys.exit( main() ) |
---|