def test_squash_simple(self): """Test toolchain filter""" tc_first = {'version': '10', 'name': self.tc_first} tc_last = {'version': '100', 'name': self.tc_last} tc_tmpl = '%(name)s == %(version)s' default_version = '1.0' all_versions = [default_version, '0.0', '1.0'] txt = [ '[SUPPORTED]', 'versions = %s' % ', '.join(all_versions), 'toolchains = %s,%s' % (tc_tmpl % tc_first, tc_tmpl % tc_last), ] co = ConfigObj(txt) cov = EBConfigObj(co) found_tcs = [tmptc.as_dict() for tmptc in cov.sections['toolchains']] self.assertEqual(found_tcs, [tc_first, tc_last]) for tc in [tc_first, tc_last]: for version in all_versions: co = ConfigObj(txt) cov = EBConfigObj(co) res = cov.squash(version, tc['name'], tc['version']) self.assertEqual(res, {}) # very simple
def test_ebconfigobj(self): """Test configobj sort""" # the as_dict method is crap # tc >= 0.0.0 returns empty as_dict, although the boundary can be used # anyway, will go away with proper defaults tcfirst = ",".join([ '%s == 0.0.0' % self.tc_namesmax[0], '%s > 0.0.0' % self.tc_namesmax[0] ]) configobj_txt = [ '[SUPPORTED]', 'toolchains=%s,%s >= 7.8.9' % (tcfirst, ','.join(self.tc_namesmax[1:])), 'versions=1.2.3,2.3.4,3.4.5', '[>= 2.3.4]', 'foo=bar', '[== 3.4.5]', 'baz=biz', '[%s == 5.6.7]' % self.tc_first, '[%s > 7.8.9]' % self.tc_lastmax, ] co = ConfigObj(configobj_txt) cov = EBConfigObj(co) # default tc is cgoolf -> cgoolf > 0.0.0 res = cov.get_specs_for(version='2.3.4', tcname=self.tc_first, tcversion='1.0.0') self.assertEqual(res, {'foo': 'bar'})
def test_nested_version(self): """Test nested config""" tc = {'version': '10', 'name': self.tc_first} default_version = '1.0' txt = [ '[SUPPORTED]', 'versions = %s, 0.0, 1.1, 1.5, 1.6, 2.0, 3.0' % default_version, 'toolchains = %(name)s == %(version)s' % tc, # set tc, don't use it '[> 1.0]', 'versionprefix = stable-', '[[>= 1.5]]', 'versionsuffix = -early', '[> 2.0]', 'versionprefix = production-', 'versionsuffix = -mature', ] # version string, attributes without version and toolchain data = [ (None, {}), (default_version, {}), ('0.0', {}), ('1.1', { 'versionprefix': 'stable-' }), ('1.5', { 'versionprefix': 'stable-', 'versionsuffix': '-early' }), ('1.6', { 'versionprefix': 'stable-', 'versionsuffix': '-early' }), ('2.0', { 'versionprefix': 'stable-', 'versionsuffix': '-early' }), ('3.0', { 'versionprefix': 'production-', 'versionsuffix': '-mature' }), ] for version, res in data: # yes, redo this for each test, even if it's static text # some of the data is modified in place co = ConfigObj(txt) cov = EBConfigObj(co) specs = cov.get_specs_for(version=version) self.assertEqual(specs, res)
def test_toolchain_squash_nested(self): """Test toolchain filter on nested sections""" tc_first = {'version': '10', 'name': self.tc_first} tc_last = {'version': '100', 'name': self.tc_last} tc_tmpl = '%(name)s == %(version)s' tc_section_first = tc_tmpl % tc_first tc_section_last = tc_tmpl % tc_last txt = [ '[SUPPORTED]', 'versions = 1.0, 0.0, 1.1, 1.6, 2.1', 'toolchains = %s,%s' % (tc_section_first, tc_tmpl % tc_last), '[DEFAULT]', 'y=a', '[> 1.0]', 'y=b', 'x = 1', '[[>= 1.5]]', 'x = 2', 'y=c', '[[[%s]]]' % tc_section_first, 'y=z2', '[[>= 1.6]]', 'z=3', '[> 2.0]', 'x = 3', 'y=d', '[%s]' % tc_section_first, 'y=z1', ] # tests tests = [ (tc_last, '1.0', {'y':'a'}), (tc_last, '1.1', {'y':'b', 'x':'1'}), (tc_last, '1.5', {}), # not a supported version (tc_last, '1.6', {'y':'c', 'x':'2', 'z':'3'}), # nested (tc_last, '2.1', {'y':'d', 'x':'3', 'z':'3'}), # values from most precise versop (tc_first, '1.0', {'y':'z1'}), # toolchain section, not default (tc_first, '1.1', {'y':'b', 'x':'1'}), # the version section precedes the toolchain section (tc_first, '1.5', {}), # not a supported version (tc_first, '1.6', {'y':'z2', 'x':'2', 'z':'3'}), # nested (tc_first, '2.1', {'y':'d', 'x':'3', 'z':'3'}), # values from most precise versop ] for tc, version, res in tests: co = ConfigObj(txt) cov = EBConfigObj(co) squashed = cov.squash(version, tc['name'], tc['version']) self.assertEqual(squashed, res, 'Test for tc %s version %s' % (tc, version))
def test_squash_invalid(self): """Try to squash invalid files. Should trigger error""" tc_first = {'version': '10', 'name': self.tc_first} tc_last = {'version': '100', 'name': self.tc_last} tc_tmpl = '%(name)s == %(version)s' default_version = '1.0' all_wrong_versions = [default_version, '>= 0.0', '< 1.0'] # all txt should have default version and first toolchain unmodified txt_wrong_versions = [ '[SUPPORTED]', 'versions = %s' % ', '.join( all_wrong_versions), # there's a conflict in the versions list 'toolchains = %s,%s' % (tc_tmpl % tc_first, tc_tmpl % tc_last), ] txt_conflict_nested_versions = [ '[SUPPORTED]', 'versions = %s' % default_version, 'toolchains = %s,%s' % (tc_tmpl % tc_first, tc_tmpl % tc_last), '[> 1]', '[[< 2]]', # although this makes sense, it's considered a conflict ] for txt in [ txt_wrong_versions, txt_conflict_nested_versions, ]: co = ConfigObj(txt) cov = EBConfigObj(co) self.assertErrorRegex(EasyBuildError, r'conflict', cov.squash, default_version, tc_first['name'], tc_first['version'])
def get_config_dict(self): """Return the best matching easyconfig dict""" self.log.experimental(self.__class__.__name__) # the toolchain name/version should not be specified in the pyheader, # but other toolchain options are allowed cfg = copy.deepcopy(self.pyheader_localvars) self.log.debug("Config dict based on Python header: %s" % cfg) co = EBConfigObj(self.configobj) version = self.specs.get('version', None) tc_spec = self.specs.get('toolchain', {}) toolchain_name = tc_spec.get('name', None) toolchain_version = tc_spec.get('version', None) # parse and interpret, dealing with defaults etc version, tcname, tcversion = co.get_version_toolchain( version, toolchain_name, toolchain_version) # format 2.0 will squash self.log.debug('Squashing with version %s and toolchain %s' % (version, (tcname, tcversion))) res = co.squash(version, tcname, tcversion) cfg.update(res) self.log.debug( "Config dict after processing applicable easyconfig sections: %s" % cfg) # FIXME what about updating dict values/appending to list values? # FIXME how do we allow both redefining and updating? = and +=? # update config with correct version/toolchain (to avoid using values specified in default section) cfg.update({ 'version': version, 'toolchain': { 'name': tcname, 'version': tcversion }, }) self.log.debug( "Final config dict (including correct version/toolchain): %s" % cfg) return cfg
def test_ebconfigobj_default(self): """Tests wrt ebconfigobj default parsing""" data = [ ('versions=1', {'version': '1'}), # == is usable ('toolchains=%s == 1' % self.tc_first, {'toolchain':{'name': self.tc_first, 'version': '1'}}), ] for val, res in data: configobj_txt = ['[SUPPORTED]', val] co = ConfigObj(configobj_txt) cov = EBConfigObj(co) self.assertEqual(cov.default, res)
def get_config_dict(self): """Return the best matching easyconfig dict""" self.log.experimental(self.__class__.__name__) # the toolchain name/version should not be specified in the pyheader, # but other toolchain options are allowed cfg = copy.deepcopy(self.pyheader_localvars) self.log.debug("Config dict based on Python header: %s" % cfg) co = EBConfigObj(self.configobj) version = self.specs.get('version', None) tc_spec = self.specs.get('toolchain', {}) toolchain_name = tc_spec.get('name', None) toolchain_version = tc_spec.get('version', None) # parse and interpret, dealing with defaults etc version, tcname, tcversion = co.get_version_toolchain(version, toolchain_name, toolchain_version) # format 2.0 will squash self.log.debug('Squashing with version %s and toolchain %s' % (version, (tcname, tcversion))) res = co.squash(version, tcname, tcversion) cfg.update(res) self.log.debug("Config dict after processing applicable easyconfig sections: %s" % cfg) # FIXME what about updating dict values/appending to list values? # FIXME how do we allow both redefining and updating? = and +=? # update config with correct version/toolchain (to avoid using values specified in default section) cfg.update({ 'version': version, 'toolchain': {'name': tcname, 'version': tcversion}, }) self.log.debug("Final config dict (including correct version/toolchain): %s" % cfg) return cfg
def test_nested_version(self): """Test nested config""" tc = {'version': '10', 'name': self.tc_first} default_version = '1.0' txt = [ '[SUPPORTED]', 'versions = %s, 0.0, 1.1, 1.5, 1.6, 2.0, 3.0' % default_version, 'toolchains = %(name)s == %(version)s' % tc, # set tc, don't use it '[> 1.0]', 'versionprefix = stable-', '[[>= 1.5]]', 'versionsuffix = -early', '[> 2.0]', 'versionprefix = production-', 'versionsuffix = -mature', ] # version string, attributes without version and toolchain data = [ (None, {}), (default_version, {}), ('0.0', {}), ('1.1', {'versionprefix': 'stable-'}), ('1.5', {'versionprefix': 'stable-', 'versionsuffix': '-early'}), ('1.6', {'versionprefix': 'stable-', 'versionsuffix': '-early'}), ('2.0', {'versionprefix': 'stable-', 'versionsuffix': '-early'}), ('3.0', {'versionprefix': 'production-', 'versionsuffix': '-mature'}), ] for version, res in data: # yes, redo this for each test, even if it's static text # some of the data is modified in place co = ConfigObj(txt) cov = EBConfigObj(co) specs = cov.get_specs_for(version=version) self.assertEqual(specs, res)
def test_ebconfigobj(self): """Test configobj sort""" # the as_dict method is crap # tc >= 0.0.0 returns empty as_dict, although the boundary can be used # anyway, will go away with proper defaults tcfirst = ",".join(['%s == 0.0.0' % self.tc_namesmax[0], '%s > 0.0.0' % self.tc_namesmax[0]]) configobj_txt = [ '[SUPPORTED]', 'toolchains=%s,%s >= 7.8.9' % (tcfirst, ','.join(self.tc_namesmax[1:])), 'versions=1.2.3,2.3.4,3.4.5', '[>= 2.3.4]', 'foo=bar', '[== 3.4.5]', 'baz=biz', '[%s == 5.6.7]' % self.tc_first, '[%s > 7.8.9]' % self.tc_lastmax, ] co = ConfigObj(configobj_txt) cov = EBConfigObj(co) # default tc is cgoolf -> cgoolf > 0.0.0 res = cov.get_specs_for(version='2.3.4', tcname=self.tc_first, tcversion='1.0.0') self.assertEqual(res, {'foo':'bar'})
def get_config_dict(self): """Return the best matching easyconfig dict""" self.log.experimental(self.__class__.__name__) # the toolchain name/version should not be specified in the pyheader, # but other toolchain options are allowed cfg = copy.deepcopy(self.pyheader_localvars) self.log.debug("Config dict based on Python header: %s" % cfg) co = EBConfigObj(self.configobj) # we only need to find one version / toolchain combo # esp. the toolchain name should be fixed, so no need to process anything but one toolchain version = self.specs.get('version', None) if version is None: # check for default version if 'version' in co.default: version = co.default['version'] self.log.info("no software version specified, using default version '%s'" % version) else: self.log.error("no software version specified, no default version found") else: self.log.debug("Using specified software version %s" % version) tc_spec = self.specs.get('toolchain', {}) toolchain_name = tc_spec.get('name', None) if toolchain_name is None: # check for default toolchain if 'toolchain' in co.default: toolchain = co.default['toolchain'] toolchain_name = toolchain['name'] self.log.info("no toolchain name specified, using default '%s'" % toolchain_name) toolchain_version = tc_spec.get('version', None) if toolchain_version is None: toolchain_version = toolchain['version'] self.log.info("no toolchain version specified, using default '%s'" % toolchain_version) else: self.log.error("no toolchain name specified, no default toolchain found") else: self.log.debug("Using specified toolchain name %s" % toolchain_name) toolchain_version = tc_spec.get('version', None) if toolchain_version is None: self.log.error("Toolchain specification incomplete: name %s provided, but no version" % toolchain_name) # toolchain name is known, remove all others toolchains from parsed easyconfig before we continue # this also performs some validation, and checks for conflicts between section markers self.log.debug("sections for full parsed configobj: %s" % co.sections) co.validate_and_filter_by_toolchain(toolchain_name) self.log.debug("sections for filtered parsed configobj: %s" % co.sections) section_specs = co.get_specs_for(version=version, tcname=toolchain_name, tcversion=toolchain_version) cfg.update(section_specs) self.log.debug("Config dict after processing applicable easyconfig sections: %s" % cfg) # FIXME what about updating dict values/appending to list values? how do we allow both redefining and updating? = and +=? # update config with correct version/toolchain (to avoid using values specified in default section) cfg.update({ 'version': version, 'toolchain': {'name': toolchain_name, 'version': toolchain_version}, }) self.log.debug("Final config dict (including correct version/toolchain): %s" % cfg) return cfg