def test_help_short(self, txt=None): """Test short help message.""" if txt is None: topt = EasyBuildOptions( go_args=['-h'], go_nosystemexit= True, # when printing help, optparse ends with sys.exit go_columns=100, # fix col size for reproducible unittest output help_to_string=True, # don't print to stdout, but to StingIO fh, prog= 'easybuildoptions_test', # generate as if called from generaloption.py ) outtxt = topt.parser.help_to_file.getvalue() else: outtxt = txt self.assertTrue(re.search(' -h ', outtxt), "Only short options included in short help") self.assertTrue(re.search("show short help message and exit", outtxt), "Documentation included in short help") self.assertEqual(re.search("--short-help ", outtxt), None, "Long options not included in short help") self.assertEqual( re.search("Software search and build options", outtxt), None, "Not all option groups included in short help (1)") self.assertEqual(re.search("Regression test options", outtxt), None, "Not all option groups included in short help (2)")
def test_deprecated(self): """Test the deprecated option""" orig_value = easybuild.tools.build_log.CURRENT_VERSION # make sure it's off by default self.assertEqual(orig_value, VERSION) log = fancylogger.getLogger() # force it to lower version using 0.x, which should no result in any raised error (only deprecation logging) topt = EasyBuildOptions(go_args=['--deprecated=0.%s' % orig_value], ) try: log.deprecated('x', str(orig_value)) except easybuild.tools.build_log.EasyBuildError, err: self.assertTrue(False, 'Deprecated logging should work')
def test_experimental(self): """Test the experimental option""" orig_value = easybuild.tools.build_log.EXPERIMENTAL # make sure it's off by default self.assertFalse(orig_value) log = fancylogger.getLogger() # force it to False topt = EasyBuildOptions(go_args=['--disable-experimental'], ) try: log.experimental('x') # sanity check, should never be reached if it works. self.assertTrue( False, "Experimental logging should be disabled by setting the --disable-experimental option" ) except easybuild.tools.build_log.EasyBuildError, err: # check error message self.assertTrue('Experimental functionality.' in str(err))
from easybuild.tools.modules import curr_module_paths, modules_tool, reset_module_caches from easybuild.tools.options import CONFIG_ENV_VAR_PREFIX, EasyBuildOptions, set_tmpdir from easybuild.tools.py2vs3 import reload # make sure tests are robust against any non-default configuration settings; # involves ignoring any existing configuration files that are picked up, and cleaning the environment # this is tackled here rather than in suite.py, to make sure this is also done when test modules are ran separately # clean up environment from unwanted $EASYBUILD_X env vars for key in os.environ.keys(): if key.startswith('%s_' % CONFIG_ENV_VAR_PREFIX): del os.environ[key] # ignore any existing configuration files go = EasyBuildOptions(go_useconfigfiles=False) os.environ['EASYBUILD_IGNORECONFIGFILES'] = ','.join(go.options.configfiles) # redefine $TEST_EASYBUILD_X env vars as $EASYBUILD_X test_env_var_prefix = 'TEST_EASYBUILD_' for key in os.environ.keys(): if key.startswith(test_env_var_prefix): val = os.environ[key] del os.environ[key] newkey = '%s_%s' % (CONFIG_ENV_VAR_PREFIX, key[len(test_env_var_prefix):]) os.environ[newkey] = val class EnhancedTestCase(TestCase): """Enhanced test case, provides extra functionality (e.g. an assertErrorRegex method)."""
def main(): """the main function""" fancylogger.logToScreen(enable=True, stdout=True) fancylogger.setLogLevelInfo() options = { 'github-user': ('Your github username to use', None, 'store', None, 'g'), 'closed-pr': ('Delete all gists from closed pull-requests', None, 'store_true', True, 'p'), 'all': ('Delete all gists from Easybuild ', None, 'store_true', False, 'a'), 'orphans': ('Delete all gists without a pull-request', None, 'store_true', False, 'o'), 'dry-run': ("Only show which gists will be deleted but don't actually delete them", None, 'store_true', False), } go = simple_option(options) log = go.log if not (go.options.all or go.options.closed_pr or go.options.orphans): raise EasyBuildError("Please tell me what to do?") if go.options.github_user is None: EasyBuildOptions.DEFAULT_LOGLEVEL = None # Don't overwrite log level eb_go = EasyBuildOptions(envvar_prefix='EASYBUILD', go_args=[]) username = eb_go.options.github_user log.debug("Fetch github username from easybuild, found: %s", username) else: username = go.options.github_user if username is None: raise EasyBuildError("Could not find a github username") else: log.info("Using username = %s", username) token = fetch_github_token(username) gh = RestClient(GITHUB_API_URL, username=username, token=token) all_gists = [] cur_page = 1 while True: status, gists = gh.gists.get(per_page=100, page=cur_page) if status != HTTP_STATUS_OK: raise EasyBuildError( "Failed to get a lists of gists for user %s: error code %s, message = %s", username, status, gists) if gists: all_gists.extend(gists) cur_page += 1 else: break log.info("Found %s gists", len(all_gists)) re_eb_gist = re.compile( r"(EasyBuild test report|EasyBuild log for failed build)(.*?)$") re_pr_nr = re.compile(r"(EB )?PR #([0-9]+)") pr_cache = {} num_deleted = 0 for gist in all_gists: if not gist["description"]: continue gist_match = re_eb_gist.search(gist["description"]) if not gist_match: log.debug("Found a non-Easybuild gist (id=%s)", gist["id"]) continue log.debug("Found an Easybuild gist (id=%s)", gist["id"]) pr_data = gist_match.group(2) pr_nrs_matches = re_pr_nr.findall(pr_data) if go.options.all: delete_gist = True elif not pr_nrs_matches: log.debug("Found Easybuild test report without PR (id=%s).", gist["id"]) delete_gist = go.options.orphans elif go.options.closed_pr: # All PRs must be closed delete_gist = True for pr_nr_match in pr_nrs_matches: eb_str, pr_num = pr_nr_match if eb_str or GITHUB_EASYBLOCKS_REPO in pr_data: repo = GITHUB_EASYBLOCKS_REPO else: repo = GITHUB_EASYCONFIGS_REPO cache_key = "%s-%s" % (repo, pr_num) if cache_key not in pr_cache: try: status, pr = gh.repos[GITHUB_EB_MAIN][repo].pulls[ pr_num].get() except HTTPError as e: status, pr = e.code, e.msg if status != HTTP_STATUS_OK: raise EasyBuildError( "Failed to get pull-request #%s: error code %s, message = %s", pr_num, status, pr) pr_cache[cache_key] = pr["state"] if pr_cache[cache_key] == "closed": log.debug("Found report from closed %s PR #%s (id=%s)", repo, pr_num, gist["id"]) elif delete_gist: if len(pr_nrs_matches) > 1: log.debug( "Found at least 1 PR, that is not closed yet: %s/%s (id=%s)", repo, pr_num, gist["id"]) delete_gist = False else: delete_gist = True if delete_gist: if go.options.dry_run: log.info("DRY-RUN: Delete gist with id=%s", gist["id"]) num_deleted += 1 continue try: status, del_gist = gh.gists[gist["id"]].delete() except HTTPError as e: status, del_gist = e.code, e.msg except URLError as e: status, del_gist = None, e.reason if status != HTTP_DELETE_OK: log.warning( "Unable to remove gist (id=%s): error code %s, message = %s", gist["id"], status, del_gist) else: log.info("Deleted gist with id=%s", gist["id"]) num_deleted += 1 if go.options.dry_run: log.info("DRY-RUN: Would delete %s gists", num_deleted) else: log.info("Deleted %s gists", num_deleted)
def main(): """the main function""" fancylogger.logToScreen(enable=True, stdout=True) fancylogger.setLogLevelInfo() options = { 'github-user': ('Your github username to use', None, 'store', None, 'g'), 'closed-pr': ('Delete all gists from closed pull-requests', None, 'store_true', True, 'p'), 'all': ('Delete all gists from Easybuild ', None, 'store_true', False, 'a'), 'orphans': ('Delete all gists without a pull-request', None, 'store_true', False, 'o'), } go = simple_option(options) log = go.log if not (go.options.all or go.options.closed_pr or go.options.orphans): log.error("Please tell me what to do?") if go.options.github_user is None: eb_go = EasyBuildOptions(envvar_prefix='EASYBUILD', go_args=[]) username = eb_go.options.github_user log.debug("Fetch github username from easybuild, found: %s", username) else: username = go.options.github_user if username is None: log.error("Could not find a github username") else: log.info("Using username = %s", username) token = fetch_github_token(username) gh = RestClient(GITHUB_API_URL, username=username, token=token) # ToDo: add support for pagination status, gists = gh.gists.get(per_page=100) if status != HTTP_STATUS_OK: log.error("Failed to get a lists of gists for user %s: error code %s, message = %s", username, status, gists) else: log.info("Found %s gists", len(gists)) regex = re.compile(r"(EasyBuild test report|EasyBuild log for failed build).*?(?:PR #(?P<PR>[0-9]+))?\)?$") pr_cache = {} num_deleted = 0 for gist in gists: if not gist["description"]: continue re_pr_num = regex.search(gist["description"]) delete_gist = False if re_pr_num: log.debug("Found a Easybuild gist (id=%s)", gist["id"]) pr_num = re_pr_num.group("PR") if go.options.all: delete_gist = True elif pr_num and go.options.closed_pr: log.debug("Found Easybuild test report for PR #%s", pr_num) if pr_num not in pr_cache: status, pr = gh.repos[GITHUB_EB_MAIN][GITHUB_EASYCONFIGS_REPO].pulls[pr_num].get() if status != HTTP_STATUS_OK: log.error("Failed to get pull-request #%s: error code %s, message = %s", pr_num, status, pr) pr_cache[pr_num] = pr["state"] if pr_cache[pr_num] == "closed": log.debug("Found report from closed PR #%s (id=%s)", pr_num, gist["id"]) delete_gist = True elif not pr_num and go.options.orphans: log.debug("Found Easybuild test report without PR (id=%s)", gist["id"]) delete_gist = True if delete_gist: status, del_gist = gh.gists[gist["id"]].delete() if status != HTTP_DELETE_OK: log.error("Unable to remove gist (id=%s): error code %s, message = %s", gist["id"], status, del_gist) else: log.info("Delete gist with id=%s", gist["id"]) num_deleted += 1 log.info("Deleted %s gists", num_deleted)
class CommandLineOptionsTest(EnhancedTestCase): """Testcases for command line options.""" logfile = None def test_help_short(self, txt=None): """Test short help message.""" if txt is None: topt = EasyBuildOptions( go_args=['-h'], go_nosystemexit= True, # when printing help, optparse ends with sys.exit go_columns=100, # fix col size for reproducible unittest output help_to_string=True, # don't print to stdout, but to StingIO fh, prog= 'easybuildoptions_test', # generate as if called from generaloption.py ) outtxt = topt.parser.help_to_file.getvalue() else: outtxt = txt self.assertTrue(re.search(' -h ', outtxt), "Only short options included in short help") self.assertTrue(re.search("show short help message and exit", outtxt), "Documentation included in short help") self.assertEqual(re.search("--short-help ", outtxt), None, "Long options not included in short help") self.assertEqual( re.search("Software search and build options", outtxt), None, "Not all option groups included in short help (1)") self.assertEqual(re.search("Regression test options", outtxt), None, "Not all option groups included in short help (2)") def test_help_long(self): """Test long help message.""" topt = EasyBuildOptions( go_args=['-H'], go_nosystemexit= True, # when printing help, optparse ends with sys.exit go_columns=100, # fix col size for reproducible unittest output help_to_string=True, # don't print to stdout, but to StingIO fh, prog= 'easybuildoptions_test', # generate as if called from generaloption.py ) outtxt = topt.parser.help_to_file.getvalue() self.assertTrue(re.search("-H, --help", outtxt), "Long documentation expanded in long help") self.assertTrue(re.search("show short help message and exit", outtxt), "Documentation included in long help") self.assertTrue(re.search("Software search and build options", outtxt), "Not all option groups included in short help (1)") self.assertTrue(re.search("Regression test options", outtxt), "Not all option groups included in short help (2)") def test_no_args(self): """Test using no arguments.""" outtxt = self.eb_main([]) error_msg = "ERROR .* Please provide one or multiple easyconfig files," error_msg += " or use software build options to make EasyBuild search for easyconfigs" self.assertTrue(re.search(error_msg, outtxt), "Error message when eb is run without arguments") def test_debug(self): """Test enabling debug logging.""" for debug_arg in ['-d', '--debug']: args = [ '--software-name=somethingrandom', debug_arg, ] outtxt = self.eb_main(args) for log_msg_type in ['DEBUG', 'INFO', 'ERROR']: res = re.search(' %s ' % log_msg_type, outtxt) self.assertTrue( res, "%s log messages are included when using %s: %s" % (log_msg_type, debug_arg, outtxt)) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None def test_info(self): """Test enabling info logging.""" for info_arg in ['--info']: args = [ '--software-name=somethingrandom', info_arg, ] outtxt = self.eb_main(args) for log_msg_type in ['INFO', 'ERROR']: res = re.search(' %s ' % log_msg_type, outtxt) self.assertTrue( res, "%s log messages are included when using %s ( out: %s)" % (log_msg_type, info_arg, outtxt)) for log_msg_type in ['DEBUG']: res = re.search(' %s ' % log_msg_type, outtxt) self.assertTrue( not res, "%s log messages are *not* included when using %s" % (log_msg_type, info_arg)) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None def test_quiet(self): """Test enabling quiet logging (errors only).""" for quiet_arg in ['--quiet']: args = [ '--software-name=somethingrandom', quiet_arg, ] outtxt = self.eb_main(args) for log_msg_type in ['ERROR']: res = re.search(' %s ' % log_msg_type, outtxt) self.assertTrue( res, "%s log messages are included when using %s (outtxt: %s)" % (log_msg_type, quiet_arg, outtxt)) for log_msg_type in ['DEBUG', 'INFO']: res = re.search(' %s ' % log_msg_type, outtxt) self.assertTrue( not res, "%s log messages are *not* included when using %s (outtxt: %s)" % (log_msg_type, quiet_arg, outtxt)) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None def test_force(self): """Test forcing installation even if the module is already available.""" # use GCC-4.6.3.eb easyconfig file that comes with the tests eb_file = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'GCC-4.6.3.eb') # check log message without --force args = [ eb_file, '--debug', ] outtxt, error_thrown = self.eb_main(args, return_error=True) self.assertTrue( not error_thrown, "No error is thrown if software is already installed (error_thrown: %s)" % error_thrown) already_msg = "GCC/4.6.3 is already installed" self.assertTrue( re.search(already_msg, outtxt), "Already installed message without --force, outtxt: %s" % outtxt) # clear log file, clean up environment write_file(self.logfile, '') modify_env(os.environ, self.orig_environ) tempfile.tempdir = None # check that --force works args = [ eb_file, '--force', '--debug', ] outtxt = self.eb_main(args) self.assertTrue(not re.search(already_msg, outtxt), "Already installed message not there with --force") def test_skip(self): """Test skipping installation of module (--skip, -k).""" # use temporary paths for build/install paths, make sure sources can be found buildpath = tempfile.mkdtemp() installpath = tempfile.mkdtemp() sourcepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sandbox', 'sources') # use toy-0.0.eb easyconfig file that comes with the tests eb_file = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'toy-0.0.eb') # check log message with --skip for existing module args = [ eb_file, '--sourcepath=%s' % sourcepath, '--buildpath=%s' % buildpath, '--installpath=%s' % installpath, '--force', '--debug', ] self.eb_main(args, do_build=True) modules_tool().purge() args.append('--skip') outtxt = self.eb_main(args, do_build=True, verbose=True) found_msg = "Module toy/0.0 found.\n[^\n]+Going to skip actual main build" found = re.search(found_msg, outtxt, re.M) self.assertTrue( found, "Module found message present with --skip, outtxt: %s" % outtxt) # cleanup for next test write_file(self.logfile, '') os.chdir(self.cwd) modules_tool().purge() # reinitialize modules tool with original $MODULEPATH, to avoid problems with future tests modify_env(os.environ, self.orig_environ) os.environ['MODULEPATH'] = '' modules_tool() tempfile.tempdir = None # check log message with --skip for non-existing module args = [ eb_file, '--sourcepath=%s' % sourcepath, '--buildpath=%s' % buildpath, '--installpath=%s' % installpath, '--try-software-version=1.2.3.4.5.6.7.8.9', '--try-amend=sources=toy-0.0.tar.gz,toy-0.0.tar.gz', # hackish, but fine '--force', '--debug', '--skip', ] outtxt = self.eb_main(args, do_build=True, verbose=True) found_msg = "Module toy/1.2.3.4.5.6.7.8.9 found." found = re.search(found_msg, outtxt) self.assertTrue( not found, "Module found message not there with --skip for non-existing modules: %s" % outtxt) not_found_msg = "No module toy/1.2.3.4.5.6.7.8.9 found. Not skipping anything." not_found = re.search(not_found_msg, outtxt) self.assertTrue( not_found, "Module not found message there with --skip for non-existing modules: %s" % outtxt) modules_tool().purge() # reinitialize modules tool with original $MODULEPATH, to avoid problems with future tests modify_env(os.environ, self.orig_environ) modules_tool() # cleanup shutil.rmtree(buildpath) shutil.rmtree(installpath) def test_job(self): """Test submitting build as a job.""" # use gzip-1.4.eb easyconfig file that comes with the tests eb_file = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'gzip-1.4.eb') # check log message with --job for job_args in [ # options passed are reordered, so order here matters to make tests pass ['--debug'], ['--debug', '--stop=configure', '--try-software-name=foo'], ]: # clear log file write_file(self.logfile, '') args = [ eb_file, '--job', ] + job_args outtxt = self.eb_main(args) job_msg = "INFO.* Command template for jobs: .* && eb %%\(spec\)s.* %s.*\n" % ' .*'.join( job_args) assertmsg = "Info log message with job command template when using --job (job_msg: %s, outtxt: %s)" % ( job_msg, outtxt) self.assertTrue(re.search(job_msg, outtxt), assertmsg) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None # 'zzz' prefix in the test name is intentional to make this test run last, # since it fiddles with the logging infrastructure which may break things def test_zzz_logtostdout(self): """Testing redirecting log to stdout.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) for stdout_arg in ['--logtostdout', '-l']: _stdout = sys.stdout fd, fn = tempfile.mkstemp() fh = os.fdopen(fd, 'w') sys.stdout = fh args = [ '--software-name=somethingrandom', '--robot', '.', '--debug', stdout_arg, ] self.eb_main(args, logfile=dummylogfn) # make sure we restore sys.stdout.flush() sys.stdout = _stdout fancylogger.logToScreen(enable=False, stdout=True) outtxt = read_file(fn) self.assertTrue( len(outtxt) > 100, "Log messages are printed to stdout when %s is used (outtxt: %s)" % (stdout_arg, outtxt)) # cleanup os.remove(fn) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None if os.path.exists(dummylogfn): os.remove(dummylogfn) fancylogger.logToFile(self.logfile) def test_avail_easyconfig_params(self): """Test listing available easyconfig parameters.""" def run_test(custom=None, extra_params=[]): """Inner function to run actual test in current setting.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) for avail_arg in [ '-a', '--avail-easyconfig-params', ]: # clear log write_file(self.logfile, '') args = [ avail_arg, '--unittest-file=%s' % self.logfile, ] if custom is not None: args.extend(['-e', custom]) outtxt = self.eb_main(args, logfile=dummylogfn, verbose=True) # check whether all parameter types are listed par_types = [ BUILD, DEPENDENCIES, EXTENSIONS, FILEMANAGEMENT, LICENSE, MANDATORY, MODULES, OTHER, TOOLCHAIN ] if custom is not None: par_types.append(CUSTOM) for param_type in [x[1] for x in par_types]: self.assertTrue( re.search( "%s\n%s" % (param_type.upper(), '-' * len(param_type)), outtxt), "Parameter type %s is featured in output of eb %s (args: %s): %s" % (param_type, avail_arg, args, outtxt)) # check a couple of easyconfig parameters for param in [ "name", "version", "toolchain", "versionsuffix", "buildopts", "sources", "start_dir", "dependencies", "group", "exts_list", "moduleclass", "buildstats" ] + extra_params: self.assertTrue( re.search("%s(?:\(\*\))?:\s*\w.*" % param, outtxt), "Parameter %s is listed with help in output of eb %s (args: %s): %s" % (param, avail_arg, args, outtxt)) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None if os.path.exists(dummylogfn): os.remove(dummylogfn) run_test(custom='EB_foo', extra_params=['foo_extra1', 'foo_extra2']) run_test(custom='bar', extra_params=['bar_extra1', 'bar_extra2']) run_test(custom='EB_foofoo', extra_params=['foofoo_extra1', 'foofoo_extra2']) # double underscore to make sure it runs first, which is required to detect certain types of bugs, # e.g. running with non-initialized EasyBuild config (truly mimicing 'eb --list-toolchains') def test__list_toolchains(self): """Test listing known compiler toolchains.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) args = [ '--list-toolchains', '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) info_msg = r"INFO List of known toolchains \(toolchainname: module\[,module\.\.\.\]\):" self.assertTrue(re.search(info_msg, outtxt), "Info message with list of known compiler toolchains") for tc in ["dummy", "goalf", "ictce"]: res = re.findall("^\s*%s: " % tc, outtxt, re.M) self.assertTrue( res, "Toolchain %s is included in list of known compiler toolchains" % tc) # every toolchain should only be mentioned once n = len(res) self.assertEqual( n, 1, "Toolchain %s is only mentioned once (count: %d)" % (tc, n)) if os.path.exists(dummylogfn): os.remove(dummylogfn) def test_avail_lists(self): """Test listing available values of certain types.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) name_items = { 'modules-tools': ['EnvironmentModulesC', 'Lmod'], 'module-naming-schemes': ['EasyBuildModuleNamingScheme'], } for (name, items) in name_items.items(): args = [ '--avail-%s' % name, '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) words = name.replace('-', ' ') info_msg = r"INFO List of supported %s:" % words self.assertTrue(re.search(info_msg, outtxt), "Info message with list of available %s" % words) for item in items: res = re.findall("^\s*%s" % item, outtxt, re.M) self.assertTrue( res, "%s is included in list of available %s" % (item, words)) # every item should only be mentioned once n = len(res) self.assertEqual( n, 1, "%s is only mentioned once (count: %d)" % (item, n)) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None if os.path.exists(dummylogfn): os.remove(dummylogfn) def test_list_easyblocks(self): """Test listing easyblock hierarchy.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) # adjust PYTHONPATH such that test easyblocks are found import easybuild eb_blocks_path = os.path.abspath( os.path.join(os.path.dirname(__file__), 'sandbox')) if not eb_blocks_path in sys.path: sys.path.append(eb_blocks_path) easybuild = reload(easybuild) import easybuild.easyblocks reload(easybuild.easyblocks) reload(easybuild.tools.module_naming_scheme ) # required to run options unit tests stand-alone # simple view for list_arg in ['--list-easyblocks', '--list-easyblocks=simple']: # clear log write_file(self.logfile, '') args = [ list_arg, '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) for pat in [ r"EasyBlock\n", r"|--\s+EB_foo\n|\s+|--\s+EB_foofoo\n", r"|--\s+bar\n", ]: self.assertTrue( re.search(pat, outtxt), "Pattern '%s' is found in output of --list-easyblocks: %s" % (pat, outtxt)) modify_env(os.environ, self.orig_environ) tempfile.tempdir = None # clear log write_file(self.logfile, '') # detailed view args = [ '--list-easyblocks=detailed', '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) for pat in [ r"EasyBlock\s+\(easybuild.framework.easyblock\)\n", r"|--\s+EB_foo\s+\(easybuild.easyblocks.foo\)\n|\s+|--\s+EB_foofoo\s+\(easybuild.easyblocks.foofoo\)\n", r"|--\s+bar\s+\(easybuild.easyblocks.generic.bar\)\n", ]: self.assertTrue( re.search(pat, outtxt), "Pattern '%s' is found in output of --list-easyblocks: %s" % (pat, outtxt)) if os.path.exists(dummylogfn): os.remove(dummylogfn) def test_search(self): """Test searching for easyconfigs.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) args = [ '--search=gzip', '--robot=%s' % os.path.join(os.path.dirname(__file__), 'easyconfigs'), '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) info_msg = r"Searching \(case-insensitive\) for 'gzip' in" self.assertTrue( re.search(info_msg, outtxt), "Info message when searching for easyconfigs in '%s'" % outtxt) for ec in ["gzip-1.4.eb", "gzip-1.4-GCC-4.6.3.eb"]: self.assertTrue(re.search(" \* \S*%s$" % ec, outtxt, re.M), "Found easyconfig %s in '%s'" % (ec, outtxt)) if os.path.exists(dummylogfn): os.remove(dummylogfn) for search_arg in ['-S', '--search-short']: open(self.logfile, 'w').write('') args = [ search_arg, 'toy-0.0', '--robot=%s' % os.path.join(os.path.dirname(__file__), 'easyconfigs'), '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) info_msg = r"Searching \(case-insensitive\) for 'toy-0.0' in" self.assertTrue( re.search(info_msg, outtxt), "Info message when searching for easyconfigs in '%s'" % outtxt) self.assertTrue(re.search('INFO CFGS\d+=', outtxt), "CFGS line message found in '%s'" % outtxt) for ec in ["toy-0.0.eb", "toy-0.0-multiple.eb"]: self.assertTrue(re.search(" \* \$CFGS\d+/*%s" % ec, outtxt), "Found easyconfig %s in '%s'" % (ec, outtxt)) if os.path.exists(dummylogfn): os.remove(dummylogfn) def test_dry_run(self): """Test dry runs.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) args = [ os.path.join(os.path.dirname(__file__), 'easyconfigs', 'gzip-1.4-GCC-4.6.3.eb'), '--dry-run', '--robot=%s' % os.path.join(os.path.dirname(__file__), 'easyconfigs'), '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) info_msg = r"Dry run: printing build status of easyconfigs and dependencies" self.assertTrue(re.search(info_msg, outtxt, re.M), "Info message dry running in '%s'" % outtxt) ecs_mods = [ ("gzip-1.4-GCC-4.6.3.eb", "gzip/1.4-GCC-4.6.3"), ("GCC-4.6.3.eb", "GCC/4.6.3"), ] for ec, mod in ecs_mods: regex = re.compile(r" \* \[.\] \S+%s \(module: %s\)" % (ec, mod), re.M) self.assertTrue( regex.search(outtxt), "Found match for pattern %s in '%s'" % (regex.pattern, outtxt)) for dry_run_arg in ['-D', '--dry-run-short']: open(self.logfile, 'w').write('') args = [ os.path.join(os.path.dirname(__file__), 'easyconfigs', 'gzip-1.4-GCC-4.6.3.eb'), dry_run_arg, '--robot=%s' % os.path.join(os.path.dirname(__file__), 'easyconfigs'), '--unittest-file=%s' % self.logfile, ] outtxt = self.eb_main(args, logfile=dummylogfn) info_msg = r"Dry run: printing build status of easyconfigs and dependencies" self.assertTrue(re.search(info_msg, outtxt, re.M), "Info message dry running in '%s'" % outtxt) self.assertTrue(re.search('CFGS=', outtxt), "CFGS line message found in '%s'" % outtxt) ecs_mods = [ ("gzip-1.4-GCC-4.6.3.eb", "gzip/1.4-GCC-4.6.3"), ("GCC-4.6.3.eb", "GCC/4.6.3"), ] for ec, mod in ecs_mods: regex = re.compile( r" \* \[.\] \$CFGS\S+%s \(module: %s\)" % (ec, mod), re.M) self.assertTrue( regex.search(outtxt), "Found match for pattern %s in '%s'" % (regex.pattern, outtxt)) if os.path.exists(dummylogfn): os.remove(dummylogfn) def test_from_pr(self): """Test fetching easyconfigs from a PR.""" fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') os.close(fd) args = [ # PR for ictce/6.2.5, see https://github.com/hpcugent/easybuild-easyconfigs/pull/726/files '--from-pr=726', '--dry-run', '--robot=%s' % os.path.join(os.path.dirname(__file__), 'easyconfigs'), '--unittest-file=%s' % self.logfile, '--github-user=easybuild_test', # a GitHub token should be available for this user ] outtxt = self.eb_main(args, logfile=dummylogfn, verbose=True) modules = [ 'icc/2013_sp1.2.144', 'ifort/2013_sp1.2.144', 'impi/4.1.3.049', 'imkl/11.1.2.144', 'ictce/6.2.5', 'gzip/1.6-ictce-6.2.5', ] for module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) regex = re.compile( r"^ \* \[.\] .*/%s \(module: %s\)$" % (ec_fn, module), re.M) self.assertTrue(regex.search(outtxt), "Found pattern %s in %s" % (regex.pattern, outtxt)) def test_no_such_software(self): """Test using no arguments.""" args = [ '--software-name=nosuchsoftware', '--robot=.', '--debug', ] outtxt = self.eb_main(args) # error message when template is not found error_msg1 = "ERROR .* No easyconfig files found for software nosuchsoftware, and no templates available. I'm all out of ideas." # error message when template is found error_msg2 = "ERROR .* Unable to find an easyconfig for the given specifications" msg = "Error message when eb can't find software with specified name (outtxt: %s)" % outtxt self.assertTrue( re.search(error_msg1, outtxt) or re.search(error_msg2, outtxt), msg) def test_footer(self): """Test specifying a module footer.""" # use temporary paths for build/install paths, make sure sources can be found buildpath = tempfile.mkdtemp() installpath = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp() sourcepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sandbox', 'sources') # create file containing modules footer module_footer_txt = '\n'.join([ "# test footer", "setenv SITE_SPECIFIC_ENV_VAR foobar", ]) fd, modules_footer = tempfile.mkstemp(prefix='modules-footer-') os.close(fd) f = open(modules_footer, 'w') f.write(module_footer_txt) f.close() # use toy-0.0.eb easyconfig file that comes with the tests eb_file = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'toy-0.0.eb') # check log message with --skip for existing module args = [ eb_file, '--sourcepath=%s' % sourcepath, '--buildpath=%s' % buildpath, '--installpath=%s' % installpath, '--debug', '--force', '--modules-footer=%s' % modules_footer, ] self.eb_main(args, do_build=True) toy_module = os.path.join(installpath, 'modules', 'all', 'toy', '0.0') toy_module_txt = read_file(toy_module) footer_regex = re.compile(r'%s$' % module_footer_txt, re.M) msg = "modules footer '%s' is present in '%s'" % (module_footer_txt, toy_module_txt) self.assertTrue(footer_regex.search(toy_module_txt), msg) # cleanup shutil.rmtree(buildpath) shutil.rmtree(installpath) shutil.rmtree(tmpdir) os.remove(modules_footer) def test_recursive_module_unload(self): """Test generating recursively unloading modules.""" # use temporary paths for build/install paths, make sure sources can be found buildpath = tempfile.mkdtemp() installpath = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp() sourcepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sandbox', 'sources') # use toy-0.0.eb easyconfig file that comes with the tests eb_file = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'toy-0.0-deps.eb') # check log message with --skip for existing module args = [ eb_file, '--sourcepath=%s' % sourcepath, '--buildpath=%s' % buildpath, '--installpath=%s' % installpath, '--debug', '--force', '--recursive-module-unload', ] self.eb_main(args, do_build=True, verbose=True) toy_module = os.path.join(installpath, 'modules', 'all', 'toy', '0.0-deps') toy_module_txt = read_file(toy_module) is_loaded_regex = re.compile(r"if { !\[is-loaded gompi/1.3.12\] }", re.M) self.assertFalse(is_loaded_regex.search(toy_module_txt), "Recursive unloading is used: %s" % toy_module_txt) # cleanup shutil.rmtree(buildpath) shutil.rmtree(installpath) shutil.rmtree(tmpdir) def test_tmpdir(self): """Test setting temporary directory to use by EasyBuild.""" # use temporary paths for build/install paths, make sure sources can be found buildpath = tempfile.mkdtemp() installpath = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp() sourcepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sandbox', 'sources') # use toy-0.0.eb easyconfig file that comes with the tests eb_file = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'toy-0.0.eb') # check log message with --skip for existing module args = [ eb_file, '--sourcepath=%s' % sourcepath, '--buildpath=%s' % buildpath, '--installpath=%s' % installpath, '--debug', '--tmpdir=%s' % tmpdir, ] outtxt = self.eb_main(args, do_build=True) tmpdir_msg = r"Using %s\S+ as temporary directory" % os.path.join( tmpdir, 'easybuild-') found = re.search(tmpdir_msg, outtxt, re.M) self.assertTrue(found, "Log message for tmpdir found in outtxt: %s" % outtxt) for var in ['TMPDIR', 'TEMP', 'TMP']: self.assertTrue(os.environ[var].startswith( os.path.join(tmpdir, 'easybuild-'))) self.assertTrue(tempfile.gettempdir().startswith( os.path.join(tmpdir, 'easybuild-'))) tempfile_tmpdir = tempfile.mkdtemp() self.assertTrue( tempfile_tmpdir.startswith(os.path.join(tmpdir, 'easybuild-'))) fd, tempfile_tmpfile = tempfile.mkstemp() self.assertTrue( tempfile_tmpfile.startswith(os.path.join(tmpdir, 'easybuild-'))) # cleanup shutil.rmtree(buildpath) shutil.rmtree(installpath) os.close(fd) shutil.rmtree(tmpdir) def test_ignore_osdeps(self): """Test ignoring of listed OS dependencies.""" txt = '\n'.join([ 'name = "pi"', 'version = "3.14"', 'homepage = "http://example.com"', 'description = "test easyconfig"', 'toolchain = {"name":"dummy", "version": "dummy"}', 'osdependencies = ["nosuchosdependency", ("nosuchdep_option1", "nosuchdep_option2")]', ]) fd, eb_file = tempfile.mkstemp(prefix='easyconfig_test_file_', suffix='.eb') os.close(fd) write_file(eb_file, txt) # check whether non-existing OS dependencies result in failure, by default args = [ eb_file, '--dry-run', ] outtxt = self.eb_main(args, do_build=True) regex = re.compile("Checking OS dependencies") self.assertTrue(regex.search(outtxt), "OS dependencies are checked, outtxt: %s" % outtxt) msg = "One or more OS dependencies were not found: " msg += "\[\('nosuchosdependency',\), \('nosuchdep_option1', 'nosuchdep_option2'\)\]" regex = re.compile(r'%s' % msg, re.M) self.assertTrue(regex.search(outtxt), "OS dependencies are honored, outtxt: %s" % outtxt) # check whether OS dependencies are effectively ignored args = [ eb_file, '--ignore-osdeps', '--dry-run', ] outtxt = self.eb_main(args, do_build=True) regex = re.compile("Not checking OS dependencies", re.M) self.assertTrue( regex.search(outtxt), "OS dependencies are ignored with --ignore-osdeps, outtxt: %s" % outtxt) txt += "\nstop = 'notavalidstop'" write_file(eb_file, txt) args = [ eb_file, '--ignore-osdeps', '--dry-run', ] outtxt = self.eb_main(args, do_build=True) regex = re.compile("stop provided 'notavalidstop' is not valid", re.M) self.assertTrue( regex.search(outtxt), "Validations are performed with --ignore-osdeps, outtxt: %s" % outtxt) def test_experimental(self): """Test the experimental option""" orig_value = easybuild.tools.build_log.EXPERIMENTAL # make sure it's off by default self.assertFalse(orig_value) log = fancylogger.getLogger() # force it to False topt = EasyBuildOptions(go_args=['--disable-experimental'], ) try: log.experimental('x') # sanity check, should never be reached if it works. self.assertTrue( False, "Experimental logging should be disabled by setting the --disable-experimental option" ) except easybuild.tools.build_log.EasyBuildError, err: # check error message self.assertTrue('Experimental functionality.' in str(err)) # toggle experimental topt = EasyBuildOptions(go_args=['--experimental'], ) try: log.experimental('x') except easybuild.tools.build_log.EasyBuildError, err: self.assertTrue( False, 'Experimental logging should be allowed by the --experimental option.' )
orig_value = easybuild.tools.build_log.CURRENT_VERSION # make sure it's off by default self.assertEqual(orig_value, VERSION) log = fancylogger.getLogger() # force it to lower version using 0.x, which should no result in any raised error (only deprecation logging) topt = EasyBuildOptions(go_args=['--deprecated=0.%s' % orig_value], ) try: log.deprecated('x', str(orig_value)) except easybuild.tools.build_log.EasyBuildError, err: self.assertTrue(False, 'Deprecated logging should work') # force it to current version, which should result in deprecation topt = EasyBuildOptions(go_args=['--deprecated=%s' % orig_value], ) try: log.deprecated('x', str(orig_value)) # not supposed to get here self.assertTrue(False, 'Deprecated logging should throw EasyBuildError') except easybuild.tools.build_log.EasyBuildError, err2: self.assertTrue('DEPRECATED' in str(err2)) # force higher version by prefixing it with 1, which should result in deprecation topt = EasyBuildOptions(go_args=['--deprecated=1%s' % orig_value], ) try: log.deprecated('x', str(orig_value)) # not supposed to get here self.assertTrue(False, 'Deprecated logging should throw EasyBuildError')