Skip to content

Commit dfa7e18

Browse files
Merge pull request #44 from buildtesters/moduleloadtest_stats
add support for retrieving stats from ModuleLoadTest
2 parents eff31c3 + 3a1d950 commit dfa7e18

File tree

5 files changed

+104
-49
lines changed

5 files changed

+104
-49
lines changed

.coveragerc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ exclude_lines =
1212

1313
# Don't complain about missing debug-only code:
1414
def __repr__
15-
if self\.debug
1615

1716
# Don't complain if tests don't hit defensive assertion code:
1817
raise AssertionError

docs/moduleloadtest.rst

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,70 +96,77 @@ Next we will show how we can filter modules while testing. Currently, we can fil
9696
and exclude modules by full canonical module name. This can be useful for site-administrators to tweak how behavior
9797
of ``ModuleLoadTest`` to their liking.
9898

99-
For example, some sites may have some modules like ``VASP``, ``Matlab``, ``Gaussian`` that can only be loaded
100-
by a specific unix group because site-administrator want to restrict this software to be loaded by anyone and end
101-
up running the software which may take up a license seat.
102-
103-
To filter by module names you can pass ``name`` option which is a list of software names to test.
99+
To filter by module names you can pass ``name`` option which is a list of software names to test. In example, below we will test for all
100+
modules `gcc` and `autoconf`.
104101

105102
.. code-block:: python
106103
107-
>>> g = ModuleLoadTest("/mxg-hpc/users/ssi29/easybuild/modules/all",name=["Automake","Bison"])
108-
Testing the Following Module Trees: /mxg-hpc/users/ssi29/easybuild/modules/all
104+
>>> ModuleLoadTest(name=["gcc","autoconf"],login=True)
105+
Testing the Following Module Trees: /Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core:/usr/local/Cellar/lmod/8.6.14/modulefiles/Darwin:/usr/local/Cellar/lmod/8.6.14/modulefiles/Core
109106
________________________________________________________________________________
110-
PASSED - Module Name: Automake/1.16.1-GCCcore-8.3.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Automake/1.16.1-GCCcore-8.3.0.lua )
111-
PASSED - Module Name: Automake/1.15.1-GCCcore-6.4.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Automake/1.15.1-GCCcore-6.4.0.lua )
112-
PASSED - Module Name: Bison/3.0.5 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.0.5.lua )
113-
PASSED - Module Name: Bison/3.0.4-GCCcore-7.1.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.0.4-GCCcore-7.1.0.lua )
114-
PASSED - Module Name: Bison/3.0.4 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.0.4.lua )
115-
PASSED - Module Name: Bison/3.3.2 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.3.2.lua )
116-
PASSED - Module Name: Bison/3.2.2-GCCcore-7.4.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.2.2-GCCcore-7.4.0.lua )
117-
PASSED - Module Name: Bison/3.0.4-GCCcore-6.4.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.0.4-GCCcore-6.4.0.lua )
118-
PASSED - Module Name: Bison/3.0.4-GCCcore-8.1.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.0.4-GCCcore-8.1.0.lua )
119-
PASSED - Module Name: Bison/3.0.5-GCCcore-6.4.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.0.5-GCCcore-6.4.0.lua )
120-
PASSED - Module Name: Bison/3.3.2-GCCcore-8.3.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.3.2-GCCcore-8.3.0.lua )
121-
PASSED - Module Name: Bison/3.0.5-GCCcore-8.1.0 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/Bison/3.0.5-GCCcore-8.1.0.lua )
122-
123-
124-
Note, when you use ``name`` it will test all modules with the name ``Automake`` and ``Bison`` found in all module trees.
107+
PASSED - Module Name: autoconf/2.69-3yrvwbu ( modulefile=/Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core/autoconf/2.69-3yrvwbu.lua )
108+
PASSED - Module Name: gcc/9.3.0-n7p74fd ( modulefile=/Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core/gcc/9.3.0-n7p74fd.lua )
109+
PASSED - Module Name: gcc/10.2.0-37fmsw7 ( modulefile=/Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core/gcc/10.2.0-37fmsw7.lua )
110+
111+
112+
Note, when you use ``name`` it will test all modules that match the name, found in all module trees.
125113
If you would like to filter and include by a full canonical name you can specify the ``include`` option. Shown below
126-
we will only test module ``CUDA/10.0.130``.
114+
we will test module ``gcc/9.3.0-n7p74fd``.
127115

128116
.. code-block:: python
129117
130-
>>> h = ModuleLoadTest("/mxg-hpc/users/ssi29/easybuild/modules/all",include=["CUDA/10.0.130"])
131-
Testing the Following Module Trees: /mxg-hpc/users/ssi29/easybuild/modules/all
118+
>>> ModuleLoadTest(include=['gcc/9.3.0-n7p74fd'],login=True)
119+
Testing the Following Module Trees: /Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core:/usr/local/Cellar/lmod/8.6.14/modulefiles/Darwin:/usr/local/Cellar/lmod/8.6.14/modulefiles/Core
132120
________________________________________________________________________________
133-
PASSED - Module Name: CUDA/10.0.130 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/CUDA/10.0.130.lua )
121+
PASSED - Module Name: gcc/9.3.0-n7p74fd ( modulefile=/Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core/gcc/9.3.0-n7p74fd.lua )
134122
135123
Likewise, we can exclude module by full canonical name using the ``exclude`` argument which is a list of module names. In
136124
example below we test the module tree ``"/usr/share/lmod/lmod/modulefiles/Core"`` which comes with ``lmod`` and ``settarg``
137125
typically found when installing Lmod. In the second example we exclude ``lmod`` from the module test.
138126

139127
.. code-block:: python
140128
141-
>>> a = ModuleLoadTest("/usr/share/lmod/lmod/modulefiles/Core")
129+
>>> ModuleLoadTest("/usr/share/lmod/lmod/modulefiles/Core")
142130
Testing the Following Module Trees: /usr/share/lmod/lmod/modulefiles/Core
143131
________________________________________________________________________________
144132
PASSED - Module Name: lmod ( modulefile=/usr/share/lmod/lmod/modulefiles/Core/lmod.lua )
145133
PASSED - Module Name: settarg ( modulefile=/usr/share/lmod/lmod/modulefiles/Core/settarg.lua )
146134
147-
>>> b = ModuleLoadTest("/usr/share/lmod/lmod/modulefiles/Core",exclude=["lmod"])
135+
>>> ModuleLoadTest("/usr/share/lmod/lmod/modulefiles/Core",exclude=["lmod"])
148136
Testing the Following Module Trees: /usr/share/lmod/lmod/modulefiles/Core
149137
________________________________________________________________________________
150138
PASSED - Module Name: settarg ( modulefile=/usr/share/lmod/lmod/modulefiles/Core/settarg.lua )
151139
152140
153-
If you pass ``include`` and ``exclude`` to *ModuleLoadTest*, then *include* will take precedence and *exclude*
154-
list will be ignored. Since these are mutually exclusive options use either arguments but don't use both at same time.
141+
If you pass ``include`` and ``exclude`` to *ModuleLoadTest*, we perform the include operation first by filtering files
142+
followed by excluding files from list. Therefore, if you run the following example, where we include ``lmod`` and ``settarg``
143+
but also exclude ``lmod``, we will see that ``lmod`` is not tested.
155144

156145
.. code-block:: python
157146
158-
>>> a = ModuleLoadTest(include=["CUDA/10.0.130","Bison/3.0.4"],exclude=["lmod"])
159-
Testing the Following Module Trees: /mxg-hpc/users/ssi29/easybuild-HMNS/modules/all/Core:/mxg-hpc/users/ssi29/spack/modules/linux-rhel7-x86_64/Core:/mxg-hpc/users/ssi29/easybuild/modules/all:/etc/modulefiles:/usr/share/modulefiles:/usr/share/modulefiles/Linux:/usr/share/modulefiles/Core:/usr/share/lmod/lmod/modulefiles/Core
147+
>>> ModuleLoadTest(name=["lmod","settarg"],exclude=['lmod'], login=True)
148+
Testing the Following Module Trees: /Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core:/usr/local/Cellar/lmod/8.6.14/modulefiles/Darwin:/usr/local/Cellar/lmod/8.6.14/modulefiles/Core
149+
________________________________________________________________________________
150+
PASSED - Module Name: settarg ( modulefile=/usr/local/Cellar/lmod/8.6.14/modulefiles/Core/settarg.lua )
151+
152+
Get Test Results
153+
-----------------
154+
155+
The **get_results** method returns a dictionary of results which can be useful if you want to know number of pass and failed test.
156+
In this next example, we will invoke the class and call ``get_results`` method which returns the test results.
157+
158+
.. code-block:: python
159+
160+
>>> test = ModuleLoadTest(name=["lmod","settarg"], login=True)
161+
Testing the Following Module Trees: /Users/siddiq90/projects/spack/share/spack/lmod/darwin-catalina-x86_64/Core:/usr/local/Cellar/lmod/8.6.14/modulefiles/Darwin:/usr/local/Cellar/lmod/8.6.14/modulefiles/Core
160162
________________________________________________________________________________
161-
PASSED - Module Name: Bison/3.0.4 ( modulefile=/mxg-hpc/users/ssi29/easybuild-HMNS/modules/all/Core/Bison/3.0.4.lua )
162-
PASSED - Module Name: CUDA/10.0.130 ( modulefile=/mxg-hpc/users/ssi29/easybuild/modules/all/CUDA/10.0.130.lua )
163+
PASSED - Module Name: lmod ( modulefile=/usr/local/Cellar/lmod/8.6.14/modulefiles/Core/lmod.lua )
164+
PASSED - Module Name: settarg ( modulefile=/usr/local/Cellar/lmod/8.6.14/modulefiles/Core/settarg.lua )
165+
>>> test.get_results()
166+
{'passed': 2, 'failed': 0, 'total': 2, 'rate': 1.0}
167+
168+
You can use this method to help write your test, for instance you may want to write a test that checks if 'rate' is 1.0 which indicates
169+
**100%** pass, or you can set a threshold such as 0.9 which indicates 90% pass.
163170

164171
Test Modules in Login Shell
165172
----------------------------
@@ -188,3 +195,20 @@ actual command using login shell.
188195
[DEBUG] Return Code: 0
189196
PASSED - Module Name: settarg ( modulefile=/usr/share/lmod/lmod/modulefiles/Core/settarg.lua )
190197
198+
199+
Edge Cases
200+
-----------
201+
202+
If you specify ``count=0``, which controls number of modules to test, you will get an error during testing with the following message
203+
204+
.. code-block:: python
205+
206+
>>> ModuleLoadTest(count=0)
207+
Please specify a number greater than 0 in order to test modules
208+
209+
Alternatively, if you specify a search criteria or MODULEPATH to tree where no modules are detected, you will get an error message
210+
211+
.. code-block:: python
212+
213+
>>> ModuleLoadTest(include=['foo'])
214+
Unable to test any modules either no modules were detected or search criteria was too restrictive

lmod/moduleloadtest.py

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import sys
23
from lmod.module import Module
34
from lmod.spider import Spider
45

@@ -66,7 +67,13 @@ def __init__(
6667
self.name = name
6768
self.include = include
6869
self.exclude = exclude
69-
filter_modules = None
70+
71+
self.failed = 0
72+
self.passed = 0
73+
self.modulecount = 0
74+
75+
if self.count < 1:
76+
sys.exit("Please specify a number greater than 0 in order to test modules")
7077

7178
spider_cmd = Spider(self.tree)
7279
module_dict, modules = (
@@ -75,15 +82,18 @@ def __init__(
7582
)
7683

7784
if self.include:
78-
filter_modules = set(self.include).intersection(modules)
85+
modules = [module for module in modules if module in self.include]
7986

8087
# only do exclusion if include list is not specified
81-
if self.exclude and not self.include:
82-
filter_modules = set(modules).difference(self.exclude)
83-
84-
modules = filter_modules or modules
85-
86-
modulecount = 0
88+
if self.exclude:
89+
for name in self.exclude:
90+
if name in modules:
91+
modules.remove(name)
92+
93+
if not modules:
94+
sys.exit(
95+
"Unable to test any modules either no modules were detected or search criteria was too restrictive"
96+
)
8797

8898
print(f"Testing the Following Module Trees: {self.tree}")
8999
print("{:_<80}".format(""))
@@ -108,13 +118,30 @@ def __init__(
108118
print(
109119
f"PASSED - Module Name: {module_name} ( modulefile={modulefile} )"
110120
)
121+
self.passed += 1
111122
else:
112123
print(
113124
f"FAILED - Module Name: {module_name} ( modulefile={modulefile} )"
114125
)
126+
self.failed += 1
115127

116-
modulecount += 1
128+
self.modulecount += 1
117129

118130
# terminate module load test once we have tested up to specified count
119-
if self.count <= modulecount:
131+
if self.count <= self.modulecount:
120132
return
133+
134+
def get_results(self):
135+
"""This method returns a dictionary of test results
136+
137+
:return: dictionary of test results
138+
:rtype: dict
139+
"""
140+
return {
141+
"passed": self.passed,
142+
"failed": self.failed,
143+
"total": self.modulecount,
144+
"rate": self.passed / self.modulecount
145+
if self.modulecount
146+
else float("nan"),
147+
}

tests/test_moduleloadtest.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import pytest
23
from lmod.moduleloadtest import ModuleLoadTest
34

45

@@ -16,13 +17,17 @@ def test_filter_exclude(self):
1617
ModuleLoadTest(exclude=["lmod"])
1718

1819
def test_filter_include_exclude(self):
19-
ModuleLoadTest(include=["lmod"], exclude=["lmod"])
20+
with pytest.raises(SystemExit):
21+
ModuleLoadTest(include=["lmod"], exclude=["lmod"])
2022

2123
def test_filter_name(self):
2224
ModuleLoadTest(name=["lmod", "settarg"])
2325

2426
def test_by_count(self):
25-
ModuleLoadTest(count=1)
27+
m = ModuleLoadTest(count=1)
28+
m.get_results()
29+
with pytest.raises(SystemExit):
30+
ModuleLoadTest(count=0)
2631

2732
def test_debug(self):
2833
ModuleLoadTest(debug=True)

tests/test_modules.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_version(self):
4040
a.version()
4141

4242
def test_avail(self):
43-
a = Module()
43+
a = Module(debug=True)
4444
a.avail()
4545
a.avail("lmod")
4646

@@ -125,7 +125,7 @@ def test_spider(self):
125125
a.spider(1)
126126

127127
def test_checkSyntax(self):
128-
a = Module("lmod")
128+
a = Module("lmod", debug=True)
129129
assert 0 == a.checkSyntax()
130130

131131
with pytest.raises(TypeError):

0 commit comments

Comments
 (0)