|
15 | 15 | """This module includes various helpers that provide fixtures, capture |
16 | 16 | information or mock the environment. |
17 | 17 |
|
18 | | -- The `control_stdin` and `capture_stdout` context managers allow one to |
19 | | - interact with the user interface. |
20 | | -
|
21 | 18 | - `has_program` checks the presence of a command on the system. |
22 | 19 |
|
23 | 20 | - The `ImportSessionFixture` allows one to run importer code while |
|
38 | 35 | from dataclasses import dataclass |
39 | 36 | from enum import Enum |
40 | 37 | from functools import cached_property |
41 | | -from io import StringIO |
42 | 38 | from pathlib import Path |
43 | 39 | from tempfile import gettempdir, mkdtemp, mkstemp |
44 | 40 | from typing import Any, ClassVar |
45 | 41 | from unittest.mock import patch |
46 | 42 |
|
| 43 | +import pytest |
47 | 44 | import responses |
48 | 45 | from mediafile import Image, MediaFile |
49 | 46 |
|
@@ -83,41 +80,6 @@ def capture_log(logger="beets"): |
83 | 80 | log.removeHandler(capture) |
84 | 81 |
|
85 | 82 |
|
86 | | -@contextmanager |
87 | | -def control_stdin(input=None): |
88 | | - """Sends ``input`` to stdin. |
89 | | -
|
90 | | - >>> with control_stdin('yes'): |
91 | | - ... input() |
92 | | - 'yes' |
93 | | - """ |
94 | | - org = sys.stdin |
95 | | - sys.stdin = StringIO(input) |
96 | | - try: |
97 | | - yield sys.stdin |
98 | | - finally: |
99 | | - sys.stdin = org |
100 | | - |
101 | | - |
102 | | -@contextmanager |
103 | | -def capture_stdout(): |
104 | | - """Save stdout in a StringIO. |
105 | | -
|
106 | | - >>> with capture_stdout() as output: |
107 | | - ... print('spam') |
108 | | - ... |
109 | | - >>> output.getvalue() |
110 | | - 'spam' |
111 | | - """ |
112 | | - org = sys.stdout |
113 | | - sys.stdout = capture = StringIO() |
114 | | - try: |
115 | | - yield sys.stdout |
116 | | - finally: |
117 | | - sys.stdout = org |
118 | | - print(capture.getvalue()) |
119 | | - |
120 | | - |
121 | 83 | def has_program(cmd, args=["--version"]): |
122 | 84 | """Returns `True` if `cmd` can be executed.""" |
123 | 85 | full_cmd = [cmd, *args] |
@@ -163,21 +125,31 @@ def config(self) -> beets.IncludeLazyConfig: |
163 | 125 | ) |
164 | 126 |
|
165 | 127 |
|
166 | | -class IOMixin: |
167 | | - @cached_property |
168 | | - def io(self) -> _common.DummyIO: |
169 | | - return _common.DummyIO() |
| 128 | +class RunMixin: |
| 129 | + def run_command(self, *args, **kwargs): |
| 130 | + """Run a beets command with an arbitrary amount of arguments. The |
| 131 | + Library` defaults to `self.lib`, but can be overridden with |
| 132 | + the keyword argument `lib`. |
| 133 | + """ |
| 134 | + sys.argv = ["beet"] # avoid leakage from test suite args |
| 135 | + lib = None |
| 136 | + if hasattr(self, "lib"): |
| 137 | + lib = self.lib |
| 138 | + lib = kwargs.get("lib", lib) |
| 139 | + beets.ui._raw_main(list(args), lib) |
170 | 140 |
|
171 | | - def setUp(self): |
172 | | - super().setUp() |
173 | | - self.io.install() |
174 | 141 |
|
175 | | - def tearDown(self): |
176 | | - super().tearDown() |
177 | | - self.io.restore() |
| 142 | +@pytest.mark.usefixtures("io") |
| 143 | +class IOMixin(RunMixin): |
| 144 | + io: _common.DummyIO |
| 145 | + |
| 146 | + def run_with_output(self, *args): |
| 147 | + self.io.getoutput() |
| 148 | + self.run_command(*args) |
| 149 | + return self.io.getoutput() |
178 | 150 |
|
179 | 151 |
|
180 | | -class TestHelper(ConfigMixin): |
| 152 | +class TestHelper(RunMixin, ConfigMixin): |
181 | 153 | """Helper mixin for high-level cli and plugin tests. |
182 | 154 |
|
183 | 155 | This mixin provides methods to isolate beets' global state provide |
@@ -392,25 +364,6 @@ def create_mediafile_fixture(self, ext="mp3", images=[], target_dir=None): |
392 | 364 |
|
393 | 365 | return path |
394 | 366 |
|
395 | | - # Running beets commands |
396 | | - |
397 | | - def run_command(self, *args, **kwargs): |
398 | | - """Run a beets command with an arbitrary amount of arguments. The |
399 | | - Library` defaults to `self.lib`, but can be overridden with |
400 | | - the keyword argument `lib`. |
401 | | - """ |
402 | | - sys.argv = ["beet"] # avoid leakage from test suite args |
403 | | - lib = None |
404 | | - if hasattr(self, "lib"): |
405 | | - lib = self.lib |
406 | | - lib = kwargs.get("lib", lib) |
407 | | - beets.ui._raw_main(list(args), lib) |
408 | | - |
409 | | - def run_with_output(self, *args): |
410 | | - with capture_stdout() as out: |
411 | | - self.run_command(*args) |
412 | | - return out.getvalue() |
413 | | - |
414 | 367 | # Safe file operations |
415 | 368 |
|
416 | 369 | def create_temp_dir(self, **kwargs) -> str: |
@@ -758,10 +711,7 @@ def _add_choice_input(self): |
758 | 711 | class TerminalImportMixin(IOMixin, ImportHelper): |
759 | 712 | """Provides_a terminal importer for the import session.""" |
760 | 713 |
|
761 | | - io: _common.DummyIO |
762 | | - |
763 | 714 | def _get_import_session(self, import_dir: bytes) -> importer.ImportSession: |
764 | | - self.io.install() |
765 | 715 | return TerminalImportSessionFixture( |
766 | 716 | self.lib, |
767 | 717 | loghandler=None, |
|
0 commit comments