Skip to content

Commit b0c9c7f

Browse files
author
LittleCoinCoin
committed
test(env): enhance environment cleanup to prevent debris accumulation
- Add comprehensive environment tracking in setUp methods for all test classes - Enhance tearDown methods to clean up conda/mamba environments, not just temp directories - Improve tearDownClass with comprehensive cleanup patterns for known environment names - Add defensive cleanup for interrupted tests with proper exception handling - Track environments created during tests to ensure proper cleanup Prevents accumulation of 23+ test environments requiring manual cleanup. Resolves systematic test cleanup problems identified in remove_hatch_envs.ps1. Ensures test environments are properly removed even if tests fail or are interrupted.
1 parent 04838bc commit b0c9c7f

File tree

1 file changed

+86
-9
lines changed

1 file changed

+86
-9
lines changed

tests/test_python_environment_manager.py

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,32 @@ def setUp(self):
2222
self.temp_dir = tempfile.mkdtemp()
2323
self.environments_dir = Path(self.temp_dir) / "envs"
2424
self.environments_dir.mkdir(exist_ok=True)
25-
25+
2626
# Create manager instance for testing
2727
self.manager = PythonEnvironmentManager(environments_dir=self.environments_dir)
2828

29+
# Track environments created during this test for cleanup
30+
self.created_environments = []
31+
2932
def tearDown(self):
3033
"""Clean up test environment."""
34+
# Clean up any conda/mamba environments created during this test
35+
if hasattr(self, 'manager') and self.manager.is_available():
36+
for env_name in self.created_environments:
37+
try:
38+
if self.manager.environment_exists(env_name):
39+
self.manager.remove_python_environment(env_name)
40+
except Exception:
41+
pass # Best effort cleanup
42+
43+
# Clean up temporary directory
3144
shutil.rmtree(self.temp_dir, ignore_errors=True)
3245

46+
def _track_environment(self, env_name):
47+
"""Track an environment for cleanup in tearDown."""
48+
if env_name not in self.created_environments:
49+
self.created_environments.append(env_name)
50+
3351
@regression_test
3452
@patch('hatch.python_environment_manager.PythonEnvironmentManager._conda_env_exists', return_value=True)
3553
@patch('hatch.python_environment_manager.PythonEnvironmentManager._get_conda_env_name', return_value='hatch_test_env')
@@ -349,26 +367,61 @@ def setUpClass(cls):
349367
cls.temp_dir = tempfile.mkdtemp()
350368
cls.environments_dir = Path(cls.temp_dir) / "envs"
351369
cls.environments_dir.mkdir(exist_ok=True)
352-
370+
353371
# Create manager instance for integration testing
354372
cls.manager = PythonEnvironmentManager(environments_dir=cls.environments_dir)
355373

374+
# Track all environments created during integration tests
375+
cls.all_created_environments = set()
376+
356377
# Skip all tests if conda/mamba is not available
357378
if not cls.manager.is_available():
358379
raise unittest.SkipTest("Conda/mamba not available for integration tests")
359380

381+
def setUp(self):
382+
"""Set up individual test."""
383+
# Track environments created during this specific test
384+
self.test_environments = []
385+
386+
def tearDown(self):
387+
"""Clean up individual test."""
388+
# Clean up environments created during this specific test
389+
for env_name in self.test_environments:
390+
try:
391+
if self.manager.environment_exists(env_name):
392+
self.manager.remove_python_environment(env_name)
393+
self.all_created_environments.discard(env_name)
394+
except Exception:
395+
pass # Best effort cleanup
396+
397+
def _track_environment(self, env_name):
398+
"""Track an environment for cleanup."""
399+
if env_name not in self.test_environments:
400+
self.test_environments.append(env_name)
401+
self.all_created_environments.add(env_name)
402+
360403
@classmethod
361404
def tearDownClass(cls):
362405
"""Clean up class-level test environment."""
363-
# Clean up any test environments that might have been created
406+
# Clean up any remaining test environments
364407
try:
365-
test_envs = ["test_integration_env", "test_python_311", "test_python_312", "test_diagnostics_env"]
366-
for env_name in test_envs:
408+
# Clean up tracked environments
409+
for env_name in list(cls.all_created_environments):
410+
if cls.manager.environment_exists(env_name):
411+
cls.manager.remove_python_environment(env_name)
412+
413+
# Clean up known test environment patterns (fallback)
414+
known_patterns = [
415+
"test_integration_env", "test_python_311", "test_python_312", "test_diagnostics_env",
416+
"test_env_1", "test_env_2", "test_env_3", "test_env_4", "test_env_5",
417+
"test_python_39", "test_python_310", "test_python_312", "test_cache_env1", "test_cache_env2"
418+
]
419+
for env_name in known_patterns:
367420
if cls.manager.environment_exists(env_name):
368421
cls.manager.remove_python_environment(env_name)
369422
except Exception:
370423
pass # Best effort cleanup
371-
424+
372425
shutil.rmtree(cls.temp_dir, ignore_errors=True)
373426

374427
@integration_test(scope="system")
@@ -418,11 +471,12 @@ def test_manager_diagnostics_real(self):
418471
def test_create_and_remove_python_environment_real(self):
419472
"""Test real Python environment creation and removal."""
420473
env_name = "test_integration_env"
421-
474+
self._track_environment(env_name)
475+
422476
# Ensure environment doesn't exist initially
423477
if self.manager.environment_exists(env_name):
424478
self.manager.remove_python_environment(env_name)
425-
479+
426480
# Create environment
427481
result = self.manager.create_python_environment(env_name)
428482
self.assertTrue(result, "Failed to create Python environment")
@@ -454,6 +508,7 @@ def test_create_and_remove_python_environment_real(self):
454508
def test_create_python_environment_with_version_real(self):
455509
"""Test real Python environment creation with specific version."""
456510
env_name = "test_python_311"
511+
self._track_environment(env_name)
457512
python_version = "3.11"
458513

459514
# Ensure environment doesn't exist initially
@@ -553,6 +608,10 @@ def test_list_environments_real(self):
553608
test_envs = ["test_env_1", "test_env_2"]
554609
final_names = ["hatch_test_env_1", "hatch_test_env_2"]
555610

611+
# Track environments for cleanup
612+
for env_name in test_envs:
613+
self._track_environment(env_name)
614+
556615
# Clean up any existing test environments
557616
for env_name in test_envs:
558617
if self.manager.environment_exists(env_name):
@@ -645,14 +704,32 @@ def setUp(self):
645704
self.temp_dir = tempfile.mkdtemp()
646705
self.environments_dir = Path(self.temp_dir) / "envs"
647706
self.environments_dir.mkdir(exist_ok=True)
648-
707+
649708
# Create manager instance for testing
650709
self.manager = PythonEnvironmentManager(environments_dir=self.environments_dir)
651710

711+
# Track environments created during this test for cleanup
712+
self.created_environments = []
713+
652714
def tearDown(self):
653715
"""Clean up test environment."""
716+
# Clean up any conda/mamba environments created during this test
717+
if hasattr(self, 'manager') and self.manager.is_available():
718+
for env_name in self.created_environments:
719+
try:
720+
if self.manager.environment_exists(env_name):
721+
self.manager.remove_python_environment(env_name)
722+
except Exception:
723+
pass # Best effort cleanup
724+
725+
# Clean up temporary directory
654726
shutil.rmtree(self.temp_dir, ignore_errors=True)
655727

728+
def _track_environment(self, env_name):
729+
"""Track an environment for cleanup in tearDown."""
730+
if env_name not in self.created_environments:
731+
self.created_environments.append(env_name)
732+
656733
@regression_test
657734
@patch('subprocess.run')
658735
def test_launch_shell_with_command(self, mock_run):

0 commit comments

Comments
 (0)