@@ -293,8 +293,65 @@ def test_run_script_argv_is_str(tmp_path):
293293
294294
295295@pytest .mark .skipif (sys .platform == 'win32' , reason = 'Fails due to weird PermissionError' )
296- def test_wrap_alembic (tmp_path , monkeypatch ):
297- """Test that Alembic migrations are covered when using wrap_alembic."""
296+ def test_wrap_spec_from_file_location (tmp_path , monkeypatch ):
297+ """Test that files loaded via spec_from_file_location are covered."""
298+ import json
299+
300+ # Create a Python file to be loaded dynamically
301+ dynamic_module = tmp_path / "dynamic_module.py"
302+ dynamic_module .write_text ('''
303+ x = 1 # line 2
304+ y = 2 # line 3
305+ z = x + y # line 4
306+ ''' )
307+
308+ # Create a script that loads the module via spec_from_file_location
309+ script = tmp_path / "main_script.py"
310+ script .write_text (f"""
311+ import importlib.util
312+ spec = importlib.util.spec_from_file_location("dynamic_module", "{ dynamic_module } ")
313+ module = importlib.util.module_from_spec(spec)
314+ spec.loader.exec_module(module)
315+ assert module.z == 3
316+ """ )
317+
318+ monkeypatch .chdir (tmp_path )
319+
320+ out = tmp_path / "coverage.json"
321+ result = subprocess .run (
322+ [sys .executable , "-m" , "slipcover" , "--json" , "--out" , str (out ),
323+ "--source" , str (tmp_path ), str (script )],
324+ capture_output = True ,
325+ text = True
326+ )
327+
328+ assert result .returncode == 0 , f"stdout: { result .stdout } \n stderr: { result .stderr } "
329+
330+ with out .open () as f :
331+ cov = json .load (f )
332+
333+ # Check that the dynamically loaded file was covered
334+ dynamic_files = [k for k in cov ['files' ].keys () if 'dynamic_module.py' in k ]
335+ assert dynamic_files , f"Dynamic module not in coverage: { list (cov ['files' ].keys ())} "
336+
337+ # Check that lines were executed
338+ file_cov = cov ['files' ][dynamic_files [0 ]]
339+ executed_lines = file_cov ['executed_lines' ]
340+ assert 2 in executed_lines and 3 in executed_lines and 4 in executed_lines , \
341+ f"Lines not executed, got: { executed_lines } "
342+
343+
344+ try :
345+ import alembic
346+ HAS_ALEMBIC = True
347+ except ImportError :
348+ HAS_ALEMBIC = False
349+
350+
351+ @pytest .mark .skipif (sys .platform == 'win32' , reason = 'Fails due to weird PermissionError' )
352+ @pytest .mark .skipif (not HAS_ALEMBIC , reason = 'Alembic not installed' )
353+ def test_wrap_spec_from_file_location_alembic (tmp_path , monkeypatch ):
354+ """Test that Alembic migrations are covered (integration test for spec_from_file_location)."""
298355 import json
299356
300357 # Create a minimal alembic setup
@@ -394,8 +451,59 @@ def downgrade():
394451
395452
396453@pytest .mark .skipif (sys .platform == 'win32' , reason = 'Fails due to weird PermissionError' )
397- def test_wrap_alembic_with_branch (tmp_path , monkeypatch ):
398- """Test that Alembic migrations are covered with branch coverage enabled."""
454+ def test_wrap_spec_from_file_location_with_branch (tmp_path , monkeypatch ):
455+ """Test that files loaded via spec_from_file_location get branch coverage."""
456+ import json
457+
458+ # Create a Python file with a branch to be loaded dynamically
459+ dynamic_module = tmp_path / "dynamic_module.py"
460+ dynamic_module .write_text ('''
461+ x = 1
462+ if x > 0: # branch
463+ y = 2
464+ else:
465+ y = 3
466+ z = y
467+ ''' )
468+
469+ # Create a script that loads the module via spec_from_file_location
470+ script = tmp_path / "main_script.py"
471+ script .write_text (f"""
472+ import importlib.util
473+ spec = importlib.util.spec_from_file_location("dynamic_module", "{ dynamic_module } ")
474+ module = importlib.util.module_from_spec(spec)
475+ spec.loader.exec_module(module)
476+ assert module.z == 2
477+ """ )
478+
479+ monkeypatch .chdir (tmp_path )
480+
481+ out = tmp_path / "coverage.json"
482+ result = subprocess .run (
483+ [sys .executable , "-m" , "slipcover" , "--branch" , "--json" , "--out" , str (out ),
484+ "--source" , str (tmp_path ), str (script )],
485+ capture_output = True ,
486+ text = True
487+ )
488+
489+ assert result .returncode == 0 , f"stdout: { result .stdout } \n stderr: { result .stderr } "
490+
491+ with out .open () as f :
492+ cov = json .load (f )
493+
494+ # Check that the dynamically loaded file was covered
495+ dynamic_files = [k for k in cov ['files' ].keys () if 'dynamic_module.py' in k ]
496+ assert dynamic_files , f"Dynamic module not in coverage: { list (cov ['files' ].keys ())} "
497+
498+ # Check that branch info is present
499+ file_cov = cov ['files' ][dynamic_files [0 ]]
500+ assert 'executed_branches' in file_cov , "Branch coverage not recorded"
501+
502+
503+ @pytest .mark .skipif (sys .platform == 'win32' , reason = 'Fails due to weird PermissionError' )
504+ @pytest .mark .skipif (not HAS_ALEMBIC , reason = 'Alembic not installed' )
505+ def test_wrap_spec_from_file_location_with_branch_alembic (tmp_path , monkeypatch ):
506+ """Test that Alembic migrations get branch coverage (integration test)."""
399507 import json
400508
401509 # Create a minimal alembic setup
0 commit comments