| 
99
 | 
     1 #!/usr/bin/env python
 | 
| 
 | 
     2 
 | 
| 
 | 
     3 import argparse
 | 
| 
 | 
     4 import datetime as dt
 | 
| 
 | 
     5 import json
 | 
| 
 | 
     6 import logging
 | 
| 
 | 
     7 import os
 | 
| 
 | 
     8 import sys
 | 
| 
 | 
     9 import tempfile
 | 
| 
 | 
    10 from collections import namedtuple
 | 
| 
 | 
    11 from concurrent.futures import thread, ThreadPoolExecutor
 | 
| 
 | 
    12 
 | 
| 
 | 
    13 import yaml
 | 
| 
 | 
    14 
 | 
| 
 | 
    15 from galaxy.tool_util.verify.interactor import (
 | 
| 
 | 
    16     DictClientTestConfig,
 | 
| 
 | 
    17     GalaxyInteractorApi,
 | 
| 
 | 
    18     verify_tool,
 | 
| 
 | 
    19 )
 | 
| 
 | 
    20 
 | 
| 
 | 
    21 DESCRIPTION = """Script to quickly run a tool test against a running Galaxy instance."""
 | 
| 
 | 
    22 DEFAULT_SUITE_NAME = "Galaxy Tool Tests"
 | 
| 
 | 
    23 ALL_TESTS = -1
 | 
| 
 | 
    24 ALL_TOOLS = "*"
 | 
| 
 | 
    25 ALL_VERSION = "*"
 | 
| 
 | 
    26 LATEST_VERSION = None
 | 
| 
 | 
    27 
 | 
| 
 | 
    28 
 | 
| 
 | 
    29 TestReference = namedtuple("TestReference", ["tool_id", "tool_version", "test_index"])
 | 
| 
 | 
    30 TestException = namedtuple("TestException", ["tool_id", "exception", "was_recorded"])
 | 
| 
 | 
    31 
 | 
| 
 | 
    32 
 | 
| 
 | 
    33 class Results:
 | 
| 
 | 
    34 
 | 
| 
 | 
    35     def __init__(self, default_suitename, test_json, append=False):
 | 
| 
 | 
    36         self.test_json = test_json or "-"
 | 
| 
 | 
    37         test_results = []
 | 
| 
 | 
    38         test_exceptions = []
 | 
| 
 | 
    39         suitename = default_suitename
 | 
| 
 | 
    40         if append:
 | 
| 
 | 
    41             assert test_json != "-"
 | 
| 
 | 
    42             with open(test_json) as f:
 | 
| 
 | 
    43                 previous_results = json.load(f)
 | 
| 
 | 
    44                 test_results = previous_results["tests"]
 | 
| 
 | 
    45                 if "suitename" in previous_results:
 | 
| 
 | 
    46                     suitename = previous_results["suitename"]
 | 
| 
 | 
    47         self.test_results = test_results
 | 
| 
 | 
    48         self.test_exceptions = test_exceptions
 | 
| 
 | 
    49         self.suitename = suitename
 | 
| 
 | 
    50 
 | 
| 
 | 
    51     def register_result(self, result):
 | 
| 
 | 
    52         self.test_results.append(result)
 | 
| 
 | 
    53 
 | 
| 
 | 
    54     def register_exception(self, test_exception):
 | 
| 
 | 
    55         self.test_exceptions.append(test_exception)
 | 
| 
 | 
    56 
 | 
| 
 | 
    57     def already_successful(self, test_reference):
 | 
| 
 | 
    58         test_id = _test_id_for_reference(test_reference)
 | 
| 
 | 
    59         for test_result in self.test_results:
 | 
| 
 | 
    60             if test_result.get('id') != test_id:
 | 
| 
 | 
    61                 continue
 | 
| 
 | 
    62 
 | 
| 
 | 
    63             has_data = test_result.get('has_data', False)
 | 
| 
 | 
    64             if has_data:
 | 
| 
 | 
    65                 test_data = test_result.get("data", {})
 | 
| 
 | 
    66                 if 'status' in test_data and test_data['status'] == 'success':
 | 
| 
 | 
    67                     return True
 | 
| 
 | 
    68 
 | 
| 
 | 
    69         return False
 | 
| 
 | 
    70 
 | 
| 
 | 
    71     def write(self):
 | 
| 
 | 
    72         tests = sorted(self.test_results, key=lambda el: el['id'])
 | 
| 
 | 
    73         n_passed, n_failures, n_skips = 0, 0, 0
 | 
| 
 | 
    74         n_errors = len([e for e in self.test_exceptions if not e.was_recorded])
 | 
| 
 | 
    75         for test in tests:
 | 
| 
 | 
    76             has_data = test.get('has_data', False)
 | 
| 
 | 
    77             if has_data:
 | 
| 
 | 
    78                 test_data = test.get("data", {})
 | 
| 
 | 
    79                 if 'status' not in test_data:
 | 
| 
 | 
    80                     raise Exception(f"Test result data {test_data} doesn't contain a status key.")
 | 
| 
 | 
    81                 status = test_data['status']
 | 
| 
 | 
    82                 if status == "success":
 | 
| 
 | 
    83                     n_passed += 1
 | 
| 
 | 
    84                 elif status == "error":
 | 
| 
 | 
    85                     n_errors += 1
 | 
| 
 | 
    86                 elif status == "skip":
 | 
| 
 | 
    87                     n_skips += 1
 | 
| 
 | 
    88                 elif status == "failure":
 | 
| 
 | 
    89                     n_failures += 1
 | 
| 
 | 
    90         report_obj = {
 | 
| 
 | 
    91             'version': '0.1',
 | 
| 
 | 
    92             'suitename': self.suitename,
 | 
| 
 | 
    93             'results': {
 | 
| 
 | 
    94                 'total': n_passed + n_failures + n_skips + n_errors,
 | 
| 
 | 
    95                 'errors': n_errors,
 | 
| 
 | 
    96                 'failures': n_failures,
 | 
| 
 | 
    97                 'skips': n_skips,
 | 
| 
 | 
    98             },
 | 
| 
 | 
    99             'tests': tests,
 | 
| 
 | 
   100         }
 | 
| 
 | 
   101         if self.test_json == "-":
 | 
| 
 | 
   102             print(json.dumps(report_obj))
 | 
| 
 | 
   103         else:
 | 
| 
 | 
   104             with open(self.test_json, "w") as f:
 | 
| 
 | 
   105                 json.dump(report_obj, f)
 | 
| 
 | 
   106 
 | 
| 
 | 
   107     def info_message(self):
 | 
| 
 | 
   108         messages = []
 | 
| 
 | 
   109         passed_tests = self._tests_with_status('success')
 | 
| 
 | 
   110         messages.append("Passed tool tests ({}): {}".format(
 | 
| 
 | 
   111             len(passed_tests),
 | 
| 
 | 
   112             [t["id"] for t in passed_tests]
 | 
| 
 | 
   113         ))
 | 
| 
 | 
   114         failed_tests = self._tests_with_status('failure')
 | 
| 
 | 
   115         messages.append("Failed tool tests ({}): {}".format(
 | 
| 
 | 
   116             len(failed_tests),
 | 
| 
 | 
   117             [t["id"] for t in failed_tests]
 | 
| 
 | 
   118         ))
 | 
| 
 | 
   119         skiped_tests = self._tests_with_status('skip')
 | 
| 
 | 
   120         messages.append("Skipped tool tests ({}): {}".format(
 | 
| 
 | 
   121             len(skiped_tests),
 | 
| 
 | 
   122             [t["id"] for t in skiped_tests]
 | 
| 
 | 
   123         ))
 | 
| 
 | 
   124         errored_tests = self._tests_with_status('error')
 | 
| 
 | 
   125         messages.append("Errored tool tests ({}): {}".format(
 | 
| 
 | 
   126             len(errored_tests),
 | 
| 
 | 
   127             [t["id"] for t in errored_tests]
 | 
| 
 | 
   128         ))
 | 
| 
 | 
   129         return "\n".join(messages)
 | 
| 
 | 
   130 
 | 
| 
 | 
   131     @property
 | 
| 
 | 
   132     def success_count(self):
 | 
| 
 | 
   133         self._tests_with_status('success')
 | 
| 
 | 
   134 
 | 
| 
 | 
   135     @property
 | 
| 
 | 
   136     def skip_count(self):
 | 
| 
 | 
   137         self._tests_with_status('skip')
 | 
| 
 | 
   138 
 | 
| 
 | 
   139     @property
 | 
| 
 | 
   140     def error_count(self):
 | 
| 
 | 
   141         return self._tests_with_status('error') + len(self.test_exceptions)
 | 
| 
 | 
   142 
 | 
| 
 | 
   143     @property
 | 
| 
 | 
   144     def failure_count(self):
 | 
| 
 | 
   145         return self._tests_with_status('failure')
 | 
| 
 | 
   146 
 | 
| 
 | 
   147     def _tests_with_status(self, status):
 | 
| 
 | 
   148         return [t for t in self.test_results if t.get("data", {}).get("status") == status]
 | 
| 
 | 
   149 
 | 
| 
 | 
   150 
 | 
| 
 | 
   151 def test_tools(
 | 
| 
 | 
   152     galaxy_interactor,
 | 
| 
 | 
   153     test_references,
 | 
| 
 | 
   154     results,
 | 
| 
 | 
   155     log=None,
 | 
| 
 | 
   156     parallel_tests=1,
 | 
| 
 | 
   157     history_per_test_case=False,
 | 
| 
 | 
   158     no_history_cleanup=False,
 | 
| 
 | 
   159     retries=0,
 | 
| 
 | 
   160     verify_kwds=None,
 | 
| 
 | 
   161 ):
 | 
| 
 | 
   162     """Run through tool tests and write report.
 | 
| 
 | 
   163 
 | 
| 
 | 
   164     Refactor this into Galaxy in 21.01.
 | 
| 
 | 
   165     """
 | 
| 
 | 
   166     verify_kwds = (verify_kwds or {}).copy()
 | 
| 
 | 
   167     tool_test_start = dt.datetime.now()
 | 
| 
 | 
   168     history_created = False
 | 
| 
 | 
   169     if history_per_test_case:
 | 
| 
 | 
   170         test_history = None
 | 
| 
 | 
   171     else:
 | 
| 
 | 
   172         history_created = True
 | 
| 
 | 
   173         test_history = galaxy_interactor.new_history(history_name=f"History for {results.suitename}")
 | 
| 
 | 
   174     verify_kwds.update({
 | 
| 
 | 
   175         "no_history_cleanup": no_history_cleanup,
 | 
| 
 | 
   176         "test_history": test_history,
 | 
| 
 | 
   177     })
 | 
| 
 | 
   178     with ThreadPoolExecutor(max_workers=parallel_tests) as executor:
 | 
| 
 | 
   179         try:
 | 
| 
 | 
   180             for test_reference in test_references:
 | 
| 
 | 
   181                 _test_tool(
 | 
| 
 | 
   182                     executor=executor,
 | 
| 
 | 
   183                     test_reference=test_reference,
 | 
| 
 | 
   184                     results=results,
 | 
| 
 | 
   185                     galaxy_interactor=galaxy_interactor,
 | 
| 
 | 
   186                     log=log,
 | 
| 
 | 
   187                     retries=retries,
 | 
| 
 | 
   188                     verify_kwds=verify_kwds,
 | 
| 
 | 
   189                 )
 | 
| 
 | 
   190         finally:
 | 
| 
 | 
   191             # Always write report, even if test was cancelled.
 | 
| 
 | 
   192             try:
 | 
| 
 | 
   193                 executor.shutdown(wait=True)
 | 
| 
 | 
   194             except KeyboardInterrupt:
 | 
| 
 | 
   195                 executor._threads.clear()
 | 
| 
 | 
   196                 thread._threads_queues.clear()
 | 
| 
 | 
   197             results.write()
 | 
| 
 | 
   198             if log:
 | 
| 
 | 
   199                 log.info("Report written to '%s'", os.path.abspath(results.test_json))
 | 
| 
 | 
   200                 log.info(results.info_message())
 | 
| 
 | 
   201                 log.info("Total tool test time: {}".format(dt.datetime.now() - tool_test_start))
 | 
| 
 | 
   202             if history_created and not no_history_cleanup:
 | 
| 
 | 
   203                 galaxy_interactor.delete_history(test_history)
 | 
| 
 | 
   204 
 | 
| 
 | 
   205 
 | 
| 
 | 
   206 def _test_id_for_reference(test_reference):
 | 
| 
 | 
   207     tool_id = test_reference.tool_id
 | 
| 
 | 
   208     tool_version = test_reference.tool_version
 | 
| 
 | 
   209     test_index = test_reference.test_index
 | 
| 
 | 
   210 
 | 
| 
 | 
   211     if tool_version and tool_id.endswith("/" + tool_version):
 | 
| 
 | 
   212         tool_id = tool_id[:-len("/" + tool_version)]
 | 
| 
 | 
   213 
 | 
| 
 | 
   214     label_base = tool_id
 | 
| 
 | 
   215     if tool_version:
 | 
| 
 | 
   216         label_base += "/" + str(tool_version)
 | 
| 
 | 
   217 
 | 
| 
 | 
   218     test_id = label_base + "-" + str(test_index)
 | 
| 
 | 
   219     return test_id
 | 
| 
 | 
   220 
 | 
| 
 | 
   221 
 | 
| 
 | 
   222 def _test_tool(
 | 
| 
 | 
   223     executor,
 | 
| 
 | 
   224     test_reference,
 | 
| 
 | 
   225     results,
 | 
| 
 | 
   226     galaxy_interactor,
 | 
| 
 | 
   227     log,
 | 
| 
 | 
   228     retries,
 | 
| 
 | 
   229     verify_kwds,
 | 
| 
 | 
   230 ):
 | 
| 
 | 
   231     tool_id = test_reference.tool_id
 | 
| 
 | 
   232     tool_version = test_reference.tool_version
 | 
| 
 | 
   233     test_index = test_reference.test_index
 | 
| 
 | 
   234     # If given a tool_id with a version suffix, strip it off so we can treat tool_version
 | 
| 
 | 
   235     # correctly at least in client_test_config.
 | 
| 
 | 
   236     if tool_version and tool_id.endswith("/" + tool_version):
 | 
| 
 | 
   237         tool_id = tool_id[:-len("/" + tool_version)]
 | 
| 
 | 
   238 
 | 
| 
 | 
   239     test_id = _test_id_for_reference(test_reference)
 | 
| 
 | 
   240 
 | 
| 
 | 
   241     def run_test():
 | 
| 
 | 
   242         run_retries = retries
 | 
| 
 | 
   243         job_data = None
 | 
| 
 | 
   244         job_exception = None
 | 
| 
 | 
   245 
 | 
| 
 | 
   246         def register(job_data_):
 | 
| 
 | 
   247             nonlocal job_data
 | 
| 
 | 
   248             job_data = job_data_
 | 
| 
 | 
   249 
 | 
| 
 | 
   250         try:
 | 
| 
 | 
   251             while run_retries >= 0:
 | 
| 
 | 
   252                 job_exception = None
 | 
| 
 | 
   253                 try:
 | 
| 
 | 
   254                     if log:
 | 
| 
 | 
   255                         log.info("Executing test '%s'", test_id)
 | 
| 
 | 
   256                     verify_tool(
 | 
| 
 | 
   257                         tool_id, galaxy_interactor, test_index=test_index, tool_version=tool_version,
 | 
| 
 | 
   258                         register_job_data=register, **verify_kwds
 | 
| 
 | 
   259                     )
 | 
| 
 | 
   260                     if log:
 | 
| 
 | 
   261                         log.info("Test '%s' passed", test_id)
 | 
| 
 | 
   262                     break
 | 
| 
 | 
   263                 except Exception as e:
 | 
| 
 | 
   264                     if log:
 | 
| 
 | 
   265                         log.warning("Test '%s' failed", test_id, exc_info=True)
 | 
| 
 | 
   266 
 | 
| 
 | 
   267                     job_exception = e
 | 
| 
 | 
   268                     run_retries -= 1
 | 
| 
 | 
   269         finally:
 | 
| 
 | 
   270             if job_data is not None:
 | 
| 
 | 
   271                 results.register_result({
 | 
| 
 | 
   272                     "id": test_id,
 | 
| 
 | 
   273                     "has_data": True,
 | 
| 
 | 
   274                     "data": job_data,
 | 
| 
 | 
   275                 })
 | 
| 
 | 
   276             if job_exception is not None:
 | 
| 
 | 
   277                 was_recorded = job_data is not None
 | 
| 
 | 
   278                 test_exception = TestException(tool_id, job_exception, was_recorded)
 | 
| 
 | 
   279                 results.register_exception(test_exception)
 | 
| 
 | 
   280 
 | 
| 
 | 
   281     executor.submit(run_test)
 | 
| 
 | 
   282 
 | 
| 
 | 
   283 
 | 
| 
 | 
   284 def build_case_references(
 | 
| 
 | 
   285     galaxy_interactor,
 | 
| 
 | 
   286     tool_id=ALL_TOOLS,
 | 
| 
 | 
   287     tool_version=LATEST_VERSION,
 | 
| 
 | 
   288     test_index=ALL_TESTS,
 | 
| 
 | 
   289     page_size=0,
 | 
| 
 | 
   290     page_number=0,
 | 
| 
 | 
   291     check_against=None,
 | 
| 
 | 
   292     log=None,
 | 
| 
 | 
   293 ):
 | 
| 
 | 
   294     test_references = []
 | 
| 
 | 
   295     if tool_id == ALL_TOOLS:
 | 
| 
 | 
   296         tests_summary = galaxy_interactor.get_tests_summary()
 | 
| 
 | 
   297         for tool_id, tool_versions_dict in tests_summary.items():
 | 
| 
 | 
   298             for tool_version, summary in tool_versions_dict.items():
 | 
| 
 | 
   299                 for test_index in range(summary["count"]):
 | 
| 
 | 
   300                     test_reference = TestReference(tool_id, tool_version, test_index)
 | 
| 
 | 
   301                     test_references.append(test_reference)
 | 
| 
 | 
   302     else:
 | 
| 
 | 
   303         assert tool_id
 | 
| 
 | 
   304         tool_test_dicts = galaxy_interactor.get_tool_tests(tool_id, tool_version=tool_version) or {}
 | 
| 
 | 
   305         for i, tool_test_dict in enumerate(tool_test_dicts):
 | 
| 
 | 
   306             this_tool_version = tool_test_dict.get("tool_version", tool_version)
 | 
| 
 | 
   307             this_test_index = i
 | 
| 
 | 
   308             if test_index == ALL_TESTS or i == test_index:
 | 
| 
 | 
   309                 test_reference = TestReference(tool_id, this_tool_version, this_test_index)
 | 
| 
 | 
   310                 test_references.append(test_reference)
 | 
| 
 | 
   311 
 | 
| 
 | 
   312     if check_against:
 | 
| 
 | 
   313         filtered_test_references = []
 | 
| 
 | 
   314         for test_reference in test_references:
 | 
| 
 | 
   315             if check_against.already_successful(test_reference):
 | 
| 
 | 
   316                 if log is not None:
 | 
| 
 | 
   317                     log.debug(f"Found successful test for {test_reference}, skipping")
 | 
| 
 | 
   318                 continue
 | 
| 
 | 
   319             filtered_test_references.append(test_reference)
 | 
| 
 | 
   320         log.info(f"Skipping {len(test_references)-len(filtered_test_references)} out of {len(test_references)} tests.")
 | 
| 
 | 
   321         test_references = filtered_test_references
 | 
| 
 | 
   322 
 | 
| 
 | 
   323     if page_size > 0:
 | 
| 
 | 
   324         slice_start = page_size * page_number
 | 
| 
 | 
   325         slice_end = page_size * (page_number + 1)
 | 
| 
 | 
   326         test_references = test_references[slice_start:slice_end]
 | 
| 
 | 
   327 
 | 
| 
 | 
   328     return test_references
 | 
| 
 | 
   329 
 | 
| 
 | 
   330 
 | 
| 
 | 
   331 def main(argv=None):
 | 
| 
 | 
   332     if argv is None:
 | 
| 
 | 
   333         argv = sys.argv[1:]
 | 
| 
 | 
   334 
 | 
| 
 | 
   335     args = _arg_parser().parse_args(argv)
 | 
| 
 | 
   336     log = setup_global_logger(__name__, verbose=args.verbose)
 | 
| 
 | 
   337     client_test_config_path = args.client_test_config
 | 
| 
 | 
   338     if client_test_config_path is not None:
 | 
| 
 | 
   339         log.debug(f"Reading client config path {client_test_config_path}")
 | 
| 
 | 
   340         with open(client_test_config_path) as f:
 | 
| 
 | 
   341             client_test_config = yaml.full_load(f)
 | 
| 
 | 
   342     else:
 | 
| 
 | 
   343         client_test_config = {}
 | 
| 
 | 
   344 
 | 
| 
 | 
   345     def get_option(key):
 | 
| 
 | 
   346         arg_val = getattr(args, key, None)
 | 
| 
 | 
   347         if arg_val is None and key in client_test_config:
 | 
| 
 | 
   348             val = client_test_config.get(key)
 | 
| 
 | 
   349         else:
 | 
| 
 | 
   350             val = arg_val
 | 
| 
 | 
   351         return val
 | 
| 
 | 
   352 
 | 
| 
 | 
   353     output_json_path = get_option("output_json")
 | 
| 
 | 
   354     galaxy_interactor_kwds = {
 | 
| 
 | 
   355         "galaxy_url": get_option("galaxy_url"),
 | 
| 
 | 
   356         "master_api_key": get_option("admin_key"),
 | 
| 
 | 
   357         "api_key": get_option("key"),
 | 
| 
 | 
   358         "keep_outputs_dir": args.output,
 | 
| 
 | 
   359         "download_attempts": get_option("download_attempts"),
 | 
| 
 | 
   360         "download_sleep": get_option("download_sleep"),
 | 
| 
 | 
   361     }
 | 
| 
 | 
   362     tool_id = args.tool_id
 | 
| 
 | 
   363     tool_version = args.tool_version
 | 
| 
 | 
   364     tools_client_test_config = DictClientTestConfig(client_test_config.get("tools"))
 | 
| 
 | 
   365     verbose = args.verbose
 | 
| 
 | 
   366 
 | 
| 
 | 
   367     galaxy_interactor = GalaxyInteractorApi(**galaxy_interactor_kwds)
 | 
| 
 | 
   368     results = Results(args.suite_name, output_json_path, append=args.append)
 | 
| 
 | 
   369     check_against = None if not args.skip_successful else results
 | 
| 
 | 
   370     test_references = build_case_references(
 | 
| 
 | 
   371         galaxy_interactor,
 | 
| 
 | 
   372         tool_id=tool_id,
 | 
| 
 | 
   373         tool_version=tool_version,
 | 
| 
 | 
   374         test_index=args.test_index,
 | 
| 
 | 
   375         page_size=args.page_size,
 | 
| 
 | 
   376         page_number=args.page_number,
 | 
| 
 | 
   377         check_against=check_against,
 | 
| 
 | 
   378         log=log,
 | 
| 
 | 
   379     )
 | 
| 
 | 
   380     log.debug(f"Built {len(test_references)} test references to executed.")
 | 
| 
 | 
   381     verify_kwds = dict(
 | 
| 
 | 
   382         client_test_config=tools_client_test_config,
 | 
| 
 | 
   383         force_path_paste=args.force_path_paste,
 | 
| 
 | 
   384         skip_with_reference_data=not args.with_reference_data,
 | 
| 
 | 
   385         quiet=not verbose,
 | 
| 
 | 
   386     )
 | 
| 
 | 
   387     test_tools(
 | 
| 
 | 
   388         galaxy_interactor,
 | 
| 
 | 
   389         test_references,
 | 
| 
 | 
   390         results,
 | 
| 
 | 
   391         log=log,
 | 
| 
 | 
   392         parallel_tests=args.parallel_tests,
 | 
| 
 | 
   393         history_per_test_case=args.history_per_test_case,
 | 
| 
 | 
   394         no_history_cleanup=args.no_history_cleanup,
 | 
| 
 | 
   395         verify_kwds=verify_kwds,
 | 
| 
 | 
   396     )
 | 
| 
 | 
   397     exceptions = results.test_exceptions
 | 
| 
 | 
   398     if exceptions:
 | 
| 
 | 
   399         exception = exceptions[0]
 | 
| 
 | 
   400         if hasattr(exception, "exception"):
 | 
| 
 | 
   401             exception = exception.exception
 | 
| 
 | 
   402         raise exception
 | 
| 
 | 
   403 
 | 
| 
 | 
   404 
 | 
| 
 | 
   405 def setup_global_logger(name, log_file=None, verbose=False):
 | 
| 
 | 
   406     formatter = logging.Formatter('%(asctime)s %(levelname)-5s - %(message)s')
 | 
| 
 | 
   407     console = logging.StreamHandler()
 | 
| 
 | 
   408     console.setFormatter(formatter)
 | 
| 
 | 
   409 
 | 
| 
 | 
   410     logger = logging.getLogger(name)
 | 
| 
 | 
   411     logger.setLevel(logging.DEBUG if verbose else logging.INFO)
 | 
| 
 | 
   412     logger.addHandler(console)
 | 
| 
 | 
   413 
 | 
| 
 | 
   414     if not log_file:
 | 
| 
 | 
   415         # delete = false is chosen here because it is always nice to have a log file
 | 
| 
 | 
   416         # ready if you need to debug. Not having the "if only I had set a log file"
 | 
| 
 | 
   417         # moment after the fact.
 | 
| 
 | 
   418         temp = tempfile.NamedTemporaryFile(prefix="ephemeris_", delete=False)
 | 
| 
 | 
   419         log_file = temp.name
 | 
| 
 | 
   420     file_handler = logging.FileHandler(log_file)
 | 
| 
 | 
   421     logger.addHandler(file_handler)
 | 
| 
 | 
   422     logger.info(f"Storing log file in: {log_file}")
 | 
| 
 | 
   423     return logger
 | 
| 
 | 
   424 
 | 
| 
 | 
   425 
 | 
| 
 | 
   426 def _arg_parser():
 | 
| 
 | 
   427     parser = argparse.ArgumentParser(description=DESCRIPTION)
 | 
| 
 | 
   428     parser.add_argument('-u', '--galaxy-url', default="http://localhost:8080", help='Galaxy URL')
 | 
| 
 | 
   429     parser.add_argument('-k', '--key', default=None, help='Galaxy User API Key')
 | 
| 
 | 
   430     parser.add_argument('-a', '--admin-key', default=None, help='Galaxy Admin API Key')
 | 
| 
 | 
   431     parser.add_argument('--force_path_paste', default=False, action="store_true", help='This requires Galaxy-side config option "allow_path_paste" enabled. Allows for fetching test data locally. Only for admins.')
 | 
| 
 | 
   432     parser.add_argument('-t', '--tool-id', default=ALL_TOOLS, help='Tool ID')
 | 
| 
 | 
   433     parser.add_argument('--tool-version', default=None, help='Tool Version (if tool id supplied). Defaults to just latest version, use * to test all versions')
 | 
| 
 | 
   434     parser.add_argument('-i', '--test-index', default=ALL_TESTS, type=int, help='Tool Test Index (starting at 0) - by default all tests will run.')
 | 
| 
 | 
   435     parser.add_argument('-o', '--output', default=None, help='directory to dump outputs to')
 | 
| 
 | 
   436     parser.add_argument('--append', default=False, action="store_true", help="Extend a test record json (created with --output-json) with additional tests.")
 | 
| 
 | 
   437     parser.add_argument('--skip-successful', default=False, action="store_true", help="When used with --append, skip previously run successful tests.")
 | 
| 
 | 
   438     parser.add_argument('-j', '--output-json', default=None, help='output metadata json')
 | 
| 
 | 
   439     parser.add_argument('--verbose', default=False, action="store_true", help="Verbose logging.")
 | 
| 
 | 
   440     parser.add_argument('-c', '--client-test-config', default=None, help="Test config YAML to help with client testing")
 | 
| 
 | 
   441     parser.add_argument('--suite-name', default=DEFAULT_SUITE_NAME, help="Suite name for tool test output")
 | 
| 
 | 
   442     parser.add_argument('--with-reference-data', dest="with_reference_data", default=False, action="store_true")
 | 
| 
 | 
   443     parser.add_argument('--skip-with-reference-data', dest="with_reference_data", action="store_false", help="Skip tests the Galaxy server believes use data tables or loc files.")
 | 
| 
 | 
   444     parser.add_argument('--history-per-suite', dest="history_per_test_case", default=False, action="store_false", help="Create new history per test suite (all tests in same history).")
 | 
| 
 | 
   445     parser.add_argument('--history-per-test-case', dest="history_per_test_case", action="store_true", help="Create new history per test case.")
 | 
| 
 | 
   446     parser.add_argument('--no-history-cleanup', default=False, action="store_true", help="Perserve histories created for testing.")
 | 
| 
 | 
   447     parser.add_argument('--parallel-tests', default=1, type=int, help="Parallel tests.")
 | 
| 
 | 
   448     parser.add_argument('--retries', default=0, type=int, help="Retry failed tests.")
 | 
| 
 | 
   449     parser.add_argument('--page-size', default=0, type=int, help="If positive, use pagination and just run one 'page' to tool tests.")
 | 
| 
 | 
   450     parser.add_argument('--page-number', default=0, type=int, help="If page size is used, run this 'page' of tests - starts with 0.")
 | 
| 
 | 
   451     parser.add_argument('--download-attempts', default=1, type=int, help="Galaxy may return a transient 500 status code for download if test results are written but not yet accessible.")
 | 
| 
 | 
   452     parser.add_argument('--download-sleep', default=1, type=int, help="If download attempts is greater than 1, the amount to sleep between download attempts.")
 | 
| 
 | 
   453     return parser
 | 
| 
 | 
   454 
 | 
| 
 | 
   455 
 | 
| 
 | 
   456 if __name__ == "__main__":
 | 
| 
 | 
   457     main()
 |