Skip to content

Commit 9a92c0c

Browse files
committed
chore: update test workflow to run all tests in one job
fix: handle cases where format is None in BrewInfo refactor: change 'serial' marker to 'destructive' and update tests to use it feat: add destructive tests and related scripts feat: add BrewFile install error handling and before/after command execution feat: implement json info cache and helper methods
1 parent 98f2534 commit 9a92c0c

13 files changed

+1444
-1261
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -138,23 +138,13 @@ jobs:
138138
github_token: "${{github.token}}"
139139
pre-commit: "${{ env.IS_MAIN }}"
140140
tmate: "${{ env.DEBUG }}"
141-
test-serial:
141+
test-destructive:
142142
strategy:
143143
fail-fast: false
144144
matrix:
145145
python-version: ['3.9']
146146
test-path:
147-
- 'tests/test_serial.py::test_init'
148-
- 'tests/test_serial.py::test_install_clean[default]'
149-
- 'tests/test_serial.py::test_install_clean[on-request]'
150-
- 'tests/test_serial.py::test_install_clean[top-packages]'
151-
- 'tests/test_serial.py::test_clean_non_request'
152-
- 'tests/test_serial.py::test_vscode_cursor'
153-
- 'tests/test_serial.py::test_update'
154-
- 'tests/test_serial.py::test_dry_run'
155-
- 'tests/test_serial.py::test_brew_command'
156-
- 'tests/test_serial.py::test_format_options'
157-
- 'tests/test_serial.py::test_cask_args'
147+
- 'tests/test_destructive.py'
158148
runs-on: macos-latest
159149
steps:
160150
- name: Set pseudo git user
@@ -173,7 +163,7 @@ jobs:
173163
pytest-separate-benchmark: 0
174164
coverage: 0
175165
pytest-cov-path: ""
176-
pytest-opt: "-n0 -m serial"
166+
pytest-opt: "-n0 -m destructive"
177167
coverage-push: 0
178168
github_token: ""
179169
pre-commit: 0

bin/brew-file

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -953,8 +953,7 @@ class BrewInfo:
953953
output_prefix = ''
954954
output = ''
955955

956-
# commands for each format
957-
# if self.helper.opt["form"] in ["file", "none"]:
956+
# comd base
958957
cmd_before = 'before '
959958
cmd_after = 'after '
960959
cmd_cask_args = 'cask_args '
@@ -969,7 +968,9 @@ class BrewInfo:
969968
cmd_cursor = 'cursor '
970969
cmd_main = 'main '
971970
cmd_file = 'file '
972-
if self.helper.opt['form'] in ['brewdler', 'bundle']:
971+
if self.helper.opt['form'] in ['file', None]:
972+
pass
973+
elif self.helper.opt['form'] in ['brewdler', 'bundle']:
973974
cmd_before = '#before '
974975
cmd_after = '#after '
975976
cmd_cask_args = '#cask_args '
@@ -1011,6 +1012,9 @@ fi
10111012
cmd_cursor = 'cursor --install-extension '
10121013
cmd_main = '#main '
10131014
cmd_file = '#file '
1015+
else:
1016+
msg = f'Invalid format: "{self.helper.opt["form"]}".\nUse "file", "brewdler", "bundle", "command" or "cmd".'
1017+
raise ValueError(msg)
10141018

10151019
# sort
10161020
self.sort()

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ build-backend = "hatchling.build"
2525

2626
[tool.pytest.ini_options]
2727
markers = [
28-
"serial: tests only for serial run.",
28+
"destructive: tests only for destructive run.",
2929
]
30-
addopts = "-n auto -m 'not serial'"
30+
addopts = "-n auto -m 'not destructive'"
3131
testpaths = ["tests",]
3232

3333
[tool.ruff]

src/brew_file/brew_info.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,7 @@ def write(self) -> None:
400400
output_prefix = ''
401401
output = ''
402402

403-
# commands for each format
404-
# if self.helper.opt["form"] in ["file", "none"]:
403+
# comd base
405404
cmd_before = 'before '
406405
cmd_after = 'after '
407406
cmd_cask_args = 'cask_args '
@@ -416,7 +415,9 @@ def write(self) -> None:
416415
cmd_cursor = 'cursor '
417416
cmd_main = 'main '
418417
cmd_file = 'file '
419-
if self.helper.opt['form'] in ['brewdler', 'bundle']:
418+
if self.helper.opt['form'] in ['file', None]:
419+
pass
420+
elif self.helper.opt['form'] in ['brewdler', 'bundle']:
420421
cmd_before = '#before '
421422
cmd_after = '#after '
422423
cmd_cask_args = '#cask_args '
@@ -458,6 +459,9 @@ def write(self) -> None:
458459
cmd_cursor = 'cursor --install-extension '
459460
cmd_main = '#main '
460461
cmd_file = '#file '
462+
else:
463+
msg = f'Invalid format: "{self.helper.opt["form"]}".\nUse "file", "brewdler", "bundle", "command" or "cmd".'
464+
raise ValueError(msg)
461465

462466
# sort
463467
self.sort()

tests/conftest.py

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,62 @@
11
from __future__ import annotations
22

3+
import getpass
34
import os
5+
import warnings
46
from pathlib import Path
57
from typing import TYPE_CHECKING
68

79
import pytest
810
from filelock import FileLock
911

10-
from .brew_file import BrewFile, BrewHelper
12+
from .brew_file import BrewFile, BrewHelper, BrewInfo, is_mac
1113

1214
if TYPE_CHECKING:
1315
from _pytest.config import Config
1416
from _pytest.nodes import Item
1517

1618

17-
def _running_under_xdist(config: Config) -> bool:
19+
def _parallel_test(config: Config) -> bool:
1820
return getattr(config, 'workerinput', None) is not None
1921

2022

23+
@pytest.hookimpl(trylast=True) # To run after marker filtering
2124
def pytest_collection_modifyitems(config: Config, items: Item) -> None:
22-
if not _running_under_xdist(config):
23-
return
24-
2525
remaining = []
26-
deselected = []
26+
destructive = []
2727

2828
for item in items:
29-
if 'serial' in item.keywords:
30-
deselected.append(item)
29+
if 'destructive' in item.keywords:
30+
destructive.append(item)
3131
else:
3232
remaining.append(item)
3333

34-
if deselected:
35-
config.hook.pytest_deselected(items=deselected)
36-
items[:] = remaining
34+
if destructive:
35+
config.destructive_clean = True
36+
if _parallel_test(config):
37+
config.hook.pytest_deselected(items=destructive)
38+
items[:] = remaining
39+
warnings.warn(
40+
'Destructive tests are skipped in parallel mode.', stacklevel=2
41+
)
42+
elif getpass.getuser() not in ['lume', 'runner']:
43+
response = input(
44+
'\n\n\033[33mThere are destructive tests.\nTHIS MAY DELETE ALL OF YOUR HOMEBREW PACKAGES.\nDo you actually want to run them on current environment?\033[0m [y/N]: '
45+
)
46+
if response.lower() != 'y':
47+
config.hook.pytest_deselected(items=destructive)
48+
items[:] = remaining
49+
warnings.warn('Destructive tests are skipped.', stacklevel=2)
50+
else:
51+
response = input(
52+
'Do you want to clean up the environment before each destructive test? [y/N]: '
53+
)
54+
if response.lower() != 'y':
55+
config.destructive_clean = False
56+
warnings.warn(
57+
'Do not run cleanup before destructive tests. Some tests may fail.',
58+
stacklevel=2,
59+
)
3760

3861

3962
@pytest.fixture(scope='session', autouse=True)
@@ -43,7 +66,7 @@ def check_brew(tmp_path_factory: pytest.TempPathFactory) -> None:
4366
with FileLock(root_tmp_dir / 'brew.lock'):
4467
bf = BrewFile({})
4568
if not (Path(bf.opt['cache']) / 'api/formula.jws.json').exists():
46-
# pseudo code to download api json
69+
# run search to download api json
4770
helper.proc('brew search python')
4871
for env in os.environ:
4972
if env.startswith('HOMEBREW_BREWFILE'):
@@ -52,17 +75,6 @@ def check_brew(tmp_path_factory: pytest.TempPathFactory) -> None:
5275
os.environ['HOMEBREW_AUTO_UPDATE_SECS'] = '100000'
5376

5477

55-
@pytest.fixture(scope='session', autouse=False)
56-
def tap(tmp_path_factory: pytest.TempPathFactory) -> list[str]:
57-
taps = ['rcmdnk/rcmdnkpac', 'rcmdnk/rcmdnkcask']
58-
root_tmp_dir = tmp_path_factory.getbasetemp().parent
59-
with FileLock(root_tmp_dir / 'tap.lock'):
60-
helper = BrewHelper()
61-
helper.proc(f'brew tap {taps[0]}')
62-
helper.proc(f'brew tap {taps[1]}')
63-
return taps
64-
65-
6678
@pytest.fixture(scope='session', autouse=False)
6779
def python(tmp_path_factory: pytest.TempPathFactory) -> str:
6880
python_formula = '[email protected]'
@@ -81,4 +93,13 @@ def bf_cmd() -> str:
8193

8294
@pytest.fixture
8395
def helper() -> BrewHelper:
84-
return BrewHelper()
96+
# BrewHelper with default options
97+
return BrewFile({}).helper
98+
99+
100+
@pytest.fixture
101+
def brew_info() -> BrewInfo:
102+
file = 'BrewfileTest' if is_mac() else 'BrewfileTestLinux'
103+
# BrewInfo with default options and input file
104+
bf = BrewFile({'input': Path(__file__).parent / 'files' / file})
105+
return bf.brewinfo

tests/scripts/destructive_test.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
if pytest --help | grep -q -- '-n numprocesses';then
3+
pytest -s -vvv -n0 -p no:cacheprovider -o "markers=destructive" -c /dev/null tests/test_destructive.py
4+
else
5+
pytest -s -vvv -p no:cacheprovider -o "markers=destructive" -c /dev/null tests/test_destructive.py
6+
fi

tests/scripts/serial_test.sh

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)