Skip to content
This repository was archived by the owner on Aug 31, 2025. It is now read-only.

Commit 14b24a9

Browse files
authored
Mock out venvs (#1318)
Ensure we don't create real venvs as part of the testing Ensure that if we fail to create a valid venv after three times we raise an exception
1 parent 9aedadd commit 14b24a9

File tree

3 files changed

+47
-30
lines changed

3 files changed

+47
-30
lines changed

mu/virtual_environment.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,11 @@ def ensure_and_create(self):
385385
self.relocate(new_dirpath)
386386
self.create()
387387
else:
388-
break
388+
return
389+
390+
raise VirtualEnvironmentError(
391+
"Unable to create a working virtual environment"
392+
)
389393

390394
def ensure(self):
391395
"""Ensure that virtual environment exists and is in a good state"""

tests/test_app.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from mu.interface.themes import NIGHT_STYLE, DAY_STYLE, CONTRAST_STYLE
1919
from mu.logic import LOG_FILE, LOG_DIR, ENCODING
2020
from mu import mu_debug
21+
from mu.virtual_environment import VirtualEnvironment as VE
2122
from PyQt5.QtCore import Qt
2223

2324

@@ -148,7 +149,9 @@ class Win(mock.MagicMock):
148149
"sys.argv", ["mu"]
149150
), mock.patch(
150151
"sys.exit"
151-
) as ex:
152+
) as ex, mock.patch.object(
153+
VE, "ensure_and_create"
154+
) as mock_ensure_and_create:
152155
run()
153156
assert set_log.call_count == 1
154157
# foo.call_count is instantiating the class
@@ -165,6 +168,7 @@ class Win(mock.MagicMock):
165168
assert win.call_count == 1
166169
assert len(win.mock_calls) == 6
167170
assert ex.call_count == 1
171+
assert mock_ensure_and_create.call_count == 1
168172
window.load_theme.emit("day")
169173
qa.assert_has_calls([mock.call().setStyleSheet(DAY_STYLE)])
170174
window.load_theme.emit("night")
@@ -213,6 +217,8 @@ def start(self, t):
213217
"mu.app.QApplication"
214218
), mock.patch("sys.exit"), mock.patch("mu.app.Editor"), mock.patch(
215219
"mu.app.AnimatedSplash", return_value=splash
220+
), mock.patch.object(
221+
VE, "ensure_and_create"
216222
):
217223
run()
218224
assert splash.finish.call_count == 1

tests/virtual_environment/test_virtual_environment.py

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import mu.wheels
3232

3333
VE = mu.virtual_environment.VirtualEnvironment
34+
VEError = mu.virtual_environment.VirtualEnvironmentError
3435
PIP = mu.virtual_environment.Pip
3536

3637
HERE = os.path.dirname(__file__)
@@ -189,7 +190,7 @@ def test_download_wheels_if_not_present(venv, test_wheels):
189190
# Ignore the exception which will arise from not actually
190191
# downloading any wheels!
191192
#
192-
except mu.virtual_environment.VirtualEnvironmentError:
193+
except VEError:
193194
pass
194195

195196
assert mock_download.called
@@ -310,39 +311,51 @@ def test_venv_is_singleton():
310311
assert module.venv is venv
311312

312313

314+
def _ensure_venv(results):
315+
def _inner_ensure_venv(self, results=results):
316+
result = results.pop()
317+
if isinstance(result, Exception):
318+
raise result
319+
else:
320+
return result
321+
322+
return _inner_ensure_venv
323+
324+
313325
def test_venv_folder_created(venv):
314326
"""When not existing venv is ensured we create a new one"""
315327
os.rmdir(venv.path)
316328
with mock.patch.object(VE, "create") as mock_create, mock.patch.object(
317329
VE,
318330
"ensure",
319-
side_effect=mu.virtual_environment.VirtualEnvironmentError(),
331+
_ensure_venv([True, VEError()]),
320332
):
321333
venv.ensure_and_create()
322334

323335
assert mock_create.called
324336

325337

326-
def _ensure_venv():
327-
def _inner_ensure_venv(self, tries=[1, 2, 3]):
328-
print("_inner_ensure_venv called with", tries)
329-
n_try = tries.pop()
330-
if n_try > 1:
331-
raise mu.virtual_environment.VirtualEnvironmentError()
332-
else:
333-
return
334-
335-
return _inner_ensure_venv
336-
337-
338338
def test_venv_second_try(venv):
339339
"""If the creation of a venv fails to produce a valid venv, try again"""
340340
with mock.patch.object(VE, "create") as mock_create, mock.patch.object(
341-
VE, "ensure", _ensure_venv()
341+
VE,
342+
"ensure",
343+
_ensure_venv([True, VEError()]),
342344
):
343345
venv.ensure_and_create()
344346

345-
assert mock_create.call_count == 2
347+
assert mock_create.call_count == 1
348+
349+
350+
def test_venv_fails_after_three_tries(venv):
351+
"""If the venv fails to ensure after three tries we raise an exception"""
352+
with mock.patch.object(VE, "create"), mock.patch.object(
353+
VE,
354+
"ensure",
355+
_ensure_venv([VEError(), VEError(), VEError()]),
356+
):
357+
with pytest.raises(VEError):
358+
venv.ensure_and_create()
346359

347360

348361
#
@@ -363,15 +376,15 @@ def test_venv_folder_already_exists(venv):
363376
def test_venv_folder_does_not_exist(venv):
364377
"""When venv_folder does exist not at all we raise an error"""
365378
os.rmdir(venv.path)
366-
with pytest.raises(mu.virtual_environment.VirtualEnvironmentError):
379+
with pytest.raises(VEError):
367380
venv.ensure_path()
368381

369382

370383
def test_venv_folder_already_exists_not_venv(venv):
371384
"""When venv_folder does exist not as a venv ensure we raise an error"""
372385
assert not os.path.isfile(os.path.join(venv.path, "pyvenv.cfg"))
373386
assert not os.path.isfile(venv.interpreter)
374-
with pytest.raises(mu.virtual_environment.VirtualEnvironmentError):
387+
with pytest.raises(VEError):
375388
venv.ensure_path()
376389

377390

@@ -382,7 +395,7 @@ def test_venv_folder_already_exists_not_directory(venv_dirpath):
382395
os.rmdir(venv_dirpath)
383396
open(venv_dirpath, "w").close()
384397
venv = mu.virtual_environment.VirtualEnvironment(venv_dirpath)
385-
with pytest.raises(mu.virtual_environment.VirtualEnvironmentError):
398+
with pytest.raises(VEError):
386399
venv.ensure_path()
387400

388401

@@ -393,9 +406,7 @@ def test_ensure_interpreter(venv):
393406
"""When venv exists but has no interpreter ensure we raise an exception"""
394407
assert not os.path.isfile(venv.interpreter)
395408

396-
with pytest.raises(
397-
mu.virtual_environment.VirtualEnvironmentError, match="Interpreter"
398-
):
409+
with pytest.raises(VEError, match="[Ii]nterpreter"):
399410
venv.ensure_interpreter()
400411

401412

@@ -404,9 +415,7 @@ def test_ensure_interpreter_version(venv):
404415
mocked_process = mock.MagicMock()
405416
mocked_process.stdout = b"x.y"
406417
with mock.patch.object(subprocess, "run", return_value=mocked_process):
407-
with pytest.raises(
408-
mu.virtual_environment.VirtualEnvironmentError, match="interpreter"
409-
):
418+
with pytest.raises(VEError, match="[Ii]nterpreter"):
410419
venv.ensure_interpreter_version()
411420

412421

@@ -425,9 +434,7 @@ def test_ensure_pip(venv):
425434
"""When venv exists but has no interpreter ensure we raise an exception"""
426435
assert not os.path.isfile(venv.interpreter)
427436

428-
with pytest.raises(
429-
mu.virtual_environment.VirtualEnvironmentError, match="Pip"
430-
):
437+
with pytest.raises(VEError, match="Pip"):
431438
venv.ensure_pip()
432439

433440

0 commit comments

Comments
 (0)