def test_which(): r"""Test location of executable.""" assert (tools.which(sys.executable) is not None) if platform._is_win: # pragma: windows assert (tools.which('python.exe') is not None) else: assert (tools.which('python') is not None) assert (tools.which('invalid') is None)
def is_interpreter(cls, cmd): r"""Determine if a command line argument is an interpreter. Args: cmd (str): Command that should be checked. Returns: bool: True if the command is an interpreter, False otherwise. """ # (cls.language not in cmd) out = ((tools.which(cmd) is not None) and (not any([cmd.endswith(e) for e in cls.language_ext]))) return out
class TestModelDriverNoInit(TestModelParam, parent.TestDriverNoInit): r"""Test runner for ModelDriver class without creating an instance.""" def tests_on_not_installed(self): r"""Tests for when the driver is not installed.""" super(TestModelDriverNoInit, self).tests_on_not_installed() self.test_comm_installed() self.test_write_if_block() self.test_write_for_loop() self.test_write_while_loop() self.test_write_try_except() def test_is_installed(self): r"""Assert that the tested model driver is installed.""" assert(self.import_cls.is_installed()) def test_comm_installed(self): r"""Tests for getting installed comm while skipping config.""" self.assert_equal(self.import_cls.is_comm_installed(), self.import_cls.is_comm_installed(skip_config=True)) self.assert_equal(self.import_cls.is_comm_installed(commtype='invalid', skip_config=True), False) def test_language_version(self): r"""Test language version.""" assert(self.import_cls.language_version()) def run_model_instance(self, **kwargs): r"""Create a driver for a model and run it.""" inst_kwargs = copy.deepcopy(self.inst_kwargs) inst_kwargs.update(kwargs) drv = self.create_instance(kwargs=inst_kwargs) drv.start() drv.wait(False) assert(not drv.errors) def test_run_model(self): r"""Test running script used without debug.""" self.run_model_instance() @unittest.skipIf(platform._is_win, "No valgrind on windows") @unittest.skipIf(tools.which('valgrind') is None, "Valgrind not installed.") def test_valgrind(self): r"""Test running with valgrind.""" valgrind_log = os.path.join( self.working_dir, 'valgrind_log_%s.log' % self.uuid.replace('-', '_')) try: self.run_model_instance(with_valgrind=True, with_strace=False, valgrind_flags=['--leak-check=full', '--log-file=%s' % valgrind_log]) finally: if os.path.isfile(valgrind_log): os.remove(valgrind_log) @unittest.skipIf(platform._is_win or platform._is_mac, "No strace on Windows or MacOS") @unittest.skipIf(tools.which('strace') is None, "strace not installed.") def test_strace(self): r"""Test running with strace.""" self.run_model_instance(with_valgrind=False, with_strace=True) # Tests for code generation def run_generated_code(self, lines): r"""Write and run generated code.""" if not self.import_cls.is_installed(): return # Write code to a file self.import_cls.run_code(lines) def get_test_types(self): r"""Return the list of tuples mapping json type to expected native type.""" if self.import_cls.type_map is None: return [] out = list(self.import_cls.type_map.items()) if 'flag' not in self.import_cls.type_map: out.append(('flag', self.import_cls.type_map['boolean'])) return out def test_invalid_function_param(self): r"""Test errors raise during class creation when parameters are invalid.""" kwargs = copy.deepcopy(self.inst_kwargs) kwargs['name'] = 'test' kwargs['args'] = ['test'] kwargs['function'] = 'invalid' kwargs['source_files'] = [] if self.import_cls.function_param is None: self.assert_raises(ValueError, self.import_cls, **kwargs) else: kwargs['args'] = ['invalid'] self.assert_raises(ValueError, self.import_cls, **kwargs) kwargs['args'] = [__file__] kwargs['is_server'] = True self.assert_raises(NotImplementedError, self.import_cls, **kwargs) def test_get_native_type(self): r"""Test translation to native type.""" test_vals = self.get_test_types() for a, b in test_vals: self.assert_equal( self.import_cls.get_native_type(type=a), b) if not isinstance(a, dict): self.assert_equal( self.import_cls.get_native_type(type={'type': a}), b) def test_write_declaration(self): r"""Test write_declaration for all supported native types.""" if (((self.import_cls.function_param is None) or ('declare' not in self.import_cls.function_param))): return test_vals = self.get_test_types() for a, b in test_vals: self.import_cls.write_declaration('test', type=a) def test_write_model_wrapper(self): r"""Test writing a model based on yaml parameters.""" if self.import_cls.function_param is None: self.assert_raises(NotImplementedError, self.import_cls.write_model_wrapper, None, None) self.assert_raises(NotImplementedError, self.import_cls.write_model_recv, None, None) self.assert_raises(NotImplementedError, self.import_cls.write_model_send, None, None) else: inputs = [{'name': 'a', 'type': 'bytes', 'outside_loop': True}, {'name': 'b', 'type': {'type': 'int', 'precision': 64}}, {'name': 'c', 'type': {'type': 'string', 'precision': 10}}] outputs = [{'name': 'y', 'type': {'type': 'float', 'precision': 32}}, {'name': 'z', 'type': 'bytes', 'outside_loop': True}, {'name': 'x', 'type': {'type': 'string', 'precision': 10}}] self.import_cls.write_model_wrapper('test', 'test', inputs=inputs, outputs=outputs) self.assert_raises(NotImplementedError, self.import_cls.format_function_param, 'invalid_key') def test_write_executable(self): r"""Test writing an executable.""" if self.import_cls.function_param is None: self.assert_raises(NotImplementedError, self.import_cls.write_executable, None) else: lines1 = self.import_cls.write_executable('dummy', prefix='dummy', suffix='dummy') lines2 = self.import_cls.write_executable(lines1) self.assert_equal(lines1, lines2) # Don't run this because it is invalid def test_error_code(self): r"""Test that error is raised when code generates one.""" if (((not self.import_cls.is_installed()) or (self.import_cls.function_param is None))): return error_msg = 'Test error' lines = [self.import_cls.function_param['error'].format(error_msg=error_msg)] assert_raises(RuntimeError, self.import_cls.run_code, lines) def test_write_if_block(self): r"""Test writing an if block.""" if self.import_cls.function_param is None: self.assert_raises(NotImplementedError, self.import_cls.write_if_block, None, None) else: lines = [] if 'declare' in self.import_cls.function_param: lines.append(self.import_cls.function_param['declare'].format( type_name='int', variable='x')) cond = self.import_cls.function_param['true'] block_contents = self.import_cls.function_param['assign'].format( name='x', value='1') lines += self.import_cls.write_if_block(cond, block_contents) self.run_generated_code(lines) def test_write_for_loop(self): r"""Test writing a for loop.""" if self.import_cls.function_param is None: self.assert_raises(NotImplementedError, self.import_cls.write_for_loop, None, None, None, None) else: lines = [] if 'declare' in self.import_cls.function_param: lines.append(self.import_cls.function_param['declare'].format( type_name='int', variable='i')) lines.append(self.import_cls.function_param['declare'].format( type_name='int', variable='x')) loop_contents = self.import_cls.function_param['assign'].format( name='x', value='i') lines += self.import_cls.write_for_loop('i', 0, 1, loop_contents) self.run_generated_code(lines) def test_write_while_loop(self): r"""Test writing a while loop.""" if self.import_cls.function_param is None: self.assert_raises(NotImplementedError, self.import_cls.write_while_loop, None, None) else: lines = [] cond = self.import_cls.function_param['true'] loop_contents = self.import_cls.function_param.get('break', 'break') lines += self.import_cls.write_while_loop(cond, loop_contents) self.run_generated_code(lines) def test_write_try_except(self, **kwargs): r"""Test writing a try/except block.""" if self.import_cls.function_param is None: self.assert_raises(NotImplementedError, self.import_cls.write_try_except, None, None) else: lines = [] try_contents = self.import_cls.function_param['error'].format( error_msg='Dummy error') except_contents = self.import_cls.function_param['print'].format( message='Dummy message') lines += self.import_cls.write_try_except(try_contents, except_contents, **kwargs) self.run_generated_code(lines)
def ygginfo(): r"""Print information about yggdrasil installation.""" from yggdrasil import __version__, tools, config, platform from yggdrasil.components import import_component lang_list = tools.get_installed_lang() prefix = ' ' curr_prefix = '' vardict = [ ('Location', os.path.dirname(__file__)), ('Version', __version__), ('Languages', ', '.join(lang_list)), ('Communication Mechanisms', ', '.join(tools.get_installed_comm())), ('Default Comm Mechanism', tools.get_default_comm()), ('Config File', config.usr_config_file) ] parser = argparse.ArgumentParser( description= 'Display information about the current yggdrasil installation.') parser.add_argument( '--no-languages', action='store_true', dest='no_languages', help='Don\'t print information about individual languages.') parser.add_argument( '--verbose', action='store_true', help='Increase the verbosity of the printed information.') args = parser.parse_args() try: # Add language information if not args.no_languages: # Install languages vardict.append(('Installed Languages:', '')) curr_prefix += prefix for lang in sorted(lang_list): drv = import_component('model', lang) vardict.append((curr_prefix + '%s:' % lang.upper(), '')) curr_prefix += prefix if lang == 'executable': vardict.append((curr_prefix + 'Location', '')) else: exec_name = drv.language_executable() if not os.path.isabs(exec_name): exec_name = tools.which(exec_name) vardict.append((curr_prefix + 'Location', exec_name)) vardict.append( (curr_prefix + 'Version', drv.language_version())) curr_prefix = curr_prefix.rsplit(prefix, 1)[0] curr_prefix = curr_prefix.rsplit(prefix, 1)[0] # Not installed languages vardict.append(("Languages Not Installed:", '')) curr_prefix += prefix for lang in tools.get_supported_lang(): if lang in lang_list: continue drv = import_component('model', lang) vardict.append((curr_prefix + '%s:' % lang.upper(), '')) curr_prefix += prefix vardict.append((curr_prefix + "Language Installed", drv.is_language_installed())) vardict.append((curr_prefix + "Base Languages Installed", drv.are_base_languages_installed())) if not drv.are_base_languages_installed(): vardict.append( (curr_prefix + "Base Languages Not Installed", [ b for b in drv.base_languages if (not import_component('model', b).is_installed()) ])) vardict.append((curr_prefix + "Dependencies Installed", drv.are_dependencies_installed())) vardict.append((curr_prefix + "Interface Installed", drv.is_interface_installed())) vardict.append( (curr_prefix + "Comm Installed", drv.is_comm_installed())) vardict.append( (curr_prefix + "Configured", drv.is_configured())) vardict.append((curr_prefix + "Disabled", drv.is_disabled())) curr_prefix = curr_prefix.rsplit(prefix, 1)[0] curr_prefix = curr_prefix.rsplit(prefix, 1)[0] # Add verbose information if args.verbose: # Conda info if os.environ.get('CONDA_PREFIX', ''): out = tools.bytes2str( subprocess.check_output(['conda', 'info'])).strip() curr_prefix += prefix vardict.append((curr_prefix + 'Conda Info:', "\n%s%s" % (curr_prefix + prefix, ("\n" + curr_prefix + prefix).join( out.splitlines(False))))) curr_prefix = curr_prefix.rsplit(prefix, 1)[0] # R and reticulate info Rdrv = import_component("model", "R") if Rdrv.is_installed(): env_reticulate = copy.deepcopy(os.environ) env_reticulate['RETICULATE_PYTHON'] = sys.executable # Stack size out = Rdrv.run_executable(["-e", "Cstack_info()"]).strip() vardict.append((curr_prefix + "R Cstack_info:", "\n%s%s" % (curr_prefix + prefix, ("\n" + curr_prefix + prefix).join( out.splitlines(False))))) # Compilation tools interp = 'R'.join(Rdrv.get_interpreter().rsplit('Rscript', 1)) vardict.append((curr_prefix + "R C Compiler:", "")) curr_prefix += prefix for x in ['CC', 'CFLAGS', 'CXX', 'CXXFLAGS']: out = tools.bytes2str( subprocess.check_output([interp, 'CMD', 'config', x])).strip() vardict.append((curr_prefix + x, "%s" % ("\n" + curr_prefix + prefix).join( out.splitlines(False)))) curr_prefix = curr_prefix.rsplit(prefix, 1)[0] # Session info out = Rdrv.run_executable(["-e", "sessionInfo()"]).strip() vardict.append((curr_prefix + "R sessionInfo:", "\n%s%s" % (curr_prefix + prefix, ("\n" + curr_prefix + prefix).join( out.splitlines(False))))) # Reticulate conda_list if os.environ.get('CONDA_PREFIX', ''): out = Rdrv.run_executable([ "-e", ("library(reticulate); " "reticulate::conda_list()") ], env=env_reticulate).strip() vardict.append( (curr_prefix + "R reticulate::conda_list():", "\n%s%s" % (curr_prefix + prefix, ("\n" + curr_prefix + prefix).join( out.splitlines(False))))) # Windows python versions if platform._is_win: # pragma: windows out = Rdrv.run_executable([ "-e", ("library(reticulate); " "reticulate::py_versions_windows()") ], env=env_reticulate).strip() vardict.append( (curr_prefix + "R reticulate::py_versions_windows():", "\n%s%s" % (curr_prefix + prefix, ("\n" + curr_prefix + prefix).join( out.splitlines(False))))) # conda_binary if platform._is_win: # pragma: windows out = Rdrv.run_executable([ "-e", ("library(reticulate); " "conda <- reticulate:::conda_binary(\"auto\"); " "system(paste(conda, \"info --json\"))") ], env=env_reticulate).strip() vardict.append( (curr_prefix + "R reticulate::py_versions_windows():", "\n%s%s" % (curr_prefix + prefix, ("\n" + curr_prefix + prefix).join( out.splitlines(False))))) # Reticulate py_config out = Rdrv.run_executable([ "-e", ("library(reticulate); " "reticulate::py_config()") ], env=env_reticulate).strip() vardict.append((curr_prefix + "R reticulate::py_config():", "\n%s%s" % (curr_prefix + prefix, ("\n" + curr_prefix + prefix).join( out.splitlines(False))))) finally: # Print things max_len = max(len(x[0]) for x in vardict) lines = [] line_format = '%-' + str(max_len) + 's' + prefix + '%s' for k, v in vardict: lines.append(line_format % (k, v)) logger.info("yggdrasil info:\n%s" % '\n'.join(lines))
def run_tsts(**kwargs): # pragma: no cover r"""Run tests for the package. Relative paths are interpreted to be relative to the package root directory. Args: verbose (bool, optional): If True, set option '-v' which increases the verbosity. Defaults to True. nocapture (bool, optional): If True, set option '--nocapture' ('--capture=no' with pytest) which allows messages to be printed to stdout. Defaults to True. stop (bool, optional): If True, set option '--stop' ('--exitfirst' for pytest) which stops tests at the first failure. Defaults to True. nologcapture (bool, optional): If True, set option '--nologcapture' which allows logged messages to be printed. Defaults to True. withcoverage (bool, optional): If True, set option '--with-coverage' which invokes coverage. Defaults to True. withexamples (bool, optional): If True, example testing will be enabled. Defaults to False. language (str, optional): Language to test. Defaults to None and all languages will be tested. """ if '-h' not in sys.argv: if _test_package is None: raise RuntimeError("Could not locate test runner pytest or nose.") elif _test_package_name == 'pytest': test_cmd = 'pytest' elif _test_package_name == 'nose': test_cmd = 'nosetests' else: raise RuntimeError("Unsupported test package: '%s'" % _test_package_name) parser = argparse.ArgumentParser(description='Run yggdrasil tests.') arguments = [ (['withcoverage', 'with-coverage'], ['nocover', 'no-cover'], True, { 'help': 'Record coverage during tests.' }), (['withexamples', 'with-examples'], ['noexamples', 'no-examples'], False, { 'help': 'Run example tests when encountered.' }), (['longrunning', 'long-running'], ['nolongrunning', 'no-long-running'], False, { 'help': 'Run long tests when encounterd.' }), (['verbose', 'v'], ['quiet'], True, { 'help': ('Increase verbosity of output from ' 'the test runner.') }), (['nocapture', 's'], ['capture'], True, { 'help': 'Don\'t capture output from tests.' }), (['stop', 'x'], ['dontstop', 'dont-stop'], True, { 'help': 'Stop after first test failure.' }), (['nologcapture'], ['logcapture'], True, { 'help': ('Don\'t capture output from log ' 'messages generated during tests.') }), (['validatecomponents', 'validate-components'], ['skipcomponentvalidation', 'skip-component-validation'], False, { 'help': ('Validate components on creation. This causes ' 'a decrease in performance so it is turned off ' 'by default.') }), (['noflaky', 'no-flaky'], ['flaky'], False, { 'help': 'Don\'t re-run flaky tests.' }) ] for pos_dest, neg_dest, default, kws in arguments: dest = pos_dest[0] for x in [pos_dest, neg_dest]: for i, y in enumerate(x): if len(y) == 1: x[i] = '-' + y else: x[i] = '--' + y if kwargs.get(dest, default): if kws['help'].startswith('Don\'t'): kws['help'].split('Don\'t', 1)[-1] kws['help'] = kws['help'].replace(kws['help'][0], kws['help'][0].upper(), 1) else: kws['help'] = kws['help'].replace(kws['help'][0], kws['help'][0].lower(), 1) kws['help'] = 'Don\'t ' + kws['help'] parser.add_argument(*neg_dest, action='store_false', dest=dest, **kws) else: parser.add_argument(*pos_dest, action='store_true', dest=dest, **kws) parser.add_argument('--language', '--languages', default=[], nargs="+", type=str, help='Language(s) that should be tested.') parser.add_argument('--ci', action='store_true', help=('Perform addition operations required ' 'for testing on continuous integration ' 'services.')) suite_args = ('--test-suite', '--test-suites') suite_kws = dict(nargs='+', action="extend", type=str, choices=['examples', 'types', 'timing'], help='Test suite(s) that should be run.', dest='test_suites') try: parser.add_argument(*suite_args, **suite_kws) except ValueError: # 'extend' introduced in 3.8 suite_kws['action'] = 'append' suite_kws.pop('nargs') parser.add_argument(*suite_args, **suite_kws) args, extra_argv = parser.parse_known_args() initial_dir = os.getcwd() package_dir = os.path.dirname(os.path.abspath(__file__)) error_code = 0 # Peform ci tests/operations # Call bash script? if args.ci: extra_argv += ['-c', 'setup.cfg', '--cov-config=.coveragerc'] # Separate out paths from options argv = [test_cmd] test_paths = [] opt_val = 0 for x in extra_argv: if opt_val > 0: argv.append(x) opt_val -= 1 elif x.endswith('yggtest'): pass elif x.startswith('-'): argv.append(x) if (_test_package_name == 'pytest') and (x in ['-c']): opt_val = 1 else: test_paths.append(x) if args.test_suites: for x in args.test_suites: if x == 'examples': args.withexamples = True test_paths.append('examples') elif x == 'types': args.withexamples = True args.longrunning = True test_paths.append( os.path.join('examples', 'tests', 'test_types.py')) elif x == 'timing': args.longrunning = True test_paths.append(os.path.join('tests', 'test_timing.py')) if _test_package_name == 'nose': argv += ['--detailed-errors', '--exe'] if args.verbose: argv.append('-v') if args.nocapture: argv.append('-s') if args.stop: argv.append('-x') if args.nologcapture and (_test_package_name == 'nose'): argv.append('--nologcapture') if args.withcoverage: if _test_package_name == 'nose': argv.append('--with-coverage') argv.append('--cover-package=yggdrasil') elif _test_package_name == 'pytest': argv.append('--cov=%s' % package_dir) if args.noflaky: if _test_package_name == 'pytest': argv += ['-p', 'no:flaky'] else: if _test_package_name == 'nose': argv.append('--with-flaky') # Get expanded tests to allow for paths that are relative to either the # yggdrasil root directory or the current working directory expanded_test_paths = [] if not test_paths: expanded_test_paths.append(package_dir) else: for x in test_paths: if not expand_and_add(x, expanded_test_paths, [package_dir, os.getcwd()]): expanded_test_paths.append(x) argv += expanded_test_paths # Run test command and perform cleanup before logging any errors logger.info("Running %s from %s", argv, os.getcwd()) old_env = {} pth_file = 'ygg_coverage.pth' assert (not os.path.isfile(pth_file)) try: # Set env if args.withexamples: old_env['YGG_ENABLE_EXAMPLE_TESTS'] = os.environ.get( 'YGG_ENABLE_EXAMPLE_TESTS', None) os.environ['YGG_ENABLE_EXAMPLE_TESTS'] = 'True' if args.language: from yggdrasil.components import import_component args.language = [ import_component('model', x).language for x in args.language ] old_env['YGG_TEST_LANGUAGE'] = os.environ.get( 'YGG_TEST_LANGUAGE', None) os.environ['YGG_TEST_LANGUAGE'] = ','.join(args.language) if args.longrunning: old_env['YGG_ENABLE_LONG_TESTS'] = os.environ.get( 'YGG_ENABLE_LONG_TESTS', None) os.environ['YGG_ENABLE_LONG_TESTS'] = 'True' if args.withcoverage: old_env['COVERAGE_PROCESS_START'] = os.environ.get( 'COVERAGE_PROCESS_START', None) os.environ['COVERAGE_PROCESS_START'] = 'True' with open(pth_file, 'w') as fd: fd.write("import coverage; coverage.process_startup()") if args.test_suites and ('timing' in args.test_suites): old_env['YGG_TEST_PRODUCTION_RUNS'] = os.environ.get( 'YGG_TEST_PRODUCTION_RUNS', None) os.environ['YGG_TEST_PRODUCTION_RUNS'] = 'True' if not args.validatecomponents: old_env['YGG_SKIP_COMPONENT_VALIDATION'] = os.environ.get( 'YGG_SKIP_COMPONENT_VALIDATION', None) if old_env['YGG_SKIP_COMPONENT_VALIDATION'] is None: os.environ['YGG_SKIP_COMPONENT_VALIDATION'] = 'True' # Perform CI specific pretest operations if args.ci: top_dir = os.path.dirname(os.getcwd()) src_cmd = ('python -c \"import versioneer; ' 'print(versioneer.get_version())\"') dst_cmd = ('python -c \"import yggdrasil; ' 'print(yggdrasil.__version__)\"') src_ver = subprocess.check_output(src_cmd, shell=True) dst_ver = subprocess.check_output(dst_cmd, shell=True, cwd=top_dir) if src_ver != dst_ver: # pragma: debug raise RuntimeError( ("Versions do not match:\n" "\tSource version: %s\n" "\tBuild version: %s\n") % (src_ver, dst_ver)) if os.environ.get("INSTALLR", None) == "1": from yggdrasil import tools print(tools.which("R")) print(tools.which("Rscript")) subprocess.check_call(["flake8", "yggdrasil"]) if os.environ.get("YGG_CONDA", None): subprocess.check_call(["python", "create_coveragerc.py"]) if not os.path.isfile(".coveragerc"): raise RuntimeError(".coveragerc file dosn't exist.") with open(".coveragerc", "r") as fd: print(fd.read()) subprocess.check_call(["ygginfo", "--verbose"]) error_code = subprocess.call(argv) except BaseException: logger.exception('Error in running test.') error_code = -1 finally: os.chdir(initial_dir) for k, v in old_env.items(): if v is None: del os.environ[k] else: os.environ[k] = v if os.path.isfile(pth_file): os.remove(pth_file) return error_code