1717from os import environ
1818
1919IS_CI = environ .get ("CI" , "false" ).lower () == "true"
20+ COVERAGE_DEFAULT = False
21+
22+
23+ def divider (text : str ):
24+ """Print a divider with text centered."""
25+ print (text .center (88 , "-" ))
2026
2127
2228def all_exist (files : List [str ] = None , directories : List [str ] = None ) -> bool :
@@ -189,6 +195,7 @@ def notebooks(
189195 k_pattern : Optional [str ] = None ,
190196 slow : bool = False ,
191197 no_skip : bool = False ,
198+ coverage : bool = COVERAGE_DEFAULT ,
192199):
193200 """Run the notebooks and check for errors.
194201
@@ -199,6 +206,7 @@ def notebooks(
199206 executed output.
200207 """
201208 assert not (not slow and overwrite ), "You cannot use `--overwrite` with `--fast`."
209+ env = {"COVERAGE_FILE" : ".coverage_notebooks" }
202210
203211 # Set the environment variable to run the notebooks in fast mode.
204212 if not slow :
@@ -220,59 +228,95 @@ def notebooks(
220228 "--durations=5" , # Show the duration of each notebook
221229 ]
222230 cmd += ["-n=auto" ] if parallel else [] # Should we run in parallel?
223- cmd += (
224- ["--overwrite" ] if overwrite else []
225- ) # Overwrite the notebooks with the executed output
231+ # Overwrite the notebooks with the executed output
232+ cmd += ["--overwrite" ] if overwrite else []
233+ cmd += ["--cov=capymoa" , "" ] if coverage else []
234+
226235 if len (skip_notebooks ) > 0 :
227236 cmd += ["--deselect " + nb for nb in skip_notebooks ] # Skip some notebooks
228237
229238 if k_pattern :
230239 cmd += [f"-k { k_pattern } " ]
231240
232- ctx .run (" " .join (cmd ), echo = True )
241+ ctx .run (" " .join (cmd ), echo = True , env = env )
233242
234243
235244@task
236- def pytest (ctx : Context , parallel : bool = True ):
245+ def pytest (ctx : Context , parallel : bool = True , coverage : bool = COVERAGE_DEFAULT ):
237246 """Run the tests using pytest."""
247+ env = {"COVERAGE_FILE" : ".coverage_pytest" }
248+
238249 cmd = [
239250 "python -m pytest" ,
240251 "--durations=5" , # Show the duration of each test
241252 "--exitfirst" , # Exit instantly on first error or failed test
242- # jpype can raise irrelevant warnings:
243- # https://github.com/jpype-project/jpype/issues/561
244- "-p no:faulthandler" ,
245253 ]
254+ if coverage :
255+ cmd .extend (
256+ [
257+ "--cov=capymoa" ,
258+ ]
259+ )
246260 cmd += ["-n=auto" ] if parallel else []
247- ctx .run (" " .join (cmd ), echo = True )
261+ ctx .run (" " .join (cmd ), echo = True , env = env )
248262
249263
250264@task
251- def doctest (ctx : Context , parallel : bool = True ):
265+ def doctest (ctx : Context , parallel : bool = True , coverage : bool = COVERAGE_DEFAULT ):
252266 """Run tests defined in docstrings using pytest."""
267+ env = {"COVERAGE_FILE" : ".coverage_doctest" }
253268 cmd = [
254269 "python -m pytest" ,
270+ "--cov=capymoa" if coverage else "" ,
255271 "--doctest-modules" , # Enable doctest tests
256272 "--durations=5" , # Show the duration of each test
257273 "--exitfirst" , # Exit instantly on first error or failed test
258- # jpype can raise irrelevant warnings:
259- # https://github.com/jpype-project/jpype/issues/561
260- "-p no:faulthandler" ,
261274 "src/capymoa" , # Don't run tests in the `tests` directory
262275 ]
263276 cmd += ["-n=auto" ] if parallel else []
277+ ctx .run (" " .join (cmd ), echo = True , env = env )
278+
279+
280+ @task (aliases = ["cov-combine" ])
281+ def coverage_combine (ctx : Context ):
282+ """Combine coverage data from different sources."""
283+ cmd = ["python -m coverage combine --keep" ]
284+ covfiles = [
285+ ".coverage_pytest" ,
286+ ".coverage_doctest" ,
287+ ".coverage_notebooks" ,
288+ ]
289+ for covfile in covfiles :
290+ if Path (covfile ).exists ():
291+ cmd += [covfile ]
264292 ctx .run (" " .join (cmd ), echo = True )
265293
266294
295+ @task (aliases = ["cov-report" ], pre = [coverage_combine ])
296+ def coverage_report (ctx : Context ):
297+ """Generate coverage report."""
298+ ctx .run ("python -m coverage html" , echo = True )
299+
300+
301+ @task (aliases = ["cov-clean" ])
302+ def coverage_clean (ctx : Context ):
303+ """Clean coverage data."""
304+ ctx .run ("python -m coverage erase" , echo = True )
305+ ctx .run ("rm -rf htmlcov" , echo = True )
306+
307+
267308@task
268- def all_tests (ctx : Context , parallel : bool = True ):
309+ def all_tests (ctx : Context , parallel : bool = True , coverage : bool = COVERAGE_DEFAULT ):
269310 """Run all the tests."""
270- print ( "Running all pytest tests ... " )
271- pytest (ctx , parallel )
272- print ( "Running all doctests ... " )
273- doctest (ctx , parallel )
274- print ( "Running all notebooks ... " )
311+ divider ( "test.pytest " )
312+ pytest (ctx , parallel , coverage )
313+ divider ( "test.doctest " )
314+ doctest (ctx , parallel , coverage )
315+ divider ( "test.notebooks " )
275316 notebooks (ctx , parallel )
317+ if coverage :
318+ divider ("test.cov-report" )
319+ coverage_combine (ctx )
276320
277321
278322@task
@@ -317,6 +361,9 @@ def format(ctx: Context):
317361test .add_task (notebooks , "nb" )
318362test .add_task (pytest , "pytest" )
319363test .add_task (doctest , "doctest" )
364+ test .add_task (coverage_combine )
365+ test .add_task (coverage_clean )
366+ test .add_task (coverage_report )
320367
321368ns = Collection ()
322369ns .add_collection (docs )
0 commit comments