From 270f1edcd798cd51fe61f8e2d12c51dd60d655a0 Mon Sep 17 00:00:00 2001 From: Ake Sandgren Date: Fri, 1 May 2020 15:06:35 +0200 Subject: [PATCH 01/12] Fix problems with CrayCCE processing when there are no actual external modules. Set name and version for EXTERNAL_MODULES which lacks an actual module in _parse_dependency. And since craympich.py doesn't have an separate MPI module, MPI_MODULE_NAME should be None and not an empty list. --- easybuild/framework/easyconfig/easyconfig.py | 4 ++++ easybuild/toolchains/mpi/craympich.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/easybuild/framework/easyconfig/easyconfig.py b/easybuild/framework/easyconfig/easyconfig.py index 3faeb84b90..3ed11f4b06 100644 --- a/easybuild/framework/easyconfig/easyconfig.py +++ b/easybuild/framework/easyconfig/easyconfig.py @@ -1457,6 +1457,10 @@ def _parse_dependency(self, dep, hidden=False, build_only=False): if dependency['full_mod_name'].split('/')[-1].startswith('.'): dependency['hidden'] = True + if dependency['name'] is None: + dependency['name'] = dependency['short_mod_name'] + dependency['version'] = '' + self.log.debug("Returning parsed external dependency: %s", dependency) return dependency diff --git a/easybuild/toolchains/mpi/craympich.py b/easybuild/toolchains/mpi/craympich.py index cf32237451..bd01662002 100644 --- a/easybuild/toolchains/mpi/craympich.py +++ b/easybuild/toolchains/mpi/craympich.py @@ -40,7 +40,7 @@ class CrayMPICH(Mpi): """Generic support for using Cray compiler wrappers""" # MPI support # no separate module, Cray compiler drivers always provide MPI support - MPI_MODULE_NAME = [] + MPI_MODULE_NAME = None MPI_FAMILY = TC_CONSTANT_MPICH MPI_TYPE = TC_CONSTANT_MPI_TYPE_MPICH From 0627a4c240046eb1885a5791410da18d40680f4a Mon Sep 17 00:00:00 2001 From: Ake Sandgren Date: Fri, 1 May 2020 16:34:03 +0200 Subject: [PATCH 02/12] external_modules: if there is external module data with name/version do not set the dependency name/version --- easybuild/framework/easyconfig/easyconfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/easybuild/framework/easyconfig/easyconfig.py b/easybuild/framework/easyconfig/easyconfig.py index 3ed11f4b06..2ee3d2e86f 100644 --- a/easybuild/framework/easyconfig/easyconfig.py +++ b/easybuild/framework/easyconfig/easyconfig.py @@ -1457,8 +1457,9 @@ def _parse_dependency(self, dep, hidden=False, build_only=False): if dependency['full_mod_name'].split('/')[-1].startswith('.'): dependency['hidden'] = True - if dependency['name'] is None: + if 'name' not in dependency['external_module_metadata']: dependency['name'] = dependency['short_mod_name'] + if 'version' not in dependency['external_module_metadata']: dependency['version'] = '' self.log.debug("Returning parsed external dependency: %s", dependency) From afa5ad6ac4e121e0af953cc59adefd2eddcbe22e Mon Sep 17 00:00:00 2001 From: Ake Sandgren Date: Fri, 1 May 2020 17:08:24 +0200 Subject: [PATCH 03/12] Don't try to find software_root when MPI_MODULE_NAME is None --- easybuild/tools/toolchain/mpi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/easybuild/tools/toolchain/mpi.py b/easybuild/tools/toolchain/mpi.py index 93f833b07a..5550aaaacd 100644 --- a/easybuild/tools/toolchain/mpi.py +++ b/easybuild/tools/toolchain/mpi.py @@ -242,6 +242,9 @@ def _set_mpi_variables(self): if not self.options.get('32bit', None): suffix = '64' + if self.MPI_MODULE_NAME is None: + return + for root in self.get_software_root(self.MPI_MODULE_NAME): self.variables.append_exists('MPI_LIB_STATIC', root, lib_dir, filename="lib%s.a" % self.MPI_LIBRARY_NAME, suffix=suffix) From 89c887f7394c68c85b1ecad4bf71bbbd01fd9a76 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Fri, 1 May 2020 22:01:12 +0200 Subject: [PATCH 04/12] enhance test for --list-software and --list-installed-software to catch bug reported in #3265 --- test/framework/options.py | 162 +++++++++++++++++++++++++++----------- 1 file changed, 118 insertions(+), 44 deletions(-) diff --git a/test/framework/options.py b/test/framework/options.py index a0a4e39d62..938c2d8182 100644 --- a/test/framework/options.py +++ b/test/framework/options.py @@ -4265,54 +4265,128 @@ def test_list_prs(self): def test_list_software(self): """Test --list-software and --list-installed-software.""" - test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'v1.0') - args = [ - '--list-software', - '--robot-paths=%s' % test_ecs, - ] - txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False) - expected = '\n'.join([ - "== Processed 5/5 easyconfigs...", - "== Found 2 different software packages", - '', - "* GCC", - "* gzip", - '', + + # copy selected test easyconfigs for testing --list-*software options with; + # full test is a nuisance, because all dependencies must be available and toolchains like intel must have + # all expected components when testing with HierarchicalMNS (which the test easyconfigs don't always have) + topdir = os.path.dirname(os.path.abspath(__file__)) + + cray_ec = os.path.join(topdir, 'easyconfigs', 'test_ecs', 'c', 'CrayCCE', 'CrayCCE-5.1.29.eb') + gcc_ec = os.path.join(topdir, 'easyconfigs', 'test_ecs', 'g', 'GCC', 'GCC-4.6.3.eb') + gzip_ec = os.path.join(topdir, 'easyconfigs', 'v1.0', 'g', 'gzip', 'gzip-1.4-GCC-4.6.3.eb') + gzip_system_ec = os.path.join(topdir, 'easyconfigs', 'v1.0', 'g', 'gzip', 'gzip-1.4.eb') + + test_ecs = os.path.join(self.test_prefix, 'test_ecs') + for ec in [cray_ec, gcc_ec, gzip_ec, gzip_system_ec]: + subdirs = os.path.dirname(ec).split(os.path.sep)[-2:] + target_dir = os.path.join(test_ecs, *subdirs) + mkdir(target_dir, parents=True) + copy_file(ec, target_dir) + + # add (fake) HPL easyconfig using CrayCCE toolchain + # (required to trigger bug reported in https://github.com/easybuilders/easybuild-framework/issues/3265) + hpl_cray_ec_txt = '\n'.join([ + 'easyblock = "ConfigureMake"', + 'name = "HPL"', + 'version = "2.3"', + "homepage = 'http://www.netlib.org/benchmark/hpl/'", + 'description = "HPL"', + 'toolchain = {"name": "CrayCCE", "version": "5.1.29"}', ]) - self.assertTrue(txt.endswith(expected)) + hpl_cray_ec = os.path.join(self.test_prefix, 'test_ecs', 'h', 'HPL', 'HPL-2.3-CrayCCE-5.1.29.eb') + write_file(hpl_cray_ec, hpl_cray_ec_txt) - args = [ - '--list-software=detailed', - '--output-format=rst', - '--robot-paths=%s' % test_ecs, - ] - txt, _ = self._run_mock_eb(args, testing=False) - self.assertTrue(re.search(r'^\*GCC\*', txt, re.M)) - self.assertTrue(re.search(r'^``4.6.3``\s+``system``', txt, re.M)) - self.assertTrue(re.search(r'^\*gzip\*', txt, re.M)) - self.assertTrue(re.search(r'^``1.5``\s+``foss/2018a``,\s+``intel/2018a``', txt, re.M)) + # put dummy Core/GCC/4.6.3 in place + modpath = os.path.join(self.test_prefix, 'modules') + write_file(os.path.join(modpath, 'Core', 'GCC', '4.6.3'), '#%Module') + self.modtool.use(modpath) - args = [ - '--list-installed-software', - '--output-format=rst', - '--robot-paths=%s' % test_ecs, - ] - txt, _ = self._run_mock_eb(args, testing=False, raise_error=True) - self.assertTrue(re.search(r'== Processed 5/5 easyconfigs...', txt, re.M)) - self.assertTrue(re.search(r'== Found 2 different software packages', txt, re.M)) - self.assertTrue(re.search(r'== Retained 1 installed software packages', txt, re.M)) - self.assertTrue(re.search(r'^\* GCC', txt, re.M)) - self.assertFalse(re.search(r'gzip', txt, re.M)) + # test with different module naming scheme active + # (see https://github.com/easybuilders/easybuild-framework/issues/3265) + for mns in ['EasyBuildMNS', 'HierarchicalMNS']: - args = [ - '--list-installed-software=detailed', - '--robot-paths=%s' % test_ecs, - ] - txt, _ = self._run_mock_eb(args, testing=False) - self.assertTrue(re.search(r'^== Retained 1 installed software packages', txt, re.M)) - self.assertTrue(re.search(r'^\* GCC', txt, re.M)) - self.assertTrue(re.search(r'^\s+\* GCC v4.6.3: system', txt, re.M)) - self.assertFalse(re.search(r'gzip', txt, re.M)) + args = [ + '--list-software', + '--robot-paths=%s' % test_ecs, + '--module-naming-scheme=%s' % mns, + ] + txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False, verbose=True) + + patterns = [ + r"^.*\s*== Processed 5/5 easyconfigs...", + r"^== Found 4 different software packages", + r"^\* CrayCCE", + r"^\* GCC", + r"^\* gzip", + r"^\* HPL", + ] + for pattern in patterns: + regex = re.compile(pattern, re.M) + self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + + args = [ + '--list-software=detailed', + '--output-format=rst', + '--robot-paths=%s' % test_ecs, + '--module-naming-scheme=%s' % mns, + ] + txt, _ = self._run_mock_eb(args, testing=False, raise_error=True, verbose=True) + + patterns = [ + r"^.*\s*== Processed 5/5 easyconfigs...", + r"^== Found 4 different software packages", + r'^\*CrayCCE\*', + r'^``5.1.29``\s+``system``', + r'^\*GCC\*', + r'^``4.6.3``\s+``system``', + r'^\*gzip\*', + r'^``1.4`` ``GCC/4.6.3``, ``system``', + ] + for pattern in patterns: + regex = re.compile(pattern, re.M) + self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + + args = [ + '--list-installed-software', + '--output-format=rst', + '--robot-paths=%s' % test_ecs, + '--module-naming-scheme=%s' % mns, + ] + txt, _ = self._run_mock_eb(args, testing=False, raise_error=True, verbose=True) + + patterns = [ + r"^.*\s*== Processed 5/5 easyconfigs...", + r"^== Found 4 different software packages", + r"^== Retained 1 installed software packages", + r'^\* GCC', + ] + for pattern in patterns: + regex = re.compile(pattern, re.M) + self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + + self.assertFalse(re.search(r'gzip', txt, re.M)) + self.assertFalse(re.search(r'CrayCCE', txt, re.M)) + + args = [ + '--list-installed-software=detailed', + '--robot-paths=%s' % test_ecs, + '--module-naming-scheme=%s' % mns, + ] + txt, _ = self._run_mock_eb(args, testing=False, raise_error=True, verbose=True) + + patterns = [ + r"^.*\s*== Processed 5/5 easyconfigs...", + r"^== Found 4 different software packages", + r"^== Retained 1 installed software packages", + r'^\* GCC', + r'^\s+\* GCC v4.6.3: system', + ] + for pattern in patterns: + regex = re.compile(pattern, re.M) + self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + + self.assertFalse(re.search(r'gzip', txt, re.M)) + self.assertFalse(re.search(r'CrayCCE', txt, re.M)) def test_parse_optarch(self): """Test correct parsing of optarch option.""" From f3cafd41a0b3a850be7ea690e2ba14dd54575746 Mon Sep 17 00:00:00 2001 From: Ake Sandgren Date: Fri, 1 May 2020 22:22:23 +0200 Subject: [PATCH 05/12] Cleaner way to handle Cray toolchains lack of MPI_MODULE_NAME --- easybuild/tools/toolchain/mpi.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/easybuild/tools/toolchain/mpi.py b/easybuild/tools/toolchain/mpi.py index 5550aaaacd..abd24e4809 100644 --- a/easybuild/tools/toolchain/mpi.py +++ b/easybuild/tools/toolchain/mpi.py @@ -242,10 +242,8 @@ def _set_mpi_variables(self): if not self.options.get('32bit', None): suffix = '64' - if self.MPI_MODULE_NAME is None: - return - - for root in self.get_software_root(self.MPI_MODULE_NAME): + # take into account that MPI_MODULE_NAME could be None (see Cray toolchains) + for root in self.get_software_root(self.MPI_MODULE_NAME or []): self.variables.append_exists('MPI_LIB_STATIC', root, lib_dir, filename="lib%s.a" % self.MPI_LIBRARY_NAME, suffix=suffix) self.variables.append_exists('MPI_LIB_SHARED', root, lib_dir, filename="lib%s.so" % self.MPI_LIBRARY_NAME, From 39ba429bef1c3f58d59f20e9fdffe328f089657f Mon Sep 17 00:00:00 2001 From: Ake Sandgren Date: Fri, 1 May 2020 23:50:11 +0200 Subject: [PATCH 06/12] Handle external modules better when short_mod_name contains name/version --- easybuild/framework/easyconfig/easyconfig.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/easybuild/framework/easyconfig/easyconfig.py b/easybuild/framework/easyconfig/easyconfig.py index 8158c9f8b6..d0261ef28a 100644 --- a/easybuild/framework/easyconfig/easyconfig.py +++ b/easybuild/framework/easyconfig/easyconfig.py @@ -1463,10 +1463,14 @@ def _parse_dependency(self, dep, hidden=False, build_only=False): if dependency['full_mod_name'].split('/')[-1].startswith('.'): dependency['hidden'] = True + name_version = dependency['short_mod_name'].split('/') if 'name' not in dependency['external_module_metadata']: - dependency['name'] = dependency['short_mod_name'] + dependency['name'] = name_version[0] if 'version' not in dependency['external_module_metadata']: - dependency['version'] = '' + if len(name_version) > 1: + dependency['version'] = dependency['short_mod_name'].split('/')[1] + else: + dependency['version'] = '' self.log.debug("Returning parsed external dependency: %s", dependency) return dependency From daf86596a60a138c736f3df0bdb9e0aadbcd1396 Mon Sep 17 00:00:00 2001 From: Ake Sandgren Date: Fri, 1 May 2020 23:58:56 +0200 Subject: [PATCH 07/12] Fix missing change, use name_version --- easybuild/framework/easyconfig/easyconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/framework/easyconfig/easyconfig.py b/easybuild/framework/easyconfig/easyconfig.py index d0261ef28a..cc081ec22c 100644 --- a/easybuild/framework/easyconfig/easyconfig.py +++ b/easybuild/framework/easyconfig/easyconfig.py @@ -1468,7 +1468,7 @@ def _parse_dependency(self, dep, hidden=False, build_only=False): dependency['name'] = name_version[0] if 'version' not in dependency['external_module_metadata']: if len(name_version) > 1: - dependency['version'] = dependency['short_mod_name'].split('/')[1] + dependency['version'] = name_version[1] else: dependency['version'] = '' From 1a0cbcf672c1dfc34e99116c2b7c2f2560d036b2 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 2 May 2020 16:43:54 +0200 Subject: [PATCH 08/12] undo hacky changes in _parse_dependency to inject guessed values for name/version in case no metadata is available for external modules --- easybuild/framework/easyconfig/easyconfig.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/easybuild/framework/easyconfig/easyconfig.py b/easybuild/framework/easyconfig/easyconfig.py index cc081ec22c..d79eed817b 100644 --- a/easybuild/framework/easyconfig/easyconfig.py +++ b/easybuild/framework/easyconfig/easyconfig.py @@ -1463,15 +1463,6 @@ def _parse_dependency(self, dep, hidden=False, build_only=False): if dependency['full_mod_name'].split('/')[-1].startswith('.'): dependency['hidden'] = True - name_version = dependency['short_mod_name'].split('/') - if 'name' not in dependency['external_module_metadata']: - dependency['name'] = name_version[0] - if 'version' not in dependency['external_module_metadata']: - if len(name_version) > 1: - dependency['version'] = name_version[1] - else: - dependency['version'] = '' - self.log.debug("Returning parsed external dependency: %s", dependency) return dependency From ced2fb04be6a1b3cd1debae9890366ba404dad0f Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 2 May 2020 16:44:34 +0200 Subject: [PATCH 09/12] make HierarhicalMNS compatible with Cray toolchains (proper fix for #3265) --- .../module_naming_scheme/hierarchical_mns.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/easybuild/tools/module_naming_scheme/hierarchical_mns.py b/easybuild/tools/module_naming_scheme/hierarchical_mns.py index f6d7cf1423..649b700003 100644 --- a/easybuild/tools/module_naming_scheme/hierarchical_mns.py +++ b/easybuild/tools/module_naming_scheme/hierarchical_mns.py @@ -41,9 +41,11 @@ CORE = 'Core' COMPILER = 'Compiler' MPI = 'MPI' +TOOLCHAIN = 'Toolchain' MODULECLASS_COMPILER = 'compiler' MODULECLASS_MPI = 'mpi' +MODULECLASS_TOOLCHAIN = 'toolchain' GCCCORE = GCCcore.NAME @@ -107,7 +109,11 @@ def det_toolchain_compilers_name_version(self, tc_comps): # no compiler in toolchain, system toolchain res = None elif len(tc_comps) == 1: - res = (tc_comps[0]['name'], self.det_full_version(tc_comps[0])) + tc_comp = tc_comps[0] + if tc_comp is None: + res = None + else: + res = (tc_comp['name'], self.det_full_version(tc_comp)) else: comp_versions = dict([(comp['name'], self.det_full_version(comp)) for comp in tc_comps]) comp_names = comp_versions.keys() @@ -135,6 +141,10 @@ def det_module_subdir(self, ec): if tc_comps is None: # no compiler in toolchain, system toolchain => Core module subdir = CORE + elif tc_comps == [None]: + # no info on toolchain compiler (cfr. Cray toolchains), + # then use toolchain name/version + subdir = os.path.join(TOOLCHAIN, ec.toolchain.name, ec.toolchain.version) else: tc_comp_name, tc_comp_ver = self.det_toolchain_compilers_name_version(tc_comps) tc_mpi = det_toolchain_mpi(ec) @@ -223,6 +233,10 @@ def det_modpath_extensions(self, ec): fullver = self.det_full_version(ec) paths.append(os.path.join(MPI, tc_comp_name, tc_comp_ver, ec['name'], fullver)) + # special case for Cray toolchains + elif modclass == MODULECLASS_TOOLCHAIN and tc_comp_info is None: + paths.append(os.path.join(TOOLCHAIN, ec.toolchain.name, ec.toolchain.version)) + return paths def expand_toolchain_load(self, ec=None): From 837f1f345641ed26abb701c6f30481d87d7bef80 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 2 May 2020 18:11:43 +0200 Subject: [PATCH 10/12] fix special case for Cray toolchains in HierarchicalMNS.det_modpath_extensions + enhance test_hierarchical_mns --- easybuild/tools/module_naming_scheme/hierarchical_mns.py | 4 ++-- test/framework/module_generator.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/easybuild/tools/module_naming_scheme/hierarchical_mns.py b/easybuild/tools/module_naming_scheme/hierarchical_mns.py index 649b700003..1647f4c96e 100644 --- a/easybuild/tools/module_naming_scheme/hierarchical_mns.py +++ b/easybuild/tools/module_naming_scheme/hierarchical_mns.py @@ -234,8 +234,8 @@ def det_modpath_extensions(self, ec): paths.append(os.path.join(MPI, tc_comp_name, tc_comp_ver, ec['name'], fullver)) # special case for Cray toolchains - elif modclass == MODULECLASS_TOOLCHAIN and tc_comp_info is None: - paths.append(os.path.join(TOOLCHAIN, ec.toolchain.name, ec.toolchain.version)) + elif modclass == MODULECLASS_TOOLCHAIN and tc_comp_info is None and ec.name.startswith('Cray'): + paths.append(os.path.join(TOOLCHAIN, ec.name, ec.version)) return paths diff --git a/test/framework/module_generator.py b/test/framework/module_generator.py index 81ce794218..439091a3a3 100644 --- a/test/framework/module_generator.py +++ b/test/framework/module_generator.py @@ -1273,6 +1273,11 @@ def test_ec(ecfile, short_modname, mod_subdir, modpath_exts, user_modpath_exts, ['MPI/intel-CUDA/%s-5.5.22/impi/5.1.2.150' % iccver], ['MPI/intel-CUDA/%s-5.5.22/impi/5.1.2.150' % iccver], ['Core']), + 'CrayCCE-5.1.29.eb': ('CrayCCE/5.1.29', 'Core', + ['Toolchain/CrayCCE/5.1.29'], + ['Toolchain/CrayCCE/5.1.29'], + ['Core']), + 'HPL-2.1-CrayCCE-5.1.29.eb': ('HPL/2.1', 'Toolchain/CrayCCE/5.1.29', [], [], ['Core']), } for ecfile, mns_vals in test_ecs.items(): test_ec(ecfile, *mns_vals) From 68db5c10d571c7d17fca0568b9a75308320ae171 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 2 May 2020 21:15:13 +0200 Subject: [PATCH 11/12] add missing HPL test easyconfig using CrayCCE as toolchain --- .../test_ecs/h/HPL/HPL-2.1-CrayCCE-5.1.29.eb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 test/framework/easyconfigs/test_ecs/h/HPL/HPL-2.1-CrayCCE-5.1.29.eb diff --git a/test/framework/easyconfigs/test_ecs/h/HPL/HPL-2.1-CrayCCE-5.1.29.eb b/test/framework/easyconfigs/test_ecs/h/HPL/HPL-2.1-CrayCCE-5.1.29.eb new file mode 100644 index 0000000000..607821faf2 --- /dev/null +++ b/test/framework/easyconfigs/test_ecs/h/HPL/HPL-2.1-CrayCCE-5.1.29.eb @@ -0,0 +1,14 @@ +easyblock = 'ConfigureMake' + +name = 'HPL' +version = '2.1' + +homepage = 'http://www.netlib.org/benchmark/hpl/' +description = "HPL, you know, LINPACK" + +toolchain = {'name': 'CrayCCE', 'version': '5.1.29'} + +source_urls = ['http://www.netlib.org/benchmark/%(namelower)s'] +sources = [SOURCELOWER_TAR_GZ] + +moduleclass = 'tools' From dff3c2e95ece4248aa6a7215575c8406b648dbd0 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sun, 3 May 2020 09:42:57 +0200 Subject: [PATCH 12/12] fix broken test_index_functions --- test/framework/filetools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/framework/filetools.py b/test/framework/filetools.py index 85b1e7eb9f..949739c808 100644 --- a/test/framework/filetools.py +++ b/test/framework/filetools.py @@ -1816,7 +1816,7 @@ def test_index_functions(self): # test with specified path with and without trailing '/'s for path in [test_ecs, test_ecs + '/', test_ecs + '//']: index = ft.create_index(path) - self.assertEqual(len(index), 81) + self.assertEqual(len(index), 82) expected = [ os.path.join('b', 'bzip2', 'bzip2-1.0.6-GCC-4.9.2.eb'),